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)
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, bool bezier)
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 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1071 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1073 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1074 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1076 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1077 if(value_desc.is_valid())
1078 param_list.add("selected_value_desc",value_desc);
1082 if(param_list2.empty())
1083 add_actions_to_menu(¶mmenu, param_list,categories);
1085 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
1087 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1089 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1092 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1094 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1098 // try to find a waypoint at the current time - if we
1099 // can't, we don't want the menu entry - an exception is thrown
1100 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1101 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1102 waypoint_set.insert(*iter);
1104 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1108 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1124 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1126 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1129 "Edit Multiple Waypoints", // Title
1131 true // use_separator
1134 Widget_WaypointModel widget_waypoint_model;
1135 widget_waypoint_model.show();
1137 dialog.get_vbox()->pack_start(widget_waypoint_model);
1139 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1140 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1143 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1145 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1147 std::list<synfigapp::ValueDesc>::iterator iter;
1148 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1150 synfigapp::ValueDesc value_desc(*iter);
1152 if(!value_desc.is_valid())
1155 ValueNode_Animated::Handle value_node;
1157 // If this value isn't a ValueNode_Animated, but
1158 // it is somewhat constant, then go ahead and convert
1159 // it to a ValueNode_Animated.
1160 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1163 if(value_desc.is_value_node())
1164 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1166 value=value_desc.get_value();
1168 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1170 synfigapp::Action::Handle action;
1172 if(!value_desc.is_value_node())
1174 action=synfigapp::Action::create("value_desc_connect");
1175 action->set_param("dest",value_desc);
1176 action->set_param("src",ValueNode::Handle(value_node));
1180 action=synfigapp::Action::create("value_node_replace");
1181 action->set_param("dest",value_desc.get_value_node());
1182 action->set_param("src",ValueNode::Handle(value_node));
1185 action->set_param("canvas",canvas_view->get_canvas());
1186 action->set_param("canvas_interface",canvas_interface);
1188 if(!canvas_interface->get_instance()->perform_action(action))
1190 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1197 if(value_desc.is_value_node())
1198 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1203 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1207 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1212 action->set_param("canvas",canvas_view->get_canvas());
1213 action->set_param("canvas_interface",canvas_interface);
1214 action->set_param("value_node",ValueNode::Handle(value_node));
1215 action->set_param("time",canvas_interface->get_time());
1216 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1218 if(!canvas_interface->get_instance()->perform_action(action))
1220 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1227 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1236 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1238 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1240 synfigapp::Action::ParamList param_list;
1241 param_list=canvas_interface->generate_param_list(value_desc_list);
1243 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1245 // Add the edit waypoints option if that might be useful
1246 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1248 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1252 &edit_several_waypoints
1256 find_canvas_view(canvas)