1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
35 #include <gtkmm/stock.h>
36 #include <gtkmm/image.h>
38 #include <gtkmm/button.h>
39 #include "canvasview.h"
41 #include <sigc++/signal.h>
42 #include <sigc++/adaptors/hide.h>
44 #include "onemoment.h"
45 #include <synfig/savecanvas.h>
47 #include "autorecover.h"
48 #include <sigc++/retype_return.h>
49 #include <sigc++/retype.h>
50 //#include <sigc++/hide.h>
51 #include <synfig/valuenode_composite.h>
52 #include <synfig/valuenode_duplicate.h>
53 #include "widget_waypointmodel.h"
54 #include <gtkmm/actiongroup.h>
55 #include "iconcontroller.h"
59 #include <ETL/stringf>
67 using namespace synfig;
68 using namespace studio;
71 /* === M A C R O S ========================================================= */
73 /* === G L O B A L S ======================================================= */
75 int studio::Instance::instance_count_=0;
77 /* === P R O C E D U R E S ================================================= */
79 /* === M E T H O D S ======================================================= */
81 Instance::Instance(synfig::Canvas::Handle canvas):
82 synfigapp::Instance (canvas),
83 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
84 // canvas_tree_store_ (Gtk::TreeStore::create()),
85 history_tree_store_ (HistoryTreeStore::create(this)),
89 CanvasTreeModel model;
90 canvas_tree_store_=Gtk::TreeStore::create(model);
92 id_=instance_count_++;
94 // Connect up all the signals
95 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
96 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
97 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
98 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
100 signal_saved().connect(
103 studio::AutoRecover::auto_backup
108 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
110 refresh_canvas_tree();
113 Instance::~Instance()
118 Instance::get_visible_canvases()const
121 CanvasViewList::const_iterator iter;
122 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
123 if((*iter)->is_visible())
129 Instance::create(synfig::Canvas::Handle canvas)
131 // Construct a new instance
132 handle<Instance> instance(new Instance(canvas));
134 // Add the new instance to the application's instance list
135 App::instance_list.push_back(instance);
137 // Set up the instance with the default UI manager
138 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
140 // Signal the new instance
141 App::signal_instance_created()(instance);
143 // And then make sure that is has been selected
144 App::set_selected_instance(instance);
146 // Create the initial window for the root canvas
147 instance->focus(canvas);
153 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
158 while(canvas->is_inline())
159 canvas=canvas->parent();
161 CanvasViewList::iterator iter;
163 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
164 if((*iter)->get_canvas()==canvas)
167 return CanvasView::create(this,canvas);
171 Instance::focus(etl::handle<synfig::Canvas> canvas)
173 handle<CanvasView> canvas_view=find_canvas_view(canvas);
175 canvas_view->present();
179 Instance::set_undo_status(bool x)
182 App::toolbox->update_undo_redo();
183 signal_undo_redo_status_changed()();
187 Instance::set_redo_status(bool x)
190 App::toolbox->update_undo_redo();
191 signal_undo_redo_status_changed()();
195 studio::Instance::save_as(const synfig::String &file_name)
197 if(synfigapp::Instance::save_as(file_name))
199 // after changing the filename, update the render settings with the new filename
200 list<handle<CanvasView> >::iterator iter;
201 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
202 (*iter)->render_settings.set_entry_filename();
203 App::add_recent_file(etl::handle<Instance>(this));
210 studio::Instance::open()
212 App::dialog_open(get_file_name());
216 studio::Instance::save()
218 // if we don't have a real filename yet then we need to ask where to save it
219 if (!has_real_filename())
221 if (dialog_save_as())
224 return STATUS_CANCEL;
227 if (synfigapp::Instance::save())
229 App::add_recent_file(etl::handle<Instance>(this));
233 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
237 // the filename will be set to "Synfig Animation 1" or some such when first created
238 // and will be changed to an absolute path once it has been saved
239 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
241 studio::Instance::has_real_filename()
243 return get_file_name().find(DEFAULT_FILENAME_PREFIX) != 0;
247 studio::Instance::dialog_save_as()
249 string filename = get_file_name();
250 Canvas::Handle canvas(get_canvas());
253 OneMoment one_moment;
254 std::set<Node*>::iterator iter;
255 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
257 synfig::Node* node(*iter);
258 for(;!node->parent_set.empty();node=*node->parent_set.begin())
260 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
261 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
263 App::dialog_error_blocking("SaveAs - Error",
264 "There is currently a bug when using \"SaveAs\"\n"
265 "on a composition that is being referenced by other\n"
266 "files that are currently open. Close these\n"
267 "other files first before trying to use \"SaveAs\"."
278 if (has_real_filename())
279 filename = absolute_path(filename);
281 // show the canvas' name if it has one, else its ID
282 while (App::dialog_save_file((_("Choose a Filename to Save As") +
284 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
286 filename, ANIMATION_DIR_PREFERENCE))
288 // If the filename still has wildcards, then we should
289 // continue looking for the file we want
290 string base_filename = basename(filename);
291 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
294 if (filename_extension(filename) == "")
299 String ext(filename_extension(filename));
300 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
301 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
311 int stat_return = stat(filename.c_str(), &s);
313 // if stat() fails with something other than 'file doesn't exist', there's been a real
314 // error of some kind. let's give up now and ask for a new path.
315 if (stat_return == -1 && errno != ENOENT)
317 perror(filename.c_str());
318 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
322 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
323 if ((stat_return == 0) &&
324 !App::dialog_yes_no("File exists",
327 "' already exists.\n\n"
328 "Do you want to replace it with the file you are saving?"))
332 if(save_as(filename))
334 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
338 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
345 Instance::update_all_titles()
347 list<handle<CanvasView> >::iterator iter;
348 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
349 (*iter)->update_title();
355 // This will increase the reference count so we don't get DELETED
356 // until we are ready
357 handle<Instance> me(this);
359 // Make sure we aren't selected as the current instance
360 if(studio::App::get_selected_instance()==this)
361 studio::App::set_selected_instance(0);
363 // Turn-off/clean-up auto recovery
364 studio::App::auto_recover->clear_backup(get_canvas());
366 // Remove us from the active instance list
367 std::list<etl::handle<studio::Instance> >::iterator iter;
368 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
371 assert(iter!=studio::App::instance_list.end());
372 if(iter!=studio::App::instance_list.end())
373 studio::App::instance_list.erase(iter);
375 // Send out a signal that we are being deleted
376 studio::App::signal_instance_deleted()(this);
378 // Hide all of the canvas views
379 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
382 // Consume pending events before deleting the canvas views
383 while(studio::App::events_pending())studio::App::iteration(false);
385 // Delete all of the canvas views
386 canvas_view_list().clear();
388 // If there is another open instance to select,
389 // go ahead and do so. If not, never mind.
390 if(studio::App::instance_list.empty())
392 studio::App::set_selected_canvas_view(0);
393 studio::App::set_selected_instance(0);
396 studio::App::instance_list.front()->canvas_view_list().front()->present();
400 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
402 CanvasTreeModel canvas_tree_model;
405 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
406 row[canvas_tree_model.id] = canvas->get_id();
407 row[canvas_tree_model.name] = canvas->get_name();
408 if(canvas->is_root())
409 row[canvas_tree_model.label] = basename(canvas->get_file_name());
411 if(!canvas->get_id().empty())
412 row[canvas_tree_model.label] = canvas->get_id();
414 if(!canvas->get_name().empty())
415 row[canvas_tree_model.label] = canvas->get_name();
417 row[canvas_tree_model.label] = _("[Unnamed]");
419 row[canvas_tree_model.canvas] = canvas;
420 row[canvas_tree_model.is_canvas] = true;
421 row[canvas_tree_model.is_value_node] = false;
424 synfig::Canvas::Children::iterator iter;
425 synfig::Canvas::Children &children(canvas->children());
427 for(iter=children.begin();iter!=children.end();iter++)
428 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
432 if(!canvas->value_node_list().empty())
434 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
436 valuenode_row[canvas_tree_model.label] = "<defs>";
437 valuenode_row[canvas_tree_model.canvas] = canvas;
438 valuenode_row[canvas_tree_model.is_canvas] = false;
439 valuenode_row[canvas_tree_model.is_value_node] = false;
441 synfig::ValueNodeList::iterator iter;
442 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
444 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
445 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
452 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
454 CanvasTreeModel canvas_tree_model;
458 row[canvas_tree_model.id] = value_node->get_id();
459 row[canvas_tree_model.name] = value_node->get_id();
460 row[canvas_tree_model.label] = value_node->get_id();
461 row[canvas_tree_model.canvas] = canvas;
462 row[canvas_tree_model.value_node] = value_node;
463 row[canvas_tree_model.is_canvas] = false;
464 row[canvas_tree_model.is_value_node] = true;
465 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
470 Instance::refresh_canvas_tree()
472 canvas_tree_store()->clear();
473 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
474 insert_canvas(row,get_canvas());
478 Instance::dialog_cvs_commit()
480 calc_repository_info();
483 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
490 if(synfigapp::Instance::get_action_count())
492 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
499 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
503 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
506 OneMoment one_moment;
511 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
517 Instance::dialog_cvs_add()
519 calc_repository_info();
522 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
529 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
531 OneMoment one_moment;
536 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
542 Instance::dialog_cvs_update()
544 calc_repository_info();
547 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
552 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
558 String filename(get_file_name());
559 if(synfigapp::Instance::get_action_count())
561 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
565 OneMoment one_moment;
566 time_t oldtime=get_original_timestamp();
568 calc_repository_info();
569 // If something has been updated...
570 if(oldtime!=get_original_timestamp())
577 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
579 //update_all_titles();
583 Instance::dialog_cvs_revert()
585 calc_repository_info();
588 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
593 String filename(get_file_name());
594 if(!App::dialog_yes_no(_("CVS Revert"),
595 _("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?")
599 OneMoment one_moment;
601 // Remove the old file
602 if(remove(get_file_name().c_str())!=0)
604 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
613 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
615 //update_all_titles();
619 Instance::_revert(Instance *instance)
621 OneMoment one_moment;
623 String filename(instance->get_file_name());
625 Canvas::Handle canvas(instance->get_canvas());
629 if(canvas->count()!=1)
632 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."));
643 // Schedule a revert to occur in a few moments
644 Glib::signal_timeout().connect(
647 sigc::ptr_fun(&Instance::_revert),
657 Instance::safe_revert()
659 if(synfigapp::Instance::get_action_count())
660 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
667 Instance::safe_close()
669 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
670 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
672 // if the animation is currently playing, closing the window will cause a crash,
674 if (canvas_view->is_playing())
676 canvas_view->present();
677 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
680 if(get_action_count())
683 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
684 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
685 if(answer==synfigapp::UIInterface::RESPONSE_YES)
687 enum Status status = save();
688 if (status == STATUS_OK) break;
689 else if (status == STATUS_CANCEL) return false;
691 if(answer==synfigapp::UIInterface::RESPONSE_NO)
693 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
699 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());
700 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
702 if(answer==synfigapp::UIInterface::RESPONSE_YES)
704 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
714 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
716 synfigapp::Action::CandidateList candidate_list;
717 synfigapp::Action::CandidateList::iterator iter;
719 candidate_list=compile_candidate_list(param_list,category);
721 candidate_list.sort();
723 // if(candidate_list.empty())
724 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
726 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
728 Gtk::StockID stock_id(get_action_stock_id(*iter));
730 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
732 action_group->add(Gtk::Action::create(
733 "action-"+iter->name,
735 iter->local_name,iter->local_name
740 *const_cast<studio::Instance*>(this),
741 &studio::Instance::process_action
748 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
754 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
756 synfigapp::Action::CandidateList candidate_list;
757 synfigapp::Action::CandidateList::iterator iter;
759 candidate_list=compile_candidate_list(param_list,category);
761 candidate_list.sort();
763 if(candidate_list.empty())
764 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
766 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
768 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
770 Gtk::Image* image(manage(new Gtk::Image()));
771 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
774 if(iter->task=="raise")
775 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="lower")
777 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
778 else if(iter->task=="move_top")
779 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
780 else if(iter->task=="move_bottom")
781 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
782 else if(iter->task=="remove")
783 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
784 else if(iter->task=="set_on")
785 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
786 else if(iter->task=="set_off")
787 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
788 else if(iter->task=="duplicate")
789 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
790 else if(iter->task=="remove")
791 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
794 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
795 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
796 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
799 menu->items().push_back(
800 Gtk::Menu_Helpers::ImageMenuElem(
806 *const_cast<studio::Instance*>(this),
807 &studio::Instance::process_action
820 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
822 synfigapp::Action::CandidateList candidate_list;
823 synfigapp::Action::CandidateList candidate_list2;
825 synfigapp::Action::CandidateList::iterator iter;
827 candidate_list=compile_candidate_list(param_list,category);
828 candidate_list2=compile_candidate_list(param_list2,category);
830 candidate_list.sort();
832 if(candidate_list.empty())
833 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
834 if(candidate_list2.empty())
835 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
837 // Separate out the candidate lists so that there are no conflicts
838 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
840 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
841 if(iter2!=candidate_list2.end())
842 candidate_list2.erase(iter2);
845 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
847 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
849 Gtk::Image* image(manage(new Gtk::Image()));
850 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
852 /* if(iter->task=="raise")
853 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="lower")
855 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="move_top")
857 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="move_bottom")
859 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="remove")
861 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
862 else if(iter->task=="set_on")
863 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
864 else if(iter->task=="set_off")
865 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
866 else if(iter->task=="duplicate")
867 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
868 else if(iter->task=="remove")
869 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
872 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
873 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
874 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
877 menu->items().push_back(
878 Gtk::Menu_Helpers::ImageMenuElem(
884 *const_cast<studio::Instance*>(this),
885 &studio::Instance::process_action
896 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
898 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
900 Gtk::Image* image(manage(new Gtk::Image()));
901 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
902 /* if(iter->task=="raise")
903 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="lower")
905 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
906 else if(iter->task=="move_top")
907 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
908 else if(iter->task=="move_bottom")
909 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
910 else if(iter->task=="remove")
911 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
912 else if(iter->task=="set_on")
913 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
914 else if(iter->task=="set_off")
915 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
916 else if(iter->task=="duplicate")
917 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
918 else if(iter->task=="remove")
919 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
922 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
923 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
924 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
927 menu->items().push_back(
928 Gtk::Menu_Helpers::ImageMenuElem(
934 *const_cast<studio::Instance*>(this),
935 &studio::Instance::process_action
948 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
950 assert(synfigapp::Action::book().count(name));
952 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
954 synfigapp::Action::Handle action(entry.factory());
958 synfig::error("Bad Action");
962 action->set_param_list(param_list);
964 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
965 synfigapp::Action::ParamVocab::const_iterator iter;
967 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
969 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
972 // If the parameter is optionally user-supplied,
973 // and has not been already provided in the param_list,
974 // then we should go ahead and see if we can
975 // provide that data.
976 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
978 switch(iter->get_type())
980 case synfigapp::Action::Param::TYPE_STRING:
983 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
985 action->set_param(iter->get_name(),str);
989 synfig::error("Unsupported user-supplied action parameter");
996 if(!action->is_ready())
998 synfig::error("Action not ready");
1002 perform_action(action);
1006 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1008 Gtk::Menu& parammenu(*menu);
1010 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1012 if(!canvas_interface)
1015 synfigapp::Action::ParamList param_list,param_list2;
1016 param_list=canvas_interface->generate_param_list(value_desc);
1017 param_list.add("origin",location);
1019 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1021 param_list2=canvas_interface->generate_param_list(
1022 synfigapp::ValueDesc(
1023 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1027 param_list2.add("origin",location);
1030 // Populate the convert menu by looping through
1031 // the ValueNode book and find the ones that are
1034 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1035 // the 'Index' parameter of a Duplicate layer
1037 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1038 if (!((value_desc.parent_is_layer_param() &&
1039 value_desc.get_layer()->get_name() == "duplicate" &&
1040 value_desc.get_param_name() == "index") ||
1041 (value_desc.is_value_node() &&
1042 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1043 !(value_desc.parent_is_layer_param() ||
1044 value_desc.parent_is_value_node()))))
1046 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1047 LinkableValueNode::Book::const_iterator iter;
1048 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1050 if(iter->second.check_type(value_desc.get_value_type()))
1052 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1056 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1066 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1069 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1072 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1074 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1075 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1077 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1078 if(value_desc.is_valid())
1079 param_list.add("selected_value_desc",value_desc);
1083 if(param_list2.empty())
1084 add_actions_to_menu(¶mmenu, param_list,categories);
1086 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
1088 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1090 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1093 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1095 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1099 // try to find a waypoint at the current time - if we
1100 // can't, we don't want the menu entry - an exception is thrown
1101 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1102 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1103 waypoint_set.insert(*iter);
1105 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1109 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1125 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1127 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1130 "Edit Multiple Waypoints", // Title
1132 true // use_separator
1135 Widget_WaypointModel widget_waypoint_model;
1136 widget_waypoint_model.show();
1138 dialog.get_vbox()->pack_start(widget_waypoint_model);
1140 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1141 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1144 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1146 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1148 std::list<synfigapp::ValueDesc>::iterator iter;
1149 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1151 synfigapp::ValueDesc value_desc(*iter);
1153 if(!value_desc.is_valid())
1156 ValueNode_Animated::Handle value_node;
1158 // If this value isn't a ValueNode_Animated, but
1159 // it is somewhat constant, then go ahead and convert
1160 // it to a ValueNode_Animated.
1161 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1164 if(value_desc.is_value_node())
1165 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1167 value=value_desc.get_value();
1169 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1171 synfigapp::Action::Handle action;
1173 if(!value_desc.is_value_node())
1175 action=synfigapp::Action::create("value_desc_connect");
1176 action->set_param("dest",value_desc);
1177 action->set_param("src",ValueNode::Handle(value_node));
1181 action=synfigapp::Action::create("value_node_replace");
1182 action->set_param("dest",value_desc.get_value_node());
1183 action->set_param("src",ValueNode::Handle(value_node));
1186 action->set_param("canvas",canvas_view->get_canvas());
1187 action->set_param("canvas_interface",canvas_interface);
1189 if(!canvas_interface->get_instance()->perform_action(action))
1191 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1198 if(value_desc.is_value_node())
1199 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1204 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1208 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1213 action->set_param("canvas",canvas_view->get_canvas());
1214 action->set_param("canvas_interface",canvas_interface);
1215 action->set_param("value_node",ValueNode::Handle(value_node));
1216 action->set_param("time",canvas_interface->get_time());
1217 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1219 if(!canvas_interface->get_instance()->perform_action(action))
1221 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1228 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1237 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1239 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1241 synfigapp::Action::ParamList param_list;
1242 param_list=canvas_interface->generate_param_list(value_desc_list);
1244 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1246 // Add the edit waypoints option if that might be useful
1247 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1249 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1253 &edit_several_waypoints
1257 find_canvas_view(canvas)