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
10 ** Copyright (c) 2008 Carlos López
12 ** This package is free software; you can redistribute it and/or
13 ** modify it under the terms of the GNU General Public License as
14 ** published by the Free Software Foundation; either version 2 of
15 ** the License, or (at your option) any later version.
17 ** This package is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ** General Public License for more details.
23 /* ========================================================================= */
25 /* === H E A D E R S ======================================================= */
36 #include <gtkmm/stock.h>
37 #include <gtkmm/image.h>
39 #include <gtkmm/button.h>
40 #include "canvasview.h"
42 #include <sigc++/signal.h>
43 #include <sigc++/adaptors/hide.h>
45 #include "onemoment.h"
46 #include <synfig/savecanvas.h>
48 #include "autorecover.h"
49 #include <sigc++/retype_return.h>
50 #include <sigc++/retype.h>
51 //#include <sigc++/hide.h>
52 #include <synfig/valuenode_composite.h>
53 #include <synfig/valuenode_duplicate.h>
54 #include "widget_waypointmodel.h"
55 #include <gtkmm/actiongroup.h>
56 #include "iconcontroller.h"
60 #include <ETL/stringf>
68 using namespace synfig;
69 using namespace studio;
72 /* === M A C R O S ========================================================= */
74 /* === G L O B A L S ======================================================= */
76 int studio::Instance::instance_count_=0;
78 /* === P R O C E D U R E S ================================================= */
80 /* === M E T H O D S ======================================================= */
82 Instance::Instance(synfig::Canvas::Handle canvas):
83 synfigapp::Instance (canvas),
84 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
85 // canvas_tree_store_ (Gtk::TreeStore::create()),
86 history_tree_store_ (HistoryTreeStore::create(this)),
90 CanvasTreeModel model;
91 canvas_tree_store_=Gtk::TreeStore::create(model);
93 id_=instance_count_++;
95 // Connect up all the signals
96 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
97 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
98 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
99 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
101 signal_saved().connect(
104 studio::AutoRecover::auto_backup
109 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
111 refresh_canvas_tree();
114 Instance::~Instance()
119 Instance::get_visible_canvases()const
122 CanvasViewList::const_iterator iter;
123 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
124 if((*iter)->is_visible())
130 Instance::create(synfig::Canvas::Handle canvas)
132 // Construct a new instance
133 handle<Instance> instance(new Instance(canvas));
135 // Add the new instance to the application's instance list
136 App::instance_list.push_back(instance);
138 // Set up the instance with the default UI manager
139 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
141 // Signal the new instance
142 App::signal_instance_created()(instance);
144 // And then make sure that is has been selected
145 App::set_selected_instance(instance);
147 // Create the initial window for the root canvas
148 instance->focus(canvas);
154 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
159 while(canvas->is_inline())
160 canvas=canvas->parent();
162 CanvasViewList::iterator iter;
164 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
165 if((*iter)->get_canvas()==canvas)
168 return CanvasView::create(this,canvas);
172 Instance::focus(etl::handle<synfig::Canvas> canvas)
174 handle<CanvasView> canvas_view=find_canvas_view(canvas);
176 canvas_view->present();
180 Instance::set_undo_status(bool x)
183 App::toolbox->update_undo_redo();
184 signal_undo_redo_status_changed()();
188 Instance::set_redo_status(bool x)
191 App::toolbox->update_undo_redo();
192 signal_undo_redo_status_changed()();
196 studio::Instance::save_as(const synfig::String &file_name)
198 if(synfigapp::Instance::save_as(file_name))
200 // after changing the filename, update the render settings with the new filename
201 list<handle<CanvasView> >::iterator iter;
202 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
203 (*iter)->render_settings.set_entry_filename();
204 App::add_recent_file(etl::handle<Instance>(this));
211 studio::Instance::open()
213 App::dialog_open(get_file_name());
217 studio::Instance::save()
219 // if we don't have a real filename yet then we need to ask where to save it
220 if (!has_real_filename())
222 if (dialog_save_as())
225 return STATUS_CANCEL;
228 if (synfigapp::Instance::save())
230 App::add_recent_file(etl::handle<Instance>(this));
233 string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
234 App::dialog_error_blocking(_("Save - Error"), msg.c_str());
238 // the filename will be set to "Synfig Animation 1" or some such when first created
239 // and will be changed to an absolute path once it has been saved
240 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
242 studio::Instance::has_real_filename()
244 return get_file_name().find(App::custom_filename_prefix.c_str()) != 0;
248 studio::Instance::dialog_save_as()
250 string filename = get_file_name();
251 Canvas::Handle canvas(get_canvas());
254 OneMoment one_moment;
255 std::set<Node*>::iterator iter;
256 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
258 synfig::Node* node(*iter);
259 for(;!node->parent_set.empty();node=*node->parent_set.begin())
261 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
262 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
264 string msg(strprintf(_("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\".")));
268 App::dialog_error_blocking(_("SaveAs - Error"), msg.c_str());
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 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
319 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
323 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
324 string msg(strprintf(_("A file named '%s' already exists.\n\n"
325 "Do you want to replace it with the file you are saving?"), filename.c_str()));
326 if ((stat_return == 0) &&
327 !App::dialog_yes_no(_("File exists"),msg.c_str()))
331 if(save_as(filename))
333 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
336 string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
337 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
344 Instance::update_all_titles()
346 list<handle<CanvasView> >::iterator iter;
347 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
348 (*iter)->update_title();
354 // This will increase the reference count so we don't get DELETED
355 // until we are ready
356 handle<Instance> me(this);
358 // Make sure we aren't selected as the current instance
359 if(studio::App::get_selected_instance()==this)
360 studio::App::set_selected_instance(0);
362 // Turn-off/clean-up auto recovery
363 studio::App::auto_recover->clear_backup(get_canvas());
365 // Remove us from the active instance list
366 std::list<etl::handle<studio::Instance> >::iterator iter;
367 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
370 assert(iter!=studio::App::instance_list.end());
371 if(iter!=studio::App::instance_list.end())
372 studio::App::instance_list.erase(iter);
374 // Send out a signal that we are being deleted
375 studio::App::signal_instance_deleted()(this);
377 // Hide all of the canvas views
378 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
381 // Consume pending events before deleting the canvas views
382 while(studio::App::events_pending())studio::App::iteration(false);
384 // Delete all of the canvas views
385 canvas_view_list().clear();
387 // If there is another open instance to select,
388 // go ahead and do so. If not, never mind.
389 if(studio::App::instance_list.empty())
391 studio::App::set_selected_canvas_view(0);
392 studio::App::set_selected_instance(0);
395 studio::App::instance_list.front()->canvas_view_list().front()->present();
399 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
401 CanvasTreeModel canvas_tree_model;
404 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
405 row[canvas_tree_model.id] = canvas->get_id();
406 row[canvas_tree_model.name] = canvas->get_name();
407 if(canvas->is_root())
408 row[canvas_tree_model.label] = basename(canvas->get_file_name());
410 if(!canvas->get_id().empty())
411 row[canvas_tree_model.label] = canvas->get_id();
413 if(!canvas->get_name().empty())
414 row[canvas_tree_model.label] = canvas->get_name();
416 row[canvas_tree_model.label] = _("[Unnamed]");
418 row[canvas_tree_model.canvas] = canvas;
419 row[canvas_tree_model.is_canvas] = true;
420 row[canvas_tree_model.is_value_node] = false;
423 synfig::Canvas::Children::iterator iter;
424 synfig::Canvas::Children &children(canvas->children());
426 for(iter=children.begin();iter!=children.end();iter++)
427 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
431 if(!canvas->value_node_list().empty())
433 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
435 valuenode_row[canvas_tree_model.label] = "<defs>";
436 valuenode_row[canvas_tree_model.canvas] = canvas;
437 valuenode_row[canvas_tree_model.is_canvas] = false;
438 valuenode_row[canvas_tree_model.is_value_node] = false;
440 synfig::ValueNodeList::iterator iter;
441 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
443 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
444 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
451 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
453 CanvasTreeModel canvas_tree_model;
457 row[canvas_tree_model.id] = value_node->get_id();
458 row[canvas_tree_model.name] = value_node->get_id();
459 row[canvas_tree_model.label] = value_node->get_id();
460 row[canvas_tree_model.canvas] = canvas;
461 row[canvas_tree_model.value_node] = value_node;
462 row[canvas_tree_model.is_canvas] = false;
463 row[canvas_tree_model.is_value_node] = true;
464 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
469 Instance::refresh_canvas_tree()
471 canvas_tree_store()->clear();
472 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
473 insert_canvas(row,get_canvas());
477 Instance::dialog_cvs_commit()
479 calc_repository_info();
482 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
489 if(synfigapp::Instance::get_action_count())
491 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
498 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
502 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
505 OneMoment one_moment;
510 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
516 Instance::dialog_cvs_add()
518 calc_repository_info();
521 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
528 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
530 OneMoment one_moment;
535 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
541 Instance::dialog_cvs_update()
543 calc_repository_info();
546 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
551 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
557 String filename(get_file_name());
558 if(synfigapp::Instance::get_action_count())
560 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
564 OneMoment one_moment;
565 time_t oldtime=get_original_timestamp();
567 calc_repository_info();
568 // If something has been updated...
569 if(oldtime!=get_original_timestamp())
576 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
578 //update_all_titles();
582 Instance::dialog_cvs_revert()
584 calc_repository_info();
587 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
592 String filename(get_file_name());
593 if(!App::dialog_yes_no(_("CVS Revert"),
594 _("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?")
598 OneMoment one_moment;
600 // Remove the old file
601 if(remove(get_file_name().c_str())!=0)
603 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
612 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
614 //update_all_titles();
618 Instance::_revert(Instance *instance)
620 OneMoment one_moment;
622 String filename(instance->get_file_name());
624 Canvas::Handle canvas(instance->get_canvas());
628 if(canvas->count()!=1)
631 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."));
642 // Schedule a revert to occur in a few moments
643 Glib::signal_timeout().connect(
646 sigc::ptr_fun(&Instance::_revert),
656 Instance::safe_revert()
658 if(synfigapp::Instance::get_action_count())
659 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
666 Instance::safe_close()
668 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
669 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
671 // if the animation is currently playing, closing the window will cause a crash,
673 if (canvas_view->is_playing())
675 canvas_view->present();
676 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
679 if(get_action_count())
682 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
683 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
684 if(answer==synfigapp::UIInterface::RESPONSE_YES)
686 enum Status status = save();
687 if (status == STATUS_OK) break;
688 else if (status == STATUS_CANCEL) return false;
690 if(answer==synfigapp::UIInterface::RESPONSE_NO)
692 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
698 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());
699 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
701 if(answer==synfigapp::UIInterface::RESPONSE_YES)
703 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
713 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
715 synfigapp::Action::CandidateList candidate_list;
716 synfigapp::Action::CandidateList::iterator iter;
718 candidate_list=compile_candidate_list(param_list,category);
720 candidate_list.sort();
722 // if(candidate_list.empty())
723 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
725 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
727 Gtk::StockID stock_id(get_action_stock_id(*iter));
729 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
731 action_group->add(Gtk::Action::create(
732 "action-"+iter->name,
734 iter->local_name,iter->local_name
739 *const_cast<studio::Instance*>(this),
740 &studio::Instance::process_action
747 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
753 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
755 synfigapp::Action::CandidateList candidate_list;
756 synfigapp::Action::CandidateList::iterator iter;
758 candidate_list=compile_candidate_list(param_list,category);
760 candidate_list.sort();
762 if(candidate_list.empty())
763 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
765 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
767 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
769 Gtk::Image* image(manage(new Gtk::Image()));
770 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
773 if(iter->task=="raise")
774 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
775 else if(iter->task=="lower")
776 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
777 else if(iter->task=="move_top")
778 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
779 else if(iter->task=="move_bottom")
780 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
781 else if(iter->task=="remove")
782 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
783 else if(iter->task=="set_on")
784 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
785 else if(iter->task=="set_off")
786 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
787 else if(iter->task=="duplicate")
788 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
789 else if(iter->task=="remove")
790 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
793 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
794 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
795 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
798 menu->items().push_back(
799 Gtk::Menu_Helpers::ImageMenuElem(
805 *const_cast<studio::Instance*>(this),
806 &studio::Instance::process_action
819 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
821 synfigapp::Action::CandidateList candidate_list;
822 synfigapp::Action::CandidateList candidate_list2;
824 synfigapp::Action::CandidateList::iterator iter;
826 candidate_list=compile_candidate_list(param_list,category);
827 candidate_list2=compile_candidate_list(param_list2,category);
829 candidate_list.sort();
831 if(candidate_list.empty())
832 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
833 if(candidate_list2.empty())
834 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
836 // Separate out the candidate lists so that there are no conflicts
837 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
839 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
840 if(iter2!=candidate_list2.end())
841 candidate_list2.erase(iter2);
844 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
846 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
848 Gtk::Image* image(manage(new Gtk::Image()));
849 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
851 /* if(iter->task=="raise")
852 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
853 else if(iter->task=="lower")
854 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
855 else if(iter->task=="move_top")
856 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
857 else if(iter->task=="move_bottom")
858 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
859 else if(iter->task=="remove")
860 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
861 else if(iter->task=="set_on")
862 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
863 else if(iter->task=="set_off")
864 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
865 else if(iter->task=="duplicate")
866 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
867 else if(iter->task=="remove")
868 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
871 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
872 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
873 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
876 menu->items().push_back(
877 Gtk::Menu_Helpers::ImageMenuElem(
883 *const_cast<studio::Instance*>(this),
884 &studio::Instance::process_action
895 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
897 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
899 Gtk::Image* image(manage(new Gtk::Image()));
900 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
901 /* if(iter->task=="raise")
902 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
903 else if(iter->task=="lower")
904 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
905 else if(iter->task=="move_top")
906 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
907 else if(iter->task=="move_bottom")
908 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
909 else if(iter->task=="remove")
910 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
911 else if(iter->task=="set_on")
912 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
913 else if(iter->task=="set_off")
914 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
915 else if(iter->task=="duplicate")
916 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
917 else if(iter->task=="remove")
918 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
921 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
922 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
923 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
926 menu->items().push_back(
927 Gtk::Menu_Helpers::ImageMenuElem(
933 *const_cast<studio::Instance*>(this),
934 &studio::Instance::process_action
947 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
950 // synfig::info("%s:%d process_action: '%s'", __FILE__, __LINE__, name.c_str());
952 assert(synfigapp::Action::book().count(name));
954 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
956 synfigapp::Action::Handle action(entry.factory());
960 synfig::error("Bad Action");
964 action->set_param_list(param_list);
966 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
967 synfigapp::Action::ParamVocab::const_iterator iter;
969 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
971 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
974 // If the parameter is optionally user-supplied,
975 // and has not been already provided in the param_list,
976 // then we should go ahead and see if we can
977 // provide that data.
978 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
980 switch(iter->get_type())
982 case synfigapp::Action::Param::TYPE_STRING:
985 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+": "+iter->get_desc(),str))
987 action->set_param(iter->get_name(),str);
991 synfig::error("Unsupported user-supplied action parameter");
998 if(!action->is_ready())
1000 synfig::error("Action not ready");
1004 perform_action(action);
1008 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1010 Gtk::Menu& parammenu(*menu);
1012 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1014 if(!canvas_interface)
1017 synfigapp::Action::ParamList param_list,param_list2;
1018 param_list=canvas_interface->generate_param_list(value_desc);
1019 param_list.add("origin",location);
1021 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1023 param_list2=canvas_interface->generate_param_list(
1024 synfigapp::ValueDesc(
1025 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1029 param_list2.add("origin",location);
1032 // Populate the convert menu by looping through
1033 // the ValueNode book and find the ones that are
1036 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1037 // the 'Index' parameter of a Duplicate layer
1039 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1040 if (!((value_desc.parent_is_layer_param() &&
1041 value_desc.get_layer()->get_name() == "duplicate" &&
1042 value_desc.get_param_name() == "index") ||
1043 (value_desc.is_value_node() &&
1044 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1045 !(value_desc.parent_is_layer_param() ||
1046 value_desc.parent_is_value_node()))))
1048 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1049 LinkableValueNode::Book::const_iterator iter;
1050 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1052 if(iter->second.check_type(value_desc.get_value_type()))
1054 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1058 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1068 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1071 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1074 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1076 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1077 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1079 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1080 if(value_desc.is_valid())
1081 param_list.add("selected_value_desc",value_desc);
1085 if(param_list2.empty())
1086 add_actions_to_menu(¶mmenu, param_list,categories);
1088 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
1090 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1092 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1095 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1097 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1101 // try to find a waypoint at the current time - if we
1102 // can't, we don't want the menu entry - an exception is thrown
1103 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1104 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1105 waypoint_set.insert(*iter);
1107 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1111 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1127 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1129 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1132 "Edit Multiple Waypoints", // Title
1134 true // use_separator
1137 Widget_WaypointModel widget_waypoint_model;
1138 widget_waypoint_model.show();
1140 dialog.get_vbox()->pack_start(widget_waypoint_model);
1142 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1143 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1146 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1148 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1150 std::list<synfigapp::ValueDesc>::iterator iter;
1151 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1153 synfigapp::ValueDesc value_desc(*iter);
1155 if(!value_desc.is_valid())
1158 ValueNode_Animated::Handle value_node;
1160 // If this value isn't a ValueNode_Animated, but
1161 // it is somewhat constant, then go ahead and convert
1162 // it to a ValueNode_Animated.
1163 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1166 if(value_desc.is_value_node())
1167 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1169 value=value_desc.get_value();
1171 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1173 synfigapp::Action::Handle action;
1175 if(!value_desc.is_value_node())
1177 action=synfigapp::Action::create("ValueDescConnect");
1178 action->set_param("dest",value_desc);
1179 action->set_param("src",ValueNode::Handle(value_node));
1183 action=synfigapp::Action::create("ValueNodeReplace");
1184 action->set_param("dest",value_desc.get_value_node());
1185 action->set_param("src",ValueNode::Handle(value_node));
1188 action->set_param("canvas",canvas_view->get_canvas());
1189 action->set_param("canvas_interface",canvas_interface);
1191 if(!canvas_interface->get_instance()->perform_action(action))
1193 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1200 if(value_desc.is_value_node())
1201 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1206 synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
1210 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1215 action->set_param("canvas",canvas_view->get_canvas());
1216 action->set_param("canvas_interface",canvas_interface);
1217 action->set_param("value_node",ValueNode::Handle(value_node));
1218 action->set_param("time",canvas_interface->get_time());
1219 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1221 if(!canvas_interface->get_instance()->perform_action(action))
1223 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1230 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1239 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1241 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1243 synfigapp::Action::ParamList param_list;
1244 param_list=canvas_interface->generate_param_list(value_desc_list);
1246 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1248 // Add the edit waypoints option if that might be useful
1249 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1251 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1255 &edit_several_waypoints
1259 find_canvas_view(canvas)