source: synfigstudio/trunk/fuentes/src/gui/instance.cpp @ 481

Last change on this file since 481 was 481, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 43.9 KB
Line 
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, 2011 Carlos López
11**      Copyright (c) 2009 Nikita Kitaev
12**      Copyright (c) 2012 Konstantin Dmitriev
13**
14**      This package is free software; you can redistribute it and/or
15**      modify it under the terms of the GNU General Public License as
16**      published by the Free Software Foundation; either version 2 of
17**      the License, or (at your option) any later version.
18**
19**      This package is distributed in the hope that it will be useful,
20**      but WITHOUT ANY WARRANTY; without even the implied warranty of
21**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22**      General Public License for more details.
23**      \endlegal
24*/
25/* ========================================================================= */
26
27/* === H E A D E R S ======================================================= */
28
29#ifdef USING_PCH
30#       include "pch.h"
31#else
32#ifdef HAVE_CONFIG_H
33#       include <config.h>
34#endif
35
36#include "instance.h"
37#include <cassert>
38
39#include <gtkmm/stock.h>
40#include <gtkmm/image.h>
41#include <gtkmm/menuitem.h>
42#include <gtkmm/imagemenuitem.h>
43#include <gtkmm/separatormenuitem.h>
44#include <gtkmm/button.h>
45
46#include <iostream>
47#include "canvasview.h"
48#include "app.h"
49#include <sigc++/sigc++.h>
50#include "docks/dock_toolbox.h"
51#include "onemoment.h"
52#include <synfig/savecanvas.h>
53
54#include "autorecover.h"
55#include <synfig/valuenodes/valuenode_composite.h>
56#include <synfig/valuenodes/valuenode_duplicate.h>
57#include "widgets/widget_waypointmodel.h"
58#include <gtkmm/actiongroup.h>
59#include "iconcontroller.h"
60#include "workarea.h"
61#include <sys/stat.h>
62#include <errno.h>
63#include <ETL/stringf>
64
65#include "general.h"
66
67#endif
68
69using namespace std;
70using namespace etl;
71using namespace synfig;
72using namespace studio;
73using namespace sigc;
74
75/* === M A C R O S ========================================================= */
76
77/* === G L O B A L S ======================================================= */
78
79int studio::Instance::instance_count_=0;
80
81/* === P R O C E D U R E S ================================================= */
82
83/* === M E T H O D S ======================================================= */
84
85Instance::Instance(synfig::Canvas::Handle canvas, etl::handle< synfig::FileContainerTemporary > container):
86        synfigapp::Instance             (canvas, container),
87        canvas_tree_store_              (Gtk::TreeStore::create(canvas_tree_model)),
88        history_tree_store_             (HistoryTreeStore::create(this)),
89        undo_status_(false),
90        redo_status_(false)
91{
92        id_=instance_count_++;
93
94        // Connect up all the signals
95        signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
96        signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
97        signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
98        signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
99
100        signal_saved().connect(
101                sigc::hide_return(
102                        sigc::ptr_fun(
103                                studio::AutoRecover::auto_backup
104                        )
105                )
106        );
107
108        refresh_canvas_tree();
109}
110
111Instance::~Instance()
112{
113}
114
115int
116Instance::get_visible_canvases()const
117{
118        int count(0);
119        CanvasViewList::const_iterator iter;
120        for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
121                if((*iter)->get_visible())
122                        count++;
123        return count;
124}
125
126handle<Instance>
127Instance::create(synfig::Canvas::Handle canvas, etl::handle< synfig::FileContainerTemporary > container)
128{
129        // Construct a new instance
130        handle<Instance> instance(new Instance(canvas, container));
131
132        // Add the new instance to the application's instance list
133        App::instance_list.push_back(instance);
134
135        // Set up the instance with the default UI manager
136        instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
137
138        // Signal the new instance
139        App::signal_instance_created()(instance);
140
141        // And then make sure that is has been selected
142        App::set_selected_instance(instance);
143
144        // Create the initial window for the root canvas
145        instance->focus(canvas);
146
147        return instance;
148}
149
150handle<CanvasView>
151Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
152{
153        if(!canvas)
154                return 0;
155
156        while(canvas->is_inline())
157                canvas=canvas->parent();
158
159        CanvasViewList::iterator iter;
160
161        for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
162                if((*iter)->get_canvas()==canvas)
163                        return *iter;
164
165        return CanvasView::create(this,canvas);
166}
167
168void
169Instance::focus(etl::handle<synfig::Canvas> canvas)
170{
171        handle<CanvasView> canvas_view=find_canvas_view(canvas);
172        assert(canvas_view);
173        canvas_view->present();
174}
175
176void
177Instance::set_undo_status(bool x)
178{
179        undo_status_=x;
180        App::dock_toolbox->update_tools();
181        signal_undo_redo_status_changed()();
182}
183
184void
185Instance::set_redo_status(bool x)
186{
187        redo_status_=x;
188        App::dock_toolbox->update_tools();
189        signal_undo_redo_status_changed()();
190}
191
192void
193studio::Instance::run_plugin(std::string plugin_path)
194{
195        handle<synfigapp::UIInterface> uim = this->find_canvas_view(this->get_canvas())->get_ui_interface();
196
197        string message = strprintf(_("Do you realy want to run plugin for file \"%s\"?" ),
198                                this->get_canvas()->get_name().c_str());
199
200        string details = strprintf(_("This operation cannot be undone and all undo history will be cleared."));
201
202        int answer = uim->confirmation(
203                                message,
204                                details,
205                                _("Cancel"),
206                                _("Proceed"),
207                                synfigapp::UIInterface::RESPONSE_OK);
208
209        if(answer == synfigapp::UIInterface::RESPONSE_OK){
210       
211                OneMoment one_moment;
212
213                Canvas::Handle canvas(this->get_canvas());
214               
215                synfigapp::PluginLauncher launcher(canvas);
216               
217                Time cur_time;
218                cur_time = canvas->get_time();
219
220                this->close();
221
222                if(canvas->count()!=1)
223                {
224                        one_moment.hide();
225                        App::dialog_message_1b(
226                                        "ERROR",
227                                        _("The plugin operation has failed."),
228                                        _("This can be due to current file "
229                                                "being referenced by another composition that is already open, "
230                                                "or because of an internal error in Synfig Studio. Try closing "
231                                                "any compositions that might reference this file and try again, "
232                                                "or restart Synfig Studio."),
233                                        _("Close"));
234
235                        one_moment.show();
236
237                } else {
238                        bool result;
239                        result = launcher.execute( plugin_path, App::get_base_path() );
240                        if (!result){
241                                one_moment.hide();
242                                App::dialog_message_1b(
243                                                "Error",
244                                                launcher.get_output(),
245                                                "details",
246                                                _("Close"));
247
248                                one_moment.show();
249                               
250                        }
251                }
252       
253       
254                canvas=0;
255
256                App::open_as(launcher.get_result_path(),launcher.get_original_path());
257       
258       
259                etl::handle<Instance> new_instance = App::instance_list.back();
260                new_instance->inc_action_count(); // This file isn't saved! mark it as such
261
262                // Restore time cursor position
263                canvas = App::instance_list.back()->get_canvas();
264                etl::handle<synfigapp::CanvasInterface> new_canvas_interface(new_instance->find_canvas_interface(canvas));
265                new_canvas_interface->set_time(cur_time);
266
267        }
268        return;
269}
270
271
272bool
273studio::Instance::save_as(const synfig::String &file_name)
274{
275        if(synfigapp::Instance::save_as(file_name))
276        {
277                // after changing the filename, update the render settings with the new filename
278                list<handle<CanvasView> >::iterator iter;
279                for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
280                        (*iter)->render_settings.set_entry_filename();
281                App::add_recent_file(etl::handle<Instance>(this));
282                return true;
283        }
284        return false;
285}
286
287void
288studio::Instance::open()
289{
290        App::dialog_open(get_file_name());
291}
292
293Instance::Status
294studio::Instance::save()
295{
296        // if we don't have a real filename yet then we need to ask where to save it
297        if (!has_real_filename())
298        {
299                if (dialog_save_as())
300                        return STATUS_OK;
301                else
302                        return STATUS_CANCEL;
303        }
304
305        if (synfigapp::Instance::save())
306        {
307                App::add_recent_file(etl::handle<Instance>(this));
308                return STATUS_OK;
309        }
310        string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
311        App::dialog_message_1b(
312                        "ERROR",
313                        msg.c_str(),
314                        "details",
315                        _("Close"));
316
317        return STATUS_ERROR;
318}
319
320// the filename will be set to "Synfig Animation 1" or some such when first created
321// and will be changed to an absolute path once it has been saved
322// so if it still begins with "Synfig Animation " then we don't have a real filename yet
323bool
324studio::Instance::has_real_filename()
325{
326        return get_file_name().find(App::custom_filename_prefix.c_str()) != 0;
327}
328
329bool
330studio::Instance::dialog_save_as()
331{
332        string filename = get_file_name();
333        Canvas::Handle canvas(get_canvas());
334
335        {
336                OneMoment one_moment;
337                std::set<Node*>::iterator iter;
338                for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
339                {
340                        synfig::Node* node(*iter);
341                        for(;!node->parent_set.empty();node=*node->parent_set.begin())
342                        {
343                                Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
344                                if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
345                                {
346                                        //! \todo Fix big involving "Save As" with referenced compositions
347                                        string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n"
348                                                "on a composition that is being referenced by other\n"
349                                                "files that are currently open. Close these\n"
350                                                "other files first before trying to use \"SaveAs\".")));
351                                        App::dialog_message_1b(
352                                                        "ERROR",
353                                                        msg.c_str(),
354                                                        "details",
355                                                        _("Close"));
356
357                                        return false;
358                                }
359                                if(parent_layer)
360                                        break;
361                        }
362                }
363        }
364
365        if (has_real_filename())
366                filename = absolute_path(filename);
367
368        // show the canvas' name if it has one, else its ID
369        while (App::dialog_save_file((_("Please choose a file name") +
370                                                                  String(" (") +
371                                                                  (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
372                                                                  ")"),
373                                                                 filename, ANIMATION_DIR_PREFERENCE))
374        {
375                // If the filename still has wildcards, then we should
376                // continue looking for the file we want
377                string base_filename = basename(filename);
378                if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
379                        continue;
380
381                // if file extension is not recognized, then forced to .sifz
382                if (filename_extension(filename) == "")
383                        filename+=".sifz";
384
385                // forced to .sifz, the below code is not need anymore
386                try
387                {
388                        String ext(filename_extension(filename));
389                        // todo: ".sfg" literal and others
390                        if (ext != ".sif" && ext != ".sifz" && ext != ".sfg" && !App::dialog_message_2b(
391                                _("Unknown extension"),
392                                _("You have given the file name an extension which I do not recognize. "
393                                        "Are you sure this is what you want?"),
394                                Gtk::MESSAGE_QUESTION,
395                                _("Cancel"),
396                                _("Sure"))
397                        )
398                                continue;
399                }
400                catch(...)
401                {
402                        continue;
403                }
404
405                {
406                        struct stat     s;
407                        int stat_return = stat(filename.c_str(), &s);
408
409                        // if stat() fails with something other than 'file doesn't exist', there's been a real
410                        // error of some kind.  let's give up now and ask for a new path.
411                        if (stat_return == -1 && errno != ENOENT)
412                        {
413                                perror(filename.c_str());
414                                string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
415                                App::dialog_message_1b(
416                                                "ERROR",
417                                                msg.c_str(),
418                                                "details",
419                                                _("Close"));
420
421                                continue;
422                        }
423
424                        // If the file exists and the user doesn't want to overwrite it, keep prompting for a filename
425                        string message = strprintf(_("A file named \"%s\" already exists. "
426                                                        "Do you want to replace it?"),
427                                                basename(filename).c_str());
428
429                        string details = strprintf(_("The file already exists in \"%s\". "
430                                                        "Replacing it will overwrite its contents."),
431                                                basename(dirname(filename)).c_str());
432
433                        if ((stat_return == 0) && !App::dialog_message_2b(
434                                message,
435                                details,
436                                Gtk::MESSAGE_QUESTION,
437                                _("Use Another Name…"),
438                                _("Replace"))
439                        )
440                                continue;
441                }
442
443                if(save_as(filename))
444                {
445                        synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
446                        return true;
447                }
448                string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
449                App::dialog_message_1b(
450                                "ERROR",
451                                msg.c_str(),
452                                "details",
453                                _("Close"));
454        }
455
456        return false;
457}
458
459void
460Instance::update_all_titles()
461{
462        list<handle<CanvasView> >::iterator iter;
463        for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
464                (*iter)->update_title();
465}
466
467void
468Instance::close()
469{
470        // This will increase the reference count so we don't get DELETED
471        // until we are ready
472        handle<Instance> me(this);
473
474        /*
475        We need to hide some panels when instance is closed.
476        This is done to avoid the crash when two conditions met:
477         1) the list is scrolled down
478         2) user closes file
479        */
480        handle<CanvasView> canvas_view=find_canvas_view(get_canvas());
481        Gtk::Widget* tree_view_keyframes = canvas_view->get_ext_widget("keyframes");
482        tree_view_keyframes->hide();
483
484        Gtk::Widget* tree_view_params = canvas_view->get_ext_widget("params");
485        tree_view_params->hide();
486
487        Gtk::Widget* tree_view_children = canvas_view->get_ext_widget("children");
488        tree_view_children->hide();
489
490        // Make sure we aren't selected as the current instance
491        if(studio::App::get_selected_instance()==this)
492                studio::App::set_selected_instance(0);
493
494        // Turn-off/clean-up auto recovery
495        studio::App::auto_recover->clear_backup(get_canvas());
496
497        // Remove us from the active instance list
498        std::list<etl::handle<studio::Instance> >::iterator iter;
499        for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
500                if(*iter==this)
501                        break;
502        assert(iter!=studio::App::instance_list.end());
503        if(iter!=studio::App::instance_list.end())
504                studio::App::instance_list.erase(iter);
505
506        // Send out a signal that we are being deleted
507        studio::App::signal_instance_deleted()(this);
508
509        // Hide all of the canvas views
510        for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
511                (*iter)->hide();
512
513        // Consume pending events before deleting the canvas views
514        while(studio::App::events_pending())studio::App::iteration(true);
515
516        // Delete all of the canvas views
517        canvas_view_list().clear();
518
519        // If there is another open instance to select,
520        // go ahead and do so. If not, never mind.
521        if(studio::App::instance_list.empty())
522        {
523                studio::App::set_selected_canvas_view(0);
524                studio::App::set_selected_instance(0);
525        }
526        else
527                studio::App::instance_list.front()->canvas_view_list().front()->present();
528}
529
530void
531Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
532{
533        CanvasTreeModel canvas_tree_model;
534        assert(canvas);
535
536        row[canvas_tree_model.icon] = Gtk::Button().render_icon_pixbuf(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
537        row[canvas_tree_model.id] = canvas->get_id();
538        row[canvas_tree_model.name] = canvas->get_name();
539        if(canvas->is_root())
540                row[canvas_tree_model.label] = basename(canvas->get_file_name());
541        else
542        if(!canvas->get_id().empty())
543                row[canvas_tree_model.label] = canvas->get_id();
544        else
545        if(!canvas->get_name().empty())
546                row[canvas_tree_model.label] = canvas->get_name();
547        else
548                row[canvas_tree_model.label] = _("[Unnamed]");
549
550        row[canvas_tree_model.canvas] = canvas;
551        row[canvas_tree_model.is_canvas] = true;
552        row[canvas_tree_model.is_value_node] = false;
553
554        {
555                synfig::Canvas::Children::iterator iter;
556                synfig::Canvas::Children &children(canvas->children());
557
558                for(iter=children.begin();iter!=children.end();iter++)
559                        insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
560        }
561}
562
563void
564Instance::refresh_canvas_tree()
565{
566        canvas_tree_store()->clear();
567        Gtk::TreeRow row = *(canvas_tree_store()->prepend());
568        insert_canvas(row,get_canvas());
569}
570
571void
572Instance::dialog_cvs_commit()
573{
574        calc_repository_info();
575        if(!in_repository())
576        {
577                App::dialog_message_1b(
578                                "ERROR",
579                                _("You must first add this composition to the repository"),
580                                "details",
581                                _("Close"));
582
583                return;
584        }
585        try
586        {
587                string message;
588
589                if(synfigapp::Instance::get_action_count())
590                {
591                        if (!App::dialog_message_2b(
592                                _("CVS Commit"),
593                                _("This will save any changes you have made. Are you sure?"),
594                                Gtk::MESSAGE_QUESTION,
595                                _("Cancel"),
596                                _("Commit"))
597                        )
598                                return;
599
600                        save();
601                }
602
603                if(!is_modified())
604                {
605                        App::dialog_message_1b(
606                                        "ERROR",
607                                        _("The local copy of the file hasn't been changed since the last update. Nothing to commit!"),
608                                        "details",
609                                        _("Close"));
610
611                        return;
612                }
613
614                if(!App::dialog_entry(_("CVS Commit"),
615                                _("Log Message: "),
616                                message,
617                                _("Cancel"),
618                                _("Commit"))
619                )
620                        return;
621
622                OneMoment one_moment;
623                cvs_commit(message);
624        }
625        catch(...)
626        {
627                App::dialog_message_1b(
628                                "ERROR",
629                                _("An error has occurred when trying to COMMIT"),
630                                "details",
631                                _("Close"));
632        }
633        update_all_titles();
634}
635
636void
637Instance::dialog_cvs_add()
638{
639        calc_repository_info();
640        if(in_repository())
641        {
642                App::dialog_message_1b(
643                                "ERROR",
644                                _("This composition has already been added to the repository"),
645                                "details",
646                                _("Close"));
647                return;
648        }
649        try
650        {
651                string message;
652
653                //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
654                //      return;
655                OneMoment one_moment;
656                cvs_add();
657        }
658        catch(...)
659        {
660                App::dialog_message_1b(
661                                "ERROR",
662                                _("An error has occurred when trying to ADD"),
663                                "details",
664                                _("Close"));
665        }
666        update_all_titles();
667}
668
669void
670Instance::dialog_cvs_update()
671{
672        calc_repository_info();
673        if(!in_repository())
674        {
675                App::dialog_message_1b(
676                                "ERROR",
677                                _("This file is not under version control, so there is nothing to update from!"),
678                                "details",
679                                _("Close"));
680
681                return;
682        }
683        if(!is_updated())
684        {
685                App::dialog_message_1b(
686                                "INFO",
687                                _("This file is up-to-date"),
688                                "details",
689                                _("Close"));
690
691                return;
692        }
693
694        try
695        {
696                String filename(get_file_name());
697                if(synfigapp::Instance::get_action_count())
698                {
699                        if (!App::dialog_message_2b(
700                                _("CVS Update"),
701                                _("This will save any changes you have made. Are you sure?"),
702                                Gtk::MESSAGE_QUESTION,
703                                _("Cancel"),
704                                _("Update"))
705                        )
706                                return;
707
708                        save();
709                }
710                OneMoment one_moment;
711                time_t oldtime=get_original_timestamp();
712                cvs_update();
713                calc_repository_info();
714                // If something has been updated...
715                if(oldtime!=get_original_timestamp())
716                {
717                        revert();
718                }
719        }
720        catch(...)
721        {
722                App::dialog_message_1b(
723                                "ERROR",
724                                _("An error has occurred when trying to UPDATE"),
725                                "details",
726                                _("Close"));
727        }
728        //update_all_titles();
729}
730
731void
732Instance::dialog_cvs_revert()
733{
734        calc_repository_info();
735        if(!in_repository())
736{
737                App::dialog_message_1b(
738                                "ERROR",
739                                _("This file is not under version control, so there is nothing to revert to!"),
740                                "details",
741                                _("Close"));
742                return;
743        }
744        try
745        {
746                String filename(get_file_name());
747
748                if (!App::dialog_message_2b(
749                        _("CVS Revert"),
750                        _("This will abandon all changes you have made since the last time you "
751                                "performed a commit operation. This cannot be undone! "
752                                "Are you sure you want to do this?"),
753                        Gtk::MESSAGE_QUESTION,
754                        _("Cancel"),
755                        _("Revert"))
756                )
757                        return;
758
759                OneMoment one_moment;
760
761                // Remove the old file
762                if(remove(get_file_name().c_str())!=0)
763                {
764                        App::dialog_message_1b(
765                                        "ERROR",
766                                        _("Unable to remove previous version"),
767                                        "details",
768                                        _("Close"));
769
770                        return;
771                }
772
773                cvs_update();
774                revert();
775        }
776        catch(...)
777        {
778                App::dialog_message_1b(
779                                "ERROR",
780                                _("An error has occurred when trying to UPDATE"),
781                                "details",
782                                _("Close"));
783        }
784        //update_all_titles();
785}
786
787void
788Instance::revert()
789{
790        OneMoment one_moment;
791
792        String filename(get_file_name());
793
794        Canvas::Handle canvas(get_canvas());
795
796        close();
797
798        if(canvas->count()!=1)
799        {
800                one_moment.hide();
801                App::dialog_message_1b(
802                                "ERROR",
803                                _("The revert operation has failed."),
804                                _("This can be due to it being referenced by another composition"
805                                        " that is already open, or because of an internal error "
806                                        "in Synfig Studio. Try closing any compositions that "
807                                        "might reference this composition and try again, or "
808                                        "restart Synfig Studio."),
809                                _("Close"));
810
811                one_moment.show();
812        }
813        canvas=0;
814
815        App::open(filename);
816}
817
818bool
819Instance::safe_revert()
820{
821        if(synfigapp::Instance::get_action_count())
822        {
823                if (!App::dialog_message_2b(
824                        _("Revert to saved"),
825                        _("You will lose any changes you have made since your last save."
826                                "Are you sure?"),
827                        Gtk::MESSAGE_QUESTION,
828                        _("Cancel"),
829                        _("Revert"))
830                )
831                        return false;
832        }
833
834        revert();
835        return true;
836}
837
838bool
839Instance::safe_close()
840{
841        handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
842        handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
843
844        // if the animation is currently playing, closing the window will cause a crash,
845        // so don't allow it
846        if (canvas_view->is_playing())
847        {
848                canvas_view->present();
849                App::dialog_message_1b(
850                                "ERROR",
851                                _("The animation is currently playing so the window cannot be closed."),
852                                "details",
853                                _("Thanks!"));
854
855                return false;
856        }
857        if(get_action_count())
858                do
859                {
860                        string message = strprintf(_("Save changes to document \"%s\" before closing?"),
861                                        basename(get_file_name()).c_str() );
862
863                        string details = (_("If you don't save, changes from the last time you saved "
864                                        "will be permanently lost."));
865
866                        int answer=uim->yes_no_cancel(
867                                                message,
868                                                details,
869                                                _("Close without Saving"),
870                                                _("Cancel"),
871                                                _("Save"),
872                                                synfigapp::UIInterface::RESPONSE_YES
873                        );
874
875                        if(answer == synfigapp::UIInterface::RESPONSE_YES){
876                                enum Status status = save();
877                                if (status == STATUS_OK) break;
878                                else if (status == STATUS_CANCEL) return false;
879                        }
880                        if(answer==synfigapp::UIInterface::RESPONSE_NO)
881                                break;
882                        if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
883                                return false;
884                } while (true);
885
886        if(is_modified())
887        {
888                string message = strprintf(_("Commit changes of \"%s\" to  the CVS repository?"),
889                                basename(get_file_name()).c_str());
890
891                string details = (_("If you don't commit, changes not yet on the CVS repository will "
892                                "be permanently lost."));
893
894                int answer=uim->yes_no_cancel(
895                                        message,
896                                        details,
897                                        _("Close without Committing"),
898                                        _("Cancel"),
899                                        _("Commit…"),
900                                        synfigapp::UIInterface::RESPONSE_YES
901                );
902
903                if(answer==synfigapp::UIInterface::RESPONSE_YES)
904                        dialog_cvs_commit();
905                if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
906                        return false;
907        }
908
909        close();
910
911        return true;
912}
913
914void
915Instance::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
916{
917        synfigapp::Action::CandidateList candidate_list;
918        synfigapp::Action::CandidateList::iterator iter;
919
920        candidate_list=compile_candidate_list(param_list,category);
921
922        candidate_list.sort();
923
924        // if(candidate_list.empty())
925        //      synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
926
927        for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
928        {
929                Gtk::StockID stock_id(get_action_stock_id(*iter));
930
931                if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
932                {
933                        action_group->add(Gtk::Action::create(
934                                "action-"+iter->name,
935                                stock_id,
936                                iter->local_name,iter->local_name
937                        ),
938                                sigc::bind(
939                                        sigc::bind(
940                                                sigc::mem_fun(
941                                                        *const_cast<studio::Instance*>(this),
942                                                        &studio::Instance::process_action
943                                                ),
944                                                param_list
945                                        ),
946                                        iter->name
947                                )
948                        );
949                        ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
950                }
951        }
952}
953
954void
955Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,synfigapp::Action::Category category)const
956{
957        synfigapp::Action::CandidateList candidate_list;
958        synfigapp::Action::CandidateList::iterator iter;
959
960        candidate_list=compile_candidate_list(param_list,category);
961
962        candidate_list.sort();
963
964        if(candidate_list.empty())
965                synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
966
967        for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
968        {
969                if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
970                {
971                        Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem(
972                                *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)),
973                                iter->local_name ));
974                        item->signal_activate().connect(
975                                sigc::bind(
976                                        sigc::bind(
977                                                sigc::mem_fun(
978                                                        *const_cast<studio::Instance*>(this),
979                                                        &studio::Instance::process_action ),
980                                                param_list ),
981                                        iter->name ));
982                        item->show_all();
983                        menu->append(*item);
984                }
985        }
986}
987
988void
989Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,const synfigapp::Action::ParamList &param_list2,synfigapp::Action::Category category)const
990{
991        synfigapp::Action::CandidateList candidate_list;
992        synfigapp::Action::CandidateList candidate_list2;
993
994        synfigapp::Action::CandidateList::iterator iter;
995
996        candidate_list=compile_candidate_list(param_list,category);
997        candidate_list2=compile_candidate_list(param_list2,category);
998
999        candidate_list.sort();
1000
1001        if(candidate_list.empty())
1002                synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
1003        if(candidate_list2.empty())
1004                synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
1005
1006        // Separate out the candidate lists so that there are no conflicts
1007        for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
1008        {
1009                synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
1010                if(iter2!=candidate_list2.end())
1011                        candidate_list2.erase(iter2);
1012        }
1013
1014        for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
1015        {
1016                if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
1017                {
1018                        Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem(
1019                                *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)),
1020                                iter->local_name ));
1021                        item->signal_activate().connect(
1022                                sigc::bind(
1023                                        sigc::bind(
1024                                                sigc::mem_fun(
1025                                                        *const_cast<studio::Instance*>(this),
1026                                                        &studio::Instance::process_action ),
1027                                                param_list2 ),
1028                                        iter->name ));
1029                        item->show_all();
1030                        menu->append(*item);
1031                }
1032        }
1033
1034        for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
1035        {
1036                if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
1037                {
1038                        Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem(
1039                                *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)),
1040                                iter->local_name ));
1041                        item->signal_activate().connect(
1042                                sigc::bind(
1043                                        sigc::bind(
1044                                                sigc::mem_fun(
1045                                                        *const_cast<studio::Instance*>(this),
1046                                                        &studio::Instance::process_action ),
1047                                                param_list ),
1048                                        iter->name ));
1049                        item->show_all();
1050                        menu->append(*item);
1051                }
1052        }
1053}
1054
1055void
1056Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
1057{
1058        if (getenv("SYNFIG_DEBUG_ACTIONS"))
1059                synfig::info("%s:%d process_action: '%s'", __FILE__, __LINE__, name.c_str());
1060
1061        assert(synfigapp::Action::book().count(name));
1062
1063        synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
1064
1065        synfigapp::Action::Handle action(entry.factory());
1066
1067        if(!action)
1068        {
1069                synfig::error("Bad Action");
1070                return;
1071        }
1072
1073        action->set_param_list(param_list);
1074
1075        synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
1076        synfigapp::Action::ParamVocab::const_iterator iter;
1077
1078        for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
1079        {
1080                if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
1081                        continue;
1082
1083                // If the parameter is optionally user-supplied,
1084                // and has not been already provided in the param_list,
1085                // then we should go ahead and see if we can
1086                // provide that data.
1087                if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
1088                {
1089                        switch(iter->get_type())
1090                        {
1091                        case synfigapp::Action::Param::TYPE_STRING:
1092                        {
1093                                String str;
1094                                if(iter->get_value_provided())
1095                                {
1096                                        synfigapp::Action::Param param;
1097                                        if (action->get_param(iter->get_name(),param))
1098                                        {
1099                                                if(param.get_type()==synfigapp::Action::Param::TYPE_STRING)
1100                                                        str = param.get_string();
1101                                        }
1102                                }
1103                                String button2 = _("Export");
1104                                String label = _("Name: ");
1105
1106                                // export and rename value dialog
1107                                if (entry.name == "ValueNodeRename") button2 = _("Rename");
1108                                // set layer description dialog
1109                                if (entry.name == "LayerSetDesc")
1110                                {
1111                                        button2 = _("Set");
1112                                        label = _("Description: ");
1113                                }
1114
1115                                if(!studio::App::dialog_entry(entry.local_name,
1116                                                        label,
1117                                                        //iter->get_local_name()+": "+iter->get_desc(),
1118                                                        str,
1119                                                        _("Cancel"),
1120                                                        button2))
1121                                        return;
1122                                action->set_param(iter->get_name(),str);
1123                                break;
1124                        }
1125                        default:
1126                                synfig::error("Unsupported user-supplied action parameter");
1127                                return;
1128                                break;
1129                        }
1130                }
1131        }
1132
1133        if(!action->is_ready())
1134        {
1135                synfig::error("Action not ready");
1136                return;
1137        }
1138
1139        perform_action(action);
1140}
1141
1142void
1143Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1144{
1145        Gtk::Menu& parammenu(*menu);
1146        synfigapp::ValueDesc value_desc2(value_desc);
1147        etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1148
1149        if(!canvas_interface)
1150                return;
1151
1152        Gtk::MenuItem *item = NULL;
1153
1154        synfigapp::Action::ParamList param_list,param_list2;
1155        param_list=canvas_interface->generate_param_list(value_desc);
1156        param_list.add("origin",location);
1157
1158#ifdef BLINEPOINT_MENU_IS_VERTEX_MENU
1159        if(value_desc.get_value_type()==type_bline_point && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1160        {
1161                param_list2=canvas_interface->generate_param_list(
1162                        synfigapp::ValueDesc(
1163                                ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1164                                ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1165                                                           ->get_link_index_from_name("point")
1166                        )
1167                );
1168                param_list2.add("origin",location);
1169        }
1170#endif  // BLINEPOINT_MENU_IS_VERTEX_MENU
1171
1172        // Populate the convert menu by looping through
1173        // the ValueNode book and find the ones that are
1174        // relevant.
1175
1176        // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1177        //   the 'Index' parameter of a Duplicate layer
1178        // or
1179        //   a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1180        if (!((value_desc.parent_is_layer() &&
1181                   value_desc.get_layer()->get_name() == "duplicate" &&
1182                   value_desc.get_param_name() == "index") ||
1183                  (value_desc.is_value_node() &&
1184                   ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1185                   !(value_desc.parent_is_layer() ||
1186                         value_desc.parent_is_value_node()))))
1187        {
1188                Gtk::Menu *convert_menu=Gtk::manage(new Gtk::Menu());
1189                LinkableValueNode::Book::const_iterator iter;
1190                for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1191                {
1192                        if(iter->second.check_type(value_desc.get_value_type()))
1193                        {
1194                                item = Gtk::manage(new Gtk::MenuItem(iter->second.local_name));
1195                                item->signal_activate().connect(
1196                                        sigc::hide_return(
1197                                                sigc::bind(
1198                                                        sigc::bind(
1199                                                                sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1200                                                                iter->first ),
1201                                                        value_desc )));
1202                                item->show();
1203                                convert_menu->append(*item);
1204                        }
1205                }
1206
1207                item = Gtk::manage(new Gtk::ImageMenuItem(
1208                        *manage(new Gtk::Image(
1209                                Gtk::StockID("gtk-convert"),
1210                                Gtk::ICON_SIZE_MENU )),
1211                        _("Convert") ));
1212                item->set_submenu(*convert_menu);
1213                item->show();
1214                parammenu.append(*item);
1215        }
1216       
1217        // Interpolation menu: Show it only if
1218        // the value description is constant or animated (layer parameter or exported) and not static
1219        // and value is not canvas type
1220        if (((value_desc.is_const() && !value_desc.get_static()) || value_desc.is_animated())
1221        &&
1222                value_desc.get_value_type()!= type_canvas
1223                )
1224        {
1225                Gtk::Menu *param_interpolation_menu=Gtk::manage(new Gtk::Menu());
1226                synfigapp::Action::ParamList param_list;
1227                param_list.add("canvas", get_canvas());
1228                param_list.add("value_desc", value_desc);
1229                param_list.add("canvas_interface",canvas_interface);
1230
1231                /////// Default
1232                param_list.add("new_value", INTERPOLATION_UNDEFINED);
1233                item = Gtk::manage(new Gtk::MenuItem(_("Default")));
1234                item->signal_activate().connect(
1235                        sigc::bind(
1236                                sigc::bind(
1237                                        sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action),
1238                                        param_list ),
1239                                "ValueDescSetInterpolation" ));
1240                item->show();
1241                param_interpolation_menu->append(*item);
1242                param_list.erase("new_value");
1243
1244                #define ADD_IMAGE_MENU_ITEM(Interpolation, StockId, Text) \
1245                param_list.add("new_value", Interpolation); \
1246                item = Gtk::manage(new Gtk::ImageMenuItem( \
1247                        *Gtk::manage(new Gtk::Image(Gtk::StockID(StockId), Gtk::IconSize::from_name("synfig-small_icon"))), \
1248                        _(Text) )); \
1249                item->signal_activate().connect( \
1250                        sigc::bind( \
1251                                sigc::bind( \
1252                                        sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), \
1253                                        param_list ), \
1254                                "ValueDescSetInterpolation" )); \
1255                item->show_all(); \
1256                param_interpolation_menu->append(*item); \
1257                param_list.erase("new_value");
1258               
1259               
1260                ADD_IMAGE_MENU_ITEM(INTERPOLATION_TCB, "synfig-interpolation_type_tcb", _("TCB"));
1261                ADD_IMAGE_MENU_ITEM(INTERPOLATION_LINEAR, "synfig-interpolation_type_linear", _("Linear"));
1262                ADD_IMAGE_MENU_ITEM(INTERPOLATION_HALT, "synfig-interpolation_type_ease", _("Ease"));
1263                ADD_IMAGE_MENU_ITEM(INTERPOLATION_CONSTANT, "synfig-interpolation_type_const", _("Constant"));
1264                ADD_IMAGE_MENU_ITEM(INTERPOLATION_CLAMPED, "synfig-interpolation_type_clamped", _("Clamped"));
1265               
1266                #undef ADD_IMAGE_MENU_ITEM
1267
1268                item = Gtk::manage(new Gtk::MenuItem(_("Interpolation")));
1269                item->set_submenu(*param_interpolation_menu);
1270                item->show();
1271                parammenu.append(*item);
1272        }
1273
1274        synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1275        if (bezier)
1276                categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1277
1278        const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1279        for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1280        {
1281                synfigapp::ValueDesc selected_value_desc((*iter)->get_value_desc());
1282                if(selected_value_desc.is_valid() && value_desc != selected_value_desc)
1283                        param_list.add("selected_value_desc",selected_value_desc);
1284        }
1285
1286        if(param_list2.empty())
1287                add_actions_to_menu(&parammenu, param_list,categories);
1288        else
1289                add_actions_to_menu(&parammenu, param_list2,param_list,categories);
1290
1291        if((value_desc2.get_value_type()==type_bline_point || value_desc2.get_value_type()==type_width_point)
1292         && value_desc2.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc2.get_value_node()))
1293        {
1294                // the index=0 is position for widthpoint and vertex for blinepoint
1295                value_desc2=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc2.get_value_node()),0);
1296        }
1297
1298        if(value_desc2.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc2.get_value_node()))
1299        {
1300                ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc2.get_value_node()));
1301
1302                try
1303                {
1304                        // try to find a waypoint at the current time - if we
1305                        // can't, we don't want the menu entry - an exception is thrown
1306                        WaypointList::iterator iter(value_node->find(canvas->get_time()));
1307                        std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1308                        waypoint_set.insert(*iter);
1309
1310                        item = Gtk::manage(new Gtk::MenuItem(_("Edit Waypoint")));
1311                        item->signal_activate().connect(
1312                                sigc::bind(
1313                                        sigc::bind(
1314                                                sigc::bind(
1315                                                        sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1316                                                        -1 ),
1317                                                waypoint_set ),
1318                                        value_desc2 ));
1319                        item->show();
1320                        parammenu.append(*item);
1321                }
1322                catch(...)
1323                { }
1324        }
1325        //// Add here the rest of actions here for specific single value descriptions
1326        //
1327        // Specific actions for Widthpoints (Composite)
1328        if(value_desc.get_value_type()==type_width_point && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1329        {
1330                ValueNode_Composite::Handle wpoint_composite(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()));
1331                synfigapp::Action::ParamList param_list;
1332                param_list.add("canvas",canvas);
1333                param_list.add("canvas_interface",canvas_interface);
1334                param_list.add("time",canvas_interface->get_time());
1335                item = Gtk::manage(new Gtk::SeparatorMenuItem());
1336                item->show();
1337                parammenu.append(*item);
1338
1339
1340                #define ADD_IMAGE_MENU_ITEM(Type, StockId, Text) \
1341                        param_list.add("new_value", ValueBase((int)WidthPoint::Type)); \
1342                        item = Gtk::manage(new Gtk::ImageMenuItem( \
1343                                *Gtk::manage(new Gtk::Image(Gtk::StockID(StockId),Gtk::IconSize::from_name("synfig-small_icon"))), \
1344                                _(Text) )); \
1345                        item->signal_activate().connect( \
1346                                sigc::bind( \
1347                                        sigc::bind( \
1348                                                sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), \
1349                                                param_list ), \
1350                                        "ValueDescSet" )); \
1351                        item->show_all(); \
1352                        parammenu.append(*item); \
1353                        param_list.erase("new_value");
1354
1355                ////// Before //////////////////
1356                param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("side_before")));
1357
1358                ADD_IMAGE_MENU_ITEM(TYPE_INTERPOLATE, "synfig-interpolate_interpolation", "Cusp Before: Interpolate")
1359                ADD_IMAGE_MENU_ITEM(TYPE_ROUNDED, "synfig-rounded_interpolation", "Cusp Before: Rounded")
1360                ADD_IMAGE_MENU_ITEM(TYPE_SQUARED, "synfig-squared_interpolation", "Cusp Before: Squared")
1361                ADD_IMAGE_MENU_ITEM(TYPE_PEAK, "synfig-peak_interpolation", "Cusp Before: Peak")
1362                ADD_IMAGE_MENU_ITEM(TYPE_FLAT, "synfig-flat_interpolation", "Cusp Before: Flat")
1363
1364                ///////
1365                item = Gtk::manage(new Gtk::SeparatorMenuItem());
1366                item->show();
1367                parammenu.append(*item);
1368               
1369                ////// After ///////////////////////
1370                param_list.erase("value_desc");
1371                param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("side_after")));
1372
1373                ADD_IMAGE_MENU_ITEM(TYPE_INTERPOLATE, "synfig-interpolate_interpolation", "Cusp After: Interpolate")
1374                ADD_IMAGE_MENU_ITEM(TYPE_ROUNDED, "synfig-rounded_interpolation", "Cusp After: Rounded")
1375                ADD_IMAGE_MENU_ITEM(TYPE_SQUARED, "synfig-squared_interpolation", "Cusp After: Squared")
1376                ADD_IMAGE_MENU_ITEM(TYPE_PEAK, "synfig-peak_interpolation", "Cusp After: Peak")
1377                ADD_IMAGE_MENU_ITEM(TYPE_FLAT, "synfig-flat_interpolation", "Cusp After: Flat")
1378
1379                ///////
1380                item = Gtk::manage(new Gtk::SeparatorMenuItem());
1381                item->show();
1382                parammenu.append(*item);
1383
1384                /////// Set WIDTH to ZERO
1385                param_list.erase("value_desc");
1386                param_list.erase("new_value");
1387                param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("width")));
1388                param_list.add("new_value", ValueBase(Real(0.0)));
1389                item = Gtk::manage(new Gtk::MenuItem(_("Set width to zero")));
1390                item->signal_activate().connect(
1391                        sigc::bind(
1392                                sigc::bind(
1393                                        sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action),
1394                                        param_list ),
1395                                "ValueDescSet" ));
1396                item->show();
1397                parammenu.append(*item);
1398
1399                /////// Set WIDTH to DEFAULT
1400                param_list.erase("new_value");
1401                param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("width")));
1402                param_list.add("new_value", ValueBase(Real(1.0)));
1403                item = Gtk::manage(new Gtk::MenuItem(_("Set width to default")));
1404                item->signal_activate().connect(
1405                        sigc::bind(
1406                                sigc::bind(
1407                                        sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action),
1408                                        param_list ),
1409                                "ValueDescSet" ));
1410                item->show();
1411                parammenu.append(*item);
1412        }
1413}
1414
1415void
1416edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1417{
1418        etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1419
1420        Gtk::Dialog dialog(
1421                "Edit Multiple Waypoints",
1422                true
1423        );
1424
1425        Widget_WaypointModel widget_waypoint_model;
1426        widget_waypoint_model.show();
1427
1428        dialog.get_vbox()->pack_start(widget_waypoint_model);
1429
1430        dialog.add_button(_("Cancel"), 0);
1431        dialog.add_button(_("Apply"), 1);
1432        dialog.show();
1433
1434        if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1435                return;
1436        synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1437
1438        std::list<synfigapp::ValueDesc>::iterator iter;
1439        for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1440        {
1441                synfigapp::ValueDesc value_desc(*iter);
1442
1443                if(!value_desc.is_valid())
1444                        continue;
1445
1446                ValueNode_Animated::Handle value_node;
1447                // Check if we are dealing with a BLinePoint or a WidthPoint value desc
1448                // If so, then change the value desc to be the position or the point.
1449
1450                if(value_desc.is_value_node() && value_desc.parent_is_linkable_value_node())
1451                {
1452                        synfig::ValueNode_Composite::Handle compo(synfig::ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()));
1453                        if(compo && compo->get_type() == type_width_point)
1454                        {
1455                                value_desc=synfigapp::ValueDesc(compo, compo->get_link_index_from_name("position"));
1456                                //value_node=ValueNode_Animated::Handle::cast_dynamic(compo->get_link(compo->get_link_index_from_name("position")));
1457                        }
1458                        if(compo && compo->get_type() == type_bline_point)
1459                        {
1460                                value_desc=synfigapp::ValueDesc(compo, compo->get_link_index_from_name("point"));
1461                                //value_node=ValueNode_Animated::Handle::cast_dynamic(compo->get_link(compo->get_link_index_from_name("point")));
1462                        }
1463                }
1464
1465                // If this value isn't a ValueNode_Animated, but
1466                // it is somewhat constant, then go ahead and convert
1467                // it to a ValueNode_Animated.
1468                if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1469                {
1470                        ValueBase value;
1471                        if(value_desc.is_value_node())
1472                                value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1473                        else
1474                                value=value_desc.get_value();
1475
1476                        value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1477
1478                        synfigapp::Action::Handle action;
1479
1480                        if(!value_desc.is_value_node())
1481                        {
1482                                action=synfigapp::Action::create("ValueDescConnect");
1483                                action->set_param("dest",value_desc);
1484                                action->set_param("src",ValueNode::Handle(value_node));
1485                        }
1486                        else
1487                        {
1488                                action=synfigapp::Action::create("ValueNodeReplace");
1489                                action->set_param("dest",value_desc.get_value_node());
1490                                action->set_param("src",ValueNode::Handle(value_node));
1491                        }
1492
1493                        action->set_param("canvas",canvas_view->get_canvas());
1494                        action->set_param("canvas_interface",canvas_interface);
1495
1496                        if(!canvas_interface->get_instance()->perform_action(action))
1497                        {
1498                                canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1499                                group.cancel();
1500                                return;
1501                        }
1502                }
1503                else
1504                {
1505                        if(value_desc.is_value_node())
1506                                value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1507                }
1508
1509                if(value_node)
1510                {
1511                        synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
1512
1513                        if(!action)
1514                        {
1515                                canvas_view->get_ui_interface()->error(_("Unable to find WaypointSetSmart action"));
1516                                group.cancel();
1517                                return;
1518                        }
1519
1520                        action->set_param("canvas",canvas_view->get_canvas());
1521                        action->set_param("canvas_interface",canvas_interface);
1522                        action->set_param("value_node",ValueNode::Handle(value_node));
1523                        action->set_param("time",canvas_interface->get_time());
1524                        action->set_param("model",widget_waypoint_model.get_waypoint_model());
1525
1526                        if(!canvas_interface->get_instance()->perform_action(action))
1527                        {
1528                                canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1529                                group.cancel();
1530                                return;
1531                        }
1532                }
1533                else
1534                {
1535                        //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1536                        //group.cancel();
1537                        //return;
1538                }
1539
1540        }
1541}
1542
1543void
1544Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list, const synfigapp::ValueDesc &value_desc)
1545{
1546        etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1547
1548        synfigapp::Action::ParamList param_list;
1549        param_list=canvas_interface->generate_param_list(value_desc_list);
1550
1551        add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1552
1553        // Add the edit waypoints option if that might be useful
1554        if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1555        {
1556                Gtk::MenuItem *item = Gtk::manage(new Gtk::MenuItem(_("Edit Waypoints")));
1557                item->signal_activate().connect(
1558                        sigc::bind(
1559                                sigc::bind(
1560                                        sigc::ptr_fun(&edit_several_waypoints),
1561                                        value_desc_list ),
1562                                find_canvas_view(canvas) ));
1563                item->show();
1564                menu->append(*item);
1565        }
1566        // add here the rest of specific actions for multiple selected value_descs
1567        if (value_desc.is_valid())
1568                make_param_menu(menu,canvas,value_desc, 0.f, false);
1569}
Note: See TracBrowser for help on using the repository browser.