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();
390 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
392 CanvasTreeModel canvas_tree_model;
395 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
396 row[canvas_tree_model.id] = canvas->get_id();
397 row[canvas_tree_model.name] = canvas->get_name();
398 if(canvas->is_root())
399 row[canvas_tree_model.label] = basename(canvas->get_file_name());
401 if(!canvas->get_id().empty())
402 row[canvas_tree_model.label] = canvas->get_id();
404 if(!canvas->get_name().empty())
405 row[canvas_tree_model.label] = canvas->get_name();
407 row[canvas_tree_model.label] = _("[Unnamed]");
409 row[canvas_tree_model.canvas] = canvas;
410 row[canvas_tree_model.is_canvas] = true;
411 row[canvas_tree_model.is_value_node] = false;
414 synfig::Canvas::Children::iterator iter;
415 synfig::Canvas::Children &children(canvas->children());
417 for(iter=children.begin();iter!=children.end();iter++)
418 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
422 if(!canvas->value_node_list().empty())
424 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
426 valuenode_row[canvas_tree_model.label] = "<defs>";
427 valuenode_row[canvas_tree_model.canvas] = canvas;
428 valuenode_row[canvas_tree_model.is_canvas] = false;
429 valuenode_row[canvas_tree_model.is_value_node] = false;
431 synfig::ValueNodeList::iterator iter;
432 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
434 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
435 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
443 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
445 CanvasTreeModel canvas_tree_model;
449 row[canvas_tree_model.id] = value_node->get_id();
450 row[canvas_tree_model.name] = value_node->get_id();
451 row[canvas_tree_model.label] = value_node->get_id();
452 row[canvas_tree_model.canvas] = canvas;
453 row[canvas_tree_model.value_node] = value_node;
454 row[canvas_tree_model.is_canvas] = false;
455 row[canvas_tree_model.is_value_node] = true;
456 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
461 Instance::refresh_canvas_tree()
463 canvas_tree_store()->clear();
464 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
465 insert_canvas(row,get_canvas());
469 Instance::dialog_cvs_commit()
471 calc_repository_info();
474 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
481 if(synfigapp::Instance::get_action_count())
483 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
490 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
494 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
497 OneMoment one_moment;
502 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
508 Instance::dialog_cvs_add()
510 calc_repository_info();
513 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
520 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
522 OneMoment one_moment;
527 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
533 Instance::dialog_cvs_update()
535 calc_repository_info();
538 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
543 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
549 String filename(get_file_name());
550 if(synfigapp::Instance::get_action_count())
552 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
556 OneMoment one_moment;
557 time_t oldtime=get_original_timestamp();
559 calc_repository_info();
560 // If something has been updated...
561 if(oldtime!=get_original_timestamp())
568 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
570 //update_all_titles();
574 Instance::dialog_cvs_revert()
576 calc_repository_info();
579 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
584 String filename(get_file_name());
585 if(!App::dialog_yes_no(_("CVS Revert"),
586 _("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?")
590 OneMoment one_moment;
592 // Remove the old file
593 if(remove(get_file_name().c_str())!=0)
595 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
604 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
606 //update_all_titles();
610 Instance::_revert(Instance *instance)
612 OneMoment one_moment;
614 String filename(instance->get_file_name());
616 Canvas::Handle canvas(instance->get_canvas());
620 if(canvas->count()!=1)
623 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."));
634 // Schedule a revert to occur in a few moments
635 Glib::signal_timeout().connect(
638 sigc::ptr_fun(&Instance::_revert),
648 Instance::safe_revert()
650 if(synfigapp::Instance::get_action_count())
651 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
658 Instance::safe_close()
660 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
661 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
663 // if the animation is currently playing, closing the window will cause a crash,
665 if (canvas_view->is_playing())
667 canvas_view->present();
668 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
671 if(get_action_count())
674 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
675 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
676 if(answer==synfigapp::UIInterface::RESPONSE_YES)
678 enum Status status = save();
679 if (status == STATUS_OK) break;
680 else if (status == STATUS_CANCEL) return false;
682 if(answer==synfigapp::UIInterface::RESPONSE_NO)
684 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
690 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());
691 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
693 if(answer==synfigapp::UIInterface::RESPONSE_YES)
695 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
706 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
708 synfigapp::Action::CandidateList candidate_list;
709 synfigapp::Action::CandidateList::iterator iter;
711 candidate_list=compile_candidate_list(param_list,category);
713 candidate_list.sort();
715 // if(candidate_list.empty())
716 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
718 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
720 Gtk::StockID stock_id(get_action_stock_id(*iter));
722 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
724 action_group->add(Gtk::Action::create(
725 "action-"+iter->name,
727 iter->local_name,iter->local_name
732 *const_cast<studio::Instance*>(this),
733 &studio::Instance::process_action
740 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
746 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
748 synfigapp::Action::CandidateList candidate_list;
749 synfigapp::Action::CandidateList::iterator iter;
751 candidate_list=compile_candidate_list(param_list,category);
753 candidate_list.sort();
755 if(candidate_list.empty())
756 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
758 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
760 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
762 Gtk::Image* image(manage(new Gtk::Image()));
763 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
766 if(iter->task=="raise")
767 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
768 else if(iter->task=="lower")
769 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
770 else if(iter->task=="move_top")
771 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
772 else if(iter->task=="move_bottom")
773 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
774 else if(iter->task=="remove")
775 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="set_on")
777 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
778 else if(iter->task=="set_off")
779 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
780 else if(iter->task=="duplicate")
781 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
782 else if(iter->task=="remove")
783 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
786 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
787 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
788 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
791 menu->items().push_back(
792 Gtk::Menu_Helpers::ImageMenuElem(
798 *const_cast<studio::Instance*>(this),
799 &studio::Instance::process_action
812 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
814 synfigapp::Action::CandidateList candidate_list;
815 synfigapp::Action::CandidateList candidate_list2;
817 synfigapp::Action::CandidateList::iterator iter;
819 candidate_list=compile_candidate_list(param_list,category);
820 candidate_list2=compile_candidate_list(param_list2,category);
822 candidate_list.sort();
824 if(candidate_list.empty())
825 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
826 if(candidate_list2.empty())
827 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
829 // Separate out the candidate lists so that there are no conflicts
830 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
832 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
833 if(iter2!=candidate_list2.end())
834 candidate_list2.erase(iter2);
837 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
839 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
841 Gtk::Image* image(manage(new Gtk::Image()));
842 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
844 /* if(iter->task=="raise")
845 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
846 else if(iter->task=="lower")
847 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
848 else if(iter->task=="move_top")
849 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
850 else if(iter->task=="move_bottom")
851 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
852 else if(iter->task=="remove")
853 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="set_on")
855 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="set_off")
857 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="duplicate")
859 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="remove")
861 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
864 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
865 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
866 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
869 menu->items().push_back(
870 Gtk::Menu_Helpers::ImageMenuElem(
876 *const_cast<studio::Instance*>(this),
877 &studio::Instance::process_action
888 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
890 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
892 Gtk::Image* image(manage(new Gtk::Image()));
893 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
894 /* if(iter->task=="raise")
895 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
896 else if(iter->task=="lower")
897 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
898 else if(iter->task=="move_top")
899 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
900 else if(iter->task=="move_bottom")
901 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
902 else if(iter->task=="remove")
903 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="set_on")
905 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
906 else if(iter->task=="set_off")
907 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
908 else if(iter->task=="duplicate")
909 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
910 else if(iter->task=="remove")
911 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
914 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
915 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
916 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
919 menu->items().push_back(
920 Gtk::Menu_Helpers::ImageMenuElem(
926 *const_cast<studio::Instance*>(this),
927 &studio::Instance::process_action
940 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
942 assert(synfigapp::Action::book().count(name));
944 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
946 synfigapp::Action::Handle action(entry.factory());
950 synfig::error("Bad Action");
954 action->set_param_list(param_list);
956 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
957 synfigapp::Action::ParamVocab::const_iterator iter;
959 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
961 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
964 // If the parameter is optionally user-supplied,
965 // and has not been already provided in the param_list,
966 // then we should go ahead and see if we can
967 // provide that data.
968 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
970 switch(iter->get_type())
972 case synfigapp::Action::Param::TYPE_STRING:
975 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
977 action->set_param(iter->get_name(),str);
981 synfig::error("Unsupported user-supplied action parameter");
988 if(!action->is_ready())
990 synfig::error("Action not ready");
994 perform_action(action);
998 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
1000 Gtk::Menu& parammenu(*menu);
1002 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1004 if(!canvas_interface)
1007 synfigapp::Action::ParamList param_list,param_list2;
1008 param_list=canvas_interface->generate_param_list(value_desc);
1009 param_list.add("origin",location);
1011 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1013 param_list2=canvas_interface->generate_param_list(
1014 synfigapp::ValueDesc(
1015 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1019 param_list2.add("origin",location);
1023 // Populate the convert menu by looping through
1024 // the ValueNode book and find the ones that are
1027 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1028 // the 'Index' parameter of a Duplicate layer
1030 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1031 if (!((value_desc.parent_is_layer_param() &&
1032 value_desc.get_layer()->get_name() == "duplicate" &&
1033 value_desc.get_param_name() == "index") ||
1034 (value_desc.is_value_node() &&
1035 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1036 !(value_desc.parent_is_layer_param() ||
1037 value_desc.parent_is_value_node()))))
1039 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1040 LinkableValueNode::Book::const_iterator iter;
1041 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1043 if(iter->second.check_type(value_desc.get_value_type()))
1045 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1049 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1059 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1062 if(param_list2.empty())
1063 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1065 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1067 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1069 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1072 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1074 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1078 // try to find a waypoint at the current time - if we
1079 // can't, we don't want the menu entry - an exception is thrown
1080 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1081 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1082 waypoint_set.insert(*iter);
1084 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1089 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1090 synfig::Waypoint::SIDE_UNSPECIFIED
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);
1123 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1124 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1127 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1129 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1131 std::list<synfigapp::ValueDesc>::iterator iter;
1132 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1134 synfigapp::ValueDesc value_desc(*iter);
1136 if(!value_desc.is_valid())
1139 ValueNode_Animated::Handle value_node;
1141 // If this value isn't a ValueNode_Animated, but
1142 // it is somewhat constant, then go ahead and convert
1143 // it to a ValueNode_Animated.
1144 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1147 if(value_desc.is_value_node())
1148 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1150 value=value_desc.get_value();
1152 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1154 synfigapp::Action::Handle action;
1156 if(!value_desc.is_value_node())
1158 action=synfigapp::Action::create("value_desc_connect");
1159 action->set_param("dest",value_desc);
1160 action->set_param("src",ValueNode::Handle(value_node));
1164 action=synfigapp::Action::create("value_node_replace");
1165 action->set_param("dest",value_desc.get_value_node());
1166 action->set_param("src",ValueNode::Handle(value_node));
1169 action->set_param("canvas",canvas_view->get_canvas());
1170 action->set_param("canvas_interface",canvas_interface);
1173 if(!canvas_interface->get_instance()->perform_action(action))
1175 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1182 if(value_desc.is_value_node())
1183 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1190 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1194 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1200 action->set_param("canvas",canvas_view->get_canvas());
1201 action->set_param("canvas_interface",canvas_interface);
1202 action->set_param("value_node",ValueNode::Handle(value_node));
1203 action->set_param("time",canvas_interface->get_time());
1204 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1206 if(!canvas_interface->get_instance()->perform_action(action))
1208 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1215 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1224 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1226 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1228 synfigapp::Action::ParamList param_list;
1229 param_list=canvas_interface->generate_param_list(value_desc_list);
1231 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1233 // Add the edit waypoints option if that might be useful
1234 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1236 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1240 &edit_several_waypoints
1244 find_canvas_view(canvas)