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 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"
46 #include "autorecover.h"
47 #include <sigc++/retype_return.h>
48 #include <sigc++/retype.h>
49 //#include <sigc++/hide.h>
50 #include <synfig/valuenode_composite.h>
51 #include <synfig/valuenode_duplicate.h>
52 #include "widget_waypointmodel.h"
53 #include <gtkmm/actiongroup.h>
54 #include "iconcontroller.h"
64 using namespace synfig;
65 using namespace studio;
68 /* === M A C R O S ========================================================= */
70 /* === G L O B A L S ======================================================= */
72 int studio::Instance::instance_count_=0;
74 /* === P R O C E D U R E S ================================================= */
76 /* === M E T H O D S ======================================================= */
78 Instance::Instance(synfig::Canvas::Handle canvas):
79 synfigapp::Instance (canvas),
80 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
81 // canvas_tree_store_ (Gtk::TreeStore::create()),
82 history_tree_store_ (HistoryTreeStore::create(this)),
86 CanvasTreeModel model;
87 canvas_tree_store_=Gtk::TreeStore::create(model);
89 id_=instance_count_++;
91 // Connect up all the signals
92 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
93 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
94 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
95 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
97 signal_saved().connect(
100 studio::AutoRecover::auto_backup
105 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
107 refresh_canvas_tree();
110 Instance::~Instance()
115 Instance::get_visible_canvases()const
118 CanvasViewList::const_iterator iter;
119 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
120 if((*iter)->is_visible())
126 Instance::create(synfig::Canvas::Handle canvas)
128 // Construct a new instance
129 handle<Instance> instance(new Instance(canvas));
131 // Add the new instance to the application's instance list
132 App::instance_list.push_back(instance);
134 // Set up the instance with the default UI manager
135 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
137 // Signal the new instance
138 App::signal_instance_created()(instance);
140 // And then make sure that is has been selected
141 App::set_selected_instance(instance);
143 // Create the initial window for the root canvas
144 instance->focus(canvas);
150 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
155 while(canvas->is_inline())
156 canvas=canvas->parent();
158 CanvasViewList::iterator iter;
160 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
161 if((*iter)->get_canvas()==canvas)
164 return CanvasView::create(this,canvas);
168 Instance::focus(etl::handle<synfig::Canvas> canvas)
170 handle<CanvasView> canvas_view=find_canvas_view(canvas);
172 canvas_view->present();
176 Instance::set_undo_status(bool x)
179 App::toolbox->update_undo_redo();
180 signal_undo_redo_status_changed()();
184 Instance::set_redo_status(bool x)
187 App::toolbox->update_undo_redo();
188 signal_undo_redo_status_changed()();
192 studio::Instance::save_as(const synfig::String &file_name)
194 if(synfigapp::Instance::save_as(file_name))
196 // after changing the filename, update the render settings with the new filename
197 list<handle<CanvasView> >::iterator iter;
198 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
199 (*iter)->render_settings.set_entry_filename();
200 App::add_recent_file(file_name);
207 studio::Instance::save()
209 // the filename will be set to "Synfig Animation 1" or some such when first created
210 // and will be changed to an absolute path once it has been saved
211 // so if it still begins with "Synfig Animation " then we need to ask where to save it
212 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
213 if (dialog_save_as())
216 return STATUS_CANCEL;
218 if (synfigapp::Instance::save())
221 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
226 studio::Instance::dialog_save_as()
228 string filename = get_file_name();
229 Canvas::Handle canvas(get_canvas());
232 OneMoment one_moment;
233 std::set<Node*>::iterator iter;
234 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
236 synfig::Node* node(*iter);
237 for(;!node->parent_set.empty();node=*node->parent_set.begin())
239 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
240 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
242 App::dialog_error_blocking("SaveAs - Error",
243 "There is currently a bug when using \"SaveAs\"\n"
244 "on a composition that is being referenced by other\n"
245 "files that are currently open. Close these\n"
246 "other files first before trying to use \"SaveAs\"."
257 // show the canvas' name if it has one, else its ID
258 while (App::dialog_save_file((_("Choose a Filename to Save As") +
260 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
262 filename, ANIMATION_DIR_PREFERENCE))
264 // If the filename still has wildcards, then we should
265 // continue looking for the file we want
266 string base_filename = basename(filename);
267 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
270 if (filename_extension(filename) == "")
275 String ext(filename_extension(filename));
276 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
277 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
287 int stat_return = stat(filename.c_str(), &s);
289 // if stat() fails with something other than 'file doesn't exist', there's been a real
290 // error of some kind. let's give up now and ask for a new path.
291 if (stat_return == -1 && errno != ENOENT)
293 perror(filename.c_str());
294 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
298 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
299 if ((stat_return == 0) &&
300 !App::dialog_yes_no("File exists",
303 "' already exists.\n\n"
304 "Do you want to replace it with the file you are saving?"))
308 if(save_as(filename))
311 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
318 Instance::update_all_titles()
320 list<handle<CanvasView> >::iterator iter;
321 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
322 (*iter)->update_title();
328 // This will increase the reference count so we don't get DELETED
329 // until we are ready
330 handle<Instance> me(this);
332 // Make sure we aren't selected as the current instance
333 if(studio::App::get_selected_instance()==this)
334 studio::App::set_selected_instance(0);
336 // Turn-off/clean-up auto recovery
337 studio::App::auto_recover->clear_backup(get_canvas());
339 // Remove us from the active instance list
340 std::list<etl::handle<studio::Instance> >::iterator iter;
341 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
344 assert(iter!=studio::App::instance_list.end());
345 if(iter!=studio::App::instance_list.end())
346 studio::App::instance_list.erase(iter);
348 // Send out a signal that we are being deleted
349 studio::App::signal_instance_deleted()(this);
351 // Hide all of the canvas views
352 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
355 // Consume pending events before deleting the canvas views
356 while(studio::App::events_pending())studio::App::iteration(false);
358 // Delete all of the canvas views
359 canvas_view_list().clear();
361 // If there is another open instance to select,
362 // go ahead and do so. If not, never mind.
363 if(studio::App::instance_list.empty())
365 studio::App::set_selected_canvas_view(0);
366 studio::App::set_selected_instance(0);
369 studio::App::instance_list.front()->canvas_view_list().front()->present();
374 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
376 CanvasTreeModel canvas_tree_model;
379 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
380 row[canvas_tree_model.id] = canvas->get_id();
381 row[canvas_tree_model.name] = canvas->get_name();
382 if(canvas->is_root())
383 row[canvas_tree_model.label] = basename(canvas->get_file_name());
385 if(!canvas->get_id().empty())
386 row[canvas_tree_model.label] = canvas->get_id();
388 if(!canvas->get_name().empty())
389 row[canvas_tree_model.label] = canvas->get_name();
391 row[canvas_tree_model.label] = _("[Unnamed]");
393 row[canvas_tree_model.canvas] = canvas;
394 row[canvas_tree_model.is_canvas] = true;
395 row[canvas_tree_model.is_value_node] = false;
398 synfig::Canvas::Children::iterator iter;
399 synfig::Canvas::Children &children(canvas->children());
401 for(iter=children.begin();iter!=children.end();iter++)
402 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
406 if(!canvas->value_node_list().empty())
408 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
410 valuenode_row[canvas_tree_model.label] = "<defs>";
411 valuenode_row[canvas_tree_model.canvas] = canvas;
412 valuenode_row[canvas_tree_model.is_canvas] = false;
413 valuenode_row[canvas_tree_model.is_value_node] = false;
415 synfig::ValueNodeList::iterator iter;
416 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
418 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
419 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
427 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
429 CanvasTreeModel canvas_tree_model;
433 row[canvas_tree_model.id] = value_node->get_id();
434 row[canvas_tree_model.name] = value_node->get_id();
435 row[canvas_tree_model.label] = value_node->get_id();
436 row[canvas_tree_model.canvas] = canvas;
437 row[canvas_tree_model.value_node] = value_node;
438 row[canvas_tree_model.is_canvas] = false;
439 row[canvas_tree_model.is_value_node] = true;
440 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
445 Instance::refresh_canvas_tree()
447 canvas_tree_store()->clear();
448 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
449 insert_canvas(row,get_canvas());
453 Instance::dialog_cvs_commit()
455 calc_repository_info();
458 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
465 if(synfigapp::Instance::get_action_count())
467 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
474 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
478 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
481 OneMoment one_moment;
486 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
492 Instance::dialog_cvs_add()
494 calc_repository_info();
497 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
504 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
506 OneMoment one_moment;
511 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
517 Instance::dialog_cvs_update()
519 calc_repository_info();
522 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
527 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
533 String filename(get_file_name());
534 if(synfigapp::Instance::get_action_count())
536 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
540 OneMoment one_moment;
541 time_t oldtime=get_original_timestamp();
543 calc_repository_info();
544 // If something has been updated...
545 if(oldtime!=get_original_timestamp())
552 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
554 //update_all_titles();
558 Instance::dialog_cvs_revert()
560 calc_repository_info();
563 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
568 String filename(get_file_name());
569 if(!App::dialog_yes_no(_("CVS Revert"),
570 _("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?")
574 OneMoment one_moment;
576 // Remove the old file
577 if(remove(get_file_name().c_str())!=0)
579 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
588 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
590 //update_all_titles();
594 Instance::_revert(Instance *instance)
596 OneMoment one_moment;
598 String filename(instance->get_file_name());
600 Canvas::Handle canvas(instance->get_canvas());
604 if(canvas->count()!=1)
607 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."));
618 // Schedule a revert to occur in a few moments
619 Glib::signal_timeout().connect(
622 sigc::ptr_fun(&Instance::_revert),
632 Instance::safe_revert()
634 if(synfigapp::Instance::get_action_count())
635 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
642 Instance::safe_close()
644 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
645 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
647 // if the animation is currently playing, closing the window will cause a crash,
649 if (canvas_view->is_playing())
651 canvas_view->present();
652 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
655 if(get_action_count())
658 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
659 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
660 if(answer==synfigapp::UIInterface::RESPONSE_YES)
662 enum Status status = save();
663 if (status == STATUS_OK) break;
664 else if (status == STATUS_CANCEL) return false;
666 if(answer==synfigapp::UIInterface::RESPONSE_NO)
668 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
674 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());
675 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
677 if(answer==synfigapp::UIInterface::RESPONSE_YES)
679 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
690 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
692 synfigapp::Action::CandidateList candidate_list;
693 synfigapp::Action::CandidateList::iterator iter;
695 candidate_list=compile_candidate_list(param_list,category);
697 candidate_list.sort();
699 if(candidate_list.empty())
700 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
702 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
704 Gtk::StockID stock_id(get_action_stock_id(*iter));
706 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
708 action_group->add(Gtk::Action::create(
709 "action-"+iter->name,
711 iter->local_name,iter->local_name
716 *const_cast<studio::Instance*>(this),
717 &studio::Instance::process_action
724 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
730 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
732 synfigapp::Action::CandidateList candidate_list;
733 synfigapp::Action::CandidateList::iterator iter;
735 candidate_list=compile_candidate_list(param_list,category);
737 candidate_list.sort();
739 if(candidate_list.empty())
740 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
742 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
744 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
746 Gtk::Image* image(manage(new Gtk::Image()));
747 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
750 if(iter->task=="raise")
751 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
752 else if(iter->task=="lower")
753 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
754 else if(iter->task=="move_top")
755 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
756 else if(iter->task=="move_bottom")
757 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
758 else if(iter->task=="remove")
759 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
760 else if(iter->task=="set_on")
761 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
762 else if(iter->task=="set_off")
763 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
764 else if(iter->task=="duplicate")
765 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
766 else if(iter->task=="remove")
767 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
770 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
771 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
772 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
775 menu->items().push_back(
776 Gtk::Menu_Helpers::ImageMenuElem(
782 *const_cast<studio::Instance*>(this),
783 &studio::Instance::process_action
796 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
798 synfigapp::Action::CandidateList candidate_list;
799 synfigapp::Action::CandidateList candidate_list2;
801 synfigapp::Action::CandidateList::iterator iter;
803 candidate_list=compile_candidate_list(param_list,category);
804 candidate_list2=compile_candidate_list(param_list2,category);
806 candidate_list.sort();
808 if(candidate_list.empty())
809 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
810 if(candidate_list2.empty())
811 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
813 // Separate out the candidate lists so that there are no conflicts
814 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
816 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
817 if(iter2!=candidate_list2.end())
818 candidate_list2.erase(iter2);
821 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
823 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
825 Gtk::Image* image(manage(new Gtk::Image()));
826 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
828 /* if(iter->task=="raise")
829 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
830 else if(iter->task=="lower")
831 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
832 else if(iter->task=="move_top")
833 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
834 else if(iter->task=="move_bottom")
835 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
836 else if(iter->task=="remove")
837 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
838 else if(iter->task=="set_on")
839 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
840 else if(iter->task=="set_off")
841 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
842 else if(iter->task=="duplicate")
843 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
844 else if(iter->task=="remove")
845 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
848 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
849 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
850 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
853 menu->items().push_back(
854 Gtk::Menu_Helpers::ImageMenuElem(
860 *const_cast<studio::Instance*>(this),
861 &studio::Instance::process_action
872 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
874 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
876 Gtk::Image* image(manage(new Gtk::Image()));
877 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
878 /* if(iter->task=="raise")
879 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
880 else if(iter->task=="lower")
881 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
882 else if(iter->task=="move_top")
883 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
884 else if(iter->task=="move_bottom")
885 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
886 else if(iter->task=="remove")
887 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
888 else if(iter->task=="set_on")
889 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
890 else if(iter->task=="set_off")
891 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
892 else if(iter->task=="duplicate")
893 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
894 else if(iter->task=="remove")
895 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
898 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
899 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
900 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
903 menu->items().push_back(
904 Gtk::Menu_Helpers::ImageMenuElem(
910 *const_cast<studio::Instance*>(this),
911 &studio::Instance::process_action
924 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
926 assert(synfigapp::Action::book().count(name));
928 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
930 synfigapp::Action::Handle action(entry.factory());
934 synfig::error("Bad Action");
938 action->set_param_list(param_list);
940 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
941 synfigapp::Action::ParamVocab::const_iterator iter;
943 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
945 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
948 // If the parameter is optionally user-supplied,
949 // and has not been already provided in the param_list,
950 // then we should go ahead and see if we can
951 // provide that data.
952 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
954 switch(iter->get_type())
956 case synfigapp::Action::Param::TYPE_STRING:
959 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
961 action->set_param(iter->get_name(),str);
965 synfig::error("Unsupported user-supplied action parameter");
972 if(!action->is_ready())
974 synfig::error("Action not ready");
978 perform_action(action);
982 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
984 Gtk::Menu& parammenu(*menu);
986 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
988 if(!canvas_interface)
991 synfigapp::Action::ParamList param_list,param_list2;
992 param_list=canvas_interface->generate_param_list(value_desc);
993 param_list.add("origin",location);
995 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
997 param_list2=canvas_interface->generate_param_list(
998 synfigapp::ValueDesc(
999 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1003 param_list2.add("origin",location);
1007 // Populate the convert menu by looping through
1008 // the ValueNode book and find the ones that are
1011 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1012 // the 'Index' parameter of a Duplicate layer
1014 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1015 if (!((value_desc.parent_is_layer_param() &&
1016 value_desc.get_layer()->get_name() == "duplicate" &&
1017 value_desc.get_param_name() == "index") ||
1018 (value_desc.is_value_node() &&
1019 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1020 !(value_desc.parent_is_layer_param() ||
1021 value_desc.parent_is_value_node()))))
1023 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1024 LinkableValueNode::Book::const_iterator iter;
1025 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1027 if(iter->second.check_type(value_desc.get_value_type()))
1029 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1033 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1043 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1046 if(param_list2.empty())
1047 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1049 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1051 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1053 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1056 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1058 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1062 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1063 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1067 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1083 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1085 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1088 "Edit Multiple Waypoints", // Title
1090 true // use_separator
1093 Widget_WaypointModel widget_waypoint_model;
1094 widget_waypoint_model.show();
1096 dialog.get_vbox()->pack_start(widget_waypoint_model);
1099 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1100 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1103 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1105 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1107 std::list<synfigapp::ValueDesc>::iterator iter;
1108 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1110 synfigapp::ValueDesc value_desc(*iter);
1112 if(!value_desc.is_valid())
1115 ValueNode_Animated::Handle value_node;
1117 // If this value isn't a ValueNode_Animated, but
1118 // it is somewhat constant, then go ahead and convert
1119 // it to a ValueNode_Animated.
1120 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1123 if(value_desc.is_value_node())
1124 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1126 value=value_desc.get_value();
1128 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1130 synfigapp::Action::Handle action;
1132 if(!value_desc.is_value_node())
1134 action=synfigapp::Action::create("value_desc_connect");
1135 action->set_param("dest",value_desc);
1136 action->set_param("src",ValueNode::Handle(value_node));
1140 action=synfigapp::Action::create("value_node_replace");
1141 action->set_param("dest",value_desc.get_value_node());
1142 action->set_param("src",ValueNode::Handle(value_node));
1145 action->set_param("canvas",canvas_view->get_canvas());
1146 action->set_param("canvas_interface",canvas_interface);
1149 if(!canvas_interface->get_instance()->perform_action(action))
1151 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1158 if(value_desc.is_value_node())
1159 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1166 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1170 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1176 action->set_param("canvas",canvas_view->get_canvas());
1177 action->set_param("canvas_interface",canvas_interface);
1178 action->set_param("value_node",ValueNode::Handle(value_node));
1179 action->set_param("time",canvas_interface->get_time());
1180 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1182 if(!canvas_interface->get_instance()->perform_action(action))
1184 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1191 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1200 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1202 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1204 synfigapp::Action::ParamList param_list;
1205 param_list=canvas_interface->generate_param_list(value_desc_list);
1207 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1209 // Add the edit waypoints option if that might be useful
1210 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1212 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1216 &edit_several_waypoints
1220 find_canvas_view(canvas)