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

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

First release to xenial

File size: 117.4 KB
Line 
1/* === S Y N F I G ========================================================= */
2/*!     \file app.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 Gerald Young
11**      Copyright (c) 2008, 2010-2013 Carlos López
12**      Copyright (c) 2009, 2011 Nikita Kitaev
13**      Copyright (c) 2012-2015 Konstantin Dmitriev
14**
15**      This package is free software; you can redistribute it and/or
16**      modify it under the terms of the GNU General Public License as
17**      published by the Free Software Foundation; either version 2 of
18**      the License, or (at your option) any later version.
19**
20**      This package is distributed in the hope that it will be useful,
21**      but WITHOUT ANY WARRANTY; without even the implied warranty of
22**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23**      General Public License for more details.
24**      \endlegal
25*/
26/* ========================================================================= */
27
28/* === H E A D E R S ======================================================= */
29
30#ifdef USING_PCH
31#       include "pch.h"
32#else
33#ifdef HAVE_CONFIG_H
34#       include <config.h>
35#endif
36
37#include <fstream>
38#include <iostream>
39#include <locale>
40#include <cstring>
41
42#ifdef __OpenBSD__
43#include <errno.h>
44#elif defined(HAVE_SYS_ERRNO_H)
45#include <sys/errno.h>
46#endif
47#include <gtkmm/filechooserdialog.h>
48#include <gtkmm/dialog.h>
49#include <gtkmm/messagedialog.h>
50#include <gtkmm/label.h>
51#include <gtkmm/stock.h>
52#include <gtkmm/stockitem.h>
53#include <gtkmm/iconsource.h>
54#include <gtkmm/accelmap.h>
55#include <gtkmm/uimanager.h>
56#include <gtkmm/textview.h>
57#include <gtkmm/filefilter.h>
58#include <gtkmm/cssprovider.h>
59
60#include <glibmm/main.h>
61#include <glibmm/thread.h>
62#include <glibmm/miscutils.h>
63#include <glibmm/spawn.h>
64
65#include <gtk/gtk.h>
66
67#include <gdkmm/general.h>
68
69#ifdef WIN32
70#define WINVER 0x0500
71#include <windows.h>
72#endif
73
74#include <synfig/loadcanvas.h>
75#include <synfig/savecanvas.h>
76#include <synfig/importer.h>
77#include <synfig/filesystemnative.h>
78#include <synfig/filesystemgroup.h>
79#include <synfig/filecontainertemporary.h>
80
81#include "app.h"
82#include "dialogs/about.h"
83#include "splash.h"
84#include "instance.h"
85#include "canvasview.h"
86#include "dialogs/dialog_setup.h"
87#include "dialogs/dialog_gradient.h"
88#include "dialogs/dialog_input.h"
89#include "dialogs/dialog_color.h"
90#include "mainwindow.h"
91#include "docks/dock_toolbox.h"
92#include "onemoment.h"
93
94#include "docks/dockmanager.h"
95
96#include "states/state_eyedrop.h"
97#include "states/state_normal.h"
98#include "states/state_mirror.h"
99#include "states/state_draw.h"
100#include "states/state_lasso.h"
101#include "states/state_fill.h"
102#include "states/state_bline.h"
103#include "states/state_brush.h"
104#include "states/state_polygon.h"
105#include "states/state_sketch.h"
106#include "states/state_gradient.h"
107#include "states/state_circle.h"
108#include "states/state_rectangle.h"
109#include "states/state_smoothmove.h"
110#include "states/state_scale.h"
111#include "states/state_star.h"
112#include "states/state_text.h"
113#include "states/state_width.h"
114#include "states/state_rotate.h"
115#include "states/state_zoom.h"
116
117#include "devicetracker.h"
118#include "docks/dialog_tooloptions.h"
119#include "widgets/widget_enum.h"
120
121#include "autorecover.h"
122
123#include <synfigapp/settings.h>
124#include "docks/dock_history.h"
125#include "docks/dock_canvases.h"
126#include "docks/dock_keyframes.h"
127#include "docks/dock_layers.h"
128#include "docks/dock_params.h"
129#include "docks/dock_metadata.h"
130#include "docks/dock_children.h"
131#include "docks/dock_info.h"
132#include "docks/dock_navigator.h"
133#include "docks/dock_layergroups.h"
134#include "docks/dock_timetrack.h"
135#include "docks/dock_curves.h"
136
137#include "modules/module.h"
138#include "modules/mod_palette/mod_palette.h"
139
140#include "ipc.h"
141
142#include "statemanager.h"
143
144#ifdef WITH_FMOD
145#include <fmod.h>
146#endif
147
148#include <gtkmm/accelmap.h>
149#include <gtkmm/filechooser.h>
150#include <gtkmm/filechooserdialog.h>
151
152#include "general.h"
153
154#endif
155
156/* === U S I N G =========================================================== */
157
158using namespace std;
159using namespace etl;
160using namespace synfig;
161using namespace studio;
162
163/* === M A C R O S ========================================================= */
164
165#ifndef DPM2DPI
166#define DPM2DPI(x)      (float(x)/39.3700787402f)
167#define DPI2DPM(x)      (float(x)*39.3700787402f)
168#endif
169
170#ifdef WIN32
171#       ifdef IMAGE_DIR
172#               undef IMAGE_DIR
173#               define IMAGE_DIR "share\\pixmaps"
174#       endif
175#endif
176
177#ifndef IMAGE_DIR
178#       define IMAGE_DIR "/usr/local/share/pixmaps"
179#endif
180
181#ifndef IMAGE_EXT
182#       define IMAGE_EXT        "tif"
183#endif
184
185#ifdef WIN32
186#       ifdef PLUGIN_DIR
187#               undef PLUGIN_DIR
188#               define PLUGIN_DIR "share\\synfig\\plugins"
189#       endif
190#endif
191
192#ifndef PLUGIN_DIR
193#       define PLUGIN_DIR "/usr/local/share/synfig/plugins"
194#endif
195
196#include <synfigapp/main.h>
197
198/* === S I G N A L S ======================================================= */
199
200static sigc::signal<void> signal_present_all_;
201sigc::signal<void>&
202App::signal_present_all() { return signal_present_all_; }
203
204static sigc::signal<void> signal_recent_files_changed_;
205sigc::signal<void>&
206App::signal_recent_files_changed() { return signal_recent_files_changed_; }
207
208static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
209sigc::signal<void,etl::loose_handle<CanvasView> >&
210App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
211
212static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
213sigc::signal<void,etl::handle<Instance> >&
214App::signal_instance_selected() { return signal_instance_selected_; }
215
216static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
217sigc::signal<void,etl::handle<Instance> >&
218App::signal_instance_created() { return signal_instance_created_; }
219
220static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
221sigc::signal<void,etl::handle<Instance> >&
222App::signal_instance_deleted() { return signal_instance_deleted_; }
223
224/* === G L O B A L S ======================================================= */
225
226static std::list<std::string> recent_files;
227const std::list<std::string>& App::get_recent_files() { return recent_files; }
228
229int     App::Busy::count;
230bool App::shutdown_in_progress;
231
232synfig::Gamma App::gamma;
233
234Glib::RefPtr<studio::UIManager> App::ui_manager_;
235
236int App::jack_locks_=0;
237
238synfig::Distance::System App::distance_system;
239
240studio::Dialog_Setup* App::dialog_setup;
241
242etl::handle< studio::ModPalette > mod_palette_;
243//studio::Dialog_Palette* App::dialog_palette;
244
245std::list<etl::handle<Instance> > App::instance_list;
246
247static etl::handle<synfigapp::UIInterface> ui_interface_;
248const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
249
250etl::handle<Instance> App::selected_instance;
251etl::handle<CanvasView> App::selected_canvas_view;
252
253studio::About *studio::App::about=NULL;
254
255studio::MainWindow *studio::App::main_window=NULL;
256
257studio::Dock_Toolbox *studio::App::dock_toolbox=NULL;
258
259studio::AutoRecover *studio::App::auto_recover=NULL;
260
261studio::IPC *ipc=NULL;
262
263studio::DockManager* studio::App::dock_manager=0;
264
265studio::DeviceTracker* studio::App::device_tracker=0;
266
267studio::Dialog_Gradient* studio::App::dialog_gradient;
268
269studio::Dialog_Color* studio::App::dialog_color;
270
271studio::Dialog_Input* studio::App::dialog_input;
272
273studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
274
275studio::Dock_History* dock_history;
276studio::Dock_Canvases* dock_canvases;
277studio::Dock_Keyframes* dock_keyframes;
278studio::Dock_Layers* dock_layers;
279studio::Dock_Params* dock_params;
280studio::Dock_MetaData* dock_meta_data;
281studio::Dock_Children* dock_children;
282studio::Dock_Info* dock_info;
283studio::Dock_LayerGroups* dock_layer_groups;
284studio::Dock_Navigator* dock_navigator;
285studio::Dock_Timetrack* dock_timetrack;
286studio::Dock_Curves* dock_curves;
287
288std::list< etl::handle< studio::Module > > module_list_;
289
290bool studio::App::use_colorspace_gamma=true;
291#ifdef SINGLE_THREADED
292        #ifdef  WIN32
293        bool studio::App::single_threaded=true;
294        #else
295        bool studio::App::single_threaded=false;
296        #endif // WIN32
297#endif  // SINGLE THREADED
298bool studio::App::restrict_radius_ducks=true;
299bool studio::App::resize_imported_images=false;
300bool studio::App::enable_experimental_features=false;
301bool studio::App::use_dark_theme=false;
302String studio::App::custom_filename_prefix(DEFAULT_FILENAME_PREFIX);
303int studio::App::preferred_x_size=480;
304int studio::App::preferred_y_size=270;
305String studio::App::predefined_size(DEFAULT_PREDEFINED_SIZE);
306String studio::App::predefined_fps(DEFAULT_PREDEFINED_FPS);
307float studio::App::preferred_fps=24.0;
308synfigapp::PluginManager studio::App::plugin_manager;
309#ifdef USE_OPEN_FOR_URLS
310String studio::App::browser_command("open"); // MacOS only
311#else
312String studio::App::browser_command("xdg-open"); // Linux XDG standard
313#endif
314String studio::App::brushes_path("");
315String studio::App::sequence_separator(".");
316bool studio::App::navigator_uses_cairo=false;
317bool studio::App::workarea_uses_cairo=false;
318
319bool studio::App::enable_mainwin_menubar = true;
320String studio::App::ui_language ("os_LANG");
321
322static int max_recent_files_=25;
323int studio::App::get_max_recent_files() { return max_recent_files_; }
324void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
325
326static synfig::String app_base_path_;
327
328namespace studio {
329
330bool
331really_delete_widget(Gtk::Widget *widget)
332{
333        // synfig::info("really delete %p", (void*)widget);
334        delete widget;
335        return false;
336}
337
338// nasty workaround - when we've finished with a popup menu, we want to delete it
339// attaching to the signal_hide() signal gets us here before the action on the menu has run,
340// so schedule the real delete to happen in 50ms, giving the action a chance to run
341void
342delete_widget(Gtk::Widget *widget)
343{
344        // synfig::info("delete %p", (void*)widget);
345        Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
346}
347
348}; // END of namespace studio
349studio::StateManager* state_manager;
350
351
352
353
354class GlobalUIInterface : public synfigapp::UIInterface
355{
356public:
357
358        virtual Response confirmation(
359                        const std::string &message,
360                        const std::string &details,
361                        const std::string &cancel,
362                        const std::string &confirm,
363                        Response dflt
364        )
365        {
366                Gtk::MessageDialog dialog(
367                        message,
368                        false,
369                        Gtk::MESSAGE_WARNING,
370                        Gtk::BUTTONS_NONE,
371                        true
372                );
373
374                if (! details.empty())
375                        dialog.set_secondary_text(details);
376
377                dialog.add_button(cancel, RESPONSE_CANCEL);
378                dialog.add_button(confirm, RESPONSE_OK);
379                dialog.set_default_response(dflt);
380
381                dialog.show_all();
382                return (Response) dialog.run();
383        }
384
385
386        virtual Response yes_no_cancel(
387                                const std::string &message,
388                                const std::string &details,
389                                const std::string &button1,
390                                const std::string &button2,
391                                const std::string &button3,
392                                Response dflt=RESPONSE_YES
393        )
394        {
395                Gtk::MessageDialog dialog(
396                        message,
397                        false,
398                        Gtk::MESSAGE_QUESTION,
399                        Gtk::BUTTONS_NONE,
400                        true
401                );
402
403                dialog.set_secondary_text(details);
404                dialog.add_button(button1, RESPONSE_NO);
405                dialog.add_button(button2, RESPONSE_CANCEL);
406                dialog.add_button(button3, RESPONSE_YES);
407
408                dialog.set_default_response(dflt);
409                dialog.show();
410                return (Response)dialog.run();
411        }
412
413
414        virtual bool
415        task(const std::string &task)
416        {
417                std::cerr<<task.c_str()<<std::endl;
418                while(studio::App::events_pending())studio::App::iteration(false);
419                return true;
420        }
421
422        virtual bool
423        error(const std::string &err)
424        {
425                Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
426                dialog.show();
427                dialog.run();
428                return true;
429        }
430
431        virtual bool
432        warning(const std::string &err)
433        {
434                std::cerr<<"warning: "<<err.c_str()<<std::endl;
435                while(studio::App::events_pending())studio::App::iteration(false);
436                return true;
437        }
438
439        virtual bool
440        amount_complete(int /*current*/, int /*total*/)
441        {
442                while(studio::App::events_pending())studio::App::iteration(false);
443                return true;
444        }
445};
446
447/* === P R O C E D U R E S ================================================= */
448
449/*
450void
451studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
452{
453        action_group_list.push_back(action_group);
454        Gtk::UIManager::insert_action_group(action_group, pos);
455}
456
457void
458studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
459{
460        std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
461        for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
462                if(*iter==action_group)
463                {
464                        action_group_list.erase(iter);
465                        Gtk::UIManager::remove_action_group(action_group);
466                        return;
467                }
468        synfig::error("Unable to find action group");
469}
470
471void
472studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
473{
474        ui_manager->insert_action_group(group,0);
475        return;
476        std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
477        std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
478
479        for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
480        {
481                if(*iter && (*iter)->get_name()!="menus")
482                {
483                        synfig::info("Removing action group "+(*iter)->get_name());
484                        ui_manager->remove_action_group(*iter);
485                }
486        }
487        ui_manager->insert_action_group(group,0);
488
489        for(;!prev_groups.empty();prev_groups.pop_front())
490        {
491                if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
492                        ui_manager->insert_action_group(prev_groups.front(),1);
493        }
494}
495*/
496class Preferences : public synfigapp::Settings
497{
498public:
499        virtual bool get_value(const synfig::String& key, synfig::String& value)const
500        {
501                try
502                {
503                        synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
504                        if(key=="gamma")
505                        {
506                                value=strprintf("%f %f %f %f",
507                                        App::gamma.get_gamma_r(),
508                                        App::gamma.get_gamma_g(),
509                                        App::gamma.get_gamma_b(),
510                                        App::gamma.get_black_level()
511                                );
512                                return true;
513                        }
514                        if(key=="time_format")
515                        {
516                                value=strprintf("%i",App::get_time_format());
517                                return true;
518                        }
519                        if(key=="file_history.size")
520                        {
521                                value=strprintf("%i",App::get_max_recent_files());
522                                return true;
523                        }
524                        if(key=="use_colorspace_gamma")
525                        {
526                                value=strprintf("%i",(int)App::use_colorspace_gamma);
527                                return true;
528                        }
529                        if(key=="distance_system")
530                        {
531                                value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
532                                return true;
533                        }
534#ifdef SINGLE_THREADED
535                        if(key=="use_single_threaded")
536                        {
537                                value=strprintf("%i",(int)App::single_threaded);
538                                return true;
539                        }
540#endif
541                        if(key=="auto_recover_backup_interval")
542                        {
543                                value=strprintf("%i",App::auto_recover->get_timeout());
544                                return true;
545                        }
546                        if(key=="restrict_radius_ducks")
547                        {
548                                value=strprintf("%i",(int)App::restrict_radius_ducks);
549                                return true;
550                        }
551                        if(key=="resize_imported_images")
552                        {
553                                value=strprintf("%i",(int)App::resize_imported_images);
554                                return true;
555                        }
556                        if(key=="enable_experimental_features")
557                        {
558                                value=strprintf("%i",(int)App::enable_experimental_features);
559                                return true;
560                        }
561                        if(key=="use_dark_theme")
562                        {
563                                value=strprintf("%i",(int)App::use_dark_theme);
564                                return true;
565                        }
566                        if(key=="browser_command")
567                        {
568                                value=App::browser_command;
569                                return true;
570                        }
571                        if(key=="brushes_path")
572                        {
573                                value=App::brushes_path;
574                                return true;
575                        }
576                        if(key=="custom_filename_prefix")
577                        {
578                                value=App::custom_filename_prefix;
579                                return true;
580                        }
581                        if(key=="preferred_x_size")
582                        {
583                                value=strprintf("%i",App::preferred_x_size);
584                                return true;
585                        }
586                        if(key=="preferred_y_size")
587                        {
588                                value=strprintf("%i",App::preferred_y_size);
589                                return true;
590                        }
591                        if(key=="predefined_size")
592                        {
593                                value=strprintf("%s",App::predefined_size.c_str());
594                                return true;
595                        }
596                        if(key=="preferred_fps")
597                        {
598                                value=strprintf("%f",App::preferred_fps);
599                                return true;
600                        }
601                        if(key=="predefined_fps")
602                        {
603                                value=strprintf("%s",App::predefined_fps.c_str());
604                                return true;
605                        }
606                        if(key=="sequence_separator")
607                        {
608                                value=App::sequence_separator;
609                                return true;
610                        }
611                        if(key=="navigator_uses_cairo")
612                        {
613                                value=strprintf("%i",(int)App::navigator_uses_cairo);
614                                return true;
615                        }
616                        if(key=="workarea_uses_cairo")
617                        {
618                                value=strprintf("%i",(int)App::workarea_uses_cairo);
619                                return true;
620                        }
621                        if(key=="enable_mainwin_menubar")
622                        {
623                                value=strprintf("%i", (int)App::enable_mainwin_menubar);
624                                return true;
625                        }
626                }
627                catch(...)
628                {
629                        synfig::warning("Preferences: Caught exception when attempting to get value.");
630                }
631                return synfigapp::Settings::get_value(key,value);
632        }
633
634        virtual bool set_value(const synfig::String& key,const synfig::String& value)
635        {
636                try
637                {
638                        synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
639                        if(key=="gamma")
640                        {
641                                float r,g,b,blk;
642
643                                strscanf(value,"%f %f %f %f",
644                                        &r,
645                                        &g,
646                                        &b,
647                                        &blk
648                                );
649
650                                App::gamma.set_all(r,g,b,blk);
651
652                                return true;
653                        }
654                        if(key=="time_format")
655                        {
656                                int i(atoi(value.c_str()));
657                                App::set_time_format(static_cast<synfig::Time::Format>(i));
658                                return true;
659                        }
660                        if(key=="auto_recover_backup_interval")
661                        {
662                                int i(atoi(value.c_str()));
663                                App::auto_recover->set_timeout(i);
664                                return true;
665                        }
666                        if(key=="file_history.size")
667                        {
668                                int i(atoi(value.c_str()));
669                                App::set_max_recent_files(i);
670                                return true;
671                        }
672                        if(key=="use_colorspace_gamma")
673                        {
674                                int i(atoi(value.c_str()));
675                                App::use_colorspace_gamma=i;
676                                return true;
677                        }
678                        if(key=="distance_system")
679                        {
680                                App::distance_system=Distance::ident_system(value);;
681                                return true;
682                        }
683#ifdef SINGLE_THREADED
684                        if(key=="use_single_threaded")
685                        {
686                                int i(atoi(value.c_str()));
687                                App::single_threaded=i;
688                                return true;
689                        }
690#endif
691                        if(key=="restrict_radius_ducks")
692                        {
693                                int i(atoi(value.c_str()));
694                                App::restrict_radius_ducks=i;
695                                return true;
696                        }
697                        if(key=="resize_imported_images")
698                        {
699                                int i(atoi(value.c_str()));
700                                App::resize_imported_images=i;
701                                return true;
702                        }
703                        if(key=="enable_experimental_features")
704                        {
705                                int i(atoi(value.c_str()));
706                                App::enable_experimental_features=i;
707                                return true;
708                        }
709                        if(key=="use_dark_theme")
710                        {
711                                int i(atoi(value.c_str()));
712                                App::use_dark_theme=i;
713                                return true;
714                        }
715                        if(key=="browser_command")
716                        {
717                                App::browser_command=value;
718                                return true;
719                        }
720                        if(key=="brushes_path")
721                        {
722                                App::brushes_path=value;
723                                return true;
724                        }
725                        if(key=="custom_filename_prefix")
726                        {
727                                App::custom_filename_prefix=value;
728                                return true;
729                        }
730                        if(key=="preferred_x_size")
731                        {
732                                int i(atoi(value.c_str()));
733                                App::preferred_x_size=i;
734                                return true;
735                        }
736                        if(key=="preferred_y_size")
737                        {
738                                int i(atoi(value.c_str()));
739                                App::preferred_y_size=i;
740                                return true;
741                        }
742                        if(key=="predefined_size")
743                        {
744                                App::predefined_size=value;
745                                return true;
746                        }
747                        if(key=="preferred_fps")
748                        {
749                                float i(atof(value.c_str()));
750                                App::preferred_fps=i;
751                                return true;
752                        }
753                        if(key=="predefined_fps")
754                        {
755                                App::predefined_fps=value;
756                                return true;
757                        }
758                        if(key=="sequence_separator")
759                        {
760                                App::sequence_separator=value;
761                                return true;
762                        }
763                        if(key=="navigator_uses_cairo")
764                        {
765                                int i(atoi(value.c_str()));
766                                App::navigator_uses_cairo=i;
767                                return true;
768                        }
769                        if(key=="workarea_uses_cairo")
770                        {
771                                int i(atoi(value.c_str()));
772                                App::workarea_uses_cairo=i;
773                                return true;
774                        }
775                        if(key=="enable_mainwin_menubar")
776                        {
777                                int i(atoi(value.c_str()));
778                                App::enable_mainwin_menubar = i;
779                                return true;
780                        }
781                }
782                catch(...)
783                {
784                        synfig::warning("Preferences: Caught exception when attempting to set value.");
785                }
786                return synfigapp::Settings::set_value(key,value);
787        }
788
789        virtual KeyList get_key_list()const
790        {
791                KeyList ret(synfigapp::Settings::get_key_list());
792                ret.push_back("gamma");
793                ret.push_back("time_format");
794                ret.push_back("distance_system");
795                ret.push_back("file_history.size");
796                ret.push_back("use_colorspace_gamma");
797#ifdef SINGLE_THREADED
798                ret.push_back("use_single_threaded");
799#endif
800                ret.push_back("auto_recover_backup_interval");
801                ret.push_back("restrict_radius_ducks");
802                ret.push_back("resize_imported_images");
803                ret.push_back("enable_experimental_features");
804                ret.push_back("use_dark_theme");
805                ret.push_back("browser_command");
806                ret.push_back("brushes_path");
807                ret.push_back("custom_filename_prefix");
808                ret.push_back("ui_language");
809                ret.push_back("preferred_x_size");
810                ret.push_back("preferred_y_size");
811                ret.push_back("predefined_size");
812                ret.push_back("preferred_fps");
813                ret.push_back("predefined_fps");
814                ret.push_back("sequence_separator");
815                ret.push_back("navigator_uses_cairo");
816                ret.push_back("workarea_uses_cairo");
817                ret.push_back("enable_mainwin_menubar");
818
819                return ret;
820        }
821};
822
823static ::Preferences _preferences;
824
825void
826init_ui_manager()
827{
828        Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
829
830        Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create("actions");
831
832        menus_action_group->add( Gtk::Action::create("menu-file", _("_File")));
833        menus_action_group->add( Gtk::Action::create("menu-open-recent", _("Open Recent")));
834
835        menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")));
836
837        menus_action_group->add( Gtk::Action::create("menu-view", _("_View")));
838        menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Handles")));
839        menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")));
840        menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")));
841
842        menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")));
843
844        menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")));
845        menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")));
846        menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")));
847        menus_action_group->add( Gtk::Action::create("menu-plugins", _("Plug-Ins")));
848
849        menus_action_group->add( Gtk::Action::create("menu-window", _("_Window")));
850        menus_action_group->add( Gtk::Action::create("menu-arrange", _("_Arrange")));
851        menus_action_group->add( Gtk::Action::create("menu-workspace", _("Work_space")));
852
853        menus_action_group->add( Gtk::Action::create("menu-help", _("_Help")) );
854
855        menus_action_group->add(Gtk::Action::create("menu-keyframe","Keyframe"));
856
857        // Add the synfigapp actions (layer panel toolbar items, etc...)
858        synfigapp::Action::Book::iterator iter;
859        for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
860        {
861                actions_action_group->add(Gtk::Action::create(
862                        "action-"+iter->second.name,
863                        get_action_stock_id(iter->second),
864                        iter->second.local_name,iter->second.local_name
865                ));
866        }
867
868// predefined actions to initial menu items, so that there is all menu items listing
869// even there is no any canvas instance existed, for example, when app just opened.
870// the menu items (action names) should be named consistently with those in canvasview.cpp and others.
871#define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); actions_action_group->add(action); }
872
873// actions in File menu
874DEFINE_ACTION("save", Gtk::StockID("synfig-save"));
875DEFINE_ACTION("save-as", Gtk::StockID("synfig-save_as"));
876DEFINE_ACTION("save-all", Gtk::StockID("synfig-save_all"));
877DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
878DEFINE_ACTION("import", _("Import..."));
879DEFINE_ACTION("render", _("Render..."));
880DEFINE_ACTION("preview", _("Preview..."));
881DEFINE_ACTION("close-document", _("Close Document"));
882DEFINE_ACTION("quit", Gtk::Stock::QUIT);
883
884// actions in Edit menu
885DEFINE_ACTION("undo", Gtk::StockID("synfig-undo"));
886DEFINE_ACTION("redo", Gtk::StockID("synfig-redo"));
887DEFINE_ACTION("copy", Gtk::Stock::COPY);
888DEFINE_ACTION("cut", Gtk::Stock::CUT);
889DEFINE_ACTION("paste", Gtk::Stock::PASTE);
890DEFINE_ACTION("select-all-ducks", _("Select All Handles"));
891DEFINE_ACTION("unselect-all-ducks", _("Unselect All Handles"));
892DEFINE_ACTION("select-all-layers", _("Select All Layers"));
893DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
894DEFINE_ACTION("input-devices", _("Input Devices..."));
895DEFINE_ACTION("setup", _("Preferences..."));
896DEFINE_ACTION("restore-default-settings", _("Restore Defaults"));
897
898// actions in View menu
899DEFINE_ACTION("toggle-mainwin-menubar", _("Menubar"));
900DEFINE_ACTION("toggle-mainwin-toolbar", _("Toolbar"));
901
902DEFINE_ACTION("mask-position-ducks", _("Show Position Handles"));
903DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Handles"));
904DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Handles"));
905DEFINE_ACTION("mask-radius-ducks", _("Show Radius Handles"));
906DEFINE_ACTION("mask-width-ducks", _("Show Width Handles"));
907DEFINE_ACTION("mask-angle-ducks", _("Show Angle Handles"));
908DEFINE_ACTION("mask-bone-setup-ducks", _("Show Bone Setup Handles"));
909DEFINE_ACTION("mask-bone-recursive-ducks", _("Show Recursive Scale Bone Handles"));
910DEFINE_ACTION("mask-bone-ducks", _("Next Bone Handles"));
911DEFINE_ACTION("mask-widthpoint-position-ducks", _("Show WidthPoints Position Handles"));
912DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
913DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
914DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
915DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
916DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
917DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
918DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
919DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
920DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
921DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
922DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
923
924for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
925  DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
926
927DEFINE_ACTION("play", _("Play"));
928// the stop is not a normal stop but a pause. So use "Pause" in UI, including TEXT and
929// icon. the internal code is still using stop.
930DEFINE_ACTION("stop", _("Pause"));
931
932DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
933DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
934DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
935DEFINE_ACTION("toggle-guide-snap", _("Toggle Guide Snap"));
936DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
937DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
938DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
939DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
940DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
941DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
942DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
943DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
944DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
945DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
946DEFINE_ACTION("jump-next-keyframe", _("Seek to Next Keyframe"));
947DEFINE_ACTION("jump-prev-keyframe", _("Seek to previous Keyframe"));
948DEFINE_ACTION("seek-next-frame", _("Seek to Next Frame"));
949DEFINE_ACTION("seek-prev-frame", _("Seek to Previous Frame"));
950DEFINE_ACTION("seek-next-second", _("Seek Forward"));
951DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
952DEFINE_ACTION("seek-begin", _("Seek to Begin"));
953DEFINE_ACTION("seek-end", _("Seek to End"));
954
955// actions in Canvas menu
956DEFINE_ACTION("properties", _("Properties..."));
957DEFINE_ACTION("options", _("Options..."));
958
959// actions in Layer menu
960DEFINE_ACTION("amount-inc", _("Increase Layer Amount"))
961DEFINE_ACTION("amount-dec", _("Decrease Layer Amount"))
962
963// actions in Window menu
964DEFINE_ACTION("workspace-compositing", _("Compositing"));
965DEFINE_ACTION("workspace-default", _("Default"));
966DEFINE_ACTION("workspace-animating", _("Animating"));
967DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
968DEFINE_ACTION("panel-toolbox","Toolbox");
969DEFINE_ACTION("panel-tool_options",_("Tool Options"));
970DEFINE_ACTION("panel-history", "History");
971DEFINE_ACTION("panel-canvases",_("Canvas Browser"));
972DEFINE_ACTION("panel-keyframes",_("Keyframes"));
973DEFINE_ACTION("panel-layers",_("Layers"));
974DEFINE_ACTION("panel-params",_("Parameters"));
975DEFINE_ACTION("panel-meta_data",_("Canvas MetaData"));
976DEFINE_ACTION("panel-children",_("Library"));
977DEFINE_ACTION("panel-info",_("Info"));
978DEFINE_ACTION("panel-navigator",_("Navigator"));
979DEFINE_ACTION("panel-timetrack",_("Timetrack"));
980DEFINE_ACTION("panel-curves",_("Graphs"));
981DEFINE_ACTION("panel-groups",_("Sets"));
982DEFINE_ACTION("panel-pal_edit",_("Palette Editor"));
983
984// actions in Help menu
985DEFINE_ACTION("help", Gtk::Stock::HELP);
986DEFINE_ACTION("help-tutorials", Gtk::Stock::HELP);
987DEFINE_ACTION("help-reference", Gtk::Stock::HELP);
988DEFINE_ACTION("help-faq", Gtk::Stock::HELP);
989DEFINE_ACTION("help-support", Gtk::Stock::HELP);
990DEFINE_ACTION("help-about", Gtk::StockID("synfig-about"));
991
992// actions: Keyframe
993DEFINE_ACTION("keyframe-properties","Properties");
994
995
996//Layout the actions in the main menu (caret menu, right click on canvas menu) and toolbar:
997        Glib::ustring ui_info_menu =
998"       <menu action='menu-file'>"
999"               <menuitem action='new' />"
1000"               <menuitem action='open' />"
1001"               <menu action='menu-open-recent' />"
1002"               <separator name='sep-file1'/>"
1003"               <menuitem action='save' />"
1004"               <menuitem action='save-as' />"
1005"               <menuitem action='save-all' />"
1006"               <menuitem action='revert' />"
1007"               <separator name='sep-file2'/>"
1008"               <menuitem action='import' />"
1009"               <separator name='sep-file4'/>"
1010"               <menuitem action='preview' />"
1011"               <menuitem action='render' />"
1012"               <separator name='sep-file5'/>"
1013"               <menuitem action='close-document' />"
1014"               <separator name='sep-file6'/>"
1015"               <menuitem action='quit' />"
1016"       </menu>"
1017"       <menu action='menu-edit'>"
1018"               <menuitem action='undo'/>"
1019"               <menuitem action='redo'/>"
1020"               <separator name='sep-edit1'/>"
1021"               <menuitem action='cut'/>"
1022"               <menuitem action='copy'/>"
1023"               <menuitem action='paste'/>"
1024"               <separator name='sep-edit2'/>"
1025"               <menuitem action='select-all-layers'/>"
1026"               <menuitem action='unselect-all-layers'/>"
1027"               <menuitem action='select-all-ducks'/>"
1028"               <menuitem action='unselect-all-ducks'/>"
1029"               <separator name='sep-edit3'/>"
1030"               <menuitem action='input-devices' />"
1031"               <menuitem action='setup' />"
1032"       </menu>"
1033"       <menu action='menu-view'>"
1034"               <menuitem action='toggle-mainwin-menubar' />"
1035"               <menuitem action='toggle-mainwin-toolbar' />"
1036"               <separator />"
1037"               <menu action='menu-duck-mask'>"
1038"                       <menuitem action='mask-position-ducks' />"
1039"                       <menuitem action='mask-vertex-ducks' />"
1040"                       <menuitem action='mask-tangent-ducks' />"
1041"                       <menuitem action='mask-radius-ducks' />"
1042"                       <menuitem action='mask-width-ducks' />"
1043"                       <menuitem action='mask-angle-ducks' />"
1044"                       <menuitem action='mask-bone-setup-ducks' />"
1045"                       <menuitem action='mask-bone-recursive-ducks' />"
1046"                       <menuitem action='mask-bone-ducks' />"
1047"                       <menuitem action='mask-widthpoint-position-ducks' />"
1048"               </menu>"
1049"               <menu action='menu-preview-quality'>"
1050"                       <menuitem action='quality-00' />"
1051"                       <menuitem action='quality-01' />"
1052"                       <menuitem action='quality-02' />"
1053"                       <menuitem action='quality-03' />"
1054"                       <menuitem action='quality-04' />"
1055"                       <menuitem action='quality-05' />"
1056"                       <menuitem action='quality-06' />"
1057"                       <menuitem action='quality-07' />"
1058"                       <menuitem action='quality-08' />"
1059"                       <menuitem action='quality-09' />"
1060"                       <menuitem action='quality-10' />"
1061"               </menu>"
1062"               <menu action='menu-lowres-pixel'>"
1063"                       <menuitem action='decrease-low-res-pixel-size'/>"
1064"                       <menuitem action='increase-low-res-pixel-size'/>"
1065"                       <separator name='pixel-size-separator'/>"
1066;
1067
1068        for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
1069                ui_info_menu += strprintf("                     <menuitem action='lowres-pixel-%d' />", *iter);
1070
1071        ui_info_menu +=
1072"               </menu>"
1073"               <separator name='sep-view1'/>"
1074"               <menuitem action='play'/>"
1075"               <menuitem action='stop'/>"
1076"               <separator name='sep-view2'/>"
1077"               <menuitem action='toggle-grid-show'/>"
1078"               <menuitem action='toggle-grid-snap'/>"
1079"               <menuitem action='toggle-guide-show'/>"
1080"               <menuitem action='toggle-guide-snap'/>"
1081"               <menuitem action='toggle-low-res'/>"
1082"               <menuitem action='toggle-onion-skin'/>"
1083"               <separator name='sep-view3'/>"
1084"               <menuitem action='canvas-zoom-in'/>"
1085"               <menuitem action='canvas-zoom-out'/>"
1086"               <menuitem action='canvas-zoom-fit'/>"
1087"               <menuitem action='canvas-zoom-100'/>"
1088"               <separator name='sep-view4'/>"
1089"               <menuitem action='time-zoom-in'/>"
1090"               <menuitem action='time-zoom-out'/>"
1091"               <separator name='sep-view5'/>"
1092"               <menuitem action='jump-prev-keyframe'/>"
1093"               <menuitem action='jump-next-keyframe'/>"
1094"               <menuitem action='seek-prev-frame'/>"
1095"               <menuitem action='seek-next-frame'/>"
1096"               <menuitem action='seek-prev-second'/>"
1097"               <menuitem action='seek-next-second'/>"
1098"               <menuitem action='seek-begin'/>"
1099"               <menuitem action='seek-end'/>"
1100"       </menu>"
1101"       <menu action='menu-canvas'>"
1102"               <menuitem action='properties'/>"
1103"               <menuitem action='options'/>"
1104"       </menu>"
1105"       <menu action='menu-toolbox'>"
1106"       </menu>"
1107"       <menu action='menu-layer'>"
1108"               <menu action='menu-layer-new'></menu>"
1109"               <menuitem action='amount-inc'/>"
1110"               <menuitem action='amount-dec'/>"
1111"       </menu>"
1112"       <menu action='menu-plugins'>"
1113;
1114
1115        list<synfigapp::PluginManager::plugin> plugin_list = studio::App::plugin_manager.get_list();
1116        for(list<synfigapp::PluginManager::plugin>::const_iterator p=plugin_list.begin();p!=plugin_list.end();++p) {
1117
1118                // TODO: (Plugins) Arrange menu items into groups
1119
1120                synfigapp::PluginManager::plugin plugin = *p;
1121               
1122                DEFINE_ACTION(plugin.id, plugin.name);
1123                ui_info_menu += strprintf("     <menuitem action='%s'/>", plugin.id.c_str());
1124        }
1125
1126        ui_info_menu +=
1127"       </menu>"
1128"       <menu action='menu-window'>"
1129"               <menu action='menu-arrange'> </menu>"
1130"               <menu action='menu-workspace'>"
1131"                       <menuitem action='workspace-default' />"
1132"                       <menuitem action='workspace-compositing' />"
1133"                       <menuitem action='workspace-animating' />"
1134"               </menu>"
1135"               <separator />"
1136"               <menuitem action='dialog-flipbook'/>"
1137"               <menuitem action='panel-toolbox' />"
1138"               <menuitem action='panel-tool_options' />"
1139"               <menuitem action='panel-history' />"
1140"               <menuitem action='panel-canvases' />"
1141"               <menuitem action='panel-keyframes' />"
1142"               <menuitem action='panel-layers' />"
1143"               <menuitem action='panel-params' />"
1144"               <menuitem action='panel-meta_data' />"
1145"               <menuitem action='panel-children' />"
1146"               <menuitem action='panel-info' />"
1147"               <menuitem action='panel-navigator' />"
1148"               <menuitem action='panel-timetrack' />"
1149"               <menuitem action='panel-curves' />"
1150"               <menuitem action='panel-groups' />"
1151"               <menuitem action='panel-pal_edit' />"
1152"               <separator />"
1153// opened documents will be listed here below the above separator.
1154"       </menu>"
1155"       <menu action='menu-help'>"
1156"               <menuitem action='help'/>"
1157"               <separator name='sep-help1'/>"
1158"               <menuitem action='help-tutorials'/>"
1159"               <menuitem action='help-reference'/>"
1160"               <menuitem action='help-faq'/>"
1161"               <separator name='sep-help2'/>"
1162"               <menuitem action='help-support'/>"
1163"               <separator name='sep-help3'/>"
1164"               <menuitem action='help-about'/>"
1165"       </menu>";
1166
1167        Glib::ustring ui_info_main_tool =
1168"               <toolitem action='new'/>"
1169"               <toolitem action='open'/>"
1170"               <toolitem action='save'/>"
1171"               <toolitem action='save-as'/>"
1172"               <toolitem action='save-all'/>"
1173"               <separator />"
1174"               <toolitem action='undo'/>"
1175"               <toolitem action='redo'/>"
1176"               <separator />"
1177"               <toolitem action='render'/>"
1178"               <toolitem action='preview'/>";
1179
1180        Glib::ustring ui_info =
1181"<ui>"
1182"   <popup name='menu-toolbox' action='menu-toolbox'>"
1183"       <menu action='menu-file'>"
1184"       </menu>"
1185"       </popup>"
1186"       <popup name='menu-main' action='menu-main'>" + ui_info_menu + "</popup>"
1187"       <menubar name='menubar-main' action='menubar-main'>" + ui_info_menu + "</menubar>"
1188"       <toolbar name='toolbar-main'>" + ui_info_main_tool + "</toolbar>"
1189"</ui>";
1190
1191        #undef DEFINE_ACTION
1192
1193        try
1194        {
1195                actions_action_group->set_sensitive(false);
1196                App::ui_manager()->set_add_tearoffs(false);
1197                App::ui_manager()->insert_action_group(menus_action_group,1);
1198                App::ui_manager()->insert_action_group(actions_action_group,1);
1199                App::ui_manager()->add_ui_from_string(ui_info);
1200
1201                //App::ui_manager()->get_accel_group()->unlock();
1202        }
1203        catch(const Glib::Error& ex)
1204        {
1205                synfig::error("building menus and toolbars failed: " + ex.what());
1206        }
1207
1208        // Add default keyboard accelerators
1209#define ACCEL(accel,path)                                               \
1210        {                                                                                       \
1211                Gtk::AccelKey accel_key(accel,path);    \
1212                Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
1213        }
1214
1215#define ACCEL2(accel)                                                   \
1216        {                                                                                       \
1217                Gtk::AccelKey accel_key(accel);                 \
1218                Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
1219        }
1220
1221        // the toolbox
1222        ACCEL("<Mod1>a",                                                                                                        "<Actions>/action_group_state_manager/state-normal"                                     );
1223        ACCEL("<Mod1>v",                                                                                                        "<Actions>/action_group_state_manager/state-smooth_move"                                );
1224        ACCEL("<Mod1>s",                                                                                                        "<Actions>/action_group_state_manager/state-scale"                                      );
1225        ACCEL("<Mod1>t",                                                                                                        "<Actions>/action_group_state_manager/state-rotate"                                     );
1226        ACCEL("<Mod1>m",                                                                                                        "<Actions>/action_group_state_manager/state-mirror"                                     );
1227        ACCEL("<Mod1>c",                                                                                                        "<Actions>/action_group_state_manager/state-circle"                                     );
1228        ACCEL("<Mod1>r",                                                                                                        "<Actions>/action_group_state_manager/state-rectangle"                          );
1229        ACCEL("<Mod1>q",                                                                                                        "<Actions>/action_group_state_manager/state-star"                                               );
1230        ACCEL("<Mod1>g",                                                                                                        "<Actions>/action_group_state_manager/state-gradient"                                   );
1231        ACCEL("<Mod1>p",                                                                                                        "<Actions>/action_group_state_manager/state-polygon"                                    );
1232        ACCEL("<Mod1>b",                                                                                                        "<Actions>/action_group_state_manager/state-bline"                                      );
1233        ACCEL("<Mod1>x",                                                                                                        "<Actions>/action_group_state_manager/state-text"                                               );
1234        ACCEL("<Mod1>f",                                                                                                        "<Actions>/action_group_state_manager/state-fill"                                               );
1235        ACCEL("<Mod1>e",                                                                                                        "<Actions>/action_group_state_manager/state-eyedrop"                                    );
1236        ACCEL("<Mod1>z",                                                                                                        "<Actions>/action_group_state_manager/state-zoom"                                               );
1237        ACCEL("<Mod1>d",                                                                                                        "<Actions>/action_group_state_manager/state-draw"                                               );
1238        ACCEL("<Mod1>k",                                                                                                        "<Actions>/action_group_state_manager/state-sketch"                                     );
1239        ACCEL("<Mod1>w",                                                                                                        "<Actions>/action_group_state_manager/state-width"                                      );
1240
1241        // everything else
1242        ACCEL("<Control>a",                                                                                                     "<Actions>/canvasview/select-all-ducks"                         );
1243        ACCEL("<Control>d",                                                                                                     "<Actions>/canvasview/unselect-all-ducks"                               );
1244        ACCEL("<Control><Shift>a",                                                                                      "<Actions>/canvasview/select-all-layers"                                );
1245        ACCEL("<Control><Shift>d",                                                                                      "<Actions>/canvasview/unselect-all-layers"                      );
1246        ACCEL("F9",                                                                                                                     "<Actions>/canvasview/render"                                                   );
1247        ACCEL("F11",                                                                                                            "<Actions>/canvasview/preview"                                          );
1248        ACCEL("F8",                                                                                                                     "<Actions>/canvasview/properties"                                               );
1249        ACCEL("F12",                                                                                                            "<Actions>/canvasview/options"                                          );
1250        ACCEL("<control>i",                                                                                                     "<Actions>/canvasview/import"                                                   );
1251        ACCEL2(Gtk::AccelKey(GDK_KEY_Escape,static_cast<Gdk::ModifierType>(0), "<Actions>/canvasview/stop"                                                      ));
1252        ACCEL("<Control>g",                                                                                                     "<Actions>/canvasview/toggle-grid-show"                         );
1253        ACCEL("<Control>l",                                                                                                     "<Actions>/canvasview/toggle-grid-snap"                         );
1254        ACCEL("<Control>n",                                                                                                     "<Actions>/mainwindow/new"                              );
1255        ACCEL("<Control>o",                                                                                                     "<Actions>/mainwindow/open"                             );
1256        ACCEL("<Control>s",                                                                                                     "<Actions>/canvasview/save"                             );
1257        ACCEL("<Control><Shift>s",                                                                                      "<Actions>/canvasview/save-as"                          );
1258        ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/toggle-low-res"                                   ));
1259        ACCEL("<Mod1>1",                                                                                                        "<Actions>/canvasview/mask-position-ducks"                      );
1260        ACCEL("<Mod1>2",                                                                                                        "<Actions>/canvasview/mask-vertex-ducks"                                );
1261        ACCEL("<Mod1>3",                                                                                                        "<Actions>/canvasview/mask-tangent-ducks"                               );
1262        ACCEL("<Mod1>4",                                                                                                        "<Actions>/canvasview/mask-radius-ducks"                                );
1263        ACCEL("<Mod1>5",                                                                                                        "<Actions>/canvasview/mask-width-ducks"                         );
1264        ACCEL("<Mod1>6",                                                                                                        "<Actions>/canvasview/mask-angle-ducks"                         );
1265        ACCEL("<Mod1>7",                                                                                                        "<Actions>/canvasview/mask-bone-setup-ducks"                    );
1266        ACCEL("<Mod1>8",                                                                                                        "<Actions>/canvasview/mask-bone-recursive-ducks"                );
1267        ACCEL("<Mod1>9",                                                                                                        "<Actions>/canvasview/mask-bone-ducks"                          );
1268        ACCEL("<Mod1>5",                                                                                                        "<Actions>/canvasview/mask-widthpoint-position-ducks"                           );
1269        ACCEL2(Gtk::AccelKey(GDK_KEY_Page_Up,Gdk::SHIFT_MASK,                           "<Actions>/action_group_layer_action_manager/action-LayerRaise"                         ));
1270        ACCEL2(Gtk::AccelKey(GDK_KEY_Page_Down,Gdk::SHIFT_MASK,                         "<Actions>/action_group_layer_action_manager/action-LayerLower"                         ));
1271        ACCEL("<Control>1",                                                                                                     "<Actions>/canvasview/quality-01"                                               );
1272        ACCEL("<Control>2",                                                                                                     "<Actions>/canvasview/quality-02"                                               );
1273        ACCEL("<Control>3",                                                                                                     "<Actions>/canvasview/quality-03"                                               );
1274        ACCEL("<Control>4",                                                                                                     "<Actions>/canvasview/quality-04"                                               );
1275        ACCEL("<Control>5",                                                                                                     "<Actions>/canvasview/quality-05"                                               );
1276        ACCEL("<Control>6",                                                                                                     "<Actions>/canvasview/quality-06"                                               );
1277        ACCEL("<Control>7",                                                                                                     "<Actions>/canvasview/quality-07"                                               );
1278        ACCEL("<Control>8",                                                                                                     "<Actions>/canvasview/quality-08"                                               );
1279        ACCEL("<Control>9",                                                                                                     "<Actions>/canvasview/quality-09"                                               );
1280        ACCEL("<Control>0",                                                                                                     "<Actions>/canvasview/quality-10"                                               );
1281        ACCEL("<Control>z",                                                                                                     "<Actions>/action_group_dock_history/undo"                                                      );
1282        ACCEL("<Control>r",                                                                                                     "<Actions>/action_group_dock_history/redo"                                                      );
1283        ACCEL2(Gtk::AccelKey(GDK_KEY_Delete,Gdk::CONTROL_MASK,                          "<Actions>/action_group_layer_action_manager/action-LayerRemove"                                ));
1284        ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/decrease-low-res-pixel-size"      ));
1285        ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/increase-low-res-pixel-size"      ));
1286        ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,                      "<Actions>/action_group_layer_action_manager/amount-dec"                                                ));
1287        ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,                      "<Actions>/action_group_layer_action_manager/amount-inc"                                                ));
1288        ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/jump-next-keyframe"                               ));
1289        ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/jump-prev-keyframe"                               ));
1290        ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/canvas-zoom-in"                                   ));
1291        ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/canvas-zoom-out"                          ));
1292        ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/time-zoom-in"                                     ));
1293        ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/time-zoom-out"                                    ));
1294        ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/seek-next-frame"                          ));
1295        ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/seek-prev-frame"                          ));
1296        ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/seek-next-second"                         ));
1297        ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,                                                     "<Actions>/canvasview/seek-prev-second"                         ));
1298        ACCEL("<Mod1>o",                                                                                                        "<Actions>/canvasview/toggle-onion-skin"                                );
1299        ACCEL("<Control><Shift>z",                                                                                      "<Actions>/canvasview/canvas-zoom-fit"                          );
1300        ACCEL("<Control>p",                                                                                                     "<Actions>/canvasview/play"                                                     );
1301        ACCEL("Home",                                                                                                           "<Actions>/canvasview/seek-begin"                                               );
1302        ACCEL("End",                                                                                                            "<Actions>/canvasview/seek-end"                                         );
1303
1304
1305#undef ACCEL
1306#undef ACCEL2
1307}
1308
1309#ifdef _WIN32
1310#define mkdir(x,y) mkdir(x)
1311#endif
1312
1313/* === M E T H O D S ======================================================= */
1314
1315App::App(const synfig::String& basepath, int *argc, char ***argv):
1316        Gtk::Main(argc,argv),
1317        IconController(basepath)
1318{
1319
1320        app_base_path_=etl::dirname(basepath);
1321
1322        // Set ui language
1323        load_language_settings();
1324        if (ui_language != "os_LANG")
1325        {
1326                Glib::setenv ("LANGUAGE",  App::ui_language.c_str(), 1);
1327        }
1328       
1329        std::string path_to_icons;
1330#ifdef WIN32
1331        path_to_icons=basepath+ETL_DIRECTORY_SEPARATOR+".."+ETL_DIRECTORY_SEPARATOR+IMAGE_DIR;
1332#else
1333        path_to_icons=IMAGE_DIR;
1334#endif
1335        char* synfig_root=getenv("SYNFIG_ROOT");
1336        if(synfig_root) {
1337                path_to_icons=synfig_root;
1338                path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1339                path_to_icons+="share";
1340                path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1341                path_to_icons+="pixmaps";
1342                path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1343                path_to_icons+="synfigstudio";
1344        }
1345        path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1346        init_icons(path_to_icons);
1347       
1348        ui_interface_=new GlobalUIInterface();
1349
1350        // don't call thread_init() if threads are already initialized
1351        // on some machines bonobo_init() initialized threads before we get here
1352        if (!g_thread_supported())
1353                Glib::thread_init();
1354
1355        distance_system=Distance::SYSTEM_PIXELS;
1356
1357        if(mkdir(synfigapp::Main::get_user_app_directory().c_str(),ACCESSPERMS)<0)
1358        {
1359                if(errno!=EEXIST)
1360                        synfig::error("UNABLE TO CREATE \"%s\"",synfigapp::Main::get_user_app_directory().c_str());
1361        }
1362        else
1363        {
1364                synfig::info("Created directory \"%s\"",synfigapp::Main::get_user_app_directory().c_str());
1365        }
1366
1367
1368        ipc=new IPC();
1369
1370        if(!SYNFIG_CHECK_VERSION())
1371        {
1372                cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1373                dialog_message_1b(
1374                        "ERROR",
1375                        _("Synfig version mismatched!"),
1376                        _("This copy of Synfig Studio was compiled against a "
1377                        "different version of libsynfig than what is currently "
1378                        "installed. Synfig Studio will now abort. Try downloading "
1379                        "the latest version from the Synfig website at "
1380                        "http://www.synfig.org/cms/en/download/"),
1381                        _("Close"));
1382
1383                throw 40;
1384        }
1385        Glib::set_application_name(_("Synfig Studio"));
1386
1387        Splash splash_screen;
1388        splash_screen.show();
1389
1390        shutdown_in_progress=false;
1391        SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1392        SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1393               
1394        // Initialize the Synfig library
1395        try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(basepath,&synfig_init_cb)); }
1396        catch(std::runtime_error x)
1397        {
1398                get_ui_interface()->error(strprintf("%s\n\n%s", _("Failed to initialize synfig!"), x.what()));
1399                throw;
1400        }
1401        catch(...)
1402        {
1403                get_ui_interface()->error(_("Failed to initialize synfig!"));
1404                throw;
1405        }
1406
1407       
1408        // add the preferences to the settings
1409        synfigapp::Main::settings().add_domain(&_preferences,"pref");
1410
1411        try
1412        {
1413                // Try to load settings early to get access to some important
1414                // values, like "enable_experimental_features".
1415                studio_init_cb.task(_("Loading Basic Settings..."));
1416               
1417                load_settings("pref.use_dark_theme");
1418                App::apply_gtk_settings(App::use_dark_theme);
1419
1420                // Set experimental features
1421                load_settings("pref.enable_experimental_features");
1422
1423                // Set main window menu and toolbar
1424                load_settings("pref.enable_mainwin_menubar");
1425
1426                studio_init_cb.task(_("Loading Plugins..."));
1427               
1428                std::string pluginsprefix;
1429       
1430                // system plugins path
1431#ifdef WIN32
1432                pluginsprefix=App::get_base_path()+ETL_DIRECTORY_SEPARATOR+PLUGIN_DIR;
1433#else
1434                pluginsprefix=PLUGIN_DIR;
1435#endif
1436                char* synfig_root=getenv("SYNFIG_ROOT");
1437                if(synfig_root) {
1438                        pluginsprefix=std::string(synfig_root)
1439                                +ETL_DIRECTORY_SEPARATOR+"share"
1440                                +ETL_DIRECTORY_SEPARATOR+"synfig"
1441                                +ETL_DIRECTORY_SEPARATOR+"plugins";
1442                }
1443                plugin_manager.load_dir(pluginsprefix);
1444               
1445                // user plugins path
1446                pluginsprefix=Glib::build_filename(synfigapp::Main::get_user_app_directory(),"plugins");
1447                plugin_manager.load_dir(pluginsprefix);
1448               
1449                studio_init_cb.task(_("Init UI Manager..."));
1450                App::ui_manager_=studio::UIManager::create();
1451                init_ui_manager();
1452
1453                studio_init_cb.task(_("Init Dock Manager..."));
1454                dock_manager=new studio::DockManager();
1455
1456                studio_init_cb.task(_("Init State Manager..."));
1457                state_manager=new StateManager();
1458
1459                studio_init_cb.task(_("Init Main Window..."));
1460                main_window=new studio::MainWindow();
1461                main_window->add_accel_group(App::ui_manager_->get_accel_group());
1462
1463                studio_init_cb.task(_("Init Toolbox..."));
1464                dock_toolbox=new studio::Dock_Toolbox();
1465                dock_manager->register_dockable(*dock_toolbox);
1466
1467                studio_init_cb.task(_("Init About Dialog..."));
1468                about=new studio::About();
1469
1470                studio_init_cb.task(_("Init Tool Options..."));
1471                dialog_tool_options=new studio::Dialog_ToolOptions();
1472                dock_manager->register_dockable(*dialog_tool_options);
1473
1474                studio_init_cb.task(_("Init History..."));
1475                dock_history=new studio::Dock_History();
1476                dock_manager->register_dockable(*dock_history);
1477
1478                studio_init_cb.task(_("Init Canvases..."));
1479                dock_canvases=new studio::Dock_Canvases();
1480                dock_manager->register_dockable(*dock_canvases);
1481
1482                studio_init_cb.task(_("Init Keyframes..."));
1483                dock_keyframes=new studio::Dock_Keyframes();
1484                dock_manager->register_dockable(*dock_keyframes);
1485
1486//! Must be done before Dock_Timetrack and Dock_Curves :
1487//! both are connected to a studio::LayerTree::param_tree_view_'s signal, and
1488//! studio::LayerTree is created from Dock_Layers::init_canvas_view_vfunc
1489                studio_init_cb.task(_("Init Layers..."));
1490                dock_layers=new studio::Dock_Layers();
1491                dock_manager->register_dockable(*dock_layers);
1492
1493                studio_init_cb.task(_("Init Parameters..."));
1494                dock_params=new studio::Dock_Params();
1495                dock_manager->register_dockable(*dock_params);
1496
1497                studio_init_cb.task(_("Init MetaData..."));
1498                dock_meta_data=new studio::Dock_MetaData();
1499                dock_manager->register_dockable(*dock_meta_data);
1500
1501                studio_init_cb.task(_("Init Library..."));
1502                dock_children=new studio::Dock_Children();
1503                dock_manager->register_dockable(*dock_children);
1504
1505                studio_init_cb.task(_("Init Info..."));
1506                dock_info = new studio::Dock_Info();
1507                dock_manager->register_dockable(*dock_info);
1508
1509                studio_init_cb.task(_("Init Navigator..."));
1510                dock_navigator = new studio::Dock_Navigator();
1511                dock_manager->register_dockable(*dock_navigator);
1512
1513                studio_init_cb.task(_("Init Timetrack..."));
1514                dock_timetrack = new studio::Dock_Timetrack();
1515                dock_manager->register_dockable(*dock_timetrack);
1516
1517                studio_init_cb.task(_("Init Curve Editor..."));
1518                dock_curves = new studio::Dock_Curves();
1519                dock_manager->register_dockable(*dock_curves);
1520
1521                studio_init_cb.task(_("Init Layer Sets..."));
1522                dock_layer_groups = new studio::Dock_LayerGroups();
1523                dock_manager->register_dockable(*dock_layer_groups);
1524
1525
1526                studio_init_cb.task(_("Init Color Dialog..."));
1527                dialog_color=new studio::Dialog_Color();
1528
1529                studio_init_cb.task(_("Init Gradient Dialog..."));
1530                dialog_gradient=new studio::Dialog_Gradient();
1531
1532                studio_init_cb.task(_("Init DeviceTracker..."));
1533                device_tracker=new studio::DeviceTracker();
1534
1535                //Init Tools...was here
1536
1537                studio_init_cb.task(_("Init ModPalette..."));
1538                module_list_.push_back(new ModPalette()); module_list_.back()->start();
1539
1540                studio_init_cb.task(_("Init Setup Dialog..."));
1541                dialog_setup=new studio::Dialog_Setup(*App::main_window);
1542
1543                studio_init_cb.task(_("Init Input Dialog..."));
1544                dialog_input=new studio::Dialog_Input(*App::main_window);
1545                dialog_input->signal_apply().connect( sigc::mem_fun( *device_tracker, &DeviceTracker::save_preferences) );
1546               
1547                studio_init_cb.task(_("Init auto recovery..."));
1548                auto_recover=new AutoRecover();
1549
1550                studio_init_cb.amount_complete(9250,10000);
1551                studio_init_cb.task(_("Loading Settings..."));
1552                load_accel_map();
1553                if (!load_settings())
1554                {
1555                        gamma.set_gamma(1.0/2.2);
1556                        set_workspace_default();
1557                }
1558                load_file_window_size();
1559
1560                // Init Tools must be done after load_accel_map() : accelerators keys
1561                // are displayed in toolbox labels
1562                studio_init_cb.task(_("Init Tools..."));
1563                /* editing tools */
1564                state_manager->add_state(&state_normal);
1565                state_manager->add_state(&state_smooth_move);
1566                state_manager->add_state(&state_scale);
1567                state_manager->add_state(&state_rotate);
1568                state_manager->add_state(&state_mirror);
1569
1570                /* geometry */
1571                state_manager->add_state(&state_circle);
1572                state_manager->add_state(&state_rectangle);
1573                state_manager->add_state(&state_star);
1574                if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1575                state_manager->add_state(&state_gradient);
1576
1577                /* bline tools */
1578                state_manager->add_state(&state_bline);
1579                if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw ); // Enabled for now.  Let's see whether they're good enough yet.
1580                state_manager->add_state(&state_lasso); // Enabled for now.  Let's see whether they're good enough yet.
1581                if(!getenv("SYNFIG_DISABLE_WIDTH"  )) state_manager->add_state(&state_width); // Enabled since 0.61.09
1582                state_manager->add_state(&state_fill);
1583                state_manager->add_state(&state_eyedrop);
1584
1585                /* other */
1586                state_manager->add_state(&state_text);
1587                if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1588                if(!getenv("SYNFIG_DISABLE_BRUSH"  )) state_manager->add_state(&state_brush);
1589                state_manager->add_state(&state_zoom);
1590
1591
1592                device_tracker->load_preferences();
1593                // If the default bline width is modified before focus a canvas
1594                // window, the Distance widget doesn't understand the given value
1595                // and produces this message:
1596                // Distance::ident_system(): Unknown distance system ".00pt"
1597                // setting the default bline width to 1 unit.
1598                // This line fixes that.
1599                synfigapp::Main::set_bline_width(synfigapp::Main::get_selected_input_device()->get_bline_width());
1600
1601                studio_init_cb.task(_("Checking auto-recover..."));
1602
1603                studio_init_cb.amount_complete(9900,10000);
1604
1605                bool opened_any = false;
1606                if (!getenv("SYNFIG_DISABLE_AUTO_RECOVERY") && auto_recover->recovery_needed())
1607                {
1608                        splash_screen.hide();
1609                        if (get_ui_interface()->confirmation(
1610                                        _("Auto recovery file(s) found. Do you want to recover unsaved changes?"),
1611                                        _("Synfig Studio seems to have crashed before you could save all your files."),
1612                                        _("Ignore"),
1613                                        _("Recover")
1614                                ) == synfigapp::UIInterface::RESPONSE_OK)
1615                        {
1616                                int number_recovered;
1617                                if(!auto_recover->recover(number_recovered))
1618                                        if (number_recovered)
1619                                                get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1620                                        else
1621                                                get_ui_interface()->error(_("Unable to recover from previous crash"));
1622                                else
1623                                        dialog_message_1b(
1624                                                "WARNING",
1625                                                _("It would be a good idea to review and save recovered files now."),
1626                                                _("Synfig Studio has attempted to recover from a previous crash. "
1627                                                "The files just recovered are NOT YET SAVED."),
1628                                                _("Thanks"));
1629
1630                                if (number_recovered)
1631                                        opened_any = true;
1632                        }
1633                        splash_screen.show();
1634                }
1635
1636                // Look for any files given on the command line,
1637                // and load them if found.
1638                for(;*argc>=1;(*argc)--)
1639                        if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1640                        {
1641                                studio_init_cb.task(_("Loading files..."));
1642                                splash_screen.hide();
1643                                open((*argv)[*argc]);
1644                                opened_any = true;
1645                                splash_screen.show();
1646                        }
1647
1648                // if no file was specified to be opened, create a new document to help new users get started more easily
1649                if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1650                        new_instance();
1651
1652                studio_init_cb.task(_("Done."));
1653                studio_init_cb.amount_complete(10000,10000);
1654
1655                // To avoid problems with some window managers and gtk >= 2.18
1656                // we should show dock dialogs after the settings load.
1657                // If dock dialogs are shown before the settings are loaded,
1658                // the windows manager can act over it.
1659                // See discussions here:
1660                // * http://synfig.org/forums/viewtopic.php?f=1&t=1131&st=0&sk=t&sd=a&start=30
1661                // * http://synfig.org/forums/viewtopic.php?f=15&t=1062
1662                dock_manager->show_all_dock_dialogs();
1663
1664                main_window->present();
1665                dock_toolbox->present();
1666
1667                splash_screen.hide();
1668
1669                String message;
1670                String details;
1671                /*
1672                if (App::enable_experimental_features) {
1673                        message = _("Following experimental features are enabled: ");
1674                        message += ("Skeleton Layer");
1675                        detials = _("The experimental features are NOT intended for production use. "
1676                                        "It is quite posiible their functionality will change in the "
1677                                        "future versions, which can break compatibility for your "
1678                                        "files. Use for testing purposes only. You can disable "
1679                                        "experimental features on the \"Misc\" tab of Setup dialog.");
1680                }
1681                */
1682#ifdef WIN32
1683                if (message!=""){
1684                        message = _("There is a bug, which can cause computer to hang/freeze when "
1685                                        "resizing the canvas window.");
1686                        details = _("If you got affected by this issue, consider pressing ALT+TAB "
1687                                        "to unfreeze your system and get it back to the working "
1688                                        "state. Please accept our apologies for inconvenience, we "
1689                                        "hope to get this issue resolved in the future versions.");
1690                }
1691#endif
1692                if (message!="")
1693                        dialog_message_1b("WARNING",
1694                                        message,
1695                                        details,
1696                                        _("Got it"));
1697        }
1698        catch(String x)
1699        {
1700                get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1701        }
1702        catch(...)
1703        {
1704                get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1705        }
1706}
1707
1708StateManager* App::get_state_manager() { return state_manager; }
1709
1710App::~App()
1711{
1712        shutdown_in_progress=true;
1713
1714        save_settings();
1715
1716        synfigapp::Main::settings().remove_domain("pref");
1717
1718        selected_instance=0;
1719
1720        // Unload all of the modules
1721        for(;!module_list_.empty();module_list_.pop_back())
1722                ;
1723
1724        delete state_manager;
1725
1726        delete ipc;
1727
1728        delete auto_recover;
1729
1730        delete about;
1731
1732        main_window->hide();
1733
1734        delete main_window;
1735
1736        delete dialog_setup;
1737
1738        delete dialog_gradient;
1739
1740        delete dialog_color;
1741
1742        delete dialog_input;
1743
1744        delete dock_manager;
1745
1746        instance_list.clear();
1747}
1748
1749synfig::String
1750App::get_config_file(const synfig::String& file)
1751{
1752        return Glib::build_filename(synfigapp::Main::get_user_app_directory(),file);
1753}
1754
1755void
1756App::add_recent_file(const etl::handle<Instance> instance)
1757{
1758        add_recent_file(absolute_path(instance->get_file_name()));
1759}
1760
1761void
1762App::add_recent_file(const std::string &file_name)
1763{
1764        std::string filename(file_name);
1765
1766        assert(!filename.empty());
1767
1768        if(filename.empty())
1769                return;
1770
1771        // Toss out any "hidden" files
1772        if(basename(filename)[0]=='.')
1773                return;
1774
1775        // If we aren't an absolute path, turn ourselves into one
1776        if(!is_absolute_path(filename))
1777                filename=absolute_path(filename);
1778
1779        list<string>::iterator iter;
1780        // Check to see if the file is already on the list.
1781        // If it is, then remove it from the list
1782        for(iter=recent_files.begin();iter!=recent_files.end();iter++)
1783                if(*iter==filename)
1784                {
1785                        recent_files.erase(iter);
1786                        break;
1787                }
1788
1789
1790        // Push the filename to the front of the list
1791        recent_files.push_front(filename);
1792
1793        // Clean out the files at the end of the list.
1794        while(recent_files.size()>(unsigned)get_max_recent_files())
1795        {
1796                recent_files.pop_back();
1797        }
1798
1799        signal_recent_files_changed_();
1800
1801        return;
1802}
1803
1804static Time::Format _App_time_format(Time::FORMAT_FRAMES);
1805
1806bool App::jack_is_locked()
1807{
1808        return jack_locks_ > 0;
1809}
1810
1811void App::jack_lock()
1812{
1813        ++jack_locks_;
1814        if (jack_locks_ == 1)
1815        {
1816                // lock jack in instances
1817                for(std::list< etl::handle<Instance> >::const_iterator i = instance_list.begin(); i != instance_list.end(); ++i)
1818                {
1819                        const Instance::CanvasViewList &views = (*i)->canvas_view_list();
1820                        for(Instance::CanvasViewList::const_iterator j = views.begin(); j != views.end(); ++j)
1821                                (*j)->jack_lock();
1822                }
1823        }
1824}
1825
1826void App::jack_unlock()
1827{
1828        --jack_locks_;
1829        assert(jack_locks_ >= 0);
1830        if (jack_locks_ == 0)
1831        {
1832                // unlock jack in instances
1833                for(std::list< etl::handle<Instance> >::const_iterator i = instance_list.begin(); i != instance_list.end(); ++i)
1834                {
1835                        const Instance::CanvasViewList &views = (*i)->canvas_view_list();
1836                        for(Instance::CanvasViewList::const_iterator j = views.begin(); j != views.end(); ++j)
1837                                (*j)->jack_unlock();
1838                }
1839        }
1840}
1841
1842
1843Time::Format
1844App::get_time_format()
1845{
1846        return _App_time_format;
1847}
1848
1849void
1850App::set_time_format(synfig::Time::Format x)
1851{
1852        _App_time_format=x;
1853}
1854
1855
1856void
1857App::save_settings()
1858{
1859        try
1860        {
1861                synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1862                {
1863                        std::string filename=get_config_file("accelrc");
1864                        Gtk::AccelMap::save(filename);
1865                }
1866                {
1867                        std::string filename=get_config_file("language");
1868
1869                        std::ofstream file(filename.c_str());
1870
1871                        if(!file)
1872                        {
1873                                synfig::warning("Unable to save %s",filename.c_str());
1874                        } else {
1875                                file<<App::ui_language.c_str()<<endl;
1876                        }
1877                }
1878                do{
1879                        std::string filename=get_config_file("recentfiles");
1880
1881                        std::ofstream file(filename.c_str());
1882
1883                        if(!file)
1884                        {
1885                                synfig::warning("Unable to save %s",filename.c_str());
1886                                break;
1887                        }
1888
1889                        list<string>::reverse_iterator iter;
1890
1891                        for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1892                                file<<(*iter).c_str()<<endl;
1893                }while(0);
1894                std::string filename=get_config_file("settings-1.0");
1895                synfigapp::Main::settings().save_to_file(filename);
1896
1897        }
1898        catch(...)
1899        {
1900                synfig::warning("Caught exception when attempting to save settings.");
1901        }
1902}
1903
1904bool
1905App::load_settings(const synfig::String& key_filter)
1906{
1907        bool ret=false;
1908        try
1909        {
1910                synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1911                std::string filename=get_config_file("settings-1.0");
1912                ret=synfigapp::Main::settings().load_from_file(filename, key_filter);
1913        }
1914        catch(...)
1915        {
1916                synfig::warning("Caught exception when attempting to load settings.");
1917        }
1918        return ret;
1919}
1920
1921void
1922App::load_accel_map()
1923{
1924        try
1925        {
1926                synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1927                {
1928                        std::string filename=get_config_file("accelrc");
1929                        Gtk::AccelMap::load(filename);
1930                }
1931        }
1932        catch(...)
1933        {
1934                synfig::warning("Caught exception when attempting to load accel map settings.");
1935        }
1936}
1937
1938void
1939App::load_file_window_size()
1940{
1941        try
1942        {
1943                synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1944                {
1945                        std::string filename=get_config_file("recentfiles");
1946                        std::ifstream file(filename.c_str());
1947
1948                        while(file)
1949                        {
1950                                std::string recent_file;
1951                                std::string recent_file_window_size;
1952                                getline(file,recent_file);
1953                                if(!recent_file.empty() && std::ifstream(recent_file.c_str()))
1954                                        add_recent_file(recent_file);
1955                        }
1956                }
1957
1958        }
1959        catch(...)
1960        {
1961                synfig::warning("Caught exception when attempting to load window settings.");
1962        }
1963}
1964
1965void
1966App::load_language_settings()
1967{
1968        try
1969        {
1970                synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1971                {
1972                        std::string filename=get_config_file("language");
1973                        std::ifstream file(filename.c_str());
1974
1975                        while(file)
1976                        {
1977                                std::string language;
1978                                getline(file,language);
1979                                if(!language.empty())
1980                                        App::ui_language=language;
1981                        }
1982                }
1983
1984        }
1985        catch(...)
1986        {
1987                synfig::warning("Caught exception when attempting to loading language settings.");
1988        }
1989}
1990
1991void
1992App::set_workspace_default()
1993{
1994        Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
1995        Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
1996        Gdk::Rectangle rect;
1997        // A proper way to obtain the primary monitor is to use the
1998        // Gdk::Screen::get_primary_monitor () const member. But as it
1999        // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2000        // primary one.
2001        screen->get_monitor_geometry(0,rect);
2002        float dx = (float)rect.get_x();
2003        float dy = (float)rect.get_y();
2004        float sx = (float)rect.get_width();
2005        float sy = (float)rect.get_height();
2006
2007        std::string tpl =
2008        "[mainwindow|%0X|%0Y|%100x|%90y|"
2009                "[hor|%75x"
2010                        "|[vert|%70y"
2011                                "|[hor|%10x"
2012                                        "|[book|toolbox]"
2013                                        "|[mainnotebook]"
2014                                "]"
2015                                "|[hor|%25x"
2016                                        "|[book|params|keyframes]"
2017                                        "|[book|timetrack|curves|children|meta_data]"
2018                                "]"
2019                        "]"
2020                        "|[vert|%20y"
2021                                "|[book|canvases|pal_edit|navigator|info]"
2022                                "|[vert|%25y"
2023                                        "|[book|tool_options|history]"
2024                                        "|[book|layers|groups]"
2025                                "]"
2026                        "]"
2027                "]"
2028        "]";
2029         
2030        std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2031        dock_manager->load_layout_from_string(layout);
2032        dock_manager->show_all_dock_dialogs();
2033}
2034
2035void
2036App::set_workspace_compositing()
2037{
2038        Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
2039        Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
2040        Gdk::Rectangle rect;
2041        // A proper way to obtain the primary monitor is to use the
2042        // Gdk::Screen::get_primary_monitor () const member. But as it
2043        // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2044        // primary one.
2045        screen->get_monitor_geometry(0,rect);
2046        float dx = (float)rect.get_x();
2047        float dy = (float)rect.get_y();
2048        float sx = (float)rect.get_width();
2049        float sy = (float)rect.get_height();
2050
2051        std::string tpl =
2052        "[mainwindow|%0X|%0Y|%100x|%90y|"
2053                "[hor|%1x"
2054                        "|[vert|%1y|[book|toolbox]|[book|tool_options]]"
2055                        "|[hor|%60x|[mainnotebook]"
2056                                "|[hor|%50x|[book|params]"
2057                                        "|[vert|%30y|[book|history|groups]|[book|layers|canvases]]"
2058                        "]"
2059                "]"
2060        "]";
2061
2062        std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2063        dock_manager->load_layout_from_string(layout);
2064        dock_manager->show_all_dock_dialogs();
2065}
2066
2067void
2068App::set_workspace_animating()
2069{
2070        Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
2071        Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
2072        Gdk::Rectangle rect;
2073        // A proper way to obtain the primary monitor is to use the
2074        // Gdk::Screen::get_primary_monitor () const member. But as it
2075        // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2076        // primary one.
2077        screen->get_monitor_geometry(0,rect);
2078        float dx = (float)rect.get_x();
2079        float dy = (float)rect.get_y();
2080        float sx = (float)rect.get_width();
2081        float sy = (float)rect.get_height();
2082
2083        std::string tpl =
2084        "[mainwindow|%0X|%0Y|%100x|%90y|"
2085                "[hor|%70x"
2086                        "|[vert|%1y"
2087                                "|[hor|%1x|[book|toolbox]|[mainnotebook]]"
2088                                "|[hor|%25x|[book|params|children]|[book|timetrack|curves]]"
2089                        "]"
2090                        "|[vert|%30y"
2091                                "|[book|keyframes|history|groups]|[book|layers|canvases]]"
2092                        "]"
2093                "]"
2094        "]";
2095
2096        std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2097        dock_manager->load_layout_from_string(layout);
2098        dock_manager->show_all_dock_dialogs();
2099}
2100
2101
2102void
2103App::restore_default_settings()
2104{
2105        synfigapp::Main::settings().set_value("pref.distance_system","pt");
2106        synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
2107#ifdef SINGLE_THREADED
2108        synfigapp::Main::settings().set_value("pref.use_single_threaded","1");
2109#endif
2110        synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","1");
2111        synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
2112        synfigapp::Main::settings().set_value("pref.enable_experimental_features","0");
2113        synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
2114        synfigapp::Main::settings().set_value("pref.ui_language", "os_LANG");
2115        synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
2116        synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
2117        synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
2118        synfigapp::Main::settings().set_value("pref.preferred_fps","24.0");
2119        synfigapp::Main::settings().set_value("pref.predefined_fps",DEFAULT_PREDEFINED_FPS);
2120        synfigapp::Main::settings().set_value("sequence_separator", ".");
2121        synfigapp::Main::settings().set_value("navigator_uses_cairo", "0");
2122        synfigapp::Main::settings().set_value("workarea_uses_cairo", "0");
2123        synfigapp::Main::settings().set_value("pref.enable_mainwin_menubar", "1");
2124}
2125
2126void
2127App::apply_gtk_settings(bool use_dark)
2128{
2129        GtkSettings *gtk_settings;
2130        gtk_settings = gtk_settings_get_default ();
2131       
2132        gchar *theme_name=getenv("SYNFIG_GTK_THEME");
2133        if(theme_name) {
2134                g_object_set (G_OBJECT (gtk_settings), "gtk-theme-name", theme_name, NULL);
2135        }
2136       
2137        // dark theme
2138        g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", use_dark, NULL);
2139       
2140        // enable menu icons
2141        g_object_set (G_OBJECT (gtk_settings), "gtk-menu-images", TRUE, NULL);
2142       
2143        // fix checkboxes for Adwaita theme
2144        g_object_get (G_OBJECT (gtk_settings), "gtk-theme-name", &theme_name, NULL);
2145        if ( String(theme_name) == "Adwaita" ){
2146                Glib::ustring data;
2147                // Fix GtkPaned (big margin makes it hard to grab first keyframe))
2148                data = "GtkPaned { margin: 2px; }";
2149                //Fix #810: Insetsetive context menus on OSX
2150                data += ".window-frame, .window-frame:backdrop { box-shadow: none; margin: 0; }";
2151                Glib::RefPtr<Gtk::CssProvider> css = Gtk::CssProvider::create();
2152                if(not css->load_from_data(data)) {
2153                        synfig::info("Failed to load css rules.");
2154                }
2155                Glib::RefPtr<Gdk::Screen> screen = Gdk::Screen::get_default();
2156                Gtk::StyleContext::add_provider_for_screen(screen,css, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
2157        }
2158}
2159
2160bool
2161App::shutdown_request(GdkEventAny*)
2162{
2163        quit();
2164        return true;
2165        //return !shutdown_in_progress;
2166}
2167
2168void
2169App::quit()
2170{
2171        if(shutdown_in_progress)return;
2172
2173        get_ui_interface()->task(_("Quit Request"));
2174        if(Busy::count)
2175        {
2176                dialog_message_1b(
2177                        "ERROR",
2178                        _("Tasks are currently running. Please cancel the current tasks and try again"),
2179                        "details",
2180                        _("Close"));
2181
2182                return;
2183        }
2184
2185        std::list<etl::handle<Instance> >::iterator iter;
2186        for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
2187        {
2188                if(!(*iter)->safe_close())
2189                        return;
2190
2191/*
2192                if((*iter)->synfigapp::Instance::get_action_count())
2193                {
2194                        handle<synfigapp::UIInterface> uim;
2195                        uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
2196                        assert(uim);
2197                        string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
2198                        switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
2199                        {
2200                                case synfigapp::UIInterface::RESPONSE_NO:
2201                                        break;
2202                                case synfigapp::UIInterface::RESPONSE_YES:
2203                                        (*iter)->save();
2204                                        break;
2205                                case synfigapp::UIInterface::RESPONSE_CANCEL:
2206                                        return;
2207                                default:
2208                                        assert(0);
2209                                        return;
2210                        }
2211                }
2212
2213
2214                if((*iter)->synfigapp::Instance::is_modified())
2215                {
2216                        handle<synfigapp::UIInterface> uim;
2217                        uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
2218                        assert(uim);
2219                        string str=strprintf(_("%s has changes not yet on the CVS repository.\nWould you like to commit these changes?"),(*iter)->get_file_name().c_str() );
2220                        switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
2221                        {
2222                                case synfigapp::UIInterface::RESPONSE_NO:
2223                                        break;
2224                                case synfigapp::UIInterface::RESPONSE_YES:
2225                                        (*iter)->dialog_cvs_commit();
2226                                        break;
2227                                case synfigapp::UIInterface::RESPONSE_CANCEL:
2228                                        return;
2229                                default:
2230                                        assert(0);
2231                                        return;
2232                        }
2233                }
2234*/
2235
2236                // This next line causes things to crash for some reason
2237                //(*iter)->close();
2238        }
2239
2240        instance_list.clear();
2241
2242        while(studio::App::events_pending())studio::App::iteration(false);
2243
2244        Gtk::Main::quit();
2245        auto_recover->normal_shutdown();
2246
2247        get_ui_interface()->task(_("Quit Request sent"));
2248}
2249
2250void
2251App::show_setup()
2252{
2253        dialog_setup->refresh();
2254        dialog_setup->show();
2255}
2256
2257gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
2258gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
2259
2260//#ifdef WIN32
2261//#define USE_WIN32_FILE_DIALOGS 1
2262//#endif
2263
2264#ifdef USE_WIN32_FILE_DIALOGS
2265static OPENFILENAME ofn={};
2266#endif
2267
2268#ifdef WIN32
2269#include <gdk/gdkwin32.h>
2270#endif
2271
2272bool
2273App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
2274{
2275        // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2276        // TODO: Win32 native dialod not ready yet
2277#ifdef USE_WIN32_FILE_DIALOGS
2278        static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2279
2280        GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2281        HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2282        HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2283
2284        ofn.lStructSize=sizeof(OPENFILENAME);
2285        ofn.hwndOwner = hWnd;
2286        ofn.hInstance = hInstance;
2287        ofn.lpstrFilter = szFilter;
2288//      ofn.lpstrCustomFilter=NULL;
2289//      ofn.nMaxCustFilter=0;
2290//      ofn.nFilterIndex=0;
2291//      ofn.lpstrFile=NULL;
2292        ofn.nMaxFile=MAX_PATH;
2293//      ofn.lpstrFileTitle=NULL;
2294//      ofn.lpstrInitialDir=NULL;
2295//      ofn.lpstrTitle=NULL;
2296        ofn.Flags=OFN_HIDEREADONLY;
2297//      ofn.nFileOffset=0;
2298//      ofn.nFileExtension=0;
2299        ofn.lpstrDefExt=TEXT("sif");
2300//      ofn.lCustData = 0l;
2301        ofn.lpfnHook=NULL;
2302//      ofn.lpTemplateName=NULL;
2303
2304        CHAR szFilename[MAX_PATH];
2305        CHAR szTitle[500];
2306        strcpy(szFilename,filename.c_str());
2307        strcpy(szTitle,title.c_str());
2308
2309        ofn.lpstrFile=szFilename;
2310        ofn.lpstrFileTitle=szTitle;
2311
2312        if(GetOpenFileName(&ofn))
2313        {
2314                filename=szFilename;
2315                return true;
2316        }
2317        return false;
2318
2319#else   // not USE_WIN32_FILE_DIALOGS
2320        synfig::String prev_path;
2321
2322        if(!_preferences.get_value(preference, prev_path))
2323                prev_path = ".";
2324
2325        prev_path = absolute_path(prev_path);
2326
2327        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2328                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2329
2330        dialog->set_transient_for(*App::main_window);
2331        dialog->set_current_folder(prev_path);
2332        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2333        dialog->add_button(Gtk::StockID(_("Import")), Gtk::RESPONSE_ACCEPT);
2334
2335        // 0 All supported files
2336        // 0.1 Synfig documents. sfg is not supported to import
2337        Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2338        filter_supported->set_name(_("All supported files"));
2339        filter_supported->add_mime_type("application/x-sif");
2340        filter_supported->add_pattern("*.sif");
2341        filter_supported->add_pattern("*.sifz");
2342        // 0.2 Image files
2343        filter_supported->add_mime_type("image/png");
2344        filter_supported->add_mime_type("image/jpeg");
2345        filter_supported->add_mime_type("image/jpg");
2346        filter_supported->add_mime_type("image/bmp");
2347        filter_supported->add_mime_type("image/svg+xml");
2348        filter_supported->add_pattern("*.png");
2349        filter_supported->add_pattern("*.jpeg");
2350        filter_supported->add_pattern("*.jpg");
2351        filter_supported->add_pattern("*.bmp");
2352        filter_supported->add_pattern("*.svg");
2353        filter_supported->add_pattern("*.lst");
2354        // 0.3 Audio files
2355        filter_supported->add_mime_type("audio/x-vorbis+ogg");
2356        filter_supported->add_mime_type("audio/mpeg");
2357        filter_supported->add_mime_type("audio/x-wav");
2358        filter_supported->add_pattern("*.ogg");
2359        filter_supported->add_pattern("*.mp3");
2360        filter_supported->add_pattern("*.wav");
2361
2362        // Sub fileters
2363        // 1 Synfig documents. sfg is not supported to import
2364        Glib::RefPtr<Gtk::FileFilter> filter_synfig = Gtk::FileFilter::create();
2365        filter_synfig->set_name(_("Synfig files (*.sif, *.sifz)"));
2366        filter_synfig->add_mime_type("application/x-sif");
2367        filter_synfig->add_pattern("*.sif");
2368        filter_synfig->add_pattern("*.sifz");
2369
2370        // 2.1 Image files
2371        Glib::RefPtr<Gtk::FileFilter> filter_image = Gtk::FileFilter::create();
2372        filter_image->set_name(_("Images (*.png, *.jpeg, *.bmp, *.svg)"));
2373        filter_image->add_mime_type("image/png");
2374        filter_image->add_mime_type("image/jpeg");
2375        filter_image->add_mime_type("image/jpg");
2376        filter_image->add_mime_type("image/bmp");
2377        filter_image->add_mime_type("image/svg+xml");
2378        filter_image->add_pattern("*.png");
2379        filter_image->add_pattern("*.jpeg");
2380        filter_image->add_pattern("*.jpg");
2381        filter_image->add_pattern("*.bmp");
2382        filter_image->add_pattern("*.svg");
2383
2384        // 2.2 Image sequence/list files
2385        Glib::RefPtr<Gtk::FileFilter> filter_image_list = Gtk::FileFilter::create();
2386        filter_image_list->set_name(_("Image sequence files (*.lst)"));
2387        filter_image_list->add_pattern("*.lst");
2388
2389        // 3 Audio files
2390        Glib::RefPtr<Gtk::FileFilter> filter_audio = Gtk::FileFilter::create();
2391        filter_audio->set_name(_("Audio (*.ogg, *.mp3, *.wav)"));
2392        filter_audio->add_mime_type("audio/x-vorbis+ogg");
2393        filter_audio->add_mime_type("audio/mpeg");
2394        filter_audio->add_mime_type("audio/x-wav");
2395        filter_audio->add_pattern("*.ogg");
2396        filter_audio->add_pattern("*.mp3");
2397        filter_audio->add_pattern("*.wav");
2398
2399        // 4 Any files
2400        Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2401        filter_any->set_name(_("Any files"));
2402        filter_any->add_pattern("*");
2403
2404        dialog->add_filter(filter_supported);
2405        dialog->add_filter(filter_synfig);
2406        dialog->add_filter(filter_image);
2407        dialog->add_filter(filter_image_list);
2408        dialog->add_filter(filter_audio);
2409        dialog->add_filter(filter_any);
2410
2411        if (filename.empty())
2412                dialog->set_filename(prev_path);
2413        else if (is_absolute_path(filename))
2414                dialog->set_filename(filename);
2415        else
2416                dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2417
2418        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2419                filename = dialog->get_filename();
2420                // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2421                _preferences.set_value(preference, dirname(filename));
2422                delete dialog;
2423                return true;
2424        }
2425
2426        delete dialog;
2427        return false;
2428#endif   // not USE_WIN32_FILE_DIALOGS
2429}
2430
2431
2432bool
2433App::dialog_open_file_spal(const std::string &title, std::string &filename, std::string preference)
2434{
2435        synfig::String prev_path;
2436
2437        if(!_preferences.get_value(preference, prev_path))
2438                prev_path = ".";
2439
2440        prev_path = absolute_path(prev_path);
2441
2442        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2443                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2444
2445        dialog->set_transient_for(*App::main_window);
2446        dialog->set_current_folder(prev_path);
2447        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2448        dialog->add_button(Gtk::StockID(_("Open")), Gtk::RESPONSE_ACCEPT);
2449       
2450        Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2451        filter_supported->set_name(_("Palette files (*.spal, *.gpl)"));
2452        filter_supported->add_pattern("*.spal");
2453        filter_supported->add_pattern("*.gpl");
2454        dialog->add_filter(filter_supported);
2455
2456        // show only Synfig color palette file (*.spal)
2457        Glib::RefPtr<Gtk::FileFilter> filter_spal = Gtk::FileFilter::create();
2458        filter_spal->set_name(_("Synfig palette files (*.spal)"));
2459        filter_spal->add_pattern("*.spal");
2460        dialog->add_filter(filter_spal);
2461       
2462        // ...and add GIMP color palette file too (*.gpl)
2463        Glib::RefPtr<Gtk::FileFilter> filter_gpl = Gtk::FileFilter::create();
2464        filter_gpl->set_name(_("GIMP palette files (*.gpl)"));
2465        filter_gpl->add_pattern("*.gpl");
2466        dialog->add_filter(filter_gpl);
2467
2468        if (filename.empty())
2469        dialog->set_filename(prev_path);
2470        else if (is_absolute_path(filename))
2471        dialog->set_filename(filename);
2472        else
2473        dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2474
2475        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2476                filename = dialog->get_filename();
2477                _preferences.set_value(preference, dirname(filename));
2478                delete dialog;
2479                return true;
2480        }
2481
2482        delete dialog;
2483        return false;
2484}
2485
2486bool
2487App::dialog_open_file_sketch(const std::string &title, std::string &filename, std::string preference)
2488{
2489        synfig::String prev_path;
2490
2491        if(!_preferences.get_value(preference, prev_path))
2492                prev_path = ".";
2493
2494        prev_path = absolute_path(prev_path);
2495
2496        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2497                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2498
2499        dialog->set_transient_for(*App::main_window);
2500        dialog->set_current_folder(prev_path);
2501        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2502        dialog->add_button(Gtk::StockID(_("Open")), Gtk::RESPONSE_ACCEPT);
2503
2504        // show only Synfig sketch file (*.sketch)
2505        Glib::RefPtr<Gtk::FileFilter> filter_sketch = Gtk::FileFilter::create();
2506        filter_sketch->set_name(_("Synfig sketch files (*.sketch)"));
2507        filter_sketch->add_pattern("*.sketch");
2508        dialog->add_filter(filter_sketch);
2509
2510        if (filename.empty())
2511        dialog->set_filename(prev_path);
2512        else if (is_absolute_path(filename))
2513        dialog->set_filename(filename);
2514        else
2515        dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2516
2517        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2518                filename = dialog->get_filename();
2519                _preferences.set_value(preference, dirname(filename));
2520                delete dialog;
2521                return true;
2522        }
2523
2524        delete dialog;
2525        return false;
2526}
2527
2528
2529bool
2530App::dialog_open_file_image(const std::string &title, std::string &filename, std::string preference)
2531{
2532        synfig::String prev_path;
2533
2534        if(!_preferences.get_value(preference, prev_path))
2535                prev_path = ".";
2536
2537        prev_path = absolute_path(prev_path);
2538
2539        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2540                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2541
2542        dialog->set_transient_for(*App::main_window);
2543        dialog->set_current_folder(prev_path);
2544        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2545        dialog->add_button(Gtk::StockID(_("Load")), Gtk::RESPONSE_ACCEPT);
2546
2547        // show only images
2548        Glib::RefPtr<Gtk::FileFilter> filter_image = Gtk::FileFilter::create();
2549        filter_image->set_name(_("Images and sequence files (*.png, *.jpg, *.jpeg, *.bmp, *.svg, *.lst)"));
2550        filter_image->add_mime_type("image/png");
2551        filter_image->add_mime_type("image/jpeg");
2552        filter_image->add_mime_type("image/jpg");
2553        filter_image->add_mime_type("image/bmp");
2554        filter_image->add_mime_type("image/svg+xml");
2555        filter_image->add_pattern("*.png");
2556        filter_image->add_pattern("*.jpeg");
2557        filter_image->add_pattern("*.jpg");
2558        filter_image->add_pattern("*.bmp");
2559        filter_image->add_pattern("*.svg");
2560        filter_image->add_pattern("*.lst");
2561        dialog->add_filter(filter_image);
2562
2563        // Any files
2564        Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2565        filter_any->set_name(_("Any files"));
2566        filter_any->add_pattern("*");
2567        dialog->add_filter(filter_any);
2568
2569        if (filename.empty())
2570                dialog->set_filename(prev_path);
2571        else if (is_absolute_path(filename))
2572                dialog->set_filename(filename);
2573        else
2574                dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2575
2576        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2577                filename = dialog->get_filename();
2578                _preferences.set_value(preference, dirname(filename));
2579                delete dialog;
2580                return true;
2581        }
2582
2583        delete dialog;
2584        return false;
2585}
2586
2587
2588bool
2589App::dialog_open_file_audio(const std::string &title, std::string &filename, std::string preference)
2590{
2591        synfig::String prev_path;
2592
2593        if(!_preferences.get_value(preference, prev_path))
2594                prev_path = ".";
2595
2596        prev_path = absolute_path(prev_path);
2597
2598        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2599                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2600
2601        dialog->set_transient_for(*App::main_window);
2602        dialog->set_current_folder(prev_path);
2603        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2604        dialog->add_button(Gtk::StockID(_("Load")), Gtk::RESPONSE_ACCEPT);
2605
2606        // Audio files
2607        Glib::RefPtr<Gtk::FileFilter> filter_audio = Gtk::FileFilter::create();
2608        filter_audio->set_name(_("Audio (*.ogg, *.mp3, *.wav)"));
2609        filter_audio->add_mime_type("audio/x-vorbis+ogg");
2610        filter_audio->add_mime_type("audio/mpeg");
2611        filter_audio->add_mime_type("audio/x-wav");
2612        filter_audio->add_pattern("*.ogg");
2613        filter_audio->add_pattern("*.mp3");
2614        filter_audio->add_pattern("*.wav");
2615        dialog->add_filter(filter_audio);
2616
2617        // Any files
2618        Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2619        filter_any->set_name(_("Any files"));
2620        filter_any->add_pattern("*");
2621        dialog->add_filter(filter_any);
2622
2623        if (filename.empty())
2624        dialog->set_filename(prev_path);
2625        else if (is_absolute_path(filename))
2626        dialog->set_filename(filename);
2627        else
2628        dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2629
2630        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2631                filename = dialog->get_filename();
2632                _preferences.set_value(preference, dirname(filename));
2633                delete dialog;
2634                return true;
2635        }
2636
2637        delete dialog;
2638        return false;
2639}
2640
2641void
2642on_open_dialog_with_history_selection_changed(Gtk::FileChooserDialog *dialog, Gtk::Button* history_button)
2643{
2644        // activate the history button when something is selected
2645        history_button->set_sensitive(!dialog->get_filename().empty());
2646}
2647
2648bool
2649App::dialog_open_file_with_history_button(const std::string &title, std::string &filename, bool &show_history, std::string preference)
2650{
2651        // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2652
2653// TODO: Win32 native dialog not ready yet
2654//#ifdef USE_WIN32_FILE_DIALOGS
2655#if 0
2656        static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2657
2658        GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2659        HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2660        HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2661
2662        ofn.lStructSize=sizeof(OPENFILENAME);
2663        ofn.hwndOwner = hWnd;
2664        ofn.hInstance = hInstance;
2665        ofn.lpstrFilter = szFilter;
2666//      ofn.lpstrCustomFilter=NULL;
2667//      ofn.nMaxCustFilter=0;
2668//      ofn.nFilterIndex=0;
2669//      ofn.lpstrFile=NULL;
2670        ofn.nMaxFile=MAX_PATH;
2671//      ofn.lpstrFileTitle=NULL;
2672//      ofn.lpstrInitialDir=NULL;
2673//      ofn.lpstrTitle=NULL;
2674        ofn.Flags=OFN_HIDEREADONLY;
2675//      ofn.nFileOffset=0;
2676//      ofn.nFileExtension=0;
2677        ofn.lpstrDefExt=TEXT("sif");
2678//      ofn.lCustData = 0l;
2679        ofn.lpfnHook=NULL;
2680//      ofn.lpTemplateName=NULL;
2681
2682        CHAR szFilename[MAX_PATH];
2683        CHAR szTitle[500];
2684        strcpy(szFilename,filename.c_str());
2685        strcpy(szTitle,title.c_str());
2686
2687        ofn.lpstrFile=szFilename;
2688        ofn.lpstrFileTitle=szTitle;
2689
2690        if(GetOpenFileName(&ofn))
2691        {
2692                filename=szFilename;
2693                return true;
2694        }
2695        return false;
2696
2697#else   // not USE_WIN32_FILE_DIALOGS
2698        synfig::String prev_path;
2699
2700        if(!_preferences.get_value(preference, prev_path))
2701                prev_path = ".";
2702
2703        prev_path = absolute_path(prev_path);
2704
2705        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2706                                title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2707
2708                dialog->set_transient_for(*App::main_window);
2709        dialog->set_current_folder(prev_path);
2710        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2711        dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
2712        Gtk::Button* history_button = dialog->add_button(_("Open history"), RESPONSE_ACCEPT_WITH_HISTORY);
2713        // TODO: the Open history button should be file type sensitive one.
2714        dialog->set_response_sensitive(RESPONSE_ACCEPT_WITH_HISTORY, true);
2715
2716        // File filters
2717        // Synfig Documents
2718        Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2719        filter_supported->set_name(_("Synfig files (*.sif, *.sifz, *.sfg)"));
2720        filter_supported->add_mime_type("application/x-sif");
2721        filter_supported->add_pattern("*.sif");
2722        filter_supported->add_pattern("*.sifz");
2723        filter_supported->add_pattern("*.sfg");
2724        // Any files
2725        Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2726        filter_any->set_name(_("Any files"));
2727        filter_any->add_pattern("*");
2728
2729        dialog->add_filter(filter_supported);
2730        dialog->add_filter(filter_any);
2731
2732        if (filename.empty())
2733                dialog->set_filename(prev_path);
2734        else if (is_absolute_path(filename))
2735                dialog->set_filename(filename);
2736        else
2737                dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2738
2739        // this ptr is't available to a static member fnc, connect to global function.
2740        sigc::connection connection_sc = dialog->signal_selection_changed().connect(sigc::bind(sigc::ptr_fun(on_open_dialog_with_history_selection_changed), dialog, history_button));
2741
2742        int response = dialog->run();
2743        if (response == Gtk::RESPONSE_ACCEPT || response == RESPONSE_ACCEPT_WITH_HISTORY) {
2744                filename = dialog->get_filename();
2745                show_history = response == RESPONSE_ACCEPT_WITH_HISTORY;
2746                // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2747                _preferences.set_value(preference, dirname(filename));
2748                delete dialog;
2749                return true;
2750        }
2751
2752        connection_sc.disconnect();
2753        delete dialog;
2754        return false;
2755#endif   // not USE_WIN32_FILE_DIALOGS
2756}
2757
2758
2759bool
2760App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
2761{
2762        // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2763
2764#if USE_WIN32_FILE_DIALOGS
2765        static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2766
2767        GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2768        HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2769        HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2770
2771        ofn.lStructSize=sizeof(OPENFILENAME);
2772        ofn.hwndOwner = hWnd;
2773        ofn.hInstance = hInstance;
2774        ofn.lpstrFilter = szFilter;
2775//      ofn.lpstrCustomFilter=NULL;
2776//      ofn.nMaxCustFilter=0;
2777//      ofn.nFilterIndex=0;
2778//      ofn.lpstrFile=NULL;
2779        ofn.nMaxFile=MAX_PATH;
2780//      ofn.lpstrFileTitle=NULL;
2781//      ofn.lpstrInitialDir=NULL;
2782//      ofn.lpstrTitle=NULL;
2783        ofn.Flags=OFN_OVERWRITEPROMPT;
2784//      ofn.nFileOffset=0;
2785//      ofn.nFileExtension=0;
2786        ofn.lpstrDefExt=TEXT("sif");
2787//      ofn.lCustData = 0l;
2788        ofn.lpfnHook=NULL;
2789//      ofn.lpTemplateName=NULL;
2790
2791        CHAR szFilename[MAX_PATH];
2792        CHAR szTitle[500];
2793        strcpy(szFilename,filename.c_str());
2794        strcpy(szTitle,title.c_str());
2795
2796        ofn.lpstrFile=szFilename;
2797        ofn.lpstrFileTitle=szTitle;
2798
2799        if(GetSaveFileName(&ofn))
2800        {
2801                filename=szFilename;
2802                _preferences.set_value(preference,dirname(filename));
2803                return true;
2804        }
2805        return false;
2806#else
2807        synfig::String prev_path;
2808
2809        if(!_preferences.get_value(preference, prev_path))
2810                prev_path=".";
2811
2812        prev_path = absolute_path(prev_path);
2813
2814        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2815
2816        // file type filters
2817        Glib::RefPtr<Gtk::FileFilter> filter_sif = Gtk::FileFilter::create();
2818        filter_sif->set_name(_("Uncompressed Synfig file (*.sif)"));
2819
2820        // sif share same mime type "application/x-sif" with sifz, so it will mixed .sif and .sifz files. Use only
2821        // pattern ("*.sif") for sif file format should be oK.
2822        //filter_sif->add_mime_type("application/x-sif");
2823        filter_sif->add_pattern("*.sif");
2824
2825        Glib::RefPtr<Gtk::FileFilter> filter_sifz = Gtk::FileFilter::create();
2826        filter_sifz->set_name(_("Compressed Synfig file (*.sifz)"));
2827        filter_sifz->add_pattern("*.sifz");
2828
2829        Glib::RefPtr<Gtk::FileFilter> filter_sfg = Gtk::FileFilter::create();
2830        filter_sfg->set_name(_("Container format file (*.sfg)"));
2831        filter_sfg->add_pattern("*.sfg");
2832
2833        dialog->set_current_folder(prev_path);
2834        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2835        dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
2836
2837        dialog->add_filter(filter_sifz);
2838        dialog->add_filter(filter_sif);
2839        dialog->add_filter(filter_sfg);
2840
2841        Widget_Enum *file_type_enum = 0;
2842        if (preference == ANIMATION_DIR_PREFERENCE)
2843        {
2844                file_type_enum = manage(new Widget_Enum());
2845                file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2846                                .add_enum_value(synfig::RELEASE_VERSION_1_0, "1.0", strprintf("1.0 (%s)", _("current")))
2847                                .add_enum_value(synfig::RELEASE_VERSION_0_64_3, "0.64.3", "0.64.3")
2848                                .add_enum_value(synfig::RELEASE_VERSION_0_64_2, "0.64.2", "0.64.2")
2849                                .add_enum_value(synfig::RELEASE_VERSION_0_64_1, "0.64.1", "0.64.1")
2850                                .add_enum_value(synfig::RELEASE_VERSION_0_64_0, "0.64.0", "0.64.0")
2851                                .add_enum_value(synfig::RELEASE_VERSION_0_63_04, "0.63.05", "0.63.05")
2852                                .add_enum_value(synfig::RELEASE_VERSION_0_63_04, "0.63.04", "0.63.04")
2853                                .add_enum_value(synfig::RELEASE_VERSION_0_63_03, "0.63.03", "0.63.03")
2854                                .add_enum_value(synfig::RELEASE_VERSION_0_63_02, "0.63.02", "0.63.02")
2855                                .add_enum_value(synfig::RELEASE_VERSION_0_63_01, "0.63.01", "0.63.01")
2856                                .add_enum_value(synfig::RELEASE_VERSION_0_63_00, "0.63.00", "0.63.00")
2857                                .add_enum_value(synfig::RELEASE_VERSION_0_62_02, "0.62.02", "0.62.02")
2858                                .add_enum_value(synfig::RELEASE_VERSION_0_62_01, "0.62.01", "0.62.01")
2859                                .add_enum_value(synfig::RELEASE_VERSION_0_62_00, "0.62.00", "0.61.00")
2860                                .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", "0.61.09")
2861                                .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2862                                .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2863                                .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2864                file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2865
2866                Gtk::HBox *hbox = manage(new Gtk::HBox);
2867                hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2868                hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2869                hbox->show_all();
2870
2871                dialog->set_extra_widget(*hbox);
2872        }
2873
2874        if (filename.empty()) {
2875                dialog->set_filename(prev_path);
2876
2877        }else{
2878                std::string full_path;
2879                if (is_absolute_path(filename))
2880                        full_path = filename;
2881                else
2882                        full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2883
2884                // select the file if it exists
2885                dialog->set_filename(full_path);
2886
2887                // if the file doesn't exist, put its name into the filename box
2888                struct stat s;
2889                if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2890                        dialog->set_current_name(basename(filename));
2891
2892        }
2893        // set file filter according to previous file format
2894        if (filename_extension(filename) == ".sif" ) dialog->set_filter(filter_sif);
2895        if (filename_extension(filename)== ".sifz" ) dialog->set_filter(filter_sifz);
2896        if (filename_extension(filename) == ".sfg" ) dialog->set_filter(filter_sfg);
2897
2898        // set focus to the file name entry(box) of dialog instead to avoid the name
2899        // we are going to save changes while changing file filter each time.
2900        dialog->set_current_name(basename(filename));
2901
2902        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2903
2904                if (preference == ANIMATION_DIR_PREFERENCE)
2905                        set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2906
2907                // add file extension according to file filter selected by user if he doesn't type file extension in
2908                // file name entry. Right now it still detetes file extension from file name entry, if extension is one
2909                // of .sif, sifz and sfg, it will be used otherwise, saved file format will depend on selected file filter.
2910                // It should be improved by changing file extension according to selted file type filter, such as:
2911                // dialog->property_filter().signal_changed().connect(sigc::mem_fun(*this, &App::on_save_dialog_filter_changed));
2912                filename = dialog->get_filename();
2913
2914                if (filename_extension(filename) != ".sif" &&
2915                        filename_extension(filename) != ".sifz" &&
2916                        filename_extension(filename) != ".sfg")
2917                {
2918                        if (dialog->get_filter() == filter_sif)
2919                                filename = dialog->get_filename() + ".sif";
2920                        else if (dialog->get_filter() == filter_sifz)
2921                                filename = dialog->get_filename() + ".sifz";
2922                        else if (dialog->get_filter() == filter_sfg)
2923                                filename = dialog->get_filename() + ".sfg";
2924                }
2925
2926        // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2927        _preferences.set_value(preference, dirname(filename));
2928        delete dialog;
2929        return true;
2930    }
2931
2932    delete dialog;
2933    return false;
2934#endif
2935}
2936
2937
2938bool
2939App::dialog_save_file_spal(const std::string &title, std::string &filename, std::string preference)
2940{
2941        synfig::String prev_path;
2942        if(!_preferences.get_value(preference, prev_path))
2943                prev_path=".";
2944        prev_path = absolute_path(prev_path);
2945
2946        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2947
2948        // file type filters
2949        Glib::RefPtr<Gtk::FileFilter> filter_spal = Gtk::FileFilter::create();
2950        filter_spal->set_name(_("Synfig palette files (*.spal)"));
2951        filter_spal->add_pattern("*.spal");
2952
2953        dialog->set_current_folder(prev_path);
2954        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2955        dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
2956
2957        dialog->add_filter(filter_spal);
2958
2959        if (filename.empty()) {
2960                dialog->set_filename(prev_path);
2961
2962        }else{
2963                std::string full_path;
2964                if (is_absolute_path(filename))
2965                        full_path = filename;
2966                else
2967                        full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2968
2969                // select the file if it exists
2970                dialog->set_filename(full_path);
2971
2972                // if the file doesn't exist, put its name into the filename box
2973                struct stat s;
2974                if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2975                        dialog->set_current_name(basename(filename));
2976
2977        }
2978
2979        dialog->set_filter(filter_spal);
2980
2981        // set focus to the file name entry(box) of dialog instead to avoid the name
2982        // we are going to save changes while changing file filter each time.
2983        dialog->set_current_name(basename(filename));
2984
2985        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2986
2987                // add file extension according to file filter selected by user
2988                filename = dialog->get_filename();
2989                if (filename_extension(filename) != ".spal")
2990                        filename = dialog->get_filename() + ".spal";
2991
2992        delete dialog;
2993        return true;
2994        }
2995
2996        delete dialog;
2997        return false;
2998}
2999
3000bool
3001App::dialog_save_file_sketch(const std::string &title, std::string &filename, std::string preference)
3002{
3003        synfig::String prev_path;
3004        if(!_preferences.get_value(preference, prev_path))
3005                prev_path=".";
3006        prev_path = absolute_path(prev_path);
3007
3008        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
3009
3010        // file type filters
3011        Glib::RefPtr<Gtk::FileFilter> filter_sketch = Gtk::FileFilter::create();
3012        filter_sketch->set_name(_("Synfig sketch files (*.sketch)"));
3013        filter_sketch->add_pattern("*.sketch");
3014
3015        dialog->set_current_folder(prev_path);
3016        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3017        dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
3018
3019        dialog->add_filter(filter_sketch);
3020
3021        if (filename.empty()) {
3022                dialog->set_filename(prev_path);
3023
3024        }else{
3025                std::string full_path;
3026                if (is_absolute_path(filename))
3027                        full_path = filename;
3028                else
3029                        full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
3030
3031                // select the file if it exists
3032                dialog->set_filename(full_path);
3033
3034                // if the file doesn't exist, put its name into the filename box
3035                struct stat s;
3036                if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
3037                        dialog->set_current_name(basename(filename));
3038
3039        }
3040
3041        dialog->set_filter(filter_sketch);
3042
3043        // set focus to the file name entry(box) of dialog instead to avoid the name
3044        // we are going to save changes while changing file filter each time.
3045        dialog->set_current_name(basename(filename));
3046
3047        if(dialog->run() == GTK_RESPONSE_ACCEPT) {
3048
3049                // add file extension according to file filter selected by user
3050                filename = dialog->get_filename();
3051                if (filename_extension(filename) != ".sketch")
3052                        filename = dialog->get_filename() + ".sketch";
3053
3054        delete dialog;
3055        return true;
3056        }
3057
3058        delete dialog;
3059        return false;
3060}
3061
3062
3063bool
3064App::dialog_save_file_render(const std::string &title, std::string &filename, std::string preference)
3065{
3066        synfig::String prev_path;
3067        if(!_preferences.get_value(preference, prev_path))
3068                prev_path=".";
3069        prev_path = absolute_path(prev_path);
3070
3071        Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
3072
3073        dialog->set_current_folder(prev_path);
3074        dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3075        dialog->add_button(Gtk::Stock::OK,   Gtk::RESPONSE_ACCEPT);
3076
3077        if (filename.empty()) {
3078                dialog->set_filename(prev_path);
3079
3080        }else{
3081                std::string full_path;
3082                if (is_absolute_path(filename))
3083                        full_path = filename;
3084                else
3085                        full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
3086
3087                // select the file if it exists
3088                dialog->set_filename(full_path);
3089
3090                // if the file doesn't exist, put its name into the filename box
3091                struct stat s;
3092                if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
3093                        dialog->set_current_name(basename(filename));
3094
3095        }
3096
3097        if(dialog->run() == GTK_RESPONSE_ACCEPT)
3098        {
3099                filename = dialog->get_filename();
3100
3101                delete dialog;
3102                return true;
3103        }
3104
3105        delete dialog;
3106        return false;
3107}
3108
3109
3110bool
3111App::dialog_select_list_item(const std::string &title, const std::string &message, const std::list<std::string> &list, int &item_index)
3112{
3113        Gtk::Dialog dialog(title, *App::main_window, true);
3114
3115        Gtk::Label label(message, 0, 0);
3116        label.set_line_wrap();
3117
3118        class ModelColumns : public Gtk::TreeModel::ColumnRecord
3119        {
3120        public:
3121                Gtk::TreeModelColumn<int> column_index;
3122                Gtk::TreeModelColumn<Glib::ustring> column_main;
3123                ModelColumns() { add(column_index); add(column_main); }
3124        } model_columns;
3125
3126        Glib::RefPtr<Gtk::ListStore> list_store = Gtk::ListStore::create(model_columns);
3127
3128        int k = 0;
3129        for(std::list<std::string>::const_iterator i = list.begin(); i != list.end(); i++) {
3130                Gtk::ListStore::iterator j = list_store->append();
3131                j->set_value(model_columns.column_index, k++);
3132                j->set_value(model_columns.column_main, Glib::ustring(*i));
3133        }
3134
3135        Gtk::TreeView tree(list_store);
3136        Gtk::TreeViewColumn column_index("", model_columns.column_index);
3137        Gtk::TreeViewColumn column_main("", model_columns.column_main);
3138        column_index.set_visible(false);
3139        tree.append_column(column_index);
3140        tree.append_column(column_main);
3141
3142        Gtk::TreeModel::Row selected_row = list_store->children()[item_index];
3143        if (selected_row)
3144                tree.get_selection()->select(selected_row);
3145
3146        Gtk::Table table(1, 2);
3147        table.attach(label, 0, 1, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
3148        table.attach(tree, 0, 1, 1, 2);
3149
3150        dialog.get_vbox()->pack_start(table);
3151        dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3152        dialog.add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
3153        dialog.set_default_size(300, 450);
3154        dialog.show_all();
3155
3156        if (dialog.run() == Gtk::RESPONSE_ACCEPT) {
3157                item_index = tree.get_selection()->get_selected()->get_value(model_columns.column_index);
3158                return true;
3159        }
3160
3161        return false;
3162}
3163
3164
3165void
3166App::dialog_not_implemented()
3167{
3168        Gtk::MessageDialog dialog(*App::main_window, _("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
3169        dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
3170        dialog.run();
3171}
3172
3173
3174// message dialog with 1 button.
3175void
3176App::dialog_message_1b(
3177        const std::string &type,
3178                //INFO:         Gtk::MESSAGE_INFO - Informational message.
3179                //WARNING:      Gtk::MESSAGE_WARNING - Non-fatal warning message.
3180                //QUESTION:     Gtk::MESSAGE_QUESTION - Question requiring a choice.
3181                //ERROR:        Gtk::MESSAGE_ERROR - Fatal error message.
3182                //OTHER:        Gtk::MESSAGE_OTHER - None of the above, doesn’t get an icon.
3183        const std::string &message,
3184        const std::string &details,
3185        const std::string &button1,
3186        const std::string &long_details)
3187{
3188        Gtk::MessageType _type;
3189        if (type == "INFO")
3190                _type = Gtk::MESSAGE_INFO;
3191        if (type == "WARNING")
3192                _type = Gtk::MESSAGE_WARNING;
3193        if (type == "QUESTION")
3194                _type = Gtk::MESSAGE_QUESTION;
3195        if (type == "ERROR")
3196                _type = Gtk::MESSAGE_ERROR;
3197        if (type == "OTHER")
3198                _type = Gtk::MESSAGE_OTHER;
3199
3200        Gtk::MessageDialog dialog(*App::main_window, message, false, _type, Gtk::BUTTONS_NONE, true);
3201
3202        if (details != "details")
3203                dialog.set_secondary_text(details);
3204       
3205        Gtk::Label label;
3206        Gtk::ScrolledWindow sw;
3207        if (long_details != "long_details")
3208        {
3209                label.set_text(long_details);
3210                label.show();
3211                sw.add(label);
3212                sw.set_size_request(400,300);
3213                sw.show();
3214                dialog.get_content_area()->pack_end(sw);
3215                dialog.set_resizable(true);
3216        }
3217
3218        dialog.add_button(button1, 0);
3219
3220        dialog.run();
3221}
3222
3223
3224// message dialog with 2 buttons.
3225bool
3226App::dialog_message_2b(const std::string &message,
3227        const std::string &details,
3228        const Gtk::MessageType &type,
3229                //MESSAGE_INFO - Informational message.
3230                //MESSAGE_WARNING - Non-fatal warning message.
3231                //MESSAGE_QUESTION - Question requiring a choice.
3232                //MESSAGE_ERROR - Fatal error message.
3233                //MESSAGE_OTHER - None of the above, doesn’t get an icon.
3234        const std::string &button1,
3235        const std::string &button2)
3236{
3237        Gtk::MessageDialog dialog(*App::main_window, message, false, type, Gtk::BUTTONS_NONE, true);
3238        dialog.set_secondary_text(details);
3239        dialog.add_button(button1, 0);
3240        dialog.add_button(button2, 1);
3241
3242        return  dialog.run();
3243}
3244
3245
3246// message dialog with 3 buttons.
3247int
3248App::dialog_message_3b(const std::string &message,
3249        const std::string &detials,
3250        const Gtk::MessageType &type,
3251                //MESSAGE_INFO - Informational message.
3252                //MESSAGE_WARNING - Non-fatal warning message.
3253                //MESSAGE_QUESTION - Question requiring a choice.
3254                //MESSAGE_ERROR - Fatal error message.
3255                //MESSAGE_OTHER - None of the above, doesn’t get an icon.
3256        const std::string &button1,
3257        const std::string &button2,
3258        const std::string &button3)
3259{
3260        Gtk::MessageDialog dialog(*App::main_window, message, false, type, Gtk::BUTTONS_NONE, true);
3261        dialog.set_secondary_text(detials);
3262        dialog.add_button(button1, 0);
3263        dialog.add_button(button2, 1);
3264        dialog.add_button(button3, 2);
3265
3266        return dialog.run();
3267}
3268
3269
3270static bool
3271try_open_url(const std::string &url)
3272{
3273#ifdef WIN32
3274        return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
3275#else // !WIN32
3276        std::vector<std::string> command_line;
3277        std::vector<std::string> browsers;
3278        browsers.reserve(23);
3279
3280        // Browser wrapper scripts
3281#ifdef USE_OPEN_FOR_URLS
3282        browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
3283#endif
3284        browsers.push_back("xdg-open");          // XDG wrapper
3285        browsers.push_back("sensible-browser");  // Debian wrapper
3286        browsers.push_back("gnome-open");        // GNOME wrapper
3287        browsers.push_back("kfmclient");         // KDE wrapper
3288        browsers.push_back("exo-open");          // XFCE wrapper
3289
3290        // Alternatives system
3291        browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
3292        browsers.push_back("x-www-browser");     // Debian GUI alternative
3293
3294        // Individual browsers
3295        browsers.push_back("firefox");
3296        browsers.push_back("epiphany-browser");
3297        browsers.push_back("epiphany");
3298        browsers.push_back("konqueror");
3299        browsers.push_back("iceweasel");
3300        browsers.push_back("mozilla");
3301        browsers.push_back("netscape");
3302        browsers.push_back("icecat");
3303        browsers.push_back("galeon");
3304        browsers.push_back("midori");
3305        browsers.push_back("safari");
3306        browsers.push_back("opera");
3307        browsers.push_back("amaya");
3308        browsers.push_back("netsurf");
3309        browsers.push_back("dillo");
3310
3311        // Try the user-specified browser first
3312        command_line.push_back(App::browser_command);
3313        if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
3314        command_line.push_back(url);
3315
3316        try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
3317        catch( Glib::SpawnError& exception ){
3318
3319                while ( !browsers.empty() )
3320                {
3321                        // Skip the browser if we already tried it
3322                        if( browsers[0] == App::browser_command )
3323                                continue;
3324
3325                        // Construct the command line
3326                        command_line.clear();
3327                        command_line.push_back(browsers[0]);
3328                        if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
3329                        command_line.push_back(url);
3330
3331                        // Remove the browser from the list
3332                        browsers.erase(browsers.begin());
3333
3334                        // Try to spawn the browser
3335                        try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
3336                        // Failed, move on to the next one
3337                        catch(Glib::SpawnError& exception){ continue; }
3338                        return true; // No exception means we succeeded!
3339                }
3340        }
3341
3342        return false;
3343#endif // !WIN32
3344}
3345
3346void
3347App::dialog_help()
3348{
3349        if (!try_open_url("http://synfig.org/wiki/Category:Manual"))
3350        {
3351                Gtk::MessageDialog dialog(*App::main_window, _("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
3352                dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://synfig.org/wiki/Category:Manual"));
3353                dialog.set_title(_("Help"));
3354                dialog.run();
3355        }
3356}
3357
3358void
3359App::open_url(const std::string &url)
3360{
3361        if(!try_open_url(url))
3362        {
3363                Gtk::MessageDialog dialog(*App::main_window, _("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
3364                dialog.set_secondary_text(url);
3365                dialog.set_title(_("No browser found"));
3366                dialog.run();
3367        }
3368}
3369
3370
3371bool
3372App::dialog_entry(const std::string &action, const std::string &content, std::string &text, const std::string &button1, const std::string &button2)
3373{
3374        Gtk::MessageDialog dialog(
3375                *App::main_window,
3376                action,
3377                false,
3378                Gtk::MESSAGE_INFO,
3379                Gtk::BUTTONS_NONE,
3380                true
3381        );
3382
3383        Gtk::Label label(content);
3384        label.show();
3385
3386        Gtk::Entry entry;
3387        entry.set_text(text);
3388        entry.show();
3389        entry.set_activates_default(true);
3390
3391        Gtk::Alignment space1;
3392        space1.set_size_request(18, 0);
3393
3394        Gtk::Alignment space2;
3395        space2.set_size_request(18, 0);
3396
3397        Gtk::Table table(3, 1);
3398        table.attach(space1, 0, 1, 0, 1, Gtk::FILL | Gtk::FILL, Gtk::FILL);
3399        table.attach(label, 1, 2, 0, 1, Gtk::FILL | Gtk::SHRINK, Gtk::FILL);
3400        table.attach(entry, 2, 3, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL);
3401        table.attach(space2, 3, 4, 0, 1, Gtk::FILL | Gtk::FILL, Gtk::FILL);
3402        table.show_all();
3403
3404        dialog.get_vbox()->pack_start(entry);
3405        dialog.get_vbox()->pack_start(table);
3406        dialog.add_button(button1, Gtk::RESPONSE_CANCEL);
3407        dialog.add_button(button2, Gtk::RESPONSE_OK);
3408
3409        dialog.set_default_response(Gtk::RESPONSE_OK);
3410        entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
3411        dialog.show();
3412
3413        if(dialog.run()!=Gtk::RESPONSE_OK)
3414                return false;
3415
3416        text = entry.get_text();
3417
3418        return true;
3419}
3420
3421
3422bool
3423App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
3424{
3425        Gtk::Dialog dialog(
3426                title,                  // Title
3427                *App::main_window,      // Parent
3428                true                    // Modal
3429        );
3430
3431        Gtk::Label label(message);
3432        label.show();
3433        dialog.get_vbox()->pack_start(label);
3434
3435        Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
3436        text_buffer->set_text(text);
3437        Gtk::TextView text_view(text_buffer);
3438        text_view.show();
3439
3440        dialog.get_vbox()->pack_start(text_view);
3441
3442        dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
3443        dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
3444        dialog.set_default_response(Gtk::RESPONSE_OK);
3445
3446        //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
3447        dialog.show();
3448
3449        if(dialog.run()!=Gtk::RESPONSE_OK)
3450                return false;
3451
3452        text=text_buffer->get_text();
3453
3454        return true;
3455}
3456
3457bool
3458App::open(std::string filename)
3459{
3460        return open_as(filename,filename);
3461}
3462
3463bool
3464App::open_as(std::string filename,std::string as,synfig::FileContainerZip::file_size_t truncate_storage_size)
3465{
3466#ifdef WIN32
3467    size_t buf_size = PATH_MAX - 1;
3468    char* long_name = (char*)malloc(buf_size);
3469    long_name[0] = '\0';
3470    if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
3471    // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
3472    // don't use it if that's the case
3473    if (long_name[0] != '\0')
3474        as=String(long_name);
3475    free(long_name);
3476#endif
3477
3478        try
3479        {
3480                OneMoment one_moment;
3481                String errors, warnings;
3482
3483                // TODO: move literal "container:" into common place
3484                std::string canvas_filename = filename;
3485                etl::handle< FileSystemGroup > file_system(new FileSystemGroup(FileSystemNative::instance()));
3486                etl::handle< FileContainerTemporary > container(new FileContainerTemporary());
3487                file_system->register_system("#", container);
3488
3489                // TODO: move literal ".sfg" into common place
3490                if (etl::filename_extension(filename) == ".sfg")
3491                {
3492                        if (!container->open_from_history(filename, truncate_storage_size))
3493                                throw (String)strprintf(_("Unable to open container \"%s\"\n\n"),filename.c_str());
3494                        // TODO: move literal "project.sifz" into common place
3495                        canvas_filename = "#project.sifz";
3496                }
3497                else
3498                {
3499                        if (!container->create(std::string()))
3500                                throw (String)strprintf(_("Unable to create container\n\n"),filename.c_str());
3501                }
3502
3503                etl::handle<synfig::Canvas> canvas(open_canvas_as(file_system->get_identifier(canvas_filename),as,errors,warnings));
3504                if(canvas && get_instance(canvas))
3505                {
3506                        get_instance(canvas)->find_canvas_view(canvas)->present();
3507                        info("%s is already open", canvas_filename.c_str());
3508                        // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
3509                }
3510                else
3511                {
3512                        if(!canvas)
3513                                throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
3514
3515                        if (warnings != "")
3516                                dialog_message_1b(
3517                                        "WARNING",
3518                                        _("Warning"),
3519                                        "details",
3520                                        _("Close"),
3521                                        warnings);
3522
3523                        if (as.find(custom_filename_prefix.c_str()) != 0)
3524                                add_recent_file(as);
3525
3526                        handle<Instance> instance(Instance::create(canvas, container));
3527
3528                        if(!instance)
3529                                throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
3530
3531                        one_moment.hide();
3532
3533                        if(instance->is_updated() && App::dialog_message_2b(
3534                                _("Newer version of this file available on the CVS repository!"),
3535                                _("repository. Would you like to update now? (It would probably be a good idea)"),
3536                                Gtk::MESSAGE_QUESTION,
3537                                _("Cancel"),
3538                                _("Update Anyway"))
3539                        )
3540                                instance->dialog_cvs_update();
3541                }
3542        }
3543        catch(String x)
3544        {
3545                dialog_message_1b(
3546                        "ERROR",
3547                        x,
3548                        "details",
3549                        _("Close"));
3550
3551                return false;
3552        }
3553        catch(runtime_error x)
3554        {
3555                dialog_message_1b(
3556                        "ERROR",
3557                        x.what(),
3558                        "details",
3559                        _("Close"));
3560
3561                return false;
3562        }
3563        catch(...)
3564        {
3565                dialog_message_1b(
3566                        "ERROR",
3567                        _("Uncaught error on file open (BUG)"),
3568                        "details",
3569                        _("Close"));
3570
3571                return false;
3572        }
3573
3574        return true;
3575}
3576
3577// this is called from autorecover.cpp:
3578//   App::open_as(get_shadow_file_name(filename),filename)
3579// other than that, 'filename' and 'as' are the same
3580bool
3581App::open_from_temporary_container_as(std::string container_filename_base,std::string as)
3582{
3583        try
3584        {
3585                OneMoment one_moment;
3586                String errors, warnings;
3587
3588                // TODO: move literals "container:" and "project.sifz" into common place
3589                std::string canvas_filename = "#project.sifz";
3590                etl::handle< FileSystemGroup > file_system(new FileSystemGroup(FileSystemNative::instance()));
3591                etl::handle< FileContainerTemporary > container(new FileContainerTemporary());
3592                file_system->register_system("#", container);
3593
3594                if (!container->open_temporary(container_filename_base))
3595                        throw (String)strprintf(_("Unable to open temporary container \"%s\"\n\n"),container_filename_base.c_str());
3596
3597                etl::handle<synfig::Canvas> canvas(open_canvas_as(file_system->get_identifier(canvas_filename),as,errors,warnings));
3598                if(canvas && get_instance(canvas))
3599                {
3600                        get_instance(canvas)->find_canvas_view(canvas)->present();
3601                        info("%s is already open", canvas_filename.c_str());
3602                        // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
3603                }
3604                else
3605                {
3606                        if(!canvas)
3607                                throw (String)strprintf(_("Unable to load \"%s\":\n\n"),container_filename_base.c_str()) + errors;
3608
3609                        if (warnings != "")
3610                                dialog_message_1b(
3611                                                "WARNING",
3612                                                strprintf("%s:\n\n%s", _("Warning"), warnings.c_str()),
3613                                                "details",
3614                                                _("Close"));
3615
3616                        if (as.find(custom_filename_prefix.c_str()) != 0)
3617                                add_recent_file(as);
3618
3619                        handle<Instance> instance(Instance::create(canvas, container));
3620
3621                        if(!instance)
3622                                throw (String)strprintf(_("Unable to create instance for \"%s\""),container_filename_base.c_str());
3623
3624                        one_moment.hide();
3625
3626                        if(instance->is_updated() && App::dialog_message_2b(
3627                                _("Newer version of this file avaliable on the CVS repository!"),
3628                                _("Would you like to update now (It would probably be a good idea)"),
3629                                Gtk::MESSAGE_QUESTION,
3630                                _("Cancel"),
3631                                _("Update Anyway"))
3632                        )
3633                                instance->dialog_cvs_update();
3634                }
3635        }
3636        catch(String x)
3637        {
3638                dialog_message_1b(
3639                                "ERROR",
3640                                 x,
3641                                "details",
3642                                _("Close"));
3643
3644                return false;
3645        }
3646        catch(runtime_error x)
3647        {
3648                dialog_message_1b(
3649                                "ERROR",
3650                                x.what(),
3651                                "details",
3652                                _("Close"));
3653
3654                return false;
3655        }
3656        catch(...)
3657        {
3658                dialog_message_1b(
3659                                "ERROR",
3660                                _("Uncaught error on file open (BUG)"),
3661                                "details",
3662                                _("Close"));
3663
3664                return false;
3665        }
3666
3667        return true;
3668}
3669
3670
3671void
3672App::new_instance()
3673{
3674        handle<synfig::Canvas> canvas=synfig::Canvas::create();
3675
3676        String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
3677        canvas->set_name(file_name);
3678
3679        canvas->rend_desc().set_frame_rate(preferred_fps);
3680        canvas->rend_desc().set_time_start(0.0);
3681        canvas->rend_desc().set_time_end(5.0);
3682        canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
3683        canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
3684        // The top left and botton right positions are expressed in units
3685        // Original convention is that 1 unit = 60 pixels
3686        canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
3687        canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
3688        canvas->rend_desc().set_w(preferred_x_size);
3689        canvas->rend_desc().set_h(preferred_y_size);
3690        canvas->rend_desc().set_antialias(1);
3691        canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
3692        canvas->set_file_name(file_name);
3693        canvas->keyframe_list().add(synfig::Keyframe());
3694
3695        etl::handle< FileSystemGroup > file_system(new FileSystemGroup(FileSystemNative::instance()));
3696        etl::handle< FileContainerTemporary > container(new FileContainerTemporary());
3697        file_system->register_system("#", container);
3698        container->create(std::string());
3699        canvas->set_identifier(file_system->get_identifier(file_name));
3700
3701        handle<Instance> instance = Instance::create(canvas, container);
3702
3703        if (getenv("SYNFIG_AUTO_ADD_SKELETON_LAYER"))
3704                instance->find_canvas_view(canvas)->add_layer("skeleton");
3705
3706        if (getenv("SYNFIG_AUTO_ADD_MOTIONBLUR_LAYER"))
3707                instance->find_canvas_view(canvas)->add_layer("MotionBlur");
3708
3709        if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
3710                instance->find_canvas_view(canvas)->canvas_properties.present();
3711}
3712
3713void
3714App::dialog_open(string filename)
3715{
3716        if (filename.empty() && selected_instance)
3717                filename = selected_instance->get_file_name();
3718        if (filename.empty())
3719                filename="*.sif";
3720
3721        bool show_history = false;
3722        while(dialog_open_file_with_history_button(_("Please select a file"), filename, show_history, ANIMATION_DIR_PREFERENCE))
3723        {
3724                // If the filename still has wildcards, then we should
3725                // continue looking for the file we want
3726                if(find(filename.begin(),filename.end(),'*')!=filename.end())
3727                        continue;
3728
3729                FileContainerZip::file_size_t truncate_storage_size = 0;
3730
3731                // TODO: ".sfg" literal
3732                if (show_history && filename_extension(filename) == ".sfg")
3733                {
3734                        // read history
3735                        std::list<FileContainerZip::HistoryRecord> history
3736                                = FileContainerZip::read_history(filename);
3737
3738                        // build list of history entries for dialog (descending)
3739                        std::list<std::string> list;
3740                        int index = 0;
3741                        for(std::list<FileContainerZip::HistoryRecord>::const_iterator i = history.begin(); i != history.end(); i++)
3742                                list.push_front(strprintf("%s%d", _("History entry #"), ++index));
3743
3744                        // show dialog
3745                        index=0;
3746                        if (!dialog_select_list_item(_("Please select a file"), _("Select one of previous versions of file"), list, index))
3747                                continue;
3748
3749                        // find selected entry in list (descending)
3750                        for(std::list<FileContainerZip::HistoryRecord>::const_reverse_iterator i = history.rbegin(); i != history.rend(); i++)
3751                                if (0 == index--)
3752                                        truncate_storage_size = i->storage_size;
3753                }
3754
3755                if(open_as(filename,filename,truncate_storage_size))
3756                        break;
3757
3758                get_ui_interface()->error(_("Unable to open file"));
3759        }
3760}
3761
3762void
3763App::set_selected_instance(etl::loose_handle<Instance> instance)
3764{
3765/*      if(get_selected_instance()==instance)
3766        {
3767                selected_instance=instance;
3768                signal_instance_selected()(instance);
3769                return;
3770        }
3771        else
3772        {
3773*/
3774                selected_instance=instance;
3775                if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
3776                {
3777                        if(instance)
3778                        {
3779                                instance->focus(instance->get_canvas());
3780                        }
3781                        else
3782                                set_selected_canvas_view(0);
3783                }
3784                signal_instance_selected()(instance);
3785}
3786
3787void
3788App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
3789{
3790        if(selected_canvas_view != canvas_view)
3791        {
3792                etl::loose_handle<CanvasView> prev = selected_canvas_view;
3793                selected_canvas_view = NULL;
3794                if (prev) prev->deactivate();
3795                selected_canvas_view = canvas_view;
3796                signal_canvas_view_focus()(selected_canvas_view);
3797                if (selected_canvas_view) selected_canvas_view->activate();
3798        }
3799
3800        if(canvas_view)
3801        {
3802                selected_instance=canvas_view->get_instance();
3803                signal_instance_selected()(selected_instance);
3804        }
3805
3806/*
3807        if(get_selected_canvas_view()==canvas_view)
3808        {
3809                signal_canvas_view_focus()(selected_canvas_view);
3810                signal_instance_selected()(canvas_view->get_instance());
3811                return;
3812        }
3813        selected_canvas_view=canvas_view;
3814        if(canvas_view && canvas_view->get_instance() != get_selected_instance())
3815                set_selected_instance(canvas_view->get_instance());
3816        signal_canvas_view_focus()(selected_canvas_view);
3817*/
3818}
3819
3820etl::loose_handle<Instance>
3821App::get_instance(etl::handle<synfig::Canvas> canvas)
3822{
3823        if(!canvas) return 0;
3824        canvas=canvas->get_root();
3825
3826        std::list<etl::handle<Instance> >::iterator iter;
3827        for(iter=instance_list.begin();iter!=instance_list.end();++iter)
3828        {
3829                if((*iter)->get_canvas()==canvas)
3830                        return *iter;
3831        }
3832        return 0;
3833}
3834
3835void
3836App::dialog_about()
3837{
3838        if(about)
3839                about->show();
3840}
3841
3842void
3843studio::App::undo()
3844{
3845        if(selected_instance)
3846                selected_instance->undo();
3847}
3848
3849void
3850studio::App::redo()
3851{
3852        if(selected_instance)
3853                selected_instance->redo();
3854}
3855
3856synfig::String
3857studio::App::get_base_path()
3858{
3859        return FileSystem::fix_slashes(app_base_path_);
3860}
3861
3862void
3863studio::App::setup_changed()
3864{
3865        std::list<etl::handle<Instance> >::iterator iter;
3866        for(iter=instance_list.begin();iter!=instance_list.end();++iter)
3867        {
3868                std::list< etl::handle<synfigapp::CanvasInterface> >::iterator citer;
3869                std::list< etl::handle<synfigapp::CanvasInterface> >& cilist((*iter)->canvas_interface_list());
3870                for(citer=cilist.begin();citer!=cilist.end();++citer)
3871                        {
3872                                (*citer)->signal_rend_desc_changed()();
3873                        }
3874        }
3875}
Note: See TracBrowser for help on using the repository browser.