Put a space between the parameter name and its description when prompting for it.
[synfig.git] / synfig-studio / trunk / src / gtkmm / instance.cpp
blob0e2ed36ea31abc040d78e89672c40878ce3ff0f2
1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
3 ** \brief writeme
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
10 ** Copyright (c) 2008 Carlos López
12 ** This package is free software; you can redistribute it and/or
13 ** modify it under the terms of the GNU General Public License as
14 ** published by the Free Software Foundation; either version 2 of
15 ** the License, or (at your option) any later version.
17 ** This package is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ** General Public License for more details.
21 ** \endlegal
23 /* ========================================================================= */
25 /* === H E A D E R S ======================================================= */
27 #ifdef USING_PCH
28 # include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
34 #include "instance.h"
35 #include <cassert>
36 #include <gtkmm/stock.h>
37 #include <gtkmm/image.h>
38 #include <iostream>
39 #include <gtkmm/button.h>
40 #include "canvasview.h"
41 #include "app.h"
42 #include <sigc++/signal.h>
43 #include <sigc++/adaptors/hide.h>
44 #include "toolbox.h"
45 #include "onemoment.h"
46 #include <synfig/savecanvas.h>
48 #include "autorecover.h"
49 #include <sigc++/retype_return.h>
50 #include <sigc++/retype.h>
51 //#include <sigc++/hide.h>
52 #include <synfig/valuenode_composite.h>
53 #include <synfig/valuenode_duplicate.h>
54 #include "widget_waypointmodel.h"
55 #include <gtkmm/actiongroup.h>
56 #include "iconcontroller.h"
57 #include "workarea.h"
58 #include <sys/stat.h>
59 #include <errno.h>
60 #include <ETL/stringf>
62 #include "general.h"
64 #endif
66 using namespace std;
67 using namespace etl;
68 using namespace synfig;
69 using namespace studio;
70 using namespace sigc;
72 /* === M A C R O S ========================================================= */
74 /* === G L O B A L S ======================================================= */
76 int studio::Instance::instance_count_=0;
78 /* === P R O C E D U R E S ================================================= */
80 /* === M E T H O D S ======================================================= */
82 Instance::Instance(synfig::Canvas::Handle canvas):
83 synfigapp::Instance (canvas),
84 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
85 // canvas_tree_store_ (Gtk::TreeStore::create()),
86 history_tree_store_ (HistoryTreeStore::create(this)),
87 undo_status_(false),
88 redo_status_(false)
90 CanvasTreeModel model;
91 canvas_tree_store_=Gtk::TreeStore::create(model);
93 id_=instance_count_++;
95 // Connect up all the signals
96 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
97 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
98 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
99 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
101 signal_saved().connect(
102 sigc::hide_return(
103 sigc::ptr_fun(
104 studio::AutoRecover::auto_backup
109 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
111 refresh_canvas_tree();
114 Instance::~Instance()
119 Instance::get_visible_canvases()const
121 int count(0);
122 CanvasViewList::const_iterator iter;
123 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
124 if((*iter)->is_visible())
125 count++;
126 return count;
129 handle<Instance>
130 Instance::create(synfig::Canvas::Handle canvas)
132 // Construct a new instance
133 handle<Instance> instance(new Instance(canvas));
135 // Add the new instance to the application's instance list
136 App::instance_list.push_back(instance);
138 // Set up the instance with the default UI manager
139 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
141 // Signal the new instance
142 App::signal_instance_created()(instance);
144 // And then make sure that is has been selected
145 App::set_selected_instance(instance);
147 // Create the initial window for the root canvas
148 instance->focus(canvas);
150 return instance;
153 handle<CanvasView>
154 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
156 if(!canvas)
157 return 0;
159 while(canvas->is_inline())
160 canvas=canvas->parent();
162 CanvasViewList::iterator iter;
164 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
165 if((*iter)->get_canvas()==canvas)
166 return *iter;
168 return CanvasView::create(this,canvas);
171 void
172 Instance::focus(etl::handle<synfig::Canvas> canvas)
174 handle<CanvasView> canvas_view=find_canvas_view(canvas);
175 assert(canvas_view);
176 canvas_view->present();
179 void
180 Instance::set_undo_status(bool x)
182 undo_status_=x;
183 App::toolbox->update_undo_redo();
184 signal_undo_redo_status_changed()();
187 void
188 Instance::set_redo_status(bool x)
190 redo_status_=x;
191 App::toolbox->update_undo_redo();
192 signal_undo_redo_status_changed()();
195 bool
196 studio::Instance::save_as(const synfig::String &file_name)
198 if(synfigapp::Instance::save_as(file_name))
200 // after changing the filename, update the render settings with the new filename
201 list<handle<CanvasView> >::iterator iter;
202 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
203 (*iter)->render_settings.set_entry_filename();
204 App::add_recent_file(etl::handle<Instance>(this));
205 return true;
207 return false;
210 void
211 studio::Instance::open()
213 App::dialog_open(get_file_name());
216 Instance::Status
217 studio::Instance::save()
219 // if we don't have a real filename yet then we need to ask where to save it
220 if (!has_real_filename())
222 if (dialog_save_as())
223 return STATUS_OK;
224 else
225 return STATUS_CANCEL;
228 if (synfigapp::Instance::save())
230 App::add_recent_file(etl::handle<Instance>(this));
231 return STATUS_OK;
233 string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
234 App::dialog_error_blocking(_("Save - Error"), msg.c_str());
235 return STATUS_ERROR;
238 // the filename will be set to "Synfig Animation 1" or some such when first created
239 // and will be changed to an absolute path once it has been saved
240 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
241 bool
242 studio::Instance::has_real_filename()
244 return get_file_name().find(App::custom_filename_prefix.c_str()) != 0;
247 bool
248 studio::Instance::dialog_save_as()
250 string filename = get_file_name();
251 Canvas::Handle canvas(get_canvas());
254 OneMoment one_moment;
255 std::set<Node*>::iterator iter;
256 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
258 synfig::Node* node(*iter);
259 for(;!node->parent_set.empty();node=*node->parent_set.begin())
261 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
262 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
264 string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n"
265 "on a composition that is being referenced by other\n"
266 "files that are currently open. Close these\n"
267 "other files first before trying to use \"SaveAs\".")));
268 App::dialog_error_blocking(_("SaveAs - Error"), msg.c_str());
270 return false;
272 if(parent_layer)
273 break;
278 if (has_real_filename())
279 filename = absolute_path(filename);
281 // show the canvas' name if it has one, else its ID
282 while (App::dialog_save_file((_("Choose a Filename to Save As") +
283 String(" (") +
284 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
285 ") ..."),
286 filename, ANIMATION_DIR_PREFERENCE))
288 // If the filename still has wildcards, then we should
289 // continue looking for the file we want
290 string base_filename = basename(filename);
291 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
292 continue;
294 if (filename_extension(filename) == "")
295 filename+=".sifz";
299 String ext(filename_extension(filename));
300 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
301 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
302 continue;
304 catch(...)
306 continue;
310 struct stat s;
311 int stat_return = stat(filename.c_str(), &s);
313 // if stat() fails with something other than 'file doesn't exist', there's been a real
314 // error of some kind. let's give up now and ask for a new path.
315 if (stat_return == -1 && errno != ENOENT)
317 perror(filename.c_str());
318 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
319 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
320 continue;
323 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
324 string msg(strprintf(_("A file named '%s' already exists.\n\n"
325 "Do you want to replace it with the file you are saving?"), filename.c_str()));
326 if ((stat_return == 0) &&
327 !App::dialog_yes_no(_("File exists"),msg.c_str()))
328 continue;
331 if(save_as(filename))
333 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
334 return true;
336 string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
337 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
340 return false;
343 void
344 Instance::update_all_titles()
346 list<handle<CanvasView> >::iterator iter;
347 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
348 (*iter)->update_title();
351 void
352 Instance::close()
354 // This will increase the reference count so we don't get DELETED
355 // until we are ready
356 handle<Instance> me(this);
358 // Make sure we aren't selected as the current instance
359 if(studio::App::get_selected_instance()==this)
360 studio::App::set_selected_instance(0);
362 // Turn-off/clean-up auto recovery
363 studio::App::auto_recover->clear_backup(get_canvas());
365 // Remove us from the active instance list
366 std::list<etl::handle<studio::Instance> >::iterator iter;
367 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
368 if(*iter==this)
369 break;
370 assert(iter!=studio::App::instance_list.end());
371 if(iter!=studio::App::instance_list.end())
372 studio::App::instance_list.erase(iter);
374 // Send out a signal that we are being deleted
375 studio::App::signal_instance_deleted()(this);
377 // Hide all of the canvas views
378 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
379 (*iter)->hide();
381 // Consume pending events before deleting the canvas views
382 while(studio::App::events_pending())studio::App::iteration(false);
384 // Delete all of the canvas views
385 canvas_view_list().clear();
387 // If there is another open instance to select,
388 // go ahead and do so. If not, never mind.
389 if(studio::App::instance_list.empty())
391 studio::App::set_selected_canvas_view(0);
392 studio::App::set_selected_instance(0);
394 else
395 studio::App::instance_list.front()->canvas_view_list().front()->present();
398 void
399 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
401 CanvasTreeModel canvas_tree_model;
402 assert(canvas);
404 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
405 row[canvas_tree_model.id] = canvas->get_id();
406 row[canvas_tree_model.name] = canvas->get_name();
407 if(canvas->is_root())
408 row[canvas_tree_model.label] = basename(canvas->get_file_name());
409 else
410 if(!canvas->get_id().empty())
411 row[canvas_tree_model.label] = canvas->get_id();
412 else
413 if(!canvas->get_name().empty())
414 row[canvas_tree_model.label] = canvas->get_name();
415 else
416 row[canvas_tree_model.label] = _("[Unnamed]");
418 row[canvas_tree_model.canvas] = canvas;
419 row[canvas_tree_model.is_canvas] = true;
420 row[canvas_tree_model.is_value_node] = false;
423 synfig::Canvas::Children::iterator iter;
424 synfig::Canvas::Children &children(canvas->children());
426 for(iter=children.begin();iter!=children.end();iter++)
427 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
431 if(!canvas->value_node_list().empty())
433 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
435 valuenode_row[canvas_tree_model.label] = "<defs>";
436 valuenode_row[canvas_tree_model.canvas] = canvas;
437 valuenode_row[canvas_tree_model.is_canvas] = false;
438 valuenode_row[canvas_tree_model.is_value_node] = false;
440 synfig::ValueNodeList::iterator iter;
441 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
443 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
444 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
450 void
451 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
453 CanvasTreeModel canvas_tree_model;
454 assert(value_node);
455 assert(canvas);
457 row[canvas_tree_model.id] = value_node->get_id();
458 row[canvas_tree_model.name] = value_node->get_id();
459 row[canvas_tree_model.label] = value_node->get_id();
460 row[canvas_tree_model.canvas] = canvas;
461 row[canvas_tree_model.value_node] = value_node;
462 row[canvas_tree_model.is_canvas] = false;
463 row[canvas_tree_model.is_value_node] = true;
464 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
468 void
469 Instance::refresh_canvas_tree()
471 canvas_tree_store()->clear();
472 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
473 insert_canvas(row,get_canvas());
476 void
477 Instance::dialog_cvs_commit()
479 calc_repository_info();
480 if(!in_repository())
482 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
483 return;
487 string message;
489 if(synfigapp::Instance::get_action_count())
491 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
492 return;
493 save();
496 if(!is_modified())
498 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
499 return;
502 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
503 return;
505 OneMoment one_moment;
506 cvs_commit(message);
508 catch(...)
510 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
512 update_all_titles();
515 void
516 Instance::dialog_cvs_add()
518 calc_repository_info();
519 if(in_repository())
521 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
522 return;
526 string message;
528 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
529 // return;
530 OneMoment one_moment;
531 cvs_add();
533 catch(...)
535 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
537 update_all_titles();
540 void
541 Instance::dialog_cvs_update()
543 calc_repository_info();
544 if(!in_repository())
546 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
547 return;
549 if(!is_updated())
551 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
552 return;
557 String filename(get_file_name());
558 if(synfigapp::Instance::get_action_count())
560 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
561 return;
562 save();
564 OneMoment one_moment;
565 time_t oldtime=get_original_timestamp();
566 cvs_update();
567 calc_repository_info();
568 // If something has been updated...
569 if(oldtime!=get_original_timestamp())
571 revert();
574 catch(...)
576 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
578 //update_all_titles();
581 void
582 Instance::dialog_cvs_revert()
584 calc_repository_info();
585 if(!in_repository())
587 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
588 return;
592 String filename(get_file_name());
593 if(!App::dialog_yes_no(_("CVS Revert"),
594 _("This will abandon all changes you have made\nsince the last time you performed a commit\noperation. This cannot be undone! Are you sure\nyou want to do this?")
596 return;
598 OneMoment one_moment;
600 // Remove the old file
601 if(remove(get_file_name().c_str())!=0)
603 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
604 return;
607 cvs_update();
608 revert();
610 catch(...)
612 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
614 //update_all_titles();
617 void
618 Instance::_revert(Instance *instance)
620 OneMoment one_moment;
622 String filename(instance->get_file_name());
624 Canvas::Handle canvas(instance->get_canvas());
626 instance->close();
628 if(canvas->count()!=1)
630 one_moment.hide();
631 App::dialog_error_blocking(_("Error: Revert Failed"),_("The revert operation has failed. This can be due to it being\nreferenced by another composition that is already open, or\nbecause of an internal error in Synfig Studio. Try closing any\ncompositions that might reference this composition and try\nagain, or restart Synfig Studio."));
632 one_moment.show();
634 canvas=0;
636 App::open(filename);
639 void
640 Instance::revert()
642 // Schedule a revert to occur in a few moments
643 Glib::signal_timeout().connect(
644 sigc::bind_return(
645 sigc::bind(
646 sigc::ptr_fun(&Instance::_revert),
647 this
649 false
651 ,500
655 bool
656 Instance::safe_revert()
658 if(synfigapp::Instance::get_action_count())
659 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
660 return false;
661 revert();
662 return true;
665 bool
666 Instance::safe_close()
668 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
669 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
671 // if the animation is currently playing, closing the window will cause a crash,
672 // so don't allow it
673 if (canvas_view->is_playing())
675 canvas_view->present();
676 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
677 return false;
679 if(get_action_count())
682 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
683 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
684 if(answer==synfigapp::UIInterface::RESPONSE_YES)
686 enum Status status = save();
687 if (status == STATUS_OK) break;
688 else if (status == STATUS_CANCEL) return false;
690 if(answer==synfigapp::UIInterface::RESPONSE_NO)
691 break;
692 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
693 return false;
694 } while (true);
696 if(is_modified())
698 string str=strprintf(_("%s has changes not yet on the CVS repository.\nWould you like to commit these changes?"),basename(get_file_name()).c_str());
699 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
701 if(answer==synfigapp::UIInterface::RESPONSE_YES)
702 dialog_cvs_commit();
703 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
704 return false;
707 close();
709 return true;
712 void
713 Instance::add_actions_to_group(const Glib::RefPtr<Gtk::ActionGroup>& action_group, synfig::String& ui_info, const synfigapp::Action::ParamList &param_list, synfigapp::Action::Category category)const
715 synfigapp::Action::CandidateList candidate_list;
716 synfigapp::Action::CandidateList::iterator iter;
718 candidate_list=compile_candidate_list(param_list,category);
720 candidate_list.sort();
722 // if(candidate_list.empty())
723 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
725 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
727 Gtk::StockID stock_id(get_action_stock_id(*iter));
729 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
731 action_group->add(Gtk::Action::create(
732 "action-"+iter->name,
733 stock_id,
734 iter->local_name,iter->local_name
736 sigc::bind(
737 sigc::bind(
738 sigc::mem_fun(
739 *const_cast<studio::Instance*>(this),
740 &studio::Instance::process_action
742 param_list
744 iter->name
747 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
752 void
753 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,synfigapp::Action::Category category)const
755 synfigapp::Action::CandidateList candidate_list;
756 synfigapp::Action::CandidateList::iterator iter;
758 candidate_list=compile_candidate_list(param_list,category);
760 candidate_list.sort();
762 if(candidate_list.empty())
763 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
765 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
767 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
769 Gtk::Image* image(manage(new Gtk::Image()));
770 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
773 if(iter->task=="raise")
774 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
775 else if(iter->task=="lower")
776 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
777 else if(iter->task=="move_top")
778 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
779 else if(iter->task=="move_bottom")
780 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
781 else if(iter->task=="remove")
782 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
783 else if(iter->task=="set_on")
784 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
785 else if(iter->task=="set_off")
786 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
787 else if(iter->task=="duplicate")
788 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
789 else if(iter->task=="remove")
790 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
791 else
793 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
794 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
795 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
798 menu->items().push_back(
799 Gtk::Menu_Helpers::ImageMenuElem(
800 iter->local_name,
801 *image,
802 sigc::bind(
803 sigc::bind(
804 sigc::mem_fun(
805 *const_cast<studio::Instance*>(this),
806 &studio::Instance::process_action
808 param_list
810 iter->name
818 void
819 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,const synfigapp::Action::ParamList &param_list2,synfigapp::Action::Category category)const
821 synfigapp::Action::CandidateList candidate_list;
822 synfigapp::Action::CandidateList candidate_list2;
824 synfigapp::Action::CandidateList::iterator iter;
826 candidate_list=compile_candidate_list(param_list,category);
827 candidate_list2=compile_candidate_list(param_list2,category);
829 candidate_list.sort();
831 if(candidate_list.empty())
832 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
833 if(candidate_list2.empty())
834 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
836 // Separate out the candidate lists so that there are no conflicts
837 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
839 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
840 if(iter2!=candidate_list2.end())
841 candidate_list2.erase(iter2);
844 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
846 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
848 Gtk::Image* image(manage(new Gtk::Image()));
849 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
851 /* if(iter->task=="raise")
852 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
853 else if(iter->task=="lower")
854 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
855 else if(iter->task=="move_top")
856 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
857 else if(iter->task=="move_bottom")
858 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
859 else if(iter->task=="remove")
860 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
861 else if(iter->task=="set_on")
862 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
863 else if(iter->task=="set_off")
864 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
865 else if(iter->task=="duplicate")
866 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
867 else if(iter->task=="remove")
868 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
869 else
871 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
872 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
873 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
876 menu->items().push_back(
877 Gtk::Menu_Helpers::ImageMenuElem(
878 iter->local_name,
879 *image,
880 sigc::bind(
881 sigc::bind(
882 sigc::mem_fun(
883 *const_cast<studio::Instance*>(this),
884 &studio::Instance::process_action
886 param_list2
888 iter->name
895 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
897 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
899 Gtk::Image* image(manage(new Gtk::Image()));
900 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
901 /* if(iter->task=="raise")
902 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
903 else if(iter->task=="lower")
904 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
905 else if(iter->task=="move_top")
906 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
907 else if(iter->task=="move_bottom")
908 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
909 else if(iter->task=="remove")
910 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
911 else if(iter->task=="set_on")
912 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
913 else if(iter->task=="set_off")
914 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
915 else if(iter->task=="duplicate")
916 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
917 else if(iter->task=="remove")
918 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
919 else
921 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
922 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
923 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
926 menu->items().push_back(
927 Gtk::Menu_Helpers::ImageMenuElem(
928 iter->local_name,
929 *image,
930 sigc::bind(
931 sigc::bind(
932 sigc::mem_fun(
933 *const_cast<studio::Instance*>(this),
934 &studio::Instance::process_action
936 param_list
938 iter->name
946 void
947 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
949 assert(synfigapp::Action::book().count(name));
951 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
953 synfigapp::Action::Handle action(entry.factory());
955 if(!action)
957 synfig::error("Bad Action");
958 return;
961 action->set_param_list(param_list);
963 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
964 synfigapp::Action::ParamVocab::const_iterator iter;
966 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
968 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
969 continue;
971 // If the parameter is optionally user-supplied,
972 // and has not been already provided in the param_list,
973 // then we should go ahead and see if we can
974 // provide that data.
975 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
977 switch(iter->get_type())
979 case synfigapp::Action::Param::TYPE_STRING:
981 String str;
982 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+": "+iter->get_desc(),str))
983 return;
984 action->set_param(iter->get_name(),str);
985 break;
987 default:
988 synfig::error("Unsupported user-supplied action parameter");
989 return;
990 break;
995 if(!action->is_ready())
997 synfig::error("Action not ready");
998 return;
1001 perform_action(action);
1004 void
1005 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1007 Gtk::Menu& parammenu(*menu);
1009 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1011 if(!canvas_interface)
1012 return;
1014 synfigapp::Action::ParamList param_list,param_list2;
1015 param_list=canvas_interface->generate_param_list(value_desc);
1016 param_list.add("origin",location);
1018 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1020 param_list2=canvas_interface->generate_param_list(
1021 synfigapp::ValueDesc(
1022 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1026 param_list2.add("origin",location);
1029 // Populate the convert menu by looping through
1030 // the ValueNode book and find the ones that are
1031 // relevant.
1033 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1034 // the 'Index' parameter of a Duplicate layer
1035 // or
1036 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1037 if (!((value_desc.parent_is_layer_param() &&
1038 value_desc.get_layer()->get_name() == "duplicate" &&
1039 value_desc.get_param_name() == "index") ||
1040 (value_desc.is_value_node() &&
1041 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1042 !(value_desc.parent_is_layer_param() ||
1043 value_desc.parent_is_value_node()))))
1045 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1046 LinkableValueNode::Book::const_iterator iter;
1047 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1049 if(iter->second.check_type(value_desc.get_value_type()))
1051 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1052 sigc::hide_return(
1053 sigc::bind(
1054 sigc::bind(
1055 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1056 iter->first
1058 value_desc
1065 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1068 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1069 if (bezier)
1071 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1073 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1074 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1076 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1077 if(value_desc.is_valid())
1078 param_list.add("selected_value_desc",value_desc);
1082 if(param_list2.empty())
1083 add_actions_to_menu(&parammenu, param_list,categories);
1084 else
1085 add_actions_to_menu(&parammenu, param_list2,param_list,categories);
1087 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1089 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1092 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1094 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1098 // try to find a waypoint at the current time - if we
1099 // can't, we don't want the menu entry - an exception is thrown
1100 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1101 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1102 waypoint_set.insert(*iter);
1104 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1105 sigc::bind(
1106 sigc::bind(
1107 sigc::bind(
1108 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1111 waypoint_set
1113 value_desc
1117 catch(...)
1123 void
1124 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1126 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1128 Gtk::Dialog dialog(
1129 "Edit Multiple Waypoints", // Title
1130 true, // Modal
1131 true // use_separator
1134 Widget_WaypointModel widget_waypoint_model;
1135 widget_waypoint_model.show();
1137 dialog.get_vbox()->pack_start(widget_waypoint_model);
1139 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1140 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1141 dialog.show();
1143 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1144 return;
1145 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1147 std::list<synfigapp::ValueDesc>::iterator iter;
1148 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1150 synfigapp::ValueDesc value_desc(*iter);
1152 if(!value_desc.is_valid())
1153 continue;
1155 ValueNode_Animated::Handle value_node;
1157 // If this value isn't a ValueNode_Animated, but
1158 // it is somewhat constant, then go ahead and convert
1159 // it to a ValueNode_Animated.
1160 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1162 ValueBase value;
1163 if(value_desc.is_value_node())
1164 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1165 else
1166 value=value_desc.get_value();
1168 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1170 synfigapp::Action::Handle action;
1172 if(!value_desc.is_value_node())
1174 action=synfigapp::Action::create("value_desc_connect");
1175 action->set_param("dest",value_desc);
1176 action->set_param("src",ValueNode::Handle(value_node));
1178 else
1180 action=synfigapp::Action::create("value_node_replace");
1181 action->set_param("dest",value_desc.get_value_node());
1182 action->set_param("src",ValueNode::Handle(value_node));
1185 action->set_param("canvas",canvas_view->get_canvas());
1186 action->set_param("canvas_interface",canvas_interface);
1188 if(!canvas_interface->get_instance()->perform_action(action))
1190 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1191 group.cancel();
1192 return;
1195 else
1197 if(value_desc.is_value_node())
1198 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1201 if(value_node)
1203 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1205 if(!action)
1207 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1208 group.cancel();
1209 return;
1212 action->set_param("canvas",canvas_view->get_canvas());
1213 action->set_param("canvas_interface",canvas_interface);
1214 action->set_param("value_node",ValueNode::Handle(value_node));
1215 action->set_param("time",canvas_interface->get_time());
1216 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1218 if(!canvas_interface->get_instance()->perform_action(action))
1220 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1221 group.cancel();
1222 return;
1225 else
1227 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1228 //group.cancel();
1229 //return;
1235 void
1236 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1238 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1240 synfigapp::Action::ParamList param_list;
1241 param_list=canvas_interface->generate_param_list(value_desc_list);
1243 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1245 // Add the edit waypoints option if that might be useful
1246 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1248 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1249 sigc::bind(
1250 sigc::bind(
1251 sigc::ptr_fun(
1252 &edit_several_waypoints
1254 value_desc_list
1256 find_canvas_view(canvas)