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"
58 #include <ETL/stringf>
66 using namespace synfig;
67 using namespace studio;
70 /* === M A C R O S ========================================================= */
72 /* === G L O B A L S ======================================================= */
74 int studio::Instance::instance_count_=0;
76 /* === P R O C E D U R E S ================================================= */
78 /* === M E T H O D S ======================================================= */
80 Instance::Instance(synfig::Canvas::Handle canvas):
81 synfigapp::Instance (canvas),
82 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
83 // canvas_tree_store_ (Gtk::TreeStore::create()),
84 history_tree_store_ (HistoryTreeStore::create(this)),
88 CanvasTreeModel model;
89 canvas_tree_store_=Gtk::TreeStore::create(model);
91 id_=instance_count_++;
93 // Connect up all the signals
94 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
95 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
96 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
97 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
99 signal_saved().connect(
102 studio::AutoRecover::auto_backup
107 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
109 refresh_canvas_tree();
112 Instance::~Instance()
117 Instance::get_visible_canvases()const
120 CanvasViewList::const_iterator iter;
121 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
122 if((*iter)->is_visible())
128 Instance::create(synfig::Canvas::Handle canvas)
130 // Construct a new instance
131 handle<Instance> instance(new Instance(canvas));
133 // Add the new instance to the application's instance list
134 App::instance_list.push_back(instance);
136 // Set up the instance with the default UI manager
137 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
139 // Signal the new instance
140 App::signal_instance_created()(instance);
142 // And then make sure that is has been selected
143 App::set_selected_instance(instance);
145 // Create the initial window for the root canvas
146 instance->focus(canvas);
152 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
157 while(canvas->is_inline())
158 canvas=canvas->parent();
160 CanvasViewList::iterator iter;
162 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
163 if((*iter)->get_canvas()==canvas)
166 return CanvasView::create(this,canvas);
170 Instance::focus(etl::handle<synfig::Canvas> canvas)
172 handle<CanvasView> canvas_view=find_canvas_view(canvas);
174 canvas_view->present();
178 Instance::set_undo_status(bool x)
181 App::toolbox->update_undo_redo();
182 signal_undo_redo_status_changed()();
186 Instance::set_redo_status(bool x)
189 App::toolbox->update_undo_redo();
190 signal_undo_redo_status_changed()();
194 studio::Instance::save_as(const synfig::String &file_name)
196 if(synfigapp::Instance::save_as(file_name))
198 // after changing the filename, update the render settings with the new filename
199 list<handle<CanvasView> >::iterator iter;
200 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
201 (*iter)->render_settings.set_entry_filename();
202 App::add_recent_file(etl::handle<Instance>(this));
209 studio::Instance::open()
211 App::dialog_open(get_file_name());
215 studio::Instance::save()
217 // if we don't have a real filename yet then we need to ask where to save it
218 if (!has_real_filename())
220 if (dialog_save_as())
223 return STATUS_CANCEL;
226 if (synfigapp::Instance::save())
228 App::add_recent_file(etl::handle<Instance>(this));
232 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
236 // the filename will be set to "Synfig Animation 1" or some such when first created
237 // and will be changed to an absolute path once it has been saved
238 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
240 studio::Instance::has_real_filename()
242 return get_file_name().find(DEFAULT_FILENAME_PREFIX) != 0;
246 studio::Instance::dialog_save_as()
248 string filename = get_file_name();
249 Canvas::Handle canvas(get_canvas());
252 OneMoment one_moment;
253 std::set<Node*>::iterator iter;
254 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
256 synfig::Node* node(*iter);
257 for(;!node->parent_set.empty();node=*node->parent_set.begin())
259 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
260 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
262 App::dialog_error_blocking("SaveAs - Error",
263 "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\"."
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 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
321 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
322 if ((stat_return == 0) &&
323 !App::dialog_yes_no("File exists",
326 "' already exists.\n\n"
327 "Do you want to replace it with the file you are saving?"))
331 if(save_as(filename))
333 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
337 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
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)
949 assert(synfigapp::Action::book().count(name));
951 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
953 synfigapp::Action::Handle action(entry.factory());
957 synfig::error("Bad Action");
961 action->set_param_list(param_list);
963 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
964 synfigapp::Action::ParamVocab::const_iterator iter;
966 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
968 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
971 // If the parameter is optionally user-supplied,
972 // and has not been already provided in the param_list,
973 // then we should go ahead and see if we can
974 // provide that data.
975 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
977 switch(iter->get_type())
979 case synfigapp::Action::Param::TYPE_STRING:
982 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
984 action->set_param(iter->get_name(),str);
988 synfig::error("Unsupported user-supplied action parameter");
995 if(!action->is_ready())
997 synfig::error("Action not ready");
1001 perform_action(action);
1005 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
1007 Gtk::Menu& parammenu(*menu);
1009 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1011 if(!canvas_interface)
1014 synfigapp::Action::ParamList param_list,param_list2;
1015 param_list=canvas_interface->generate_param_list(value_desc);
1016 param_list.add("origin",location);
1018 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1020 param_list2=canvas_interface->generate_param_list(
1021 synfigapp::ValueDesc(
1022 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1026 param_list2.add("origin",location);
1029 // Populate the convert menu by looping through
1030 // the ValueNode book and find the ones that are
1033 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1034 // the 'Index' parameter of a Duplicate layer
1036 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1037 if (!((value_desc.parent_is_layer_param() &&
1038 value_desc.get_layer()->get_name() == "duplicate" &&
1039 value_desc.get_param_name() == "index") ||
1040 (value_desc.is_value_node() &&
1041 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1042 !(value_desc.parent_is_layer_param() ||
1043 value_desc.parent_is_value_node()))))
1045 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1046 LinkableValueNode::Book::const_iterator iter;
1047 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1049 if(iter->second.check_type(value_desc.get_value_type()))
1051 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1055 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1065 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1068 if(param_list2.empty())
1069 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1071 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1073 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1075 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1078 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1080 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1084 // try to find a waypoint at the current time - if we
1085 // can't, we don't want the menu entry - an exception is thrown
1086 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1087 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1088 waypoint_set.insert(*iter);
1090 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1094 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1110 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1112 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1115 "Edit Multiple Waypoints", // Title
1117 true // use_separator
1120 Widget_WaypointModel widget_waypoint_model;
1121 widget_waypoint_model.show();
1123 dialog.get_vbox()->pack_start(widget_waypoint_model);
1125 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1126 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1129 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1131 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1133 std::list<synfigapp::ValueDesc>::iterator iter;
1134 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1136 synfigapp::ValueDesc value_desc(*iter);
1138 if(!value_desc.is_valid())
1141 ValueNode_Animated::Handle value_node;
1143 // If this value isn't a ValueNode_Animated, but
1144 // it is somewhat constant, then go ahead and convert
1145 // it to a ValueNode_Animated.
1146 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1149 if(value_desc.is_value_node())
1150 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1152 value=value_desc.get_value();
1154 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1156 synfigapp::Action::Handle action;
1158 if(!value_desc.is_value_node())
1160 action=synfigapp::Action::create("value_desc_connect");
1161 action->set_param("dest",value_desc);
1162 action->set_param("src",ValueNode::Handle(value_node));
1166 action=synfigapp::Action::create("value_node_replace");
1167 action->set_param("dest",value_desc.get_value_node());
1168 action->set_param("src",ValueNode::Handle(value_node));
1171 action->set_param("canvas",canvas_view->get_canvas());
1172 action->set_param("canvas_interface",canvas_interface);
1174 if(!canvas_interface->get_instance()->perform_action(action))
1176 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1183 if(value_desc.is_value_node())
1184 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1189 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1193 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1198 action->set_param("canvas",canvas_view->get_canvas());
1199 action->set_param("canvas_interface",canvas_interface);
1200 action->set_param("value_node",ValueNode::Handle(value_node));
1201 action->set_param("time",canvas_interface->get_time());
1202 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1204 if(!canvas_interface->get_instance()->perform_action(action))
1206 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1213 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1222 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1224 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1226 synfigapp::Action::ParamList param_list;
1227 param_list=canvas_interface->generate_param_list(value_desc_list);
1229 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1231 // Add the edit waypoints option if that might be useful
1232 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1234 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1238 &edit_several_waypoints
1242 find_canvas_view(canvas)