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));
232 string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
233 App::dialog_error_blocking(_("Save - Error"), msg.c_str());
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(App::custom_filename_prefix.c_str()) != 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 string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n"
264 "on a composition that is being referenced by other\n"
265 "files that are currently open. Close these\n"
266 "other files first before trying to use \"SaveAs\".")));
267 App::dialog_error_blocking(_("SaveAs - Error"), msg.c_str());
277 if (has_real_filename())
278 filename = absolute_path(filename);
280 // show the canvas' name if it has one, else its ID
281 while (App::dialog_save_file((_("Choose a Filename to Save As") +
283 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
285 filename, ANIMATION_DIR_PREFERENCE))
287 // If the filename still has wildcards, then we should
288 // continue looking for the file we want
289 string base_filename = basename(filename);
290 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
293 if (filename_extension(filename) == "")
298 String ext(filename_extension(filename));
299 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
300 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
310 int stat_return = stat(filename.c_str(), &s);
312 // if stat() fails with something other than 'file doesn't exist', there's been a real
313 // error of some kind. let's give up now and ask for a new path.
314 if (stat_return == -1 && errno != ENOENT)
316 perror(filename.c_str());
317 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
318 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
322 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
323 string msg(strprintf(_("A file named '%s' already exists.\n\n"
324 "Do you want to replace it with the file you are saving?"), filename.c_str()));
325 if ((stat_return == 0) &&
326 !App::dialog_yes_no(_("File exists"),msg.c_str()))
330 if(save_as(filename))
332 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
335 string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
336 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
343 Instance::update_all_titles()
345 list<handle<CanvasView> >::iterator iter;
346 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
347 (*iter)->update_title();
353 // This will increase the reference count so we don't get DELETED
354 // until we are ready
355 handle<Instance> me(this);
357 // Make sure we aren't selected as the current instance
358 if(studio::App::get_selected_instance()==this)
359 studio::App::set_selected_instance(0);
361 // Turn-off/clean-up auto recovery
362 studio::App::auto_recover->clear_backup(get_canvas());
364 // Remove us from the active instance list
365 std::list<etl::handle<studio::Instance> >::iterator iter;
366 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
369 assert(iter!=studio::App::instance_list.end());
370 if(iter!=studio::App::instance_list.end())
371 studio::App::instance_list.erase(iter);
373 // Send out a signal that we are being deleted
374 studio::App::signal_instance_deleted()(this);
376 // Hide all of the canvas views
377 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
380 // Consume pending events before deleting the canvas views
381 while(studio::App::events_pending())studio::App::iteration(false);
383 // Delete all of the canvas views
384 canvas_view_list().clear();
386 // If there is another open instance to select,
387 // go ahead and do so. If not, never mind.
388 if(studio::App::instance_list.empty())
390 studio::App::set_selected_canvas_view(0);
391 studio::App::set_selected_instance(0);
394 studio::App::instance_list.front()->canvas_view_list().front()->present();
398 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
400 CanvasTreeModel canvas_tree_model;
403 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
404 row[canvas_tree_model.id] = canvas->get_id();
405 row[canvas_tree_model.name] = canvas->get_name();
406 if(canvas->is_root())
407 row[canvas_tree_model.label] = basename(canvas->get_file_name());
409 if(!canvas->get_id().empty())
410 row[canvas_tree_model.label] = canvas->get_id();
412 if(!canvas->get_name().empty())
413 row[canvas_tree_model.label] = canvas->get_name();
415 row[canvas_tree_model.label] = _("[Unnamed]");
417 row[canvas_tree_model.canvas] = canvas;
418 row[canvas_tree_model.is_canvas] = true;
419 row[canvas_tree_model.is_value_node] = false;
422 synfig::Canvas::Children::iterator iter;
423 synfig::Canvas::Children &children(canvas->children());
425 for(iter=children.begin();iter!=children.end();iter++)
426 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
430 if(!canvas->value_node_list().empty())
432 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
434 valuenode_row[canvas_tree_model.label] = "<defs>";
435 valuenode_row[canvas_tree_model.canvas] = canvas;
436 valuenode_row[canvas_tree_model.is_canvas] = false;
437 valuenode_row[canvas_tree_model.is_value_node] = false;
439 synfig::ValueNodeList::iterator iter;
440 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
442 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
443 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
450 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
452 CanvasTreeModel canvas_tree_model;
456 row[canvas_tree_model.id] = value_node->get_id();
457 row[canvas_tree_model.name] = value_node->get_id();
458 row[canvas_tree_model.label] = value_node->get_id();
459 row[canvas_tree_model.canvas] = canvas;
460 row[canvas_tree_model.value_node] = value_node;
461 row[canvas_tree_model.is_canvas] = false;
462 row[canvas_tree_model.is_value_node] = true;
463 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
468 Instance::refresh_canvas_tree()
470 canvas_tree_store()->clear();
471 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
472 insert_canvas(row,get_canvas());
476 Instance::dialog_cvs_commit()
478 calc_repository_info();
481 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
488 if(synfigapp::Instance::get_action_count())
490 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
497 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
501 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
504 OneMoment one_moment;
509 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
515 Instance::dialog_cvs_add()
517 calc_repository_info();
520 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
527 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
529 OneMoment one_moment;
534 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
540 Instance::dialog_cvs_update()
542 calc_repository_info();
545 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
550 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
556 String filename(get_file_name());
557 if(synfigapp::Instance::get_action_count())
559 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
563 OneMoment one_moment;
564 time_t oldtime=get_original_timestamp();
566 calc_repository_info();
567 // If something has been updated...
568 if(oldtime!=get_original_timestamp())
575 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
577 //update_all_titles();
581 Instance::dialog_cvs_revert()
583 calc_repository_info();
586 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
591 String filename(get_file_name());
592 if(!App::dialog_yes_no(_("CVS Revert"),
593 _("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?")
597 OneMoment one_moment;
599 // Remove the old file
600 if(remove(get_file_name().c_str())!=0)
602 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
611 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
613 //update_all_titles();
617 Instance::_revert(Instance *instance)
619 OneMoment one_moment;
621 String filename(instance->get_file_name());
623 Canvas::Handle canvas(instance->get_canvas());
627 if(canvas->count()!=1)
630 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."));
641 // Schedule a revert to occur in a few moments
642 Glib::signal_timeout().connect(
645 sigc::ptr_fun(&Instance::_revert),
655 Instance::safe_revert()
657 if(synfigapp::Instance::get_action_count())
658 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
665 Instance::safe_close()
667 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
668 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
670 // if the animation is currently playing, closing the window will cause a crash,
672 if (canvas_view->is_playing())
674 canvas_view->present();
675 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
678 if(get_action_count())
681 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
682 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
683 if(answer==synfigapp::UIInterface::RESPONSE_YES)
685 enum Status status = save();
686 if (status == STATUS_OK) break;
687 else if (status == STATUS_CANCEL) return false;
689 if(answer==synfigapp::UIInterface::RESPONSE_NO)
691 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
697 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());
698 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
700 if(answer==synfigapp::UIInterface::RESPONSE_YES)
702 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
712 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
714 synfigapp::Action::CandidateList candidate_list;
715 synfigapp::Action::CandidateList::iterator iter;
717 candidate_list=compile_candidate_list(param_list,category);
719 candidate_list.sort();
721 // if(candidate_list.empty())
722 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
724 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
726 Gtk::StockID stock_id(get_action_stock_id(*iter));
728 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
730 action_group->add(Gtk::Action::create(
731 "action-"+iter->name,
733 iter->local_name,iter->local_name
738 *const_cast<studio::Instance*>(this),
739 &studio::Instance::process_action
746 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
752 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
754 synfigapp::Action::CandidateList candidate_list;
755 synfigapp::Action::CandidateList::iterator iter;
757 candidate_list=compile_candidate_list(param_list,category);
759 candidate_list.sort();
761 if(candidate_list.empty())
762 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
764 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
766 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
768 Gtk::Image* image(manage(new Gtk::Image()));
769 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
772 if(iter->task=="raise")
773 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
774 else if(iter->task=="lower")
775 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="move_top")
777 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
778 else if(iter->task=="move_bottom")
779 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
780 else if(iter->task=="remove")
781 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
782 else if(iter->task=="set_on")
783 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
784 else if(iter->task=="set_off")
785 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
786 else if(iter->task=="duplicate")
787 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
788 else if(iter->task=="remove")
789 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
792 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
793 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
794 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
797 menu->items().push_back(
798 Gtk::Menu_Helpers::ImageMenuElem(
804 *const_cast<studio::Instance*>(this),
805 &studio::Instance::process_action
818 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
820 synfigapp::Action::CandidateList candidate_list;
821 synfigapp::Action::CandidateList candidate_list2;
823 synfigapp::Action::CandidateList::iterator iter;
825 candidate_list=compile_candidate_list(param_list,category);
826 candidate_list2=compile_candidate_list(param_list2,category);
828 candidate_list.sort();
830 if(candidate_list.empty())
831 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
832 if(candidate_list2.empty())
833 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
835 // Separate out the candidate lists so that there are no conflicts
836 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
838 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
839 if(iter2!=candidate_list2.end())
840 candidate_list2.erase(iter2);
843 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
845 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
847 Gtk::Image* image(manage(new Gtk::Image()));
848 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
850 /* if(iter->task=="raise")
851 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
852 else if(iter->task=="lower")
853 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="move_top")
855 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="move_bottom")
857 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="remove")
859 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="set_on")
861 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
862 else if(iter->task=="set_off")
863 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
864 else if(iter->task=="duplicate")
865 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
866 else if(iter->task=="remove")
867 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
870 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
871 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
872 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
875 menu->items().push_back(
876 Gtk::Menu_Helpers::ImageMenuElem(
882 *const_cast<studio::Instance*>(this),
883 &studio::Instance::process_action
894 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
896 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
898 Gtk::Image* image(manage(new Gtk::Image()));
899 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
900 /* if(iter->task=="raise")
901 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
902 else if(iter->task=="lower")
903 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="move_top")
905 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
906 else if(iter->task=="move_bottom")
907 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
908 else if(iter->task=="remove")
909 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
910 else if(iter->task=="set_on")
911 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
912 else if(iter->task=="set_off")
913 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
914 else if(iter->task=="duplicate")
915 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
916 else if(iter->task=="remove")
917 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
920 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
921 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
922 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
925 menu->items().push_back(
926 Gtk::Menu_Helpers::ImageMenuElem(
932 *const_cast<studio::Instance*>(this),
933 &studio::Instance::process_action
946 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
948 assert(synfigapp::Action::book().count(name));
950 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
952 synfigapp::Action::Handle action(entry.factory());
956 synfig::error("Bad Action");
960 action->set_param_list(param_list);
962 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
963 synfigapp::Action::ParamVocab::const_iterator iter;
965 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
967 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
970 // If the parameter is optionally user-supplied,
971 // and has not been already provided in the param_list,
972 // then we should go ahead and see if we can
973 // provide that data.
974 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
976 switch(iter->get_type())
978 case synfigapp::Action::Param::TYPE_STRING:
981 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
983 action->set_param(iter->get_name(),str);
987 synfig::error("Unsupported user-supplied action parameter");
994 if(!action->is_ready())
996 synfig::error("Action not ready");
1000 perform_action(action);
1004 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1006 Gtk::Menu& parammenu(*menu);
1008 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1010 if(!canvas_interface)
1013 synfigapp::Action::ParamList param_list,param_list2;
1014 param_list=canvas_interface->generate_param_list(value_desc);
1015 param_list.add("origin",location);
1017 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1019 param_list2=canvas_interface->generate_param_list(
1020 synfigapp::ValueDesc(
1021 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1025 param_list2.add("origin",location);
1028 // Populate the convert menu by looping through
1029 // the ValueNode book and find the ones that are
1032 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1033 // the 'Index' parameter of a Duplicate layer
1035 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1036 if (!((value_desc.parent_is_layer_param() &&
1037 value_desc.get_layer()->get_name() == "duplicate" &&
1038 value_desc.get_param_name() == "index") ||
1039 (value_desc.is_value_node() &&
1040 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1041 !(value_desc.parent_is_layer_param() ||
1042 value_desc.parent_is_value_node()))))
1044 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1045 LinkableValueNode::Book::const_iterator iter;
1046 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1048 if(iter->second.check_type(value_desc.get_value_type()))
1050 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1054 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1064 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1067 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1070 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1072 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1073 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1075 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1076 if(value_desc.is_valid())
1077 param_list.add("selected_value_desc",value_desc);
1081 if(param_list2.empty())
1082 add_actions_to_menu(¶mmenu, param_list,categories);
1084 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
1086 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1088 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1091 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1093 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1097 // try to find a waypoint at the current time - if we
1098 // can't, we don't want the menu entry - an exception is thrown
1099 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1100 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1101 waypoint_set.insert(*iter);
1103 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1107 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1123 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1125 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1128 "Edit Multiple Waypoints", // Title
1130 true // use_separator
1133 Widget_WaypointModel widget_waypoint_model;
1134 widget_waypoint_model.show();
1136 dialog.get_vbox()->pack_start(widget_waypoint_model);
1138 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1139 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1142 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1144 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1146 std::list<synfigapp::ValueDesc>::iterator iter;
1147 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1149 synfigapp::ValueDesc value_desc(*iter);
1151 if(!value_desc.is_valid())
1154 ValueNode_Animated::Handle value_node;
1156 // If this value isn't a ValueNode_Animated, but
1157 // it is somewhat constant, then go ahead and convert
1158 // it to a ValueNode_Animated.
1159 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1162 if(value_desc.is_value_node())
1163 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1165 value=value_desc.get_value();
1167 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1169 synfigapp::Action::Handle action;
1171 if(!value_desc.is_value_node())
1173 action=synfigapp::Action::create("value_desc_connect");
1174 action->set_param("dest",value_desc);
1175 action->set_param("src",ValueNode::Handle(value_node));
1179 action=synfigapp::Action::create("value_node_replace");
1180 action->set_param("dest",value_desc.get_value_node());
1181 action->set_param("src",ValueNode::Handle(value_node));
1184 action->set_param("canvas",canvas_view->get_canvas());
1185 action->set_param("canvas_interface",canvas_interface);
1187 if(!canvas_interface->get_instance()->perform_action(action))
1189 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1196 if(value_desc.is_value_node())
1197 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1202 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1206 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1211 action->set_param("canvas",canvas_view->get_canvas());
1212 action->set_param("canvas_interface",canvas_interface);
1213 action->set_param("value_node",ValueNode::Handle(value_node));
1214 action->set_param("time",canvas_interface->get_time());
1215 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1217 if(!canvas_interface->get_instance()->perform_action(action))
1219 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1226 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1235 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1237 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1239 synfigapp::Action::ParamList param_list;
1240 param_list=canvas_interface->generate_param_list(value_desc_list);
1242 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1244 // Add the edit waypoints option if that might be useful
1245 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1247 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1251 &edit_several_waypoints
1255 find_canvas_view(canvas)