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 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"
46 #include "autorecover.h"
47 #include <sigc++/retype_return.h>
48 #include <sigc++/retype.h>
49 //#include <sigc++/hide.h>
50 #include <synfig/valuenode_composite.h>
51 #include <synfig/valuenode_duplicate.h>
52 #include "widget_waypointmodel.h"
53 #include <gtkmm/actiongroup.h>
54 #include "iconcontroller.h"
57 #include <ETL/stringf>
65 using namespace synfig;
66 using namespace studio;
69 /* === M A C R O S ========================================================= */
71 /* === G L O B A L S ======================================================= */
73 int studio::Instance::instance_count_=0;
75 /* === P R O C E D U R E S ================================================= */
77 /* === M E T H O D S ======================================================= */
79 Instance::Instance(synfig::Canvas::Handle canvas):
80 synfigapp::Instance (canvas),
81 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
82 // canvas_tree_store_ (Gtk::TreeStore::create()),
83 history_tree_store_ (HistoryTreeStore::create(this)),
87 CanvasTreeModel model;
88 canvas_tree_store_=Gtk::TreeStore::create(model);
90 id_=instance_count_++;
92 // Connect up all the signals
93 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
94 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
95 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
96 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
98 signal_saved().connect(
101 studio::AutoRecover::auto_backup
106 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
108 refresh_canvas_tree();
111 Instance::~Instance()
116 Instance::get_visible_canvases()const
119 CanvasViewList::const_iterator iter;
120 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
121 if((*iter)->is_visible())
127 Instance::create(synfig::Canvas::Handle canvas)
129 // Construct a new instance
130 handle<Instance> instance(new Instance(canvas));
132 // Add the new instance to the application's instance list
133 App::instance_list.push_back(instance);
135 // Set up the instance with the default UI manager
136 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
138 // Signal the new instance
139 App::signal_instance_created()(instance);
141 // And then make sure that is has been selected
142 App::set_selected_instance(instance);
144 // Create the initial window for the root canvas
145 instance->focus(canvas);
151 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
156 while(canvas->is_inline())
157 canvas=canvas->parent();
159 CanvasViewList::iterator iter;
161 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
162 if((*iter)->get_canvas()==canvas)
165 return CanvasView::create(this,canvas);
169 Instance::focus(etl::handle<synfig::Canvas> canvas)
171 handle<CanvasView> canvas_view=find_canvas_view(canvas);
173 canvas_view->present();
177 Instance::set_undo_status(bool x)
180 App::toolbox->update_undo_redo();
181 signal_undo_redo_status_changed()();
185 Instance::set_redo_status(bool x)
188 App::toolbox->update_undo_redo();
189 signal_undo_redo_status_changed()();
193 studio::Instance::save_as(const synfig::String &file_name)
195 if(synfigapp::Instance::save_as(file_name))
197 // after changing the filename, update the render settings with the new filename
198 list<handle<CanvasView> >::iterator iter;
199 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
200 (*iter)->render_settings.set_entry_filename();
201 App::add_recent_file(file_name);
208 studio::Instance::save()
210 // the filename will be set to "Synfig Animation 1" or some such when first created
211 // and will be changed to an absolute path once it has been saved
212 // so if it still begins with "Synfig Animation " then we need to ask where to save it
213 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
215 if (dialog_save_as())
218 return STATUS_CANCEL;
221 if (synfigapp::Instance::save())
224 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
229 studio::Instance::dialog_save_as()
231 string filename = get_file_name();
232 Canvas::Handle canvas(get_canvas());
235 OneMoment one_moment;
236 std::set<Node*>::iterator iter;
237 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
239 synfig::Node* node(*iter);
240 for(;!node->parent_set.empty();node=*node->parent_set.begin())
242 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
243 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
245 App::dialog_error_blocking("SaveAs - Error",
246 "There is currently a bug when using \"SaveAs\"\n"
247 "on a composition that is being referenced by other\n"
248 "files that are currently open. Close these\n"
249 "other files first before trying to use \"SaveAs\"."
260 if (get_file_name().find(DEFAULT_FILENAME_PREFIX) != 0)
261 filename = absolute_path(filename);
263 // show the canvas' name if it has one, else its ID
264 while (App::dialog_save_file((_("Choose a Filename to Save As") +
266 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
268 filename, ANIMATION_DIR_PREFERENCE))
270 // If the filename still has wildcards, then we should
271 // continue looking for the file we want
272 string base_filename = basename(filename);
273 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
276 if (filename_extension(filename) == "")
281 String ext(filename_extension(filename));
282 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
283 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
293 int stat_return = stat(filename.c_str(), &s);
295 // if stat() fails with something other than 'file doesn't exist', there's been a real
296 // error of some kind. let's give up now and ask for a new path.
297 if (stat_return == -1 && errno != ENOENT)
299 perror(filename.c_str());
300 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
304 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
305 if ((stat_return == 0) &&
306 !App::dialog_yes_no("File exists",
309 "' already exists.\n\n"
310 "Do you want to replace it with the file you are saving?"))
314 if(save_as(filename))
317 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
324 Instance::update_all_titles()
326 list<handle<CanvasView> >::iterator iter;
327 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
328 (*iter)->update_title();
334 // This will increase the reference count so we don't get DELETED
335 // until we are ready
336 handle<Instance> me(this);
338 // Make sure we aren't selected as the current instance
339 if(studio::App::get_selected_instance()==this)
340 studio::App::set_selected_instance(0);
342 // Turn-off/clean-up auto recovery
343 studio::App::auto_recover->clear_backup(get_canvas());
345 // Remove us from the active instance list
346 std::list<etl::handle<studio::Instance> >::iterator iter;
347 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
350 assert(iter!=studio::App::instance_list.end());
351 if(iter!=studio::App::instance_list.end())
352 studio::App::instance_list.erase(iter);
354 // Send out a signal that we are being deleted
355 studio::App::signal_instance_deleted()(this);
357 // Hide all of the canvas views
358 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
361 // Consume pending events before deleting the canvas views
362 while(studio::App::events_pending())studio::App::iteration(false);
364 // Delete all of the canvas views
365 canvas_view_list().clear();
367 // If there is another open instance to select,
368 // go ahead and do so. If not, never mind.
369 if(studio::App::instance_list.empty())
371 studio::App::set_selected_canvas_view(0);
372 studio::App::set_selected_instance(0);
375 studio::App::instance_list.front()->canvas_view_list().front()->present();
380 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
382 CanvasTreeModel canvas_tree_model;
385 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
386 row[canvas_tree_model.id] = canvas->get_id();
387 row[canvas_tree_model.name] = canvas->get_name();
388 if(canvas->is_root())
389 row[canvas_tree_model.label] = basename(canvas->get_file_name());
391 if(!canvas->get_id().empty())
392 row[canvas_tree_model.label] = canvas->get_id();
394 if(!canvas->get_name().empty())
395 row[canvas_tree_model.label] = canvas->get_name();
397 row[canvas_tree_model.label] = _("[Unnamed]");
399 row[canvas_tree_model.canvas] = canvas;
400 row[canvas_tree_model.is_canvas] = true;
401 row[canvas_tree_model.is_value_node] = false;
404 synfig::Canvas::Children::iterator iter;
405 synfig::Canvas::Children &children(canvas->children());
407 for(iter=children.begin();iter!=children.end();iter++)
408 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
412 if(!canvas->value_node_list().empty())
414 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
416 valuenode_row[canvas_tree_model.label] = "<defs>";
417 valuenode_row[canvas_tree_model.canvas] = canvas;
418 valuenode_row[canvas_tree_model.is_canvas] = false;
419 valuenode_row[canvas_tree_model.is_value_node] = false;
421 synfig::ValueNodeList::iterator iter;
422 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
424 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
425 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
433 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
435 CanvasTreeModel canvas_tree_model;
439 row[canvas_tree_model.id] = value_node->get_id();
440 row[canvas_tree_model.name] = value_node->get_id();
441 row[canvas_tree_model.label] = value_node->get_id();
442 row[canvas_tree_model.canvas] = canvas;
443 row[canvas_tree_model.value_node] = value_node;
444 row[canvas_tree_model.is_canvas] = false;
445 row[canvas_tree_model.is_value_node] = true;
446 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
451 Instance::refresh_canvas_tree()
453 canvas_tree_store()->clear();
454 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
455 insert_canvas(row,get_canvas());
459 Instance::dialog_cvs_commit()
461 calc_repository_info();
464 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
471 if(synfigapp::Instance::get_action_count())
473 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
480 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
484 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
487 OneMoment one_moment;
492 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
498 Instance::dialog_cvs_add()
500 calc_repository_info();
503 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
510 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
512 OneMoment one_moment;
517 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
523 Instance::dialog_cvs_update()
525 calc_repository_info();
528 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
533 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
539 String filename(get_file_name());
540 if(synfigapp::Instance::get_action_count())
542 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
546 OneMoment one_moment;
547 time_t oldtime=get_original_timestamp();
549 calc_repository_info();
550 // If something has been updated...
551 if(oldtime!=get_original_timestamp())
558 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
560 //update_all_titles();
564 Instance::dialog_cvs_revert()
566 calc_repository_info();
569 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
574 String filename(get_file_name());
575 if(!App::dialog_yes_no(_("CVS Revert"),
576 _("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?")
580 OneMoment one_moment;
582 // Remove the old file
583 if(remove(get_file_name().c_str())!=0)
585 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
594 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
596 //update_all_titles();
600 Instance::_revert(Instance *instance)
602 OneMoment one_moment;
604 String filename(instance->get_file_name());
606 Canvas::Handle canvas(instance->get_canvas());
610 if(canvas->count()!=1)
613 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."));
624 // Schedule a revert to occur in a few moments
625 Glib::signal_timeout().connect(
628 sigc::ptr_fun(&Instance::_revert),
638 Instance::safe_revert()
640 if(synfigapp::Instance::get_action_count())
641 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
648 Instance::safe_close()
650 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
651 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
653 // if the animation is currently playing, closing the window will cause a crash,
655 if (canvas_view->is_playing())
657 canvas_view->present();
658 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
661 if(get_action_count())
664 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
665 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
666 if(answer==synfigapp::UIInterface::RESPONSE_YES)
668 enum Status status = save();
669 if (status == STATUS_OK) break;
670 else if (status == STATUS_CANCEL) return false;
672 if(answer==synfigapp::UIInterface::RESPONSE_NO)
674 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
680 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());
681 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
683 if(answer==synfigapp::UIInterface::RESPONSE_YES)
685 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
696 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
698 synfigapp::Action::CandidateList candidate_list;
699 synfigapp::Action::CandidateList::iterator iter;
701 candidate_list=compile_candidate_list(param_list,category);
703 candidate_list.sort();
705 // if(candidate_list.empty())
706 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
708 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
710 Gtk::StockID stock_id(get_action_stock_id(*iter));
712 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
714 action_group->add(Gtk::Action::create(
715 "action-"+iter->name,
717 iter->local_name,iter->local_name
722 *const_cast<studio::Instance*>(this),
723 &studio::Instance::process_action
730 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
736 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
738 synfigapp::Action::CandidateList candidate_list;
739 synfigapp::Action::CandidateList::iterator iter;
741 candidate_list=compile_candidate_list(param_list,category);
743 candidate_list.sort();
745 if(candidate_list.empty())
746 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
748 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
750 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
752 Gtk::Image* image(manage(new Gtk::Image()));
753 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
756 if(iter->task=="raise")
757 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
758 else if(iter->task=="lower")
759 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
760 else if(iter->task=="move_top")
761 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
762 else if(iter->task=="move_bottom")
763 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
764 else if(iter->task=="remove")
765 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
766 else if(iter->task=="set_on")
767 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
768 else if(iter->task=="set_off")
769 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
770 else if(iter->task=="duplicate")
771 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
772 else if(iter->task=="remove")
773 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
776 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
777 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
778 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
781 menu->items().push_back(
782 Gtk::Menu_Helpers::ImageMenuElem(
788 *const_cast<studio::Instance*>(this),
789 &studio::Instance::process_action
802 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
804 synfigapp::Action::CandidateList candidate_list;
805 synfigapp::Action::CandidateList candidate_list2;
807 synfigapp::Action::CandidateList::iterator iter;
809 candidate_list=compile_candidate_list(param_list,category);
810 candidate_list2=compile_candidate_list(param_list2,category);
812 candidate_list.sort();
814 if(candidate_list.empty())
815 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
816 if(candidate_list2.empty())
817 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
819 // Separate out the candidate lists so that there are no conflicts
820 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
822 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
823 if(iter2!=candidate_list2.end())
824 candidate_list2.erase(iter2);
827 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
829 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
831 Gtk::Image* image(manage(new Gtk::Image()));
832 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
834 /* if(iter->task=="raise")
835 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
836 else if(iter->task=="lower")
837 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
838 else if(iter->task=="move_top")
839 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
840 else if(iter->task=="move_bottom")
841 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
842 else if(iter->task=="remove")
843 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
844 else if(iter->task=="set_on")
845 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
846 else if(iter->task=="set_off")
847 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
848 else if(iter->task=="duplicate")
849 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
850 else if(iter->task=="remove")
851 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
854 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
855 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
856 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
859 menu->items().push_back(
860 Gtk::Menu_Helpers::ImageMenuElem(
866 *const_cast<studio::Instance*>(this),
867 &studio::Instance::process_action
878 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
880 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
882 Gtk::Image* image(manage(new Gtk::Image()));
883 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
884 /* if(iter->task=="raise")
885 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
886 else if(iter->task=="lower")
887 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
888 else if(iter->task=="move_top")
889 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
890 else if(iter->task=="move_bottom")
891 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
892 else if(iter->task=="remove")
893 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
894 else if(iter->task=="set_on")
895 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
896 else if(iter->task=="set_off")
897 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
898 else if(iter->task=="duplicate")
899 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
900 else if(iter->task=="remove")
901 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
904 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
905 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
906 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
909 menu->items().push_back(
910 Gtk::Menu_Helpers::ImageMenuElem(
916 *const_cast<studio::Instance*>(this),
917 &studio::Instance::process_action
930 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
932 assert(synfigapp::Action::book().count(name));
934 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
936 synfigapp::Action::Handle action(entry.factory());
940 synfig::error("Bad Action");
944 action->set_param_list(param_list);
946 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
947 synfigapp::Action::ParamVocab::const_iterator iter;
949 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
951 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
954 // If the parameter is optionally user-supplied,
955 // and has not been already provided in the param_list,
956 // then we should go ahead and see if we can
957 // provide that data.
958 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
960 switch(iter->get_type())
962 case synfigapp::Action::Param::TYPE_STRING:
965 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
967 action->set_param(iter->get_name(),str);
971 synfig::error("Unsupported user-supplied action parameter");
978 if(!action->is_ready())
980 synfig::error("Action not ready");
984 perform_action(action);
988 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
990 Gtk::Menu& parammenu(*menu);
992 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
994 if(!canvas_interface)
997 synfigapp::Action::ParamList param_list,param_list2;
998 param_list=canvas_interface->generate_param_list(value_desc);
999 param_list.add("origin",location);
1001 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1003 param_list2=canvas_interface->generate_param_list(
1004 synfigapp::ValueDesc(
1005 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1009 param_list2.add("origin",location);
1013 // Populate the convert menu by looping through
1014 // the ValueNode book and find the ones that are
1017 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1018 // the 'Index' parameter of a Duplicate layer
1020 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1021 if (!((value_desc.parent_is_layer_param() &&
1022 value_desc.get_layer()->get_name() == "duplicate" &&
1023 value_desc.get_param_name() == "index") ||
1024 (value_desc.is_value_node() &&
1025 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1026 !(value_desc.parent_is_layer_param() ||
1027 value_desc.parent_is_value_node()))))
1029 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1030 LinkableValueNode::Book::const_iterator iter;
1031 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1033 if(iter->second.check_type(value_desc.get_value_type()))
1035 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1039 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1049 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1052 if(param_list2.empty())
1053 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1055 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1057 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1059 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1062 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1064 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1068 // try to find a waypoint at the current time - if we
1069 // can't, we don't want the menu entry - an exception is thrown
1070 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1071 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1072 waypoint_set.insert(*iter);
1074 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1079 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1080 synfig::Waypoint::SIDE_UNSPECIFIED
1097 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1099 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1102 "Edit Multiple Waypoints", // Title
1104 true // use_separator
1107 Widget_WaypointModel widget_waypoint_model;
1108 widget_waypoint_model.show();
1110 dialog.get_vbox()->pack_start(widget_waypoint_model);
1113 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1114 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1117 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1119 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1121 std::list<synfigapp::ValueDesc>::iterator iter;
1122 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1124 synfigapp::ValueDesc value_desc(*iter);
1126 if(!value_desc.is_valid())
1129 ValueNode_Animated::Handle value_node;
1131 // If this value isn't a ValueNode_Animated, but
1132 // it is somewhat constant, then go ahead and convert
1133 // it to a ValueNode_Animated.
1134 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1137 if(value_desc.is_value_node())
1138 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1140 value=value_desc.get_value();
1142 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1144 synfigapp::Action::Handle action;
1146 if(!value_desc.is_value_node())
1148 action=synfigapp::Action::create("value_desc_connect");
1149 action->set_param("dest",value_desc);
1150 action->set_param("src",ValueNode::Handle(value_node));
1154 action=synfigapp::Action::create("value_node_replace");
1155 action->set_param("dest",value_desc.get_value_node());
1156 action->set_param("src",ValueNode::Handle(value_node));
1159 action->set_param("canvas",canvas_view->get_canvas());
1160 action->set_param("canvas_interface",canvas_interface);
1163 if(!canvas_interface->get_instance()->perform_action(action))
1165 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1172 if(value_desc.is_value_node())
1173 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1180 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1184 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1190 action->set_param("canvas",canvas_view->get_canvas());
1191 action->set_param("canvas_interface",canvas_interface);
1192 action->set_param("value_node",ValueNode::Handle(value_node));
1193 action->set_param("time",canvas_interface->get_time());
1194 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1196 if(!canvas_interface->get_instance()->perform_action(action))
1198 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1205 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1214 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1216 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1218 synfigapp::Action::ParamList param_list;
1219 param_list=canvas_interface->generate_param_list(value_desc_list);
1221 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1223 // Add the edit waypoints option if that might be useful
1224 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1226 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1230 &edit_several_waypoints
1234 find_canvas_view(canvas)