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)const
190 if(synfigapp::Instance::save_as(file_name))
192 App::add_recent_file(file_name);
199 studio::Instance::save_as(const synfig::String &file_name)
201 if(synfigapp::Instance::save_as(file_name))
203 App::add_recent_file(file_name);
210 studio::Instance::save()
212 if(basename(get_file_name()).find("untitled")==0)
218 return synfigapp::Instance::save();
223 studio::Instance::dialog_save_as()
225 string filename="*.sif";
227 Canvas::Handle canvas(get_canvas());
230 OneMoment one_moment;
231 std::set<Node*>::iterator iter;
232 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
234 synfig::Node* node(*iter);
235 for(;!node->parent_set.empty();node=*node->parent_set.begin())
237 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
238 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
240 App::dialog_error_blocking("SaveAs - Error",
241 "There is currently a bug when using \"SaveAs\"\n"
242 "on a composition that is being referenced by other\n"
243 "files that are currently open. Close these\n"
244 "other files first before trying to use \"SaveAs\"."
255 // show the canvas' name if it has one, else its ID
256 while(App::dialog_saveas_file(_("Choose a Filename to Save As") +
258 (canvas->get_name().empty()
260 : canvas->get_name()) +
263 // If the filename still has wildcards, then we should
264 // continue looking for the file we want
265 if(find(filename.begin(),filename.end(),'*')!=filename.end())
268 if(find(filename.begin(),filename.end(),'.')==filename.end())
273 String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
274 if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
275 _("You have given the file name an extension\nwhich I do not recognise. Are you sure this is what you want?")))
287 // if stat() succeeds, or it fails with something other than 'file doesn't exist', the file exists
288 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
289 if ((stat(filename.c_str(), &s) != -1 || errno != ENOENT) &&
290 !App::dialog_yes_no("File exists",
293 "' already exists.\n\n"
294 "Do you want to replace it with the file you are saving?"))
298 if(save_as(filename))
301 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 loose 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())
640 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
641 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
642 if(answer==synfigapp::UIInterface::RESPONSE_YES)
644 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
650 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());
651 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
653 if(answer==synfigapp::UIInterface::RESPONSE_YES)
655 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
666 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
668 synfigapp::Action::CandidateList candidate_list;
669 synfigapp::Action::CandidateList::iterator iter;
671 candidate_list=compile_candidate_list(param_list,category);
673 candidate_list.sort();
675 if(candidate_list.empty())
676 synfig::warning("Action CandidateList is empty!");
678 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
680 Gtk::StockID stock_id(get_action_stock_id(*iter));
682 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
684 action_group->add(Gtk::Action::create(
685 "action-"+iter->name,
687 iter->local_name,iter->local_name
692 *const_cast<studio::Instance*>(this),
693 &studio::Instance::process_action
700 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
706 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
708 synfigapp::Action::CandidateList candidate_list;
709 synfigapp::Action::CandidateList::iterator iter;
711 candidate_list=compile_candidate_list(param_list,category);
713 candidate_list.sort();
715 if(candidate_list.empty())
716 synfig::warning("Action CandidateList is empty!");
718 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
720 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
722 Gtk::Image* image(manage(new Gtk::Image()));
723 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
726 if(iter->task=="raise")
727 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
728 else if(iter->task=="lower")
729 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
730 else if(iter->task=="move_top")
731 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
732 else if(iter->task=="move_bottom")
733 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
734 else if(iter->task=="remove")
735 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
736 else if(iter->task=="set_on")
737 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
738 else if(iter->task=="set_off")
739 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
740 else if(iter->task=="duplicate")
741 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
742 else if(iter->task=="remove")
743 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
746 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
747 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
748 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
751 menu->items().push_back(
752 Gtk::Menu_Helpers::ImageMenuElem(
758 *const_cast<studio::Instance*>(this),
759 &studio::Instance::process_action
772 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
774 synfigapp::Action::CandidateList candidate_list;
775 synfigapp::Action::CandidateList candidate_list2;
777 synfigapp::Action::CandidateList::iterator iter;
779 candidate_list=compile_candidate_list(param_list,category);
780 candidate_list2=compile_candidate_list(param_list2,category);
782 candidate_list.sort();
784 if(candidate_list.empty())
785 synfig::warning("Action CandidateList is empty!");
786 if(candidate_list2.empty())
787 synfig::warning("Action CandidateList2 is empty!");
789 // Seperate out the candidate lists so that there are no conflicts
790 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
792 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
793 if(iter2!=candidate_list2.end())
794 candidate_list2.erase(iter2);
797 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
799 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
801 Gtk::Image* image(manage(new Gtk::Image()));
802 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
804 /* if(iter->task=="raise")
805 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
806 else if(iter->task=="lower")
807 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
808 else if(iter->task=="move_top")
809 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
810 else if(iter->task=="move_bottom")
811 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
812 else if(iter->task=="remove")
813 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
814 else if(iter->task=="set_on")
815 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
816 else if(iter->task=="set_off")
817 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
818 else if(iter->task=="duplicate")
819 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
820 else if(iter->task=="remove")
821 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
824 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
825 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
826 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
829 menu->items().push_back(
830 Gtk::Menu_Helpers::ImageMenuElem(
836 *const_cast<studio::Instance*>(this),
837 &studio::Instance::process_action
848 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
850 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
852 Gtk::Image* image(manage(new Gtk::Image()));
853 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
854 /* if(iter->task=="raise")
855 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="lower")
857 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="move_top")
859 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="move_bottom")
861 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
862 else if(iter->task=="remove")
863 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
864 else if(iter->task=="set_on")
865 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
866 else if(iter->task=="set_off")
867 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
868 else if(iter->task=="duplicate")
869 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
870 else if(iter->task=="remove")
871 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
874 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
875 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
876 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
879 menu->items().push_back(
880 Gtk::Menu_Helpers::ImageMenuElem(
886 *const_cast<studio::Instance*>(this),
887 &studio::Instance::process_action
900 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
902 assert(synfigapp::Action::book().count(name));
904 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
906 synfigapp::Action::Handle action(entry.factory());
910 synfig::error("Bad Action");
914 action->set_param_list(param_list);
916 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
917 synfigapp::Action::ParamVocab::const_iterator iter;
919 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
921 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
924 // If the parameter is optionally user-supplied,
925 // and has not been already provided in the param_list,
926 // then we should go ahead and see if we can
927 // provide that data.
928 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
930 switch(iter->get_type())
932 case synfigapp::Action::Param::TYPE_STRING:
935 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
937 action->set_param(iter->get_name(),str);
941 synfig::error("Unsupported user-supplied action parameter");
948 if(!action->is_ready())
950 synfig::error("Action not ready");
954 perform_action(action);
958 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
960 Gtk::Menu& parammenu(*menu);
962 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
964 if(!canvas_interface)
967 synfigapp::Action::ParamList param_list,param_list2;
968 param_list=canvas_interface->generate_param_list(value_desc);
969 param_list.add("origin",location);
971 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
973 param_list2=canvas_interface->generate_param_list(
974 synfigapp::ValueDesc(
975 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
979 param_list2.add("origin",location);
983 // Populate the convert menu by looping through
984 // the ValueNode book and find the ones that are
987 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
988 LinkableValueNode::Book::const_iterator iter;
989 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
991 if(iter->second.check_type(value_desc.get_value_type()))
993 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
997 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1007 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1010 if(param_list2.empty())
1011 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1013 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1015 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1017 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1020 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1022 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1026 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1027 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1031 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1047 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1049 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1052 "Edit Multiple Waypoints", // Title
1054 true // use_separator
1057 Widget_WaypointModel widget_waypoint_model;
1058 widget_waypoint_model.show();
1060 dialog.get_vbox()->pack_start(widget_waypoint_model);
1063 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1064 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1068 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1071 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1073 std::list<synfigapp::ValueDesc>::iterator iter;
1074 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1076 synfigapp::ValueDesc value_desc(*iter);
1078 if(!value_desc.is_valid())
1081 ValueNode_Animated::Handle value_node;
1083 // If this value isn't a ValueNode_Animated, but
1084 // it is somewhat constant, then go ahead and convert
1085 // it to a ValueNode_Animated.
1086 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1089 if(value_desc.is_value_node())
1090 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1092 value=value_desc.get_value();
1094 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1096 synfigapp::Action::Handle action;
1098 if(!value_desc.is_value_node())
1100 action=synfigapp::Action::create("value_desc_connect");
1101 action->set_param("dest",value_desc);
1102 action->set_param("src",ValueNode::Handle(value_node));
1106 action=synfigapp::Action::create("value_node_replace");
1107 action->set_param("dest",value_desc.get_value_node());
1108 action->set_param("src",ValueNode::Handle(value_node));
1111 action->set_param("canvas",canvas_view->get_canvas());
1112 action->set_param("canvas_interface",canvas_interface);
1115 if(!canvas_interface->get_instance()->perform_action(action))
1117 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1124 if(value_desc.is_value_node())
1125 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1132 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1136 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1142 action->set_param("canvas",canvas_view->get_canvas());
1143 action->set_param("canvas_interface",canvas_interface);
1144 action->set_param("value_node",ValueNode::Handle(value_node));
1145 action->set_param("time",canvas_interface->get_time());
1146 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1148 if(!canvas_interface->get_instance()->perform_action(action))
1150 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1157 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1166 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1168 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1170 synfigapp::Action::ParamList param_list;
1171 param_list=canvas_interface->generate_param_list(value_desc_list);
1173 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1175 // Add the edit waypoints option if that might be useful
1176 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1178 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1182 &edit_several_waypoints
1186 find_canvas_view(canvas)