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 "widget_waypointmodel.h"
52 #include <gtkmm/actiongroup.h>
53 #include "iconcontroller.h"
63 using namespace synfig;
64 using namespace studio;
67 /* === M A C R O S ========================================================= */
69 /* === G L O B A L S ======================================================= */
71 int studio::Instance::instance_count_=0;
73 /* === P R O C E D U R E S ================================================= */
75 /* === M E T H O D S ======================================================= */
77 Instance::Instance(synfig::Canvas::Handle canvas):
78 synfigapp::Instance (canvas),
79 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
80 // canvas_tree_store_ (Gtk::TreeStore::create()),
81 history_tree_store_ (HistoryTreeStore::create(this)),
85 CanvasTreeModel model;
86 canvas_tree_store_=Gtk::TreeStore::create(model);
88 id_=instance_count_++;
90 // Connect up all the signals
91 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
92 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
93 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
94 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
96 signal_saved().connect(
99 studio::AutoRecover::auto_backup
104 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
106 refresh_canvas_tree();
109 Instance::~Instance()
114 Instance::get_visible_canvases()const
117 CanvasViewList::const_iterator iter;
118 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
119 if((*iter)->is_visible())
125 Instance::create(synfig::Canvas::Handle canvas)
127 // Construct a new instance
128 handle<Instance> instance(new Instance(canvas));
130 // Add the new instance to the application's instance list
131 App::instance_list.push_back(instance);
133 // Set up the instance with the default UI manager
134 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
136 // Signal the new instance
137 App::signal_instance_created()(instance);
139 // And then make sure that is has been selected
140 App::set_selected_instance(instance);
142 // Create the initial window for the root canvas
143 instance->focus(canvas);
149 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
154 while(canvas->is_inline())
155 canvas=canvas->parent();
157 CanvasViewList::iterator iter;
159 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
160 if((*iter)->get_canvas()==canvas)
163 return CanvasView::create(this,canvas);
167 Instance::focus(etl::handle<synfig::Canvas> canvas)
169 handle<CanvasView> canvas_view=find_canvas_view(canvas);
171 canvas_view->present();
175 Instance::set_undo_status(bool x)
178 App::toolbox->update_undo_redo();
179 signal_undo_redo_status_changed()();
183 Instance::set_redo_status(bool x)
186 App::toolbox->update_undo_redo();
187 signal_undo_redo_status_changed()();
191 studio::Instance::save_as(const synfig::String &file_name)
193 if(synfigapp::Instance::save_as(file_name))
195 // after changing the filename, update the render settings with the new filename
196 list<handle<CanvasView> >::iterator iter;
197 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
198 (*iter)->render_settings.set_entry_filename();
199 App::add_recent_file(file_name);
206 studio::Instance::save()
208 // the filename will be set to "Synfig Animation 1" or some such when first created
209 // and will be changed to an absolute path once it has been saved
210 // so if it still begins with "Synfig Animation " then we need to ask where to save it
211 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
212 if (dialog_save_as())
215 return STATUS_CANCEL;
217 if (synfigapp::Instance::save())
220 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
225 studio::Instance::dialog_save_as()
227 string filename=basename(get_file_name());
228 Canvas::Handle canvas(get_canvas());
231 OneMoment one_moment;
232 std::set<Node*>::iterator iter;
233 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
235 synfig::Node* node(*iter);
236 for(;!node->parent_set.empty();node=*node->parent_set.begin())
238 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
239 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
241 App::dialog_error_blocking("SaveAs - Error",
242 "There is currently a bug when using \"SaveAs\"\n"
243 "on a composition that is being referenced by other\n"
244 "files that are currently open. Close these\n"
245 "other files first before trying to use \"SaveAs\"."
256 // show the canvas' name if it has one, else its ID
257 while(App::dialog_save_file(_("Choose a Filename to Save As") +
259 (canvas->get_name().empty()
261 : canvas->get_name()) +
264 // If the filename still has wildcards, then we should
265 // continue looking for the file we want
266 if(find(filename.begin(),filename.end(),'*')!=filename.end())
269 if (filename_extension(filename) == "")
274 String ext(filename_extension(filename));
275 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
276 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
286 int stat_return = stat(filename.c_str(), &s);
288 // if stat() fails with something other than 'file doesn't exist', there's been a real
289 // error of some kind. let's give up now and ask for a new path.
290 if (stat_return == -1 && errno != ENOENT)
292 perror(filename.c_str());
293 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
297 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
298 if ((stat_return == 0) &&
299 !App::dialog_yes_no("File exists",
302 "' already exists.\n\n"
303 "Do you want to replace it with the file you are saving?"))
307 if(save_as(filename))
310 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
317 Instance::update_all_titles()
319 list<handle<CanvasView> >::iterator iter;
320 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
321 (*iter)->update_title();
327 // This will increase the reference count so we don't get DELETED
328 // until we are ready
329 handle<Instance> me(this);
331 // Make sure we aren't selected as the current instance
332 if(studio::App::get_selected_instance()==this)
333 studio::App::set_selected_instance(0);
335 // Turn-off/clean-up auto recovery
336 studio::App::auto_recover->clear_backup(get_canvas());
338 // Remove us from the active instance list
339 std::list<etl::handle<studio::Instance> >::iterator iter;
340 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
343 assert(iter!=studio::App::instance_list.end());
344 if(iter!=studio::App::instance_list.end())
345 studio::App::instance_list.erase(iter);
347 // Send out a signal that we are being deleted
348 studio::App::signal_instance_deleted()(this);
350 // Hide all of the canvas views
351 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
354 // Consume pending events before deleting the canvas views
355 while(studio::App::events_pending())studio::App::iteration(false);
357 // Delete all of the canvas views
358 canvas_view_list().clear();
360 // If there is another open instance to select,
361 // go ahead and do so. If not, never mind.
362 if(studio::App::instance_list.empty())
364 studio::App::set_selected_canvas_view(0);
365 studio::App::set_selected_instance(0);
368 studio::App::instance_list.front()->canvas_view_list().front()->present();
373 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
375 CanvasTreeModel canvas_tree_model;
378 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
379 row[canvas_tree_model.id] = canvas->get_id();
380 row[canvas_tree_model.name] = canvas->get_name();
381 if(canvas->is_root())
382 row[canvas_tree_model.label] = basename(canvas->get_file_name());
384 if(!canvas->get_id().empty())
385 row[canvas_tree_model.label] = canvas->get_id();
387 if(!canvas->get_name().empty())
388 row[canvas_tree_model.label] = canvas->get_name();
390 row[canvas_tree_model.label] = _("[Unnamed]");
392 row[canvas_tree_model.canvas] = canvas;
393 row[canvas_tree_model.is_canvas] = true;
394 row[canvas_tree_model.is_value_node] = false;
397 synfig::Canvas::Children::iterator iter;
398 synfig::Canvas::Children &children(canvas->children());
400 for(iter=children.begin();iter!=children.end();iter++)
401 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
405 if(!canvas->value_node_list().empty())
407 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
409 valuenode_row[canvas_tree_model.label] = "<defs>";
410 valuenode_row[canvas_tree_model.canvas] = canvas;
411 valuenode_row[canvas_tree_model.is_canvas] = false;
412 valuenode_row[canvas_tree_model.is_value_node] = false;
414 synfig::ValueNodeList::iterator iter;
415 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
417 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
418 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
426 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
428 CanvasTreeModel canvas_tree_model;
432 row[canvas_tree_model.id] = value_node->get_id();
433 row[canvas_tree_model.name] = value_node->get_id();
434 row[canvas_tree_model.label] = value_node->get_id();
435 row[canvas_tree_model.canvas] = canvas;
436 row[canvas_tree_model.value_node] = value_node;
437 row[canvas_tree_model.is_canvas] = false;
438 row[canvas_tree_model.is_value_node] = true;
439 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
444 Instance::refresh_canvas_tree()
446 canvas_tree_store()->clear();
447 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
448 insert_canvas(row,get_canvas());
452 Instance::dialog_cvs_commit()
454 calc_repository_info();
457 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
464 if(synfigapp::Instance::get_action_count())
466 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
473 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
477 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
480 OneMoment one_moment;
485 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
491 Instance::dialog_cvs_add()
493 calc_repository_info();
496 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
503 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
505 OneMoment one_moment;
510 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
516 Instance::dialog_cvs_update()
518 calc_repository_info();
521 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
526 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
532 String filename(get_file_name());
533 if(synfigapp::Instance::get_action_count())
535 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
539 OneMoment one_moment;
540 time_t oldtime=get_original_timestamp();
542 calc_repository_info();
543 // If something has been updated...
544 if(oldtime!=get_original_timestamp())
551 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
553 //update_all_titles();
557 Instance::dialog_cvs_revert()
559 calc_repository_info();
562 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
567 String filename(get_file_name());
568 if(!App::dialog_yes_no(_("CVS Revert"),
569 _("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?")
573 OneMoment one_moment;
575 // Remove the old file
576 if(remove(get_file_name().c_str())!=0)
578 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
587 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
589 //update_all_titles();
593 Instance::_revert(Instance *instance)
595 OneMoment one_moment;
597 String filename(instance->get_file_name());
599 Canvas::Handle canvas(instance->get_canvas());
603 if(canvas->count()!=1)
606 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."));
617 // Schedule a revert to occur in a few moments
618 Glib::signal_timeout().connect(
621 sigc::ptr_fun(&Instance::_revert),
631 Instance::safe_revert()
633 if(synfigapp::Instance::get_action_count())
634 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
641 Instance::safe_close()
643 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
644 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
646 // if the animation is currently playing, closing the window will cause a crash,
648 if (canvas_view->is_playing())
650 canvas_view->present();
651 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
654 if(get_action_count())
657 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
658 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
659 if(answer==synfigapp::UIInterface::RESPONSE_YES)
661 enum Status status = save();
662 if (status == STATUS_OK) break;
663 else if (status == STATUS_CANCEL) return false;
665 if(answer==synfigapp::UIInterface::RESPONSE_NO)
667 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
673 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());
674 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
676 if(answer==synfigapp::UIInterface::RESPONSE_YES)
678 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
689 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
691 synfigapp::Action::CandidateList candidate_list;
692 synfigapp::Action::CandidateList::iterator iter;
694 candidate_list=compile_candidate_list(param_list,category);
696 candidate_list.sort();
698 if(candidate_list.empty())
699 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
701 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
703 Gtk::StockID stock_id(get_action_stock_id(*iter));
705 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
707 action_group->add(Gtk::Action::create(
708 "action-"+iter->name,
710 iter->local_name,iter->local_name
715 *const_cast<studio::Instance*>(this),
716 &studio::Instance::process_action
723 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
729 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
731 synfigapp::Action::CandidateList candidate_list;
732 synfigapp::Action::CandidateList::iterator iter;
734 candidate_list=compile_candidate_list(param_list,category);
736 candidate_list.sort();
738 if(candidate_list.empty())
739 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
741 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
743 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
745 Gtk::Image* image(manage(new Gtk::Image()));
746 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
749 if(iter->task=="raise")
750 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
751 else if(iter->task=="lower")
752 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
753 else if(iter->task=="move_top")
754 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
755 else if(iter->task=="move_bottom")
756 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
757 else if(iter->task=="remove")
758 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
759 else if(iter->task=="set_on")
760 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
761 else if(iter->task=="set_off")
762 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
763 else if(iter->task=="duplicate")
764 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
765 else if(iter->task=="remove")
766 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
769 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
770 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
771 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
774 menu->items().push_back(
775 Gtk::Menu_Helpers::ImageMenuElem(
781 *const_cast<studio::Instance*>(this),
782 &studio::Instance::process_action
795 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
797 synfigapp::Action::CandidateList candidate_list;
798 synfigapp::Action::CandidateList candidate_list2;
800 synfigapp::Action::CandidateList::iterator iter;
802 candidate_list=compile_candidate_list(param_list,category);
803 candidate_list2=compile_candidate_list(param_list2,category);
805 candidate_list.sort();
807 if(candidate_list.empty())
808 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
809 if(candidate_list2.empty())
810 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
812 // Separate out the candidate lists so that there are no conflicts
813 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
815 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
816 if(iter2!=candidate_list2.end())
817 candidate_list2.erase(iter2);
820 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
822 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
824 Gtk::Image* image(manage(new Gtk::Image()));
825 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
827 /* if(iter->task=="raise")
828 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
829 else if(iter->task=="lower")
830 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
831 else if(iter->task=="move_top")
832 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
833 else if(iter->task=="move_bottom")
834 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
835 else if(iter->task=="remove")
836 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
837 else if(iter->task=="set_on")
838 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
839 else if(iter->task=="set_off")
840 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
841 else if(iter->task=="duplicate")
842 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
843 else if(iter->task=="remove")
844 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
847 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
848 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
849 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
852 menu->items().push_back(
853 Gtk::Menu_Helpers::ImageMenuElem(
859 *const_cast<studio::Instance*>(this),
860 &studio::Instance::process_action
871 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
873 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
875 Gtk::Image* image(manage(new Gtk::Image()));
876 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
877 /* if(iter->task=="raise")
878 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
879 else if(iter->task=="lower")
880 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
881 else if(iter->task=="move_top")
882 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
883 else if(iter->task=="move_bottom")
884 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
885 else if(iter->task=="remove")
886 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
887 else if(iter->task=="set_on")
888 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
889 else if(iter->task=="set_off")
890 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
891 else if(iter->task=="duplicate")
892 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
893 else if(iter->task=="remove")
894 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
897 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
898 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
899 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
902 menu->items().push_back(
903 Gtk::Menu_Helpers::ImageMenuElem(
909 *const_cast<studio::Instance*>(this),
910 &studio::Instance::process_action
923 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
925 assert(synfigapp::Action::book().count(name));
927 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
929 synfigapp::Action::Handle action(entry.factory());
933 synfig::error("Bad Action");
937 action->set_param_list(param_list);
939 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
940 synfigapp::Action::ParamVocab::const_iterator iter;
942 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
944 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
947 // If the parameter is optionally user-supplied,
948 // and has not been already provided in the param_list,
949 // then we should go ahead and see if we can
950 // provide that data.
951 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
953 switch(iter->get_type())
955 case synfigapp::Action::Param::TYPE_STRING:
958 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
960 action->set_param(iter->get_name(),str);
964 synfig::error("Unsupported user-supplied action parameter");
971 if(!action->is_ready())
973 synfig::error("Action not ready");
977 perform_action(action);
981 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
983 Gtk::Menu& parammenu(*menu);
985 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
987 if(!canvas_interface)
990 synfigapp::Action::ParamList param_list,param_list2;
991 param_list=canvas_interface->generate_param_list(value_desc);
992 param_list.add("origin",location);
994 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
996 param_list2=canvas_interface->generate_param_list(
997 synfigapp::ValueDesc(
998 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1002 param_list2.add("origin",location);
1006 // Populate the convert menu by looping through
1007 // the ValueNode book and find the ones that are
1010 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1011 LinkableValueNode::Book::const_iterator iter;
1012 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1014 if(iter->second.check_type(value_desc.get_value_type()))
1016 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1020 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1030 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1033 if(param_list2.empty())
1034 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1036 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1038 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1040 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1043 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1045 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1049 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1050 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1054 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1070 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1072 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1075 "Edit Multiple Waypoints", // Title
1077 true // use_separator
1080 Widget_WaypointModel widget_waypoint_model;
1081 widget_waypoint_model.show();
1083 dialog.get_vbox()->pack_start(widget_waypoint_model);
1086 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1087 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1091 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1094 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1096 std::list<synfigapp::ValueDesc>::iterator iter;
1097 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1099 synfigapp::ValueDesc value_desc(*iter);
1101 if(!value_desc.is_valid())
1104 ValueNode_Animated::Handle value_node;
1106 // If this value isn't a ValueNode_Animated, but
1107 // it is somewhat constant, then go ahead and convert
1108 // it to a ValueNode_Animated.
1109 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1112 if(value_desc.is_value_node())
1113 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1115 value=value_desc.get_value();
1117 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1119 synfigapp::Action::Handle action;
1121 if(!value_desc.is_value_node())
1123 action=synfigapp::Action::create("value_desc_connect");
1124 action->set_param("dest",value_desc);
1125 action->set_param("src",ValueNode::Handle(value_node));
1129 action=synfigapp::Action::create("value_node_replace");
1130 action->set_param("dest",value_desc.get_value_node());
1131 action->set_param("src",ValueNode::Handle(value_node));
1134 action->set_param("canvas",canvas_view->get_canvas());
1135 action->set_param("canvas_interface",canvas_interface);
1138 if(!canvas_interface->get_instance()->perform_action(action))
1140 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1147 if(value_desc.is_value_node())
1148 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1155 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1159 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1165 action->set_param("canvas",canvas_view->get_canvas());
1166 action->set_param("canvas_interface",canvas_interface);
1167 action->set_param("value_node",ValueNode::Handle(value_node));
1168 action->set_param("time",canvas_interface->get_time());
1169 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1171 if(!canvas_interface->get_instance()->perform_action(action))
1173 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1180 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1189 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1191 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1193 synfigapp::Action::ParamList param_list;
1194 param_list=canvas_interface->generate_param_list(value_desc_list);
1196 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1198 // Add the edit waypoints option if that might be useful
1199 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1201 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1205 &edit_several_waypoints
1209 find_canvas_view(canvas)