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 // the filename will be set to "Synfig Animation 1" or some such when first created
218 // and will be changed to an absolute path once it has been saved
219 // so if it still begins with "Synfig Animation " then we need to ask where to save it
220 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
222 if (dialog_save_as())
225 return STATUS_CANCEL;
228 if (synfigapp::Instance::save())
231 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
236 studio::Instance::dialog_save_as()
238 string filename = get_file_name();
239 Canvas::Handle canvas(get_canvas());
242 OneMoment one_moment;
243 std::set<Node*>::iterator iter;
244 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
246 synfig::Node* node(*iter);
247 for(;!node->parent_set.empty();node=*node->parent_set.begin())
249 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
250 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
252 App::dialog_error_blocking("SaveAs - Error",
253 "There is currently a bug when using \"SaveAs\"\n"
254 "on a composition that is being referenced by other\n"
255 "files that are currently open. Close these\n"
256 "other files first before trying to use \"SaveAs\"."
267 if (get_file_name().find(DEFAULT_FILENAME_PREFIX) != 0)
268 filename = absolute_path(filename);
270 // show the canvas' name if it has one, else its ID
271 while (App::dialog_save_file((_("Choose a Filename to Save As") +
273 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
275 filename, ANIMATION_DIR_PREFERENCE))
277 // If the filename still has wildcards, then we should
278 // continue looking for the file we want
279 string base_filename = basename(filename);
280 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
283 if (filename_extension(filename) == "")
288 String ext(filename_extension(filename));
289 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
290 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
300 int stat_return = stat(filename.c_str(), &s);
302 // if stat() fails with something other than 'file doesn't exist', there's been a real
303 // error of some kind. let's give up now and ask for a new path.
304 if (stat_return == -1 && errno != ENOENT)
306 perror(filename.c_str());
307 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
311 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
312 if ((stat_return == 0) &&
313 !App::dialog_yes_no("File exists",
316 "' already exists.\n\n"
317 "Do you want to replace it with the file you are saving?"))
321 if(save_as(filename))
323 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
327 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
334 Instance::update_all_titles()
336 list<handle<CanvasView> >::iterator iter;
337 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
338 (*iter)->update_title();
344 // This will increase the reference count so we don't get DELETED
345 // until we are ready
346 handle<Instance> me(this);
348 // Make sure we aren't selected as the current instance
349 if(studio::App::get_selected_instance()==this)
350 studio::App::set_selected_instance(0);
352 // Turn-off/clean-up auto recovery
353 studio::App::auto_recover->clear_backup(get_canvas());
355 // Remove us from the active instance list
356 std::list<etl::handle<studio::Instance> >::iterator iter;
357 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
360 assert(iter!=studio::App::instance_list.end());
361 if(iter!=studio::App::instance_list.end())
362 studio::App::instance_list.erase(iter);
364 // Send out a signal that we are being deleted
365 studio::App::signal_instance_deleted()(this);
367 // Hide all of the canvas views
368 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
371 // Consume pending events before deleting the canvas views
372 while(studio::App::events_pending())studio::App::iteration(false);
374 // Delete all of the canvas views
375 canvas_view_list().clear();
377 // If there is another open instance to select,
378 // go ahead and do so. If not, never mind.
379 if(studio::App::instance_list.empty())
381 studio::App::set_selected_canvas_view(0);
382 studio::App::set_selected_instance(0);
385 studio::App::instance_list.front()->canvas_view_list().front()->present();
389 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
391 CanvasTreeModel canvas_tree_model;
394 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
395 row[canvas_tree_model.id] = canvas->get_id();
396 row[canvas_tree_model.name] = canvas->get_name();
397 if(canvas->is_root())
398 row[canvas_tree_model.label] = basename(canvas->get_file_name());
400 if(!canvas->get_id().empty())
401 row[canvas_tree_model.label] = canvas->get_id();
403 if(!canvas->get_name().empty())
404 row[canvas_tree_model.label] = canvas->get_name();
406 row[canvas_tree_model.label] = _("[Unnamed]");
408 row[canvas_tree_model.canvas] = canvas;
409 row[canvas_tree_model.is_canvas] = true;
410 row[canvas_tree_model.is_value_node] = false;
413 synfig::Canvas::Children::iterator iter;
414 synfig::Canvas::Children &children(canvas->children());
416 for(iter=children.begin();iter!=children.end();iter++)
417 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
421 if(!canvas->value_node_list().empty())
423 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
425 valuenode_row[canvas_tree_model.label] = "<defs>";
426 valuenode_row[canvas_tree_model.canvas] = canvas;
427 valuenode_row[canvas_tree_model.is_canvas] = false;
428 valuenode_row[canvas_tree_model.is_value_node] = false;
430 synfig::ValueNodeList::iterator iter;
431 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
433 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
434 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
441 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
443 CanvasTreeModel canvas_tree_model;
447 row[canvas_tree_model.id] = value_node->get_id();
448 row[canvas_tree_model.name] = value_node->get_id();
449 row[canvas_tree_model.label] = value_node->get_id();
450 row[canvas_tree_model.canvas] = canvas;
451 row[canvas_tree_model.value_node] = value_node;
452 row[canvas_tree_model.is_canvas] = false;
453 row[canvas_tree_model.is_value_node] = true;
454 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
459 Instance::refresh_canvas_tree()
461 canvas_tree_store()->clear();
462 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
463 insert_canvas(row,get_canvas());
467 Instance::dialog_cvs_commit()
469 calc_repository_info();
472 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
479 if(synfigapp::Instance::get_action_count())
481 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
488 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
492 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
495 OneMoment one_moment;
500 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
506 Instance::dialog_cvs_add()
508 calc_repository_info();
511 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
518 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
520 OneMoment one_moment;
525 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
531 Instance::dialog_cvs_update()
533 calc_repository_info();
536 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
541 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
547 String filename(get_file_name());
548 if(synfigapp::Instance::get_action_count())
550 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
554 OneMoment one_moment;
555 time_t oldtime=get_original_timestamp();
557 calc_repository_info();
558 // If something has been updated...
559 if(oldtime!=get_original_timestamp())
566 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
568 //update_all_titles();
572 Instance::dialog_cvs_revert()
574 calc_repository_info();
577 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
582 String filename(get_file_name());
583 if(!App::dialog_yes_no(_("CVS Revert"),
584 _("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?")
588 OneMoment one_moment;
590 // Remove the old file
591 if(remove(get_file_name().c_str())!=0)
593 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
602 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
604 //update_all_titles();
608 Instance::_revert(Instance *instance)
610 OneMoment one_moment;
612 String filename(instance->get_file_name());
614 Canvas::Handle canvas(instance->get_canvas());
618 if(canvas->count()!=1)
621 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."));
632 // Schedule a revert to occur in a few moments
633 Glib::signal_timeout().connect(
636 sigc::ptr_fun(&Instance::_revert),
646 Instance::safe_revert()
648 if(synfigapp::Instance::get_action_count())
649 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
656 Instance::safe_close()
658 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
659 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
661 // if the animation is currently playing, closing the window will cause a crash,
663 if (canvas_view->is_playing())
665 canvas_view->present();
666 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
669 if(get_action_count())
672 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
673 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
674 if(answer==synfigapp::UIInterface::RESPONSE_YES)
676 enum Status status = save();
677 if (status == STATUS_OK) break;
678 else if (status == STATUS_CANCEL) return false;
680 if(answer==synfigapp::UIInterface::RESPONSE_NO)
682 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
688 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());
689 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
691 if(answer==synfigapp::UIInterface::RESPONSE_YES)
693 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
703 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
705 synfigapp::Action::CandidateList candidate_list;
706 synfigapp::Action::CandidateList::iterator iter;
708 candidate_list=compile_candidate_list(param_list,category);
710 candidate_list.sort();
712 // if(candidate_list.empty())
713 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
715 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
717 Gtk::StockID stock_id(get_action_stock_id(*iter));
719 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
721 action_group->add(Gtk::Action::create(
722 "action-"+iter->name,
724 iter->local_name,iter->local_name
729 *const_cast<studio::Instance*>(this),
730 &studio::Instance::process_action
737 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
743 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
745 synfigapp::Action::CandidateList candidate_list;
746 synfigapp::Action::CandidateList::iterator iter;
748 candidate_list=compile_candidate_list(param_list,category);
750 candidate_list.sort();
752 if(candidate_list.empty())
753 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
755 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
757 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
759 Gtk::Image* image(manage(new Gtk::Image()));
760 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
763 if(iter->task=="raise")
764 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
765 else if(iter->task=="lower")
766 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
767 else if(iter->task=="move_top")
768 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
769 else if(iter->task=="move_bottom")
770 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
771 else if(iter->task=="remove")
772 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
773 else if(iter->task=="set_on")
774 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
775 else if(iter->task=="set_off")
776 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
777 else if(iter->task=="duplicate")
778 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
779 else if(iter->task=="remove")
780 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
783 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
784 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
785 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
788 menu->items().push_back(
789 Gtk::Menu_Helpers::ImageMenuElem(
795 *const_cast<studio::Instance*>(this),
796 &studio::Instance::process_action
809 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
811 synfigapp::Action::CandidateList candidate_list;
812 synfigapp::Action::CandidateList candidate_list2;
814 synfigapp::Action::CandidateList::iterator iter;
816 candidate_list=compile_candidate_list(param_list,category);
817 candidate_list2=compile_candidate_list(param_list2,category);
819 candidate_list.sort();
821 if(candidate_list.empty())
822 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
823 if(candidate_list2.empty())
824 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
826 // Separate out the candidate lists so that there are no conflicts
827 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
829 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
830 if(iter2!=candidate_list2.end())
831 candidate_list2.erase(iter2);
834 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
836 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
838 Gtk::Image* image(manage(new Gtk::Image()));
839 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
841 /* if(iter->task=="raise")
842 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
843 else if(iter->task=="lower")
844 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
845 else if(iter->task=="move_top")
846 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
847 else if(iter->task=="move_bottom")
848 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
849 else if(iter->task=="remove")
850 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
851 else if(iter->task=="set_on")
852 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
853 else if(iter->task=="set_off")
854 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
855 else if(iter->task=="duplicate")
856 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
857 else if(iter->task=="remove")
858 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
861 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
862 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
863 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
866 menu->items().push_back(
867 Gtk::Menu_Helpers::ImageMenuElem(
873 *const_cast<studio::Instance*>(this),
874 &studio::Instance::process_action
885 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
887 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
889 Gtk::Image* image(manage(new Gtk::Image()));
890 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
891 /* if(iter->task=="raise")
892 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
893 else if(iter->task=="lower")
894 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
895 else if(iter->task=="move_top")
896 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
897 else if(iter->task=="move_bottom")
898 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
899 else if(iter->task=="remove")
900 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
901 else if(iter->task=="set_on")
902 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
903 else if(iter->task=="set_off")
904 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
905 else if(iter->task=="duplicate")
906 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
907 else if(iter->task=="remove")
908 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
911 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
912 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
913 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
916 menu->items().push_back(
917 Gtk::Menu_Helpers::ImageMenuElem(
923 *const_cast<studio::Instance*>(this),
924 &studio::Instance::process_action
937 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
939 assert(synfigapp::Action::book().count(name));
941 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
943 synfigapp::Action::Handle action(entry.factory());
947 synfig::error("Bad Action");
951 action->set_param_list(param_list);
953 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
954 synfigapp::Action::ParamVocab::const_iterator iter;
956 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
958 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
961 // If the parameter is optionally user-supplied,
962 // and has not been already provided in the param_list,
963 // then we should go ahead and see if we can
964 // provide that data.
965 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
967 switch(iter->get_type())
969 case synfigapp::Action::Param::TYPE_STRING:
972 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
974 action->set_param(iter->get_name(),str);
978 synfig::error("Unsupported user-supplied action parameter");
985 if(!action->is_ready())
987 synfig::error("Action not ready");
991 perform_action(action);
995 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
997 Gtk::Menu& parammenu(*menu);
999 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1001 if(!canvas_interface)
1004 synfigapp::Action::ParamList param_list,param_list2;
1005 param_list=canvas_interface->generate_param_list(value_desc);
1006 param_list.add("origin",location);
1008 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1010 param_list2=canvas_interface->generate_param_list(
1011 synfigapp::ValueDesc(
1012 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1016 param_list2.add("origin",location);
1019 // Populate the convert menu by looping through
1020 // the ValueNode book and find the ones that are
1023 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1024 // the 'Index' parameter of a Duplicate layer
1026 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1027 if (!((value_desc.parent_is_layer_param() &&
1028 value_desc.get_layer()->get_name() == "duplicate" &&
1029 value_desc.get_param_name() == "index") ||
1030 (value_desc.is_value_node() &&
1031 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1032 !(value_desc.parent_is_layer_param() ||
1033 value_desc.parent_is_value_node()))))
1035 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1036 LinkableValueNode::Book::const_iterator iter;
1037 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1039 if(iter->second.check_type(value_desc.get_value_type()))
1041 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1045 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1055 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1058 if(param_list2.empty())
1059 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1061 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1063 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1065 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1068 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1070 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1074 // try to find a waypoint at the current time - if we
1075 // can't, we don't want the menu entry - an exception is thrown
1076 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1077 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1078 waypoint_set.insert(*iter);
1080 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1084 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1100 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1102 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1105 "Edit Multiple Waypoints", // Title
1107 true // use_separator
1110 Widget_WaypointModel widget_waypoint_model;
1111 widget_waypoint_model.show();
1113 dialog.get_vbox()->pack_start(widget_waypoint_model);
1115 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1116 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1119 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1121 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1123 std::list<synfigapp::ValueDesc>::iterator iter;
1124 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1126 synfigapp::ValueDesc value_desc(*iter);
1128 if(!value_desc.is_valid())
1131 ValueNode_Animated::Handle value_node;
1133 // If this value isn't a ValueNode_Animated, but
1134 // it is somewhat constant, then go ahead and convert
1135 // it to a ValueNode_Animated.
1136 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1139 if(value_desc.is_value_node())
1140 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1142 value=value_desc.get_value();
1144 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1146 synfigapp::Action::Handle action;
1148 if(!value_desc.is_value_node())
1150 action=synfigapp::Action::create("value_desc_connect");
1151 action->set_param("dest",value_desc);
1152 action->set_param("src",ValueNode::Handle(value_node));
1156 action=synfigapp::Action::create("value_node_replace");
1157 action->set_param("dest",value_desc.get_value_node());
1158 action->set_param("src",ValueNode::Handle(value_node));
1161 action->set_param("canvas",canvas_view->get_canvas());
1162 action->set_param("canvas_interface",canvas_interface);
1164 if(!canvas_interface->get_instance()->perform_action(action))
1166 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1173 if(value_desc.is_value_node())
1174 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1179 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1183 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1188 action->set_param("canvas",canvas_view->get_canvas());
1189 action->set_param("canvas_interface",canvas_interface);
1190 action->set_param("value_node",ValueNode::Handle(value_node));
1191 action->set_param("time",canvas_interface->get_time());
1192 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1194 if(!canvas_interface->get_instance()->perform_action(action))
1196 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1203 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1212 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1214 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1216 synfigapp::Action::ParamList param_list;
1217 param_list=canvas_interface->generate_param_list(value_desc_list);
1219 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1221 // Add the edit waypoints option if that might be useful
1222 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1224 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1228 &edit_several_waypoints
1232 find_canvas_view(canvas)