1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
34 #include <gtkmm/stock.h>
35 #include <gtkmm/image.h>
37 #include <gtkmm/button.h>
38 #include "canvasview.h"
40 #include <sigc++/signal.h>
41 #include <sigc++/adaptors/hide.h>
43 #include "onemoment.h"
45 #include "autorecover.h"
46 #include <sigc++/retype_return.h>
47 #include <sigc++/retype.h>
48 //#include <sigc++/hide.h>
49 #include <synfig/valuenode_composite.h>
50 #include "widget_waypointmodel.h"
51 #include <gtkmm/actiongroup.h>
52 #include "iconcontroler.h"
60 using namespace synfig;
61 using namespace studio;
64 /* === M A C R O S ========================================================= */
66 /* === G L O B A L S ======================================================= */
68 int studio::Instance::instance_count_=0;
70 /* === P R O C E D U R E S ================================================= */
72 /* === M E T H O D S ======================================================= */
74 Instance::Instance(Canvas::Handle canvas):
75 synfigapp::Instance (canvas),
76 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
77 // canvas_tree_store_ (Gtk::TreeStore::create()),
78 history_tree_store_ (HistoryTreeStore::create(this)),
82 CanvasTreeModel model;
83 canvas_tree_store_=Gtk::TreeStore::create(model);
85 id_=instance_count_++;
87 // Connect up all the signals
88 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
89 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
90 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
91 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
93 signal_saved().connect(
96 studio::AutoRecover::auto_backup
101 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
103 refresh_canvas_tree();
106 Instance::~Instance()
111 Instance::get_visible_canvases()const
114 CanvasViewList::const_iterator iter;
115 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
116 if((*iter)->is_visible())
122 Instance::create(Canvas::Handle canvas)
124 // Construct a new instance
125 handle<Instance> instance(new Instance(canvas));
127 // Add the new instance to the application's instance list
128 App::instance_list.push_back(instance);
130 // Set up the instance with the default UI manager
131 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
133 // Signal the new instance
134 App::signal_instance_created()(instance);
136 // And then make sure that is has been selected
137 App::set_selected_instance(instance);
139 // Create the initial window for the root canvas
140 instance->focus(canvas);
146 Instance::find_canvas_view(Canvas::Handle canvas)
151 while(canvas->is_inline())
152 canvas=canvas->parent();
154 CanvasViewList::iterator iter;
156 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
157 if((*iter)->get_canvas()==canvas)
160 return CanvasView::create(this,canvas);
164 Instance::focus(Canvas::Handle canvas)
166 handle<CanvasView> canvas_view=find_canvas_view(canvas);
168 canvas_view->present();
172 Instance::set_undo_status(bool x)
175 App::toolbox->update_undo_redo();
176 signal_undo_redo_status_changed()();
180 Instance::set_redo_status(bool x)
183 App::toolbox->update_undo_redo();
184 signal_undo_redo_status_changed()();
188 studio::Instance::save_as(const synfig::String &file_name)
190 if(synfigapp::Instance::save_as(file_name))
192 // after changing the filename, update the render settings with the new filename
193 list<handle<CanvasView> >::iterator iter;
194 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
195 (*iter)->render_settings.set_entry_filename();
196 App::add_recent_file(file_name);
203 studio::Instance::save()
205 // the filename will be set to "Synfig Animation 1" or some such when first created
206 // and will be changed to an absolute path once it has been saved
207 // so if it still begins with "Synfig Animation " then we need to ask where to save it
208 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
209 if (dialog_save_as())
212 return STATUS_CANCEL;
214 if (synfigapp::Instance::save())
221 studio::Instance::dialog_save_as()
223 string filename=basename(get_file_name());
224 Canvas::Handle canvas(get_canvas());
227 OneMoment one_moment;
228 std::set<Node*>::iterator iter;
229 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
231 synfig::Node* node(*iter);
232 for(;!node->parent_set.empty();node=*node->parent_set.begin())
234 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
235 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
237 App::dialog_error_blocking("SaveAs - Error",
238 "There is currently a bug when using \"SaveAs\"\n"
239 "on a composition that is being referenced by other\n"
240 "files that are currently open. Close these\n"
241 "other files first before trying to use \"SaveAs\"."
252 // show the canvas' name if it has one, else its ID
253 while(App::dialog_save_file(_("Choose a Filename to Save As") +
255 (canvas->get_name().empty()
257 : canvas->get_name()) +
260 // If the filename still has wildcards, then we should
261 // continue looking for the file we want
262 if(find(filename.begin(),filename.end(),'*')!=filename.end())
265 std::string base = basename(filename);
266 if(find(base.begin(),base.end(),'.')==base.end())
271 String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
272 if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
273 _("You have given the file name an extension\nwhich I do not recognise. Are you sure this is what you want?")))
285 // if stat() succeeds, or it fails with something other than 'file doesn't exist', the file exists
286 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
287 if ((stat(filename.c_str(), &s) != -1 || errno != ENOENT) &&
288 !App::dialog_yes_no("File exists",
291 "' already exists.\n\n"
292 "Do you want to replace it with the file you are saving?"))
296 if(save_as(filename))
299 App::dialog_error_blocking("SaveAs - Error","Unable to save file");
306 Instance::update_all_titles()
308 list<handle<CanvasView> >::iterator iter;
309 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
310 (*iter)->update_title();
316 // This will increase the reference count so we don't get DELETED
317 // until we are ready
318 handle<Instance> me(this);
320 // Make sure we aren't selected as the current instance
321 if(studio::App::get_selected_instance()==this)
322 studio::App::set_selected_instance(0);
324 // Turn-off/clean-up auto recovery
325 studio::App::auto_recover->clear_backup(get_canvas());
327 // Remove us from the active instance list
328 std::list<etl::handle<studio::Instance> >::iterator iter;
329 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
332 assert(iter!=studio::App::instance_list.end());
333 if(iter!=studio::App::instance_list.end())
334 studio::App::instance_list.erase(iter);
336 // Send out a signal that we are being deleted
337 studio::App::signal_instance_deleted()(this);
339 // Hide all of the canvas views
340 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
343 // Consume pending events before deleting the canvas views
344 while(studio::App::events_pending())studio::App::iteration(false);
346 // Delete all of the canvas views
347 canvas_view_list().clear();
349 // If there is another open instance to select,
350 // go ahead and do so. If not, never mind.
351 if(studio::App::instance_list.empty())
353 studio::App::set_selected_canvas_view(0);
354 studio::App::set_selected_instance(0);
358 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
359 //studio::App::set_selected_instance(studio::App::instance_list.front());
365 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
367 CanvasTreeModel canvas_tree_model;
370 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
371 row[canvas_tree_model.id] = canvas->get_id();
372 row[canvas_tree_model.name] = canvas->get_name();
373 if(canvas->is_root())
374 row[canvas_tree_model.label] = basename(canvas->get_file_name());
376 if(!canvas->get_id().empty())
377 row[canvas_tree_model.label] = canvas->get_id();
379 if(!canvas->get_name().empty())
380 row[canvas_tree_model.label] = canvas->get_name();
382 row[canvas_tree_model.label] = _("[Unnamed]");
384 row[canvas_tree_model.canvas] = canvas;
385 row[canvas_tree_model.is_canvas] = true;
386 row[canvas_tree_model.is_value_node] = false;
389 synfig::Canvas::Children::iterator iter;
390 synfig::Canvas::Children &children(canvas->children());
392 for(iter=children.begin();iter!=children.end();iter++)
393 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
397 if(!canvas->value_node_list().empty())
399 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
401 valuenode_row[canvas_tree_model.label] = "<defs>";
402 valuenode_row[canvas_tree_model.canvas] = canvas;
403 valuenode_row[canvas_tree_model.is_canvas] = false;
404 valuenode_row[canvas_tree_model.is_value_node] = false;
406 synfig::ValueNodeList::iterator iter;
407 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
409 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
410 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
418 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
420 CanvasTreeModel canvas_tree_model;
424 row[canvas_tree_model.id] = value_node->get_id();
425 row[canvas_tree_model.name] = value_node->get_id();
426 row[canvas_tree_model.label] = value_node->get_id();
427 row[canvas_tree_model.canvas] = canvas;
428 row[canvas_tree_model.value_node] = value_node;
429 row[canvas_tree_model.is_canvas] = false;
430 row[canvas_tree_model.is_value_node] = true;
431 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
436 Instance::refresh_canvas_tree()
438 canvas_tree_store()->clear();
439 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
440 insert_canvas(row,get_canvas());
444 Instance::dialog_cvs_commit()
446 calc_repository_info();
449 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
456 if(synfigapp::Instance::get_action_count())
458 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
465 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
469 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
472 OneMoment one_moment;
477 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
483 Instance::dialog_cvs_add()
485 calc_repository_info();
488 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
495 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
497 OneMoment one_moment;
502 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
508 Instance::dialog_cvs_update()
510 calc_repository_info();
513 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
518 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
524 String filename(get_file_name());
525 if(synfigapp::Instance::get_action_count())
527 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
531 OneMoment one_moment;
532 time_t oldtime=get_original_timestamp();
534 calc_repository_info();
535 // If something has been updated...
536 if(oldtime!=get_original_timestamp())
543 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
545 //update_all_titles();
549 Instance::dialog_cvs_revert()
551 calc_repository_info();
554 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
559 String filename(get_file_name());
560 if(!App::dialog_yes_no(_("CVS Revert"),
561 _("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?")
565 OneMoment one_moment;
567 // Remove the old file
568 if(remove(get_file_name().c_str())!=0)
570 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
579 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
581 //update_all_titles();
585 Instance::_revert(Instance *instance)
587 OneMoment one_moment;
589 String filename(instance->get_file_name());
591 Canvas::Handle canvas(instance->get_canvas());
595 if(canvas->count()!=1)
598 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."));
609 // Schedule a revert to occur in a few moments
610 Glib::signal_timeout().connect(
613 sigc::ptr_fun(&Instance::_revert),
623 Instance::safe_revert()
625 if(synfigapp::Instance::get_action_count())
626 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
633 Instance::safe_close()
635 handle<synfigapp::UIInterface> uim;
636 uim=find_canvas_view(get_canvas())->get_ui_interface();
638 if(get_action_count())
641 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
642 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
643 if(answer==synfigapp::UIInterface::RESPONSE_YES)
645 enum Status status = save();
646 if (status == STATUS_OK) break;
647 else if (status == STATUS_CANCEL) return false;
649 if(answer==synfigapp::UIInterface::RESPONSE_NO)
651 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
657 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());
658 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
660 if(answer==synfigapp::UIInterface::RESPONSE_YES)
662 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
673 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
675 synfigapp::Action::CandidateList candidate_list;
676 synfigapp::Action::CandidateList::iterator iter;
678 candidate_list=compile_candidate_list(param_list,category);
680 candidate_list.sort();
682 if(candidate_list.empty())
683 synfig::warning("Action CandidateList is empty!");
685 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
687 Gtk::StockID stock_id(get_action_stock_id(*iter));
689 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
691 action_group->add(Gtk::Action::create(
692 "action-"+iter->name,
694 iter->local_name,iter->local_name
699 *const_cast<studio::Instance*>(this),
700 &studio::Instance::process_action
707 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
713 Instance::add_actions_to_menu(Gtk::Menu *menu, 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("Action CandidateList is empty!");
725 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
727 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
729 Gtk::Image* image(manage(new Gtk::Image()));
730 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
733 if(iter->task=="raise")
734 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
735 else if(iter->task=="lower")
736 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
737 else if(iter->task=="move_top")
738 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
739 else if(iter->task=="move_bottom")
740 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
741 else if(iter->task=="remove")
742 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
743 else if(iter->task=="set_on")
744 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
745 else if(iter->task=="set_off")
746 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
747 else if(iter->task=="duplicate")
748 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
749 else if(iter->task=="remove")
750 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
753 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
754 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
755 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
758 menu->items().push_back(
759 Gtk::Menu_Helpers::ImageMenuElem(
765 *const_cast<studio::Instance*>(this),
766 &studio::Instance::process_action
779 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
781 synfigapp::Action::CandidateList candidate_list;
782 synfigapp::Action::CandidateList candidate_list2;
784 synfigapp::Action::CandidateList::iterator iter;
786 candidate_list=compile_candidate_list(param_list,category);
787 candidate_list2=compile_candidate_list(param_list2,category);
789 candidate_list.sort();
791 if(candidate_list.empty())
792 synfig::warning("Action CandidateList is empty!");
793 if(candidate_list2.empty())
794 synfig::warning("Action CandidateList2 is empty!");
796 // Seperate out the candidate lists so that there are no conflicts
797 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
799 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
800 if(iter2!=candidate_list2.end())
801 candidate_list2.erase(iter2);
804 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
806 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
808 Gtk::Image* image(manage(new Gtk::Image()));
809 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
811 /* if(iter->task=="raise")
812 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
813 else if(iter->task=="lower")
814 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
815 else if(iter->task=="move_top")
816 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
817 else if(iter->task=="move_bottom")
818 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
819 else if(iter->task=="remove")
820 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
821 else if(iter->task=="set_on")
822 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
823 else if(iter->task=="set_off")
824 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
825 else if(iter->task=="duplicate")
826 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
827 else if(iter->task=="remove")
828 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
831 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
832 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
833 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
836 menu->items().push_back(
837 Gtk::Menu_Helpers::ImageMenuElem(
843 *const_cast<studio::Instance*>(this),
844 &studio::Instance::process_action
855 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
857 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
859 Gtk::Image* image(manage(new Gtk::Image()));
860 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
861 /* if(iter->task=="raise")
862 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
863 else if(iter->task=="lower")
864 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
865 else if(iter->task=="move_top")
866 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
867 else if(iter->task=="move_bottom")
868 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
869 else if(iter->task=="remove")
870 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
871 else if(iter->task=="set_on")
872 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
873 else if(iter->task=="set_off")
874 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
875 else if(iter->task=="duplicate")
876 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
877 else if(iter->task=="remove")
878 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
881 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
882 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
883 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
886 menu->items().push_back(
887 Gtk::Menu_Helpers::ImageMenuElem(
893 *const_cast<studio::Instance*>(this),
894 &studio::Instance::process_action
907 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
909 assert(synfigapp::Action::book().count(name));
911 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
913 synfigapp::Action::Handle action(entry.factory());
917 synfig::error("Bad Action");
921 action->set_param_list(param_list);
923 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
924 synfigapp::Action::ParamVocab::const_iterator iter;
926 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
928 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
931 // If the parameter is optionally user-supplied,
932 // and has not been already provided in the param_list,
933 // then we should go ahead and see if we can
934 // provide that data.
935 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
937 switch(iter->get_type())
939 case synfigapp::Action::Param::TYPE_STRING:
942 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
944 action->set_param(iter->get_name(),str);
948 synfig::error("Unsupported user-supplied action parameter");
955 if(!action->is_ready())
957 synfig::error("Action not ready");
961 perform_action(action);
965 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
967 Gtk::Menu& parammenu(*menu);
969 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
971 if(!canvas_interface)
974 synfigapp::Action::ParamList param_list,param_list2;
975 param_list=canvas_interface->generate_param_list(value_desc);
976 param_list.add("origin",location);
978 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
980 param_list2=canvas_interface->generate_param_list(
981 synfigapp::ValueDesc(
982 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
986 param_list2.add("origin",location);
990 // Populate the convert menu by looping through
991 // the ValueNode book and find the ones that are
994 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
995 LinkableValueNode::Book::const_iterator iter;
996 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
998 if(iter->second.check_type(value_desc.get_value_type()))
1000 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1004 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1014 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1017 if(param_list2.empty())
1018 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1020 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1022 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1024 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1027 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1029 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1033 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1034 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1038 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1054 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1056 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1059 "Edit Multiple Waypoints", // Title
1061 true // use_separator
1064 Widget_WaypointModel widget_waypoint_model;
1065 widget_waypoint_model.show();
1067 dialog.get_vbox()->pack_start(widget_waypoint_model);
1070 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1071 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1075 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1078 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1080 std::list<synfigapp::ValueDesc>::iterator iter;
1081 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1083 synfigapp::ValueDesc value_desc(*iter);
1085 if(!value_desc.is_valid())
1088 ValueNode_Animated::Handle value_node;
1090 // If this value isn't a ValueNode_Animated, but
1091 // it is somewhat constant, then go ahead and convert
1092 // it to a ValueNode_Animated.
1093 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1096 if(value_desc.is_value_node())
1097 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1099 value=value_desc.get_value();
1101 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1103 synfigapp::Action::Handle action;
1105 if(!value_desc.is_value_node())
1107 action=synfigapp::Action::create("value_desc_connect");
1108 action->set_param("dest",value_desc);
1109 action->set_param("src",ValueNode::Handle(value_node));
1113 action=synfigapp::Action::create("value_node_replace");
1114 action->set_param("dest",value_desc.get_value_node());
1115 action->set_param("src",ValueNode::Handle(value_node));
1118 action->set_param("canvas",canvas_view->get_canvas());
1119 action->set_param("canvas_interface",canvas_interface);
1122 if(!canvas_interface->get_instance()->perform_action(action))
1124 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1131 if(value_desc.is_value_node())
1132 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1139 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1143 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1149 action->set_param("canvas",canvas_view->get_canvas());
1150 action->set_param("canvas_interface",canvas_interface);
1151 action->set_param("value_node",ValueNode::Handle(value_node));
1152 action->set_param("time",canvas_interface->get_time());
1153 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1155 if(!canvas_interface->get_instance()->perform_action(action))
1157 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1164 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1173 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1175 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1177 synfigapp::Action::ParamList param_list;
1178 param_list=canvas_interface->generate_param_list(value_desc_list);
1180 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1182 // Add the edit waypoints option if that might be useful
1183 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1185 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1189 &edit_several_waypoints
1193 find_canvas_view(canvas)