1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
34 #include <gtkmm/stock.h>
35 #include <gtkmm/image.h>
37 #include <gtkmm/button.h>
38 #include "canvasview.h"
40 #include <sigc++/signal.h>
41 #include <sigc++/adaptors/hide.h>
43 #include "onemoment.h"
45 #include "autorecover.h"
46 #include <sigc++/retype_return.h>
47 #include <sigc++/retype.h>
48 //#include <sigc++/hide.h>
49 #include <synfig/valuenode_composite.h>
50 #include "widget_waypointmodel.h"
51 #include <gtkmm/actiongroup.h>
52 #include "iconcontroler.h"
60 using namespace synfig;
61 using namespace studio;
64 /* === M A C R O S ========================================================= */
66 /* === G L O B A L S ======================================================= */
68 int studio::Instance::instance_count_=0;
70 /* === P R O C E D U R E S ================================================= */
72 /* === M E T H O D S ======================================================= */
74 Instance::Instance(Canvas::Handle canvas):
75 synfigapp::Instance (canvas),
76 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
77 // canvas_tree_store_ (Gtk::TreeStore::create()),
78 history_tree_store_ (HistoryTreeStore::create(this)),
82 CanvasTreeModel model;
83 canvas_tree_store_=Gtk::TreeStore::create(model);
85 id_=instance_count_++;
87 // Connect up all the signals
88 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
89 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
90 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
91 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
93 signal_saved().connect(
96 studio::AutoRecover::auto_backup
101 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
103 refresh_canvas_tree();
106 Instance::~Instance()
111 Instance::get_visible_canvases()const
114 CanvasViewList::const_iterator iter;
115 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
116 if((*iter)->is_visible())
122 Instance::create(Canvas::Handle canvas)
124 // Construct a new instance
125 handle<Instance> instance(new Instance(canvas));
127 // Add the new instance to the application's instance list
128 App::instance_list.push_back(instance);
130 // Set up the instance with the default UI manager
131 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
133 // Signal the new instance
134 App::signal_instance_created()(instance);
136 // And then make sure that is has been selected
137 App::set_selected_instance(instance);
139 // Create the initial window for the root canvas
140 instance->focus(canvas);
146 Instance::find_canvas_view(Canvas::Handle canvas)
151 while(canvas->is_inline())
152 canvas=canvas->parent();
154 CanvasViewList::iterator iter;
156 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
157 if((*iter)->get_canvas()==canvas)
160 return CanvasView::create(this,canvas);
164 Instance::focus(Canvas::Handle canvas)
166 handle<CanvasView> canvas_view=find_canvas_view(canvas);
168 canvas_view->present();
172 Instance::set_undo_status(bool x)
175 App::toolbox->update_undo_redo();
176 signal_undo_redo_status_changed()();
180 Instance::set_redo_status(bool x)
183 App::toolbox->update_undo_redo();
184 signal_undo_redo_status_changed()();
188 studio::Instance::save_as(const synfig::String &file_name)
190 if(synfigapp::Instance::save_as(file_name))
192 // after changing the filename, update the render settings with the new filename
193 list<handle<CanvasView> >::iterator iter;
194 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
195 (*iter)->render_settings.set_entry_filename();
196 App::add_recent_file(file_name);
203 studio::Instance::save()
205 // the filename will be set to "Synfig Animation 1" or some such when first created
206 // and will be changed to an absolute path once it has been saved
207 // so if it still begins with "Synfig Animation " then we need to ask where to save it
208 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
209 if (dialog_save_as())
212 return STATUS_CANCEL;
214 if (synfigapp::Instance::save())
217 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
222 studio::Instance::dialog_save_as()
224 string filename=basename(get_file_name());
225 Canvas::Handle canvas(get_canvas());
228 OneMoment one_moment;
229 std::set<Node*>::iterator iter;
230 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
232 synfig::Node* node(*iter);
233 for(;!node->parent_set.empty();node=*node->parent_set.begin())
235 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
236 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
238 App::dialog_error_blocking("SaveAs - Error",
239 "There is currently a bug when using \"SaveAs\"\n"
240 "on a composition that is being referenced by other\n"
241 "files that are currently open. Close these\n"
242 "other files first before trying to use \"SaveAs\"."
253 // show the canvas' name if it has one, else its ID
254 while(App::dialog_save_file(_("Choose a Filename to Save As") +
256 (canvas->get_name().empty()
258 : canvas->get_name()) +
261 // If the filename still has wildcards, then we should
262 // continue looking for the file we want
263 if(find(filename.begin(),filename.end(),'*')!=filename.end())
266 std::string base = basename(filename);
267 if(find(base.begin(),base.end(),'.')==base.end())
272 String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
273 if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
274 _("You have given the file name an extension\nwhich I do not recognise. 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);
369 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
370 //studio::App::set_selected_instance(studio::App::instance_list.front());
376 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
378 CanvasTreeModel canvas_tree_model;
381 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
382 row[canvas_tree_model.id] = canvas->get_id();
383 row[canvas_tree_model.name] = canvas->get_name();
384 if(canvas->is_root())
385 row[canvas_tree_model.label] = basename(canvas->get_file_name());
387 if(!canvas->get_id().empty())
388 row[canvas_tree_model.label] = canvas->get_id();
390 if(!canvas->get_name().empty())
391 row[canvas_tree_model.label] = canvas->get_name();
393 row[canvas_tree_model.label] = _("[Unnamed]");
395 row[canvas_tree_model.canvas] = canvas;
396 row[canvas_tree_model.is_canvas] = true;
397 row[canvas_tree_model.is_value_node] = false;
400 synfig::Canvas::Children::iterator iter;
401 synfig::Canvas::Children &children(canvas->children());
403 for(iter=children.begin();iter!=children.end();iter++)
404 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
408 if(!canvas->value_node_list().empty())
410 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
412 valuenode_row[canvas_tree_model.label] = "<defs>";
413 valuenode_row[canvas_tree_model.canvas] = canvas;
414 valuenode_row[canvas_tree_model.is_canvas] = false;
415 valuenode_row[canvas_tree_model.is_value_node] = false;
417 synfig::ValueNodeList::iterator iter;
418 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
420 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
421 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
429 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
431 CanvasTreeModel canvas_tree_model;
435 row[canvas_tree_model.id] = value_node->get_id();
436 row[canvas_tree_model.name] = value_node->get_id();
437 row[canvas_tree_model.label] = value_node->get_id();
438 row[canvas_tree_model.canvas] = canvas;
439 row[canvas_tree_model.value_node] = value_node;
440 row[canvas_tree_model.is_canvas] = false;
441 row[canvas_tree_model.is_value_node] = true;
442 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
447 Instance::refresh_canvas_tree()
449 canvas_tree_store()->clear();
450 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
451 insert_canvas(row,get_canvas());
455 Instance::dialog_cvs_commit()
457 calc_repository_info();
460 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
467 if(synfigapp::Instance::get_action_count())
469 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
476 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
480 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
483 OneMoment one_moment;
488 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
494 Instance::dialog_cvs_add()
496 calc_repository_info();
499 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
506 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
508 OneMoment one_moment;
513 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
519 Instance::dialog_cvs_update()
521 calc_repository_info();
524 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
529 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
535 String filename(get_file_name());
536 if(synfigapp::Instance::get_action_count())
538 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
542 OneMoment one_moment;
543 time_t oldtime=get_original_timestamp();
545 calc_repository_info();
546 // If something has been updated...
547 if(oldtime!=get_original_timestamp())
554 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
556 //update_all_titles();
560 Instance::dialog_cvs_revert()
562 calc_repository_info();
565 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
570 String filename(get_file_name());
571 if(!App::dialog_yes_no(_("CVS Revert"),
572 _("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?")
576 OneMoment one_moment;
578 // Remove the old file
579 if(remove(get_file_name().c_str())!=0)
581 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
590 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
592 //update_all_titles();
596 Instance::_revert(Instance *instance)
598 OneMoment one_moment;
600 String filename(instance->get_file_name());
602 Canvas::Handle canvas(instance->get_canvas());
606 if(canvas->count()!=1)
609 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."));
620 // Schedule a revert to occur in a few moments
621 Glib::signal_timeout().connect(
624 sigc::ptr_fun(&Instance::_revert),
634 Instance::safe_revert()
636 if(synfigapp::Instance::get_action_count())
637 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
644 Instance::safe_close()
646 handle<synfigapp::UIInterface> uim;
647 uim=find_canvas_view(get_canvas())->get_ui_interface();
649 if(get_action_count())
652 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
653 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
654 if(answer==synfigapp::UIInterface::RESPONSE_YES)
656 enum Status status = save();
657 if (status == STATUS_OK) break;
658 else if (status == STATUS_CANCEL) return false;
660 if(answer==synfigapp::UIInterface::RESPONSE_NO)
662 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
668 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());
669 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
671 if(answer==synfigapp::UIInterface::RESPONSE_YES)
673 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
684 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
686 synfigapp::Action::CandidateList candidate_list;
687 synfigapp::Action::CandidateList::iterator iter;
689 candidate_list=compile_candidate_list(param_list,category);
691 candidate_list.sort();
693 if(candidate_list.empty())
694 synfig::warning("Action CandidateList is empty!");
696 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
698 Gtk::StockID stock_id(get_action_stock_id(*iter));
700 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
702 action_group->add(Gtk::Action::create(
703 "action-"+iter->name,
705 iter->local_name,iter->local_name
710 *const_cast<studio::Instance*>(this),
711 &studio::Instance::process_action
718 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
724 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
726 synfigapp::Action::CandidateList candidate_list;
727 synfigapp::Action::CandidateList::iterator iter;
729 candidate_list=compile_candidate_list(param_list,category);
731 candidate_list.sort();
733 if(candidate_list.empty())
734 synfig::warning("Action CandidateList is empty!");
736 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
738 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
740 Gtk::Image* image(manage(new Gtk::Image()));
741 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
744 if(iter->task=="raise")
745 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
746 else if(iter->task=="lower")
747 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
748 else if(iter->task=="move_top")
749 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
750 else if(iter->task=="move_bottom")
751 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
752 else if(iter->task=="remove")
753 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
754 else if(iter->task=="set_on")
755 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
756 else if(iter->task=="set_off")
757 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
758 else if(iter->task=="duplicate")
759 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
760 else if(iter->task=="remove")
761 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
764 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
765 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
766 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
769 menu->items().push_back(
770 Gtk::Menu_Helpers::ImageMenuElem(
776 *const_cast<studio::Instance*>(this),
777 &studio::Instance::process_action
790 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
792 synfigapp::Action::CandidateList candidate_list;
793 synfigapp::Action::CandidateList candidate_list2;
795 synfigapp::Action::CandidateList::iterator iter;
797 candidate_list=compile_candidate_list(param_list,category);
798 candidate_list2=compile_candidate_list(param_list2,category);
800 candidate_list.sort();
802 if(candidate_list.empty())
803 synfig::warning("Action CandidateList is empty!");
804 if(candidate_list2.empty())
805 synfig::warning("Action CandidateList2 is empty!");
807 // Seperate out the candidate lists so that there are no conflicts
808 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
810 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
811 if(iter2!=candidate_list2.end())
812 candidate_list2.erase(iter2);
815 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
817 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
819 Gtk::Image* image(manage(new Gtk::Image()));
820 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
822 /* if(iter->task=="raise")
823 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
824 else if(iter->task=="lower")
825 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
826 else if(iter->task=="move_top")
827 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
828 else if(iter->task=="move_bottom")
829 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
830 else if(iter->task=="remove")
831 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
832 else if(iter->task=="set_on")
833 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
834 else if(iter->task=="set_off")
835 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
836 else if(iter->task=="duplicate")
837 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
838 else if(iter->task=="remove")
839 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
842 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
843 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
844 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
847 menu->items().push_back(
848 Gtk::Menu_Helpers::ImageMenuElem(
854 *const_cast<studio::Instance*>(this),
855 &studio::Instance::process_action
866 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
868 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
870 Gtk::Image* image(manage(new Gtk::Image()));
871 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
872 /* if(iter->task=="raise")
873 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
874 else if(iter->task=="lower")
875 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
876 else if(iter->task=="move_top")
877 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
878 else if(iter->task=="move_bottom")
879 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
880 else if(iter->task=="remove")
881 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
882 else if(iter->task=="set_on")
883 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
884 else if(iter->task=="set_off")
885 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
886 else if(iter->task=="duplicate")
887 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
888 else if(iter->task=="remove")
889 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
892 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
893 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
894 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
897 menu->items().push_back(
898 Gtk::Menu_Helpers::ImageMenuElem(
904 *const_cast<studio::Instance*>(this),
905 &studio::Instance::process_action
918 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
920 assert(synfigapp::Action::book().count(name));
922 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
924 synfigapp::Action::Handle action(entry.factory());
928 synfig::error("Bad Action");
932 action->set_param_list(param_list);
934 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
935 synfigapp::Action::ParamVocab::const_iterator iter;
937 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
939 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
942 // If the parameter is optionally user-supplied,
943 // and has not been already provided in the param_list,
944 // then we should go ahead and see if we can
945 // provide that data.
946 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
948 switch(iter->get_type())
950 case synfigapp::Action::Param::TYPE_STRING:
953 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
955 action->set_param(iter->get_name(),str);
959 synfig::error("Unsupported user-supplied action parameter");
966 if(!action->is_ready())
968 synfig::error("Action not ready");
972 perform_action(action);
976 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
978 Gtk::Menu& parammenu(*menu);
980 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
982 if(!canvas_interface)
985 synfigapp::Action::ParamList param_list,param_list2;
986 param_list=canvas_interface->generate_param_list(value_desc);
987 param_list.add("origin",location);
989 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
991 param_list2=canvas_interface->generate_param_list(
992 synfigapp::ValueDesc(
993 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
997 param_list2.add("origin",location);
1001 // Populate the convert menu by looping through
1002 // the ValueNode book and find the ones that are
1005 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1006 LinkableValueNode::Book::const_iterator iter;
1007 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1009 if(iter->second.check_type(value_desc.get_value_type()))
1011 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1015 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1025 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1028 if(param_list2.empty())
1029 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1031 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1033 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1035 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1038 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1040 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1044 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1045 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1049 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1065 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1067 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1070 "Edit Multiple Waypoints", // Title
1072 true // use_separator
1075 Widget_WaypointModel widget_waypoint_model;
1076 widget_waypoint_model.show();
1078 dialog.get_vbox()->pack_start(widget_waypoint_model);
1081 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1082 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1086 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1089 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1091 std::list<synfigapp::ValueDesc>::iterator iter;
1092 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1094 synfigapp::ValueDesc value_desc(*iter);
1096 if(!value_desc.is_valid())
1099 ValueNode_Animated::Handle value_node;
1101 // If this value isn't a ValueNode_Animated, but
1102 // it is somewhat constant, then go ahead and convert
1103 // it to a ValueNode_Animated.
1104 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1107 if(value_desc.is_value_node())
1108 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1110 value=value_desc.get_value();
1112 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1114 synfigapp::Action::Handle action;
1116 if(!value_desc.is_value_node())
1118 action=synfigapp::Action::create("value_desc_connect");
1119 action->set_param("dest",value_desc);
1120 action->set_param("src",ValueNode::Handle(value_node));
1124 action=synfigapp::Action::create("value_node_replace");
1125 action->set_param("dest",value_desc.get_value_node());
1126 action->set_param("src",ValueNode::Handle(value_node));
1129 action->set_param("canvas",canvas_view->get_canvas());
1130 action->set_param("canvas_interface",canvas_interface);
1133 if(!canvas_interface->get_instance()->perform_action(action))
1135 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1142 if(value_desc.is_value_node())
1143 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1150 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1154 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1160 action->set_param("canvas",canvas_view->get_canvas());
1161 action->set_param("canvas_interface",canvas_interface);
1162 action->set_param("value_node",ValueNode::Handle(value_node));
1163 action->set_param("time",canvas_interface->get_time());
1164 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1166 if(!canvas_interface->get_instance()->perform_action(action))
1168 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1175 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1184 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1186 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1188 synfigapp::Action::ParamList param_list;
1189 param_list=canvas_interface->generate_param_list(value_desc_list);
1191 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1193 // Add the edit waypoints option if that might be useful
1194 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1196 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1200 &edit_several_waypoints
1204 find_canvas_view(canvas)