Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / 0.61.08 / src / gtkmm / app.cpp
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 **
12 **      This package is free software; you can redistribute it and/or
13 **      modify it under the terms of the GNU General Public License as
14 **      published by the Free Software Foundation; either version 2 of
15 **      the License, or (at your option) any later version.
16 **
17 **      This package is distributed in the hope that it will be useful,
18 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **      General Public License for more details.
21 **      \endlegal
22 */
23 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #ifdef WIN32
35 #define WINVER 0x0500
36 #include <windows.h>
37 #endif
38
39 #include <fstream>
40 #include <iostream>
41 #include <locale>
42 #include <cstring>
43
44 #ifdef HAVE_SYS_ERRNO_H
45 #include <sys/errno.h>
46 #endif
47 #include <gtkmm/fileselection.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/inputdialog.h>
55 #include <gtkmm/accelmap.h>
56 #include <gtkmm/uimanager.h>
57
58 #include <gtk/gtk.h>
59
60 #include <gdkmm/general.h>
61
62 #include <synfig/loadcanvas.h>
63 #include <synfig/savecanvas.h>
64
65 #include "app.h"
66 #include "about.h"
67 #include "splash.h"
68 #include "instance.h"
69 #include "canvasview.h"
70 #include "dialog_setup.h"
71 #include "dialog_gradient.h"
72 #include "dialog_color.h"
73 #include "toolbox.h"
74 #include "compview.h"
75 #include "onemoment.h"
76
77 #include "dockmanager.h"
78
79 #include "state_eyedrop.h"
80 #include "state_normal.h"
81 #include "state_draw.h"
82 #include "state_fill.h"
83 #include "state_bline.h"
84 #include "state_polygon.h"
85 #include "state_sketch.h"
86 #include "state_gradient.h"
87 #include "state_circle.h"
88 #include "state_rectangle.h"
89 #include "state_smoothmove.h"
90 #include "state_scale.h"
91 #include "state_width.h"
92 #include "state_rotate.h"
93 #include "state_zoom.h"
94
95 #include "devicetracker.h"
96 #include "dialog_tooloptions.h"
97 #include "widget_enum.h"
98
99 #include "autorecover.h"
100
101 #include <synfigapp/settings.h>
102 #include "dock_history.h"
103 #include "dock_canvases.h"
104 #include "dock_keyframes.h"
105 #include "dock_layers.h"
106 #include "dock_params.h"
107 #include "dock_metadata.h"
108 #include "dock_children.h"
109 #include "dock_info.h"
110 #include "dock_navigator.h"
111 #include "dock_layergroups.h"
112 #include "dock_timetrack.h"
113 #include "dock_curves.h"
114
115 #include "mod_palette/mod_palette.h"
116 #include "mod_mirror/mod_mirror.h"
117
118 #include <sys/stat.h>
119
120 #include "ipc.h"
121
122 #include "module.h"
123
124 #include "statemanager.h"
125
126 #ifdef WITH_FMOD
127 #include <fmod.h>
128 #endif
129
130 #include <gtkmm/accelmap.h>
131 #include <gtkmm/filechooser.h>
132 #include <gtkmm/filechooserdialog.h>
133
134 #include "general.h"
135
136 #endif
137
138 /* === U S I N G =========================================================== */
139
140 using namespace std;
141 using namespace etl;
142 using namespace synfig;
143 using namespace studio;
144
145 /* === M A C R O S ========================================================= */
146
147 #ifndef SYNFIG_USER_APP_DIR
148 #ifdef __APPLE__
149 #define SYNFIG_USER_APP_DIR     "Library/Synfig"
150 #elif defined(_WIN32)
151 #define SYNFIG_USER_APP_DIR     "Synfig"
152 #else
153 #define SYNFIG_USER_APP_DIR     ".synfig"
154 #endif
155 #endif
156
157 #ifndef DPM2DPI
158 #define DPM2DPI(x)      (float(x)/39.3700787402f)
159 #define DPI2DPM(x)      (float(x)*39.3700787402f)
160 #endif
161
162 #ifdef WIN32
163 #       ifdef IMAGE_DIR
164 #               undef IMAGE_DIR
165 #               define IMAGE_DIR "share\\pixmaps"
166 #       endif
167 #endif
168
169 #ifndef IMAGE_DIR
170 #       define IMAGE_DIR "/usr/local/share/pixmaps"
171 #endif
172
173 #ifndef IMAGE_EXT
174 #       define IMAGE_EXT        "tif"
175 #endif
176
177 #include <synfigapp/main.h>
178
179 /* === S I G N A L S ======================================================= */
180
181 static sigc::signal<void> signal_present_all_;
182 sigc::signal<void>&
183 App::signal_present_all() { return signal_present_all_; }
184
185 static sigc::signal<void> signal_recent_files_changed_;
186 sigc::signal<void>&
187 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
188
189 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
190 sigc::signal<void,etl::loose_handle<CanvasView> >&
191 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
192
193 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
194 sigc::signal<void,etl::handle<Instance> >&
195 App::signal_instance_selected() { return signal_instance_selected_; }
196
197 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
198 sigc::signal<void,etl::handle<Instance> >&
199 App::signal_instance_created() { return signal_instance_created_; }
200
201 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
202 sigc::signal<void,etl::handle<Instance> >&
203 App::signal_instance_deleted() { return signal_instance_deleted_; }
204
205 /* === G L O B A L S ======================================================= */
206
207 static std::list<std::string> recent_files;
208 const std::list<std::string>& App::get_recent_files() { return recent_files; }
209
210 static std::list<std::string> recent_files_window_size;
211
212 int     App::Busy::count;
213 bool App::shutdown_in_progress;
214
215 synfig::Gamma App::gamma;
216
217 Glib::RefPtr<studio::UIManager> App::ui_manager_;
218
219 synfig::Distance::System App::distance_system;
220
221 studio::Dialog_Setup* App::dialog_setup;
222
223 etl::handle< studio::ModPalette > mod_palette_;
224 //studio::Dialog_Palette* App::dialog_palette;
225
226 std::list<etl::handle<Instance> > App::instance_list;
227
228 static etl::handle<synfigapp::UIInterface> ui_interface_;
229 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
230
231 etl::handle<Instance> App::selected_instance;
232 etl::handle<CanvasView> App::selected_canvas_view;
233
234 studio::About *studio::App::about=NULL;
235
236 studio::Toolbox *studio::App::toolbox=NULL;
237
238 studio::AutoRecover *studio::App::auto_recover=NULL;
239
240 studio::IPC *ipc=NULL;
241
242 studio::DockManager* studio::App::dock_manager=0;
243
244 studio::DeviceTracker* studio::App::device_tracker=0;
245
246 studio::Dialog_Gradient* studio::App::dialog_gradient;
247
248 studio::Dialog_Color* studio::App::dialog_color;
249
250 Gtk::InputDialog* studio::App::dialog_input;
251
252 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
253
254 studio::Dock_History* dock_history;
255 studio::Dock_Canvases* dock_canvases;
256 studio::Dock_Keyframes* dock_keyframes;
257 studio::Dock_Layers* dock_layers;
258 studio::Dock_Params* dock_params;
259 studio::Dock_MetaData* dock_meta_data;
260 studio::Dock_Children* dock_children;
261 studio::Dock_Info* dock_info;
262 studio::Dock_LayerGroups* dock_layer_groups;
263 studio::Dock_Navigator* dock_navigator;
264 studio::Dock_Timetrack* dock_timetrack;
265 studio::Dock_Curves* dock_curves;
266
267 std::list< etl::handle< studio::Module > > module_list_;
268
269 bool studio::App::use_colorspace_gamma=true;
270 #ifdef SINGLE_THREADED
271 bool studio::App::single_threaded=false;
272 #endif
273 bool studio::App::restrict_radius_ducks=false;
274 #ifdef USE_OPEN_FOR_URLS
275 String studio::App::browser_command("open"); // MacOS only
276 #else
277 String studio::App::browser_command("xdg-open"); // Linux XDG standard
278 #endif
279
280 static int max_recent_files_=25;
281 int studio::App::get_max_recent_files() { return max_recent_files_; }
282 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
283
284 static synfig::String app_base_path_;
285
286 namespace studio {
287
288 bool
289 really_delete_widget(Gtk::Widget *widget)
290 {
291         // synfig::info("really delete %p", (void*)widget);
292         delete widget;
293         return false;
294 }
295
296 // nasty workaround - when we've finished with a popup menu, we want to delete it
297 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
298 // so schedule the real delete to happen in 50ms, giving the action a chance to run
299 void
300 delete_widget(Gtk::Widget *widget)
301 {
302         // synfig::info("delete %p", (void*)widget);
303         Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
304 }
305
306 }; // END of namespace studio
307 studio::StateManager* state_manager;
308
309
310
311
312 class GlobalUIInterface : public synfigapp::UIInterface
313 {
314 public:
315
316         virtual Response yes_no(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
317         {
318                 Gtk::Dialog dialog(
319                         title,          // Title
320                         true,           // Modal
321                         true            // use_separator
322                 );
323                 Gtk::Label label(message);
324                 label.show();
325
326                 dialog.get_vbox()->pack_start(label);
327                 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
328                 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
329
330                 dialog.set_default_response(dflt);
331                 dialog.show();
332                 return (Response)dialog.run();
333         }
334         virtual Response yes_no_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
335         {
336                 Gtk::Dialog dialog(
337                         title,          // Title
338                         true,           // Modal
339                         true            // use_separator
340                 );
341                 Gtk::Label label(message);
342                 label.show();
343
344                 dialog.get_vbox()->pack_start(label);
345                 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
346                 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
347                 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
348
349                 dialog.set_default_response(dflt);
350                 dialog.show();
351                 return (Response)dialog.run();
352         }
353         virtual Response ok_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_OK)
354         {
355                 Gtk::Dialog dialog(
356                         title,          // Title
357                         true,           // Modal
358                         true            // use_separator
359                 );
360                 Gtk::Label label(message);
361                 label.show();
362
363                 dialog.get_vbox()->pack_start(label);
364                 dialog.add_button(Gtk::StockID("gtk-ok"),RESPONSE_OK);
365                 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
366
367                 dialog.set_default_response(dflt);
368                 dialog.show();
369                 return (Response)dialog.run();
370         }
371
372         virtual bool
373         task(const std::string &task)
374         {
375                 std::cerr<<task<<std::endl;
376                 while(studio::App::events_pending())studio::App::iteration(false);
377                 return true;
378         }
379
380         virtual bool
381         error(const std::string &err)
382         {
383                 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
384                 dialog.show();
385                 dialog.run();
386                 return true;
387         }
388
389         virtual bool
390         warning(const std::string &err)
391         {
392                 std::cerr<<"warning: "<<err<<std::endl;
393                 while(studio::App::events_pending())studio::App::iteration(false);
394                 return true;
395         }
396
397         virtual bool
398         amount_complete(int /*current*/, int /*total*/)
399         {
400                 while(studio::App::events_pending())studio::App::iteration(false);
401                 return true;
402         }
403 };
404
405 /* === P R O C E D U R E S ================================================= */
406
407 /*
408 void
409 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
410 {
411         action_group_list.push_back(action_group);
412         Gtk::UIManager::insert_action_group(action_group, pos);
413 }
414
415 void
416 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
417 {
418         std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
419         for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
420                 if(*iter==action_group)
421                 {
422                         action_group_list.erase(iter);
423                         Gtk::UIManager::remove_action_group(action_group);
424                         return;
425                 }
426         synfig::error("Unable to find action group");
427 }
428
429 void
430 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
431 {
432         ui_manager->insert_action_group(group,0);
433         return;
434         std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
435         std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
436
437         for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
438         {
439                 if(*iter && (*iter)->get_name()!="menus")
440                 {
441                         synfig::info("Removing action group "+(*iter)->get_name());
442                         ui_manager->remove_action_group(*iter);
443                 }
444         }
445         ui_manager->insert_action_group(group,0);
446
447         for(;!prev_groups.empty();prev_groups.pop_front())
448         {
449                 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
450                         ui_manager->insert_action_group(prev_groups.front(),1);
451         }
452 }
453 */
454 class Preferences : public synfigapp::Settings
455 {
456 public:
457         virtual bool get_value(const synfig::String& key, synfig::String& value)const
458         {
459                 if(key=="gamma")
460                 {
461                         value=strprintf("%f %f %f %f",
462                                 App::gamma.get_gamma_r(),
463                                 App::gamma.get_gamma_g(),
464                                 App::gamma.get_gamma_b(),
465                                 App::gamma.get_black_level()
466                         );
467                         return true;
468                 }
469                 if(key=="time_format")
470                 {
471                         value=strprintf("%i",App::get_time_format());
472                         return true;
473                 }
474                 if(key=="file_history.size")
475                 {
476                         value=strprintf("%i",App::get_max_recent_files());
477                         return true;
478                 }
479                 if(key=="use_colorspace_gamma")
480                 {
481                         value=strprintf("%i",(int)App::use_colorspace_gamma);
482                         return true;
483                 }
484                 if(key=="distance_system")
485                 {
486                         value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
487                         return true;
488                 }
489 #ifdef SINGLE_THREADED
490                 if(key=="single_threaded")
491                 {
492                         value=strprintf("%i",(int)App::single_threaded);
493                         return true;
494                 }
495 #endif
496                 if(key=="auto_recover_backup_interval")
497                 {
498                         value=strprintf("%i",App::auto_recover->get_timeout());
499                         return true;
500                 }
501                 if(key=="restrict_radius_ducks")
502                 {
503                         value=strprintf("%i",(int)App::restrict_radius_ducks);
504                         return true;
505                 }
506                 if(key=="browser_command")
507                 {
508                         value=App::browser_command;
509                         return true;
510                 }
511
512                 return synfigapp::Settings::get_value(key,value);
513         }
514
515         virtual bool set_value(const synfig::String& key,const synfig::String& value)
516         {
517                 if(key=="gamma")
518                 {
519                         float r,g,b,blk;
520
521                         strscanf(value,"%f %f %f %f",
522                                 &r,
523                                 &g,
524                                 &b,
525                                 &blk
526                         );
527
528                         App::gamma.set_all(r,g,b,blk);
529
530                         return true;
531                 }
532                 if(key=="time_format")
533                 {
534                         int i(atoi(value.c_str()));
535                         App::set_time_format(static_cast<synfig::Time::Format>(i));
536                         return true;
537                 }
538                 if(key=="auto_recover_backup_interval")
539                 {
540                         int i(atoi(value.c_str()));
541                         App::auto_recover->set_timeout(i);
542                         return true;
543                 }
544                 if(key=="file_history.size")
545                 {
546                         int i(atoi(value.c_str()));
547                         App::set_max_recent_files(i);
548                         return true;
549                 }
550                 if(key=="use_colorspace_gamma")
551                 {
552                         int i(atoi(value.c_str()));
553                         App::use_colorspace_gamma=i;
554                         return true;
555                 }
556                 if(key=="distance_system")
557                 {
558                         App::distance_system=Distance::ident_system(value);;
559                         return true;
560                 }
561 #ifdef SINGLE_THREADED
562                 if(key=="single_threaded")
563                 {
564                         int i(atoi(value.c_str()));
565                         App::single_threaded=i;
566                         return true;
567                 }
568 #endif
569                 if(key=="restrict_radius_ducks")
570                 {
571                         int i(atoi(value.c_str()));
572                         App::restrict_radius_ducks=i;
573                         return true;
574                 }
575                 if(key=="browser_command")
576                 {
577                         App::browser_command=value;
578                         return true;
579                 }
580
581                 return synfigapp::Settings::set_value(key,value);
582         }
583
584         virtual KeyList get_key_list()const
585         {
586                 KeyList ret(synfigapp::Settings::get_key_list());
587                 ret.push_back("gamma");
588                 ret.push_back("time_format");
589                 ret.push_back("distance_system");
590                 ret.push_back("file_history.size");
591                 ret.push_back("use_colorspace_gamma");
592 #ifdef SINGLE_THREADED
593                 ret.push_back("single_threaded");
594 #endif
595                 ret.push_back("auto_recover_backup_interval");
596                 ret.push_back("restrict_radius_ducks");
597                 ret.push_back("browser_command");
598                 return ret;
599         }
600 };
601
602 static ::Preferences _preferences;
603
604 void
605 init_ui_manager()
606 {
607         Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
608
609         Glib::RefPtr<Gtk::ActionGroup> toolbox_action_group = Gtk::ActionGroup::create("toolbox");
610
611         Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create();
612
613         menus_action_group->add( Gtk::Action::create("menu-file", _("_File")) );
614         menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")) );
615         menus_action_group->add( Gtk::Action::create("menu-view", _("_View")) );
616         menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")) );
617         menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
618         menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
619         menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
620         menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")) );
621         menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
622         menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
623         menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
624         menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
625         menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
626
627         // Add the synfigapp actions...
628         synfigapp::Action::Book::iterator iter;
629         for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
630         {
631                 actions_action_group->add(Gtk::Action::create(
632                         "action-"+iter->second.name,
633                         get_action_stock_id(iter->second),
634                         iter->second.local_name,iter->second.local_name
635                 ));
636         }
637
638 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
639 #define DEFINE_ACTION2(x,stock,label) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock,label,label) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
640 #define DEFINE_ACTION_SIG(group,x,stock,sig) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ group->add(action,sig); }
641
642         DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
643         DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
644         DEFINE_ACTION("new", Gtk::Stock::NEW);
645         DEFINE_ACTION("open", Gtk::Stock::OPEN);
646         DEFINE_ACTION("save", Gtk::Stock::SAVE);
647         DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
648         DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
649         DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
650         DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
651         DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
652         DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
653         DEFINE_ACTION("import", _("Import"));
654         DEFINE_ACTION("render", _("Render"));
655         DEFINE_ACTION("preview", _("Preview"));
656         DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
657         DEFINE_ACTION("sound", _("Sound File"));
658         DEFINE_ACTION("options", _("Options"));
659         DEFINE_ACTION("close", _("Close View"));
660         DEFINE_ACTION("close-document", _("Close Document"));
661         DEFINE_ACTION("quit", Gtk::Stock::QUIT);
662
663
664         DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
665         DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
666         DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
667         DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
668         DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
669         DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
670         DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
671         DEFINE_ACTION("properties", _("Properties"));
672
673         DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
674         DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
675         DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
676         DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
677         DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
678         DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
679         DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
680         DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
681         DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
682         DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
683         DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
684         DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
685         DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
686         DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
687         DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
688         DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
689         DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
690         for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
691                 DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
692         DEFINE_ACTION("play", _("Play"));
693         // DEFINE_ACTION("pause", _("Pause"));
694         DEFINE_ACTION("stop", _("Stop"));
695         DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
696         DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
697         DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
698         DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
699         DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
700         DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
701         DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
702         DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
703         DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
704         DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
705         DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
706         DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
707         DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
708         DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
709         DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
710         DEFINE_ACTION("seek-next-frame", _("Next Frame"));
711         DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
712         DEFINE_ACTION("seek-next-second", _("Seek Forward"));
713         DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
714         DEFINE_ACTION("seek-begin", _("Seek to Begin"));
715         DEFINE_ACTION("seek-end", _("Seek to End"));
716
717         DEFINE_ACTION("action-group_add", _("Add group"));
718
719         DEFINE_ACTION("canvas-new", _("New Canvas"));
720
721         DEFINE_ACTION("amount-inc", _("Increase Amount"));
722         DEFINE_ACTION("amount-dec", _("Decrease Amount"));
723
724 #undef DEFINE_ACTION
725
726
727 // Set up synfigapp actions
728         /*{
729                 synfigapp::Action::Book::iterator iter;
730
731                 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
732                 {
733                         Gtk::StockID stock_id;
734
735                         if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
736                         {
737                                 //Gtk::Image* image(manage(new Gtk::Image()));
738                                 if(iter->second.task=="raise")                  stock_id=Gtk::Stock::GO_UP;
739                                 else if(iter->second.task=="lower")             stock_id=Gtk::Stock::GO_DOWN;
740                                 else if(iter->second.task=="move_top")  stock_id=Gtk::Stock::GOTO_TOP;
741                                 else if(iter->second.task=="move_bottom")       stock_id=Gtk::Stock::GOTO_BOTTOM;
742                                 else if(iter->second.task=="remove")    stock_id=Gtk::Stock::DELETE;
743                                 else if(iter->second.task=="set_on")    stock_id=Gtk::Stock::YES;
744                                 else if(iter->second.task=="set_off")   stock_id=Gtk::Stock::NO;
745                                 //else if(iter->second.task=="duplicate")       stock_id=Gtk::Stock::COPY;
746                                 else if(iter->second.task=="remove")    stock_id=Gtk::Stock::DELETE;
747                                 else                                                                    stock_id=Gtk::StockID("synfig-"+iter->second.task);
748
749                                 actions_action_group->add(Gtk::Action::create(
750                                         "action-"+iter->second.name,
751                                         stock_id,
752                                         iter->second.local_name,iter->second.local_name
753                                 ));
754                         }
755                 }
756         }
757 */
758
759
760     Glib::ustring ui_info =
761 "<ui>"
762 "       <popup name='menu-toolbox' action='menu-toolbox'>"
763 "       <menu action='menu-file'>"
764 "       </menu>"
765 "       </popup>"
766 "       <popup name='menu-main' action='menu-main'>"
767 "       <menu action='menu-file'>"
768 "               <menuitem action='new' />"
769 "               <menuitem action='open' />"
770 "               <menuitem action='save' />"
771 "               <menuitem action='save-as' />"
772 "               <menuitem action='revert' />"
773 "               <separator name='bleh01'/>"
774 "               <menuitem action='cvs-add' />"
775 "               <menuitem action='cvs-update' />"
776 "               <menuitem action='cvs-commit' />"
777 "               <menuitem action='cvs-revert' />"
778 "               <separator name='bleh02'/>"
779 "               <menuitem action='import' />"
780 "               <separator name='bleh03'/>"
781 "               <menuitem action='render' />"
782 "               <menuitem action='preview' />"
783 "               <menuitem action='sound' />"
784 "               <separator name='bleh04'/>"
785 "               <menuitem action='options' />"
786 "               <menuitem action='close' />"
787 "               <menuitem action='close-document' />"
788 "               <menuitem action='quit' />"
789 "       </menu>"
790 "       <menu action='menu-edit'>"
791 "               <menuitem action='undo'/>"
792 "               <menuitem action='redo'/>"
793 "               <separator name='bleh05'/>"
794 "               <menuitem action='cut'/>"
795 "               <menuitem action='copy'/>"
796 "               <menuitem action='paste'/>"
797 "               <separator name='bleh06'/>"
798 "               <menuitem action='select-all-ducks'/>"
799 "               <menuitem action='unselect-all-layers'/>"
800 "               <separator name='bleh07'/>"
801 "               <menuitem action='properties'/>"
802 "       </menu>"
803 "       <menu action='menu-view'>"
804 "               <menu action='menu-duck-mask'>"
805 "                       <menuitem action='mask-position-ducks' />"
806 "                       <menuitem action='mask-vertex-ducks' />"
807 "                       <menuitem action='mask-tangent-ducks' />"
808 "                       <menuitem action='mask-radius-ducks' />"
809 "                       <menuitem action='mask-width-ducks' />"
810 "                       <menuitem action='mask-angle-ducks' />"
811 "               </menu>"
812 "               <menu action='menu-preview-quality'>"
813 "                       <menuitem action='quality-00' />"
814 "                       <menuitem action='quality-01' />"
815 "                       <menuitem action='quality-02' />"
816 "                       <menuitem action='quality-03' />"
817 "                       <menuitem action='quality-04' />"
818 "                       <menuitem action='quality-05' />"
819 "                       <menuitem action='quality-06' />"
820 "                       <menuitem action='quality-07' />"
821 "                       <menuitem action='quality-08' />"
822 "                       <menuitem action='quality-09' />"
823 "                       <menuitem action='quality-10' />"
824 "               </menu>"
825 "               <menu action='menu-lowres-pixel'>"
826 "               <menuitem action='decrease-low-res-pixel-size'/>"
827 "               <menuitem action='increase-low-res-pixel-size'/>"
828 "               <separator name='pixel-size-separator'/>"
829 ;
830
831         for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
832                 ui_info += strprintf("                  <menuitem action='lowres-pixel-%d' />", *iter);
833
834         ui_info +=
835 "               </menu>"
836 "               <separator name='bleh08'/>"
837 "               <menuitem action='play'/>"
838 //"             <menuitem action='pause'/>"
839 "               <menuitem action='stop'/>"
840 "               <menuitem action='dialog-flipbook'/>"
841 "               <separator name='bleh09'/>"
842 "               <menuitem action='toggle-grid-show'/>"
843 "               <menuitem action='toggle-grid-snap'/>"
844 "               <menuitem action='toggle-guide-show'/>"
845 "               <menuitem action='toggle-low-res'/>"
846 "               <menuitem action='toggle-onion-skin'/>"
847 "               <separator name='bleh10'/>"
848 "               <menuitem action='canvas-zoom-in'/>"
849 "               <menuitem action='canvas-zoom-out'/>"
850 "               <menuitem action='canvas-zoom-fit'/>"
851 "               <menuitem action='canvas-zoom-100'/>"
852 "               <separator name='bleh11'/>"
853 "               <menuitem action='time-zoom-in'/>"
854 "               <menuitem action='time-zoom-out'/>"
855 "               <separator name='bleh12'/>"
856 "               <menuitem action='jump-next-keyframe'/>"
857 "               <menuitem action='jump-prev-keyframe'/>"
858 "               <menuitem action='seek-next-frame'/>"
859 "               <menuitem action='seek-prev-frame'/>"
860 "               <menuitem action='seek-next-second'/>"
861 "               <menuitem action='seek-prev-second'/>"
862 "               <menuitem action='seek-begin'/>"
863 "               <menuitem action='seek-end'/>"
864 "       </menu>"
865 "       <menu action='menu-canvas'>"
866 "               <menuitem action='canvas-new'/>"
867 "       </menu>"
868 "       <menu name='menu-state' action='menu-state'>"
869 "       </menu>"
870 "       <menu action='menu-group'>"
871 "               <menuitem action='action-group_add'/>"
872 "       </menu>"
873 "       <menu action='menu-layer'>"
874 //"             <menuitem action='cut'/>"
875 //"             <menuitem action='copy'/>"
876 //"             <menuitem action='paste'/>"
877 //"             <separator name='bleh06'/>"
878 "               <menu action='menu-layer-new'></menu>"
879 "               <menuitem action='amount-inc'/>"
880 "               <menuitem action='amount-dec'/>"
881 "       </menu>"
882 "       <menu action='menu-keyframe'>"
883 "               <menuitem action='keyframe-properties'/>"
884 "       </menu>"
885 "       </popup>"
886
887 "</ui>"
888 ;
889 /*              "<ui>"
890         "  <menubar name='MenuBar'>"
891         "    <menu action='MenuFile'>"
892         "      <menuitem action='New'/>"
893         "      <menuitem action='Open'/>"
894         "      <separator/>"
895         "      <menuitem action='Quit'/>"
896         "    </menu>"
897         "    <menu action='MenuEdit'>"
898         "      <menuitem action='Cut'/>"
899         "      <menuitem action='Copy'/>"
900         "      <menuitem action='Paste'/>"
901         "    </menu>"
902         "  </menubar>"
903         "  <toolbar  name='ToolBar'>"
904         "    <toolitem action='Open'/>"
905         "    <toolitem action='Quit'/>"
906         "  </toolbar>"
907         "</ui>";
908 */
909         try
910         {
911                 actions_action_group->set_sensitive(false);
912                 App::ui_manager()->set_add_tearoffs(true);
913                 App::ui_manager()->insert_action_group(menus_action_group,1);
914                 App::ui_manager()->insert_action_group(actions_action_group,1);
915                 App::ui_manager()->add_ui_from_string(ui_info);
916
917                 //App::ui_manager()->get_accel_group()->unlock();
918         }
919         catch(const Glib::Error& ex)
920         {
921                 synfig::error("building menus and toolbars failed: " + ex.what());
922         }
923
924         // Add default keyboard accelerators
925 #define ACCEL(path,accel)                                               \
926         {                                                                                       \
927                 Gtk::AccelKey accel_key(accel,path);    \
928                 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
929         }
930
931 #define ACCEL2(accel)                                                   \
932         {                                                                                       \
933                 Gtk::AccelKey accel_key(accel);                 \
934                 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
935         }
936
937         ACCEL("<Actions>//select-all-ducks","<Control>a");
938         ACCEL("<Actions>//unselect-all-layers","<Control>d");
939         ACCEL("<Actions>//render","F9");
940         ACCEL("<Actions>//preview","F11");
941         ACCEL("<Actions>//properties","F8");
942         ACCEL("<Actions>//options","F12");
943         ACCEL("<Actions>//import","<control>i");
944         ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
945         ACCEL("<Actions>//toggle-grid-show","<Control>g");
946         ACCEL("<Actions>//toggle-grid-snap","<Control>l");
947         ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
948         ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
949         ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
950         ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
951         ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
952         ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
953         ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
954
955         ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
956         ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
957
958         ACCEL("<Actions>//quality-01","<Control>1");
959         ACCEL("<Actions>//quality-02","<Control>2");
960         ACCEL("<Actions>//quality-03","<Control>3");
961         ACCEL("<Actions>//quality-04","<Control>4");
962         ACCEL("<Actions>//quality-05","<Control>5");
963         ACCEL("<Actions>//quality-06","<Control>6");
964         ACCEL("<Actions>//quality-07","<Control>7");
965         ACCEL("<Actions>//quality-08","<Control>8");
966         ACCEL("<Actions>//quality-09","<Control>9");
967         ACCEL("<Actions>//quality-10","<Control>0");
968         ACCEL("<Actions>//undo","<Control>z");
969         ACCEL("<Actions>//redo","<Control>r");
970         ACCEL("<Actions>//action-layer_remove","Delete");
971
972 /*      ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
973         ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
974         ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
975         ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
976         ACCEL("<Actions>//time-zoom-in","+");
977         ACCEL("<Actions>//time-zoom-out","_");
978 */
979         ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,"<Actions>//decrease-low-res-pixel-size"));
980         ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,"<Actions>//increase-low-res-pixel-size"));
981
982         ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
983         ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
984
985         ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
986         ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
987         ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
988         ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
989         ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
990         ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
991         ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
992         ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
993         ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
994         ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
995         ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
996         ACCEL("<Actions>//play",              "<Control>p");
997         ACCEL("<Actions>//seek-begin","Home");
998         ACCEL("<Actions>//seek-end","End");
999
1000         ACCEL("<Actions>//state-normal",      "<Mod1>a");
1001         ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1002         ACCEL("<Actions>//state-scale",       "<Mod1>d");
1003         ACCEL("<Actions>//state-rotate",      "<Mod1>s");
1004
1005         ACCEL("<Actions>//state-bline",       "<Mod1>b");
1006         ACCEL("<Actions>//state-circle",      "<Mod1>c");
1007         ACCEL("<Actions>//state-rectangle",   "<Mod1>r");
1008         ACCEL("<Actions>//state-gradient",    "<Mod1>g");
1009
1010         ACCEL("<Actions>//state-eyedrop",     "<Mod1>e");
1011         ACCEL("<Actions>//state-fill",        "<Mod1>f");
1012         ACCEL("<Actions>//state-zoom",        "<Mod1>z");
1013         ACCEL("<Actions>//state-polygon",     "<Mod1>p");
1014
1015         ACCEL("<Actions>//state-draw",        "<Mod1>w");
1016         ACCEL("<Actions>//state-sketch",      "<Mod1>k");
1017         ACCEL("<Actions>//state-width",       "<Mod1>t");
1018         ACCEL("<Actions>//state-mirror",      "<Mod1>m");
1019
1020         ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1021
1022 #undef ACCEL
1023 }
1024
1025 #ifdef _WIN32
1026 #define mkdir(x,y) mkdir(x)
1027 #endif
1028
1029 /* === M E T H O D S ======================================================= */
1030
1031 App::App(int *argc, char ***argv):
1032         Gtk::Main(argc,argv),
1033         IconController(etl::dirname((*argv)[0]))
1034 {
1035         app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1036
1037
1038         ui_interface_=new GlobalUIInterface();
1039
1040         gdk_rgb_init();
1041
1042         // don't call thread_init() if threads are already initialized
1043         // on some machines bonobo_init() initialized threads before we get here
1044         if (!g_thread_supported())
1045                 Glib::thread_init();
1046
1047         distance_system=Distance::SYSTEM_UNITS;
1048
1049         if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1050         {
1051                 if(errno!=EEXIST)
1052                         synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1053         }
1054         else
1055         {
1056                 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1057         }
1058
1059
1060         ipc=new IPC();
1061
1062         if(!SYNFIG_CHECK_VERSION())
1063         {
1064                 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1065                 dialog_error_blocking("Synfig Studio",
1066                         "This copy of Synfig Studio was compiled against a\n"
1067                         "different version of libsynfig than what is currently\n"
1068                         "installed. Synfig Studio will now abort. Try downloading\n"
1069                         "the latest version from the Synfig website at\n"
1070                         "http://synfig.org/Download"
1071                 );
1072                 throw 40;
1073         }
1074         Glib::set_application_name(_("Synfig Studio"));
1075
1076         Splash splash_screen;
1077         splash_screen.show();
1078
1079         shutdown_in_progress=false;
1080         SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1081         SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1082
1083         // Initialize the Synfig library
1084         try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1085         catch(...)
1086         {
1087                 get_ui_interface()->error(_("Failed to initialize synfig!"));
1088                 throw;
1089         }
1090
1091         // add the preferences to the settings
1092         synfigapp::Main::settings().add_domain(&_preferences,"pref");
1093
1094         try
1095         {
1096                 studio_init_cb.task(_("Init UI Manager..."));
1097                 App::ui_manager_=studio::UIManager::create();
1098                 init_ui_manager();
1099
1100                 studio_init_cb.task(_("Init Dock Manager..."));
1101                 dock_manager=new studio::DockManager();
1102
1103                 studio_init_cb.task(_("Init State Manager..."));
1104                 state_manager=new StateManager();
1105
1106                 studio_init_cb.task(_("Init Toolbox..."));
1107                 toolbox=new studio::Toolbox();
1108
1109                 studio_init_cb.task(_("Init About Dialog..."));
1110                 about=new studio::About();
1111
1112                 studio_init_cb.task(_("Init Tool Options..."));
1113                 dialog_tool_options=new studio::Dialog_ToolOptions();
1114                 dock_manager->register_dockable(*dialog_tool_options);
1115
1116                 studio_init_cb.task(_("Init History..."));
1117                 dock_history=new studio::Dock_History();
1118                 dock_manager->register_dockable(*dock_history);
1119
1120                 studio_init_cb.task(_("Init Canvases..."));
1121                 dock_canvases=new studio::Dock_Canvases();
1122                 dock_manager->register_dockable(*dock_canvases);
1123
1124                 studio_init_cb.task(_("Init Keyframes..."));
1125                 dock_keyframes=new studio::Dock_Keyframes();
1126                 dock_manager->register_dockable(*dock_keyframes);
1127
1128                 studio_init_cb.task(_("Init Layers..."));
1129                 dock_layers=new studio::Dock_Layers();
1130                 dock_manager->register_dockable(*dock_layers);
1131
1132                 studio_init_cb.task(_("Init Params..."));
1133                 dock_params=new studio::Dock_Params();
1134                 dock_manager->register_dockable(*dock_params);
1135
1136                 studio_init_cb.task(_("Init MetaData..."));
1137                 dock_meta_data=new studio::Dock_MetaData();
1138                 dock_manager->register_dockable(*dock_meta_data);
1139
1140                 studio_init_cb.task(_("Init Children..."));
1141                 dock_children=new studio::Dock_Children();
1142                 dock_manager->register_dockable(*dock_children);
1143
1144                 studio_init_cb.task(_("Init Info..."));
1145                 dock_info = new studio::Dock_Info();
1146                 dock_manager->register_dockable(*dock_info);
1147
1148                 studio_init_cb.task(_("Init Navigator..."));
1149                 dock_navigator = new studio::Dock_Navigator();
1150                 dock_manager->register_dockable(*dock_navigator);
1151
1152                 studio_init_cb.task(_("Init Timetrack..."));
1153                 dock_timetrack = new studio::Dock_Timetrack();
1154                 dock_manager->register_dockable(*dock_timetrack);
1155
1156                 studio_init_cb.task(_("Init Curve Editor..."));
1157                 dock_curves = new studio::Dock_Curves();
1158                 dock_manager->register_dockable(*dock_curves);
1159
1160                 studio_init_cb.task(_("Init Layer Groups..."));
1161                 dock_layer_groups = new studio::Dock_LayerGroups();
1162                 dock_manager->register_dockable(*dock_layer_groups);
1163
1164
1165                 studio_init_cb.task(_("Init Color Dialog..."));
1166                 dialog_color=new studio::Dialog_Color();
1167
1168                 studio_init_cb.task(_("Init Gradient Dialog..."));
1169                 dialog_gradient=new studio::Dialog_Gradient();
1170
1171                 studio_init_cb.task(_("Init DeviceTracker..."));
1172                 device_tracker=new studio::DeviceTracker();
1173
1174                 studio_init_cb.task(_("Init Tools..."));
1175
1176                 /* row 1 */
1177                 state_manager->add_state(&state_normal);
1178                 state_manager->add_state(&state_smooth_move);
1179                 state_manager->add_state(&state_scale);
1180                 state_manager->add_state(&state_rotate);
1181                 studio_init_cb.task(_("Init ModMirror...")); module_list_.push_back(new ModMirror()); module_list_.back()->start();
1182
1183                 /* row 2 */
1184                 state_manager->add_state(&state_bline);
1185                 state_manager->add_state(&state_circle);
1186                 state_manager->add_state(&state_rectangle);
1187                 state_manager->add_state(&state_gradient);
1188                 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1189
1190                 /* row 3 */
1191                 if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw); // Enabled for now.  Let's see whether they're good enough yet.
1192                 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1193                 state_manager->add_state(&state_fill);
1194                 state_manager->add_state(&state_eyedrop);
1195                 state_manager->add_state(&state_zoom);
1196
1197                 // Disabled by default - it doesn't work properly?
1198                 if(getenv("SYNFIG_ENABLE_WIDTH"    )) state_manager->add_state(&state_width);
1199
1200                 studio_init_cb.task(_("Init ModPalette..."));
1201                 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1202
1203                 studio_init_cb.task(_("Init Setup Dialog..."));
1204                 dialog_setup=new studio::Dialog_Setup();
1205
1206                 studio_init_cb.task(_("Init Input Dialog..."));
1207                 dialog_input=new Gtk::InputDialog();
1208                 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1209                 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1210
1211                 studio_init_cb.task(_("Init auto recovery..."));
1212                 auto_recover=new AutoRecover();
1213
1214                 studio_init_cb.amount_complete(9250,10000);
1215                 studio_init_cb.task(_("Loading Settings..."));
1216                 load_settings();
1217                 studio_init_cb.task(_("Checking auto-recover..."));
1218
1219                 studio_init_cb.amount_complete(9900,10000);
1220
1221                 bool opened_any = false;
1222                 if(auto_recover->recovery_needed())
1223                 {
1224                         splash_screen.hide();
1225                         if (get_ui_interface()->yes_no(_("Auto Recovery"),
1226                                                                                    _("Synfig Studio seems to have crashed\n"
1227                                                                                          "before you could save all your files.\n"
1228                                                                                          "Would you like to re-open those files\n"
1229                                                                                          "and recover your unsaved changes?")) ==
1230                                 synfigapp::UIInterface::RESPONSE_YES)
1231                         {
1232                                 int number_recovered;
1233                                 if(!auto_recover->recover(number_recovered))
1234                                         if (number_recovered)
1235                                                 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1236                                         else
1237                                                 get_ui_interface()->error(_("Unable to recover from previous crash"));
1238                                 else
1239                                         get_ui_interface()->error(
1240                                                 _("Synfig Studio has attempted to recover\n"
1241                                                   "from a previous crash. The files that it has\n"
1242                                                   "recovered are NOT YET SAVED. It would be a good\n"
1243                                                   "idea to review them and save them now."));
1244
1245                                 if (number_recovered)
1246                                         opened_any = true;
1247                         }
1248                         splash_screen.show();
1249                 }
1250
1251                 // Look for any files given on the command line,
1252                 // and load them if found.
1253                 for(;*argc>=1;(*argc)--)
1254                         if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1255                         {
1256                                 studio_init_cb.task(_("Loading files..."));
1257                                 splash_screen.hide();
1258                                 open((*argv)[*argc]);
1259                                 opened_any = true;
1260                                 splash_screen.show();
1261                         }
1262
1263                 // if no file was specified to be opened, create a new document to help new users get started more easily
1264                 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1265                         new_instance();
1266
1267                 studio_init_cb.task(_("Done."));
1268                 studio_init_cb.amount_complete(10000,10000);
1269
1270                 toolbox->present();
1271         }
1272         catch(String x)
1273         {
1274                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1275         }
1276         catch(...)
1277         {
1278                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1279         }
1280 }
1281
1282 StateManager* App::get_state_manager() { return state_manager; }
1283
1284 App::~App()
1285 {
1286         shutdown_in_progress=true;
1287
1288         save_settings();
1289
1290         synfigapp::Main::settings().remove_domain("pref");
1291
1292         selected_instance=0;
1293
1294         // Unload all of the modules
1295         for(;!module_list_.empty();module_list_.pop_back())
1296                 ;
1297
1298         delete state_manager;
1299
1300         delete ipc;
1301
1302         delete auto_recover;
1303
1304         delete about;
1305         
1306         toolbox->hide();
1307
1308 //      studio::App::iteration(false);
1309
1310         delete toolbox;
1311
1312 //      studio::App::iteration(false);
1313
1314 //      studio::App::iteration(false);
1315
1316         delete dialog_setup;
1317
1318         delete dialog_gradient;
1319
1320         delete dialog_color;
1321
1322         delete dialog_input;
1323
1324         delete dock_manager;
1325
1326         instance_list.clear();
1327
1328 //      studio::App::iteration(false);
1329 }
1330
1331 String
1332 App::get_user_app_directory()
1333 {
1334 //! \todo do we need locale_from_utf8() on non-Windows boxes too?  (bug #1837445)
1335 #ifdef WIN32
1336         return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1337 #else
1338         return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1339 #endif
1340 }
1341
1342 synfig::String
1343 App::get_config_file(const synfig::String& file)
1344 {
1345         return Glib::build_filename(get_user_app_directory(),file);
1346 }
1347
1348 #define SCALE_FACTOR    (1280)
1349 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1350 void
1351 App::set_recent_file_window_size(etl::handle<Instance> instance)
1352 {
1353         int screen_w(Gdk::screen_width());
1354         int screen_h(Gdk::screen_height());
1355
1356         const std::string &canvas_window_size = *recent_files_window_size.begin();
1357
1358         if(canvas_window_size.empty())
1359                 return;
1360
1361         synfig::String::size_type current=0;
1362         bool seen_root(false), shown_non_root(false);
1363
1364         while(current != synfig::String::npos)
1365         {
1366                 // find end of first field (canvas) or return
1367                 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1368                 if(separator == synfig::String::npos) break;
1369
1370                 // find the canvas
1371                 synfig::Canvas::Handle canvas;
1372                 try {
1373                         canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current));
1374                 }
1375                 catch(Exception::IDNotFound) {
1376                         // can't find the canvas; skip to the next canvas or return
1377                         separator = canvas_window_size.find_first_of('\t', current);
1378                         if(separator == synfig::String::npos) return;
1379                         current = separator+1;
1380                         continue;
1381                 }
1382
1383                 if (canvas->is_root())
1384                         seen_root = true;
1385                 else
1386                         shown_non_root = true;
1387
1388                 // check that we have the tab character the ends this canvas' data or return
1389                 current = separator+1;
1390                 separator = canvas_window_size.find_first_of('\t', current);
1391                 if(separator == synfig::String::npos) return;
1392
1393                 int x,y,w,h;
1394                 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1395                 {
1396                         current = separator+1;
1397                         continue;
1398                 }
1399
1400                 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1401                 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1402                 x=x*screen_w/SCALE_FACTOR;
1403                 y=y*screen_h/SCALE_FACTOR;
1404                 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1405                         x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1406                 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1407                         y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1408
1409                 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1410                 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1411
1412                 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1413                 canvasview->move(x,y);
1414                 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1415                 canvasview->present();
1416
1417                 current = separator+1;
1418         }
1419
1420         if (shown_non_root && !seen_root)
1421                 instance->find_canvas_view(instance->get_canvas())->hide();
1422 }
1423
1424 void
1425 App::add_recent_file(const etl::handle<Instance> instance)
1426 {
1427         int screen_w(Gdk::screen_width());
1428         int screen_h(Gdk::screen_height());
1429
1430         std::string canvas_window_size;
1431
1432         const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1433         Instance::CanvasViewList::const_iterator iter;
1434
1435         for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1436         {
1437                 if( !((*iter)->is_visible()) )
1438                         continue;
1439
1440                 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1441                 int x_pos, y_pos, x_size, y_size;
1442                 (*iter)->get_position(x_pos,y_pos);
1443                 (*iter)->get_size(x_size,y_size);
1444
1445                 canvas_window_size += strprintf("%s %d %d %d %d\t",
1446                                                                                 canvas->get_relative_id(canvas->get_root()).c_str(),
1447                                                                                 x_pos*SCALE_FACTOR/screen_w,  y_pos*SCALE_FACTOR/screen_h,
1448                                                                                 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1449         }
1450
1451         add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1452 }
1453 #undef SCALE_FACTOR
1454
1455 void
1456 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1457 {
1458         std::string filename(file_name);
1459
1460         assert(!filename.empty());
1461
1462         if(filename.empty())
1463                 return;
1464
1465         // Toss out any "hidden" files
1466         if(basename(filename)[0]=='.')
1467                 return;
1468
1469         // If we aren't an absolute path, turn ourselves into one
1470         if(!is_absolute_path(filename))
1471                 filename=absolute_path(filename);
1472
1473         std::string old_window_size;
1474
1475         list<string>::iterator iter;
1476         list<string>::iterator iter_wsize;
1477         // Check to see if the file is already on the list.
1478         // If it is, then remove it from the list
1479         for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1480                 if(*iter==filename)
1481                 {
1482                         recent_files.erase(iter);
1483                         old_window_size = *iter_wsize;
1484                         recent_files_window_size.erase(iter_wsize);
1485                         break;
1486                 }
1487
1488
1489         // Push the filename to the front of the list
1490         recent_files.push_front(filename);
1491         if(window_size.empty())
1492                 recent_files_window_size.push_front(old_window_size);
1493         else
1494                 recent_files_window_size.push_front(window_size);
1495
1496         // Clean out the files at the end of the list.
1497         while(recent_files.size()>(unsigned)get_max_recent_files())
1498         {
1499                 recent_files.pop_back();
1500                 recent_files_window_size.pop_back();
1501         }
1502
1503         signal_recent_files_changed_();
1504
1505         return;
1506 }
1507
1508 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1509
1510 Time::Format
1511 App::get_time_format()
1512 {
1513         return _App_time_format;
1514 }
1515
1516 void
1517 App::set_time_format(synfig::Time::Format x)
1518 {
1519         _App_time_format=x;
1520 }
1521
1522
1523 void
1524 App::save_settings()
1525 {
1526         char * old_locale;
1527         try
1528         {
1529         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1530         setlocale(LC_NUMERIC, "C");
1531                 {
1532                         std::string filename=get_config_file("accelrc");
1533                         Gtk::AccelMap::save(filename);
1534                 }
1535                 do{
1536                         std::string filename=get_config_file("recentfiles");
1537
1538                         std::ofstream file(filename.c_str());
1539
1540                         if(!file)
1541                         {
1542                                 synfig::warning("Unable to save %s",filename.c_str());
1543                                 break;
1544                         }
1545
1546                         list<string>::reverse_iterator iter;
1547
1548                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1549                                 file<<*iter<<endl;
1550                 }while(0);
1551                 do{
1552                         std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1553
1554                         std::ofstream file(filename.c_str());
1555
1556                         if(!file)
1557                         {
1558                                 synfig::warning("Unable to save %s",filename.c_str());
1559                                 break;
1560                         }
1561
1562                         list<string>::reverse_iterator iter;
1563
1564                         for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1565                                 file<<*iter<<endl;
1566
1567                 }while(0);
1568                 std::string filename=get_config_file("settings");
1569                 synfigapp::Main::settings().save_to_file(filename);
1570         setlocale(LC_NUMERIC,old_locale);
1571         }
1572         catch(...)
1573         {
1574                 synfig::warning("Caught exception when attempting to save settings.");
1575         }
1576 }
1577
1578 void
1579 App::load_settings()
1580 {
1581         char  * old_locale;
1582         try
1583         {
1584         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1585         setlocale(LC_NUMERIC, "C");
1586                 {
1587                         std::string filename=get_config_file("accelrc");
1588                         Gtk::AccelMap::load(filename);
1589                 }
1590                 {
1591                         bool window_size_broken = false;
1592
1593                         std::string filename=get_config_file("recentfiles");
1594                         std::string filename_window_size=filename+std::string("_window_size");
1595
1596                         std::ifstream file(filename.c_str());
1597                         std::ifstream file_window_size(filename_window_size.c_str());
1598
1599                         if(!file_window_size)
1600                                 window_size_broken = true;
1601
1602                         while(file)
1603                         {
1604                                 std::string recent_file;
1605                                 std::string recent_file_window_size;
1606                                 getline(file,recent_file);
1607                                 if(!window_size_broken)
1608                                         getline(file_window_size,recent_file_window_size);
1609                                 if(!recent_file.empty())
1610                                 {
1611                                         if(!window_size_broken && !file_window_size)
1612                                                 window_size_broken = true;
1613                                         if(!window_size_broken)
1614                                                 add_recent_file(recent_file,recent_file_window_size);
1615                                         else
1616                                                 add_recent_file(recent_file);
1617                                 }
1618                         }
1619                         if(!window_size_broken && file_window_size)
1620                                 window_size_broken = true;
1621
1622                         if(window_size_broken)
1623                         {
1624                                 recent_files_window_size.clear();
1625                                 recent_files_window_size.resize(recent_files.size());
1626                         }
1627                 }
1628                 std::string filename=get_config_file("settings");
1629                 if(!synfigapp::Main::settings().load_from_file(filename))
1630                 {
1631                         //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1632                         //if(!synfigapp::Main::settings().load_from_file(filename))
1633                         {
1634                                 gamma.set_gamma(1.0/2.2);
1635                                 reset_initial_window_configuration();
1636                         }
1637                 }
1638         setlocale(LC_NUMERIC,old_locale);
1639         }
1640         catch(...)
1641         {
1642                 synfig::warning("Caught exception when attempting to load settings.");
1643         }
1644 }
1645
1646 void
1647 App::reset_initial_window_configuration()
1648 {
1649         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1650         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1651         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1652         synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1653         synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1654         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1655         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1656         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1657         synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1658         synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1659         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1660         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1661 #ifdef SINGLE_THREADED
1662         synfigapp::Main::settings().set_value("pref.single_threaded","0");
1663 #endif
1664         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1665         synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1666 }
1667
1668 bool
1669 App::shutdown_request(GdkEventAny*)
1670 {
1671         quit();
1672         return true;
1673         //return !shutdown_in_progress;
1674 }
1675
1676 void
1677 App::quit()
1678 {
1679         if(shutdown_in_progress)return;
1680
1681
1682         get_ui_interface()->task(_("Quit Request"));
1683         if(Busy::count)
1684         {
1685                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1686                 return;
1687         }
1688
1689         std::list<etl::handle<Instance> >::iterator iter;
1690         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1691         {
1692                 if(!(*iter)->safe_close())
1693                         return;
1694
1695 /*
1696                 if((*iter)->synfigapp::Instance::get_action_count())
1697                 {
1698                         handle<synfigapp::UIInterface> uim;
1699                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1700                         assert(uim);
1701                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1702                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1703                         {
1704                                 case synfigapp::UIInterface::RESPONSE_NO:
1705                                         break;
1706                                 case synfigapp::UIInterface::RESPONSE_YES:
1707                                         (*iter)->save();
1708                                         break;
1709                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1710                                         return;
1711                                 default:
1712                                         assert(0);
1713                                         return;
1714                         }
1715                 }
1716
1717
1718                 if((*iter)->synfigapp::Instance::is_modified())
1719                 {
1720                         handle<synfigapp::UIInterface> uim;
1721                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1722                         assert(uim);
1723                         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() );
1724                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1725                         {
1726                                 case synfigapp::UIInterface::RESPONSE_NO:
1727                                         break;
1728                                 case synfigapp::UIInterface::RESPONSE_YES:
1729                                         (*iter)->dialog_cvs_commit();
1730                                         break;
1731                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1732                                         return;
1733                                 default:
1734                                         assert(0);
1735                                         return;
1736                         }
1737                 }
1738 */
1739
1740                 // This next line causes things to crash for some reason
1741                 //(*iter)->close();
1742         }
1743
1744         shutdown_in_progress=true;
1745
1746         instance_list.clear();
1747
1748         while(studio::App::events_pending())studio::App::iteration(false);
1749
1750         Gtk::Main::quit();
1751         auto_recover->normal_shutdown();
1752
1753         get_ui_interface()->task(_("Quit Request sent"));
1754 }
1755
1756 void
1757 App::show_setup()
1758 {
1759         dialog_setup->refresh();
1760         dialog_setup->show();
1761 }
1762
1763 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1764 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1765
1766 //#ifdef WIN32
1767 //#define USE_WIN32_FILE_DIALOGS 1
1768 //#endif
1769
1770 #ifdef USE_WIN32_FILE_DIALOGS
1771 static OPENFILENAME ofn={};
1772 #endif
1773
1774 #ifdef WIN32
1775 #include <gdk/gdkwin32.h>
1776 #endif
1777
1778 bool
1779 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1780 {
1781         info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1782
1783 #ifdef USE_WIN32_FILE_DIALOGS
1784         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1785
1786         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1787         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1788         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1789
1790         ofn.lStructSize=sizeof(OPENFILENAME);
1791         ofn.hwndOwner = hWnd;
1792         ofn.hInstance = hInstance;
1793         ofn.lpstrFilter = szFilter;
1794 //      ofn.lpstrCustomFilter=NULL;
1795 //      ofn.nMaxCustFilter=0;
1796 //      ofn.nFilterIndex=0;
1797 //      ofn.lpstrFile=NULL;
1798         ofn.nMaxFile=MAX_PATH;
1799 //      ofn.lpstrFileTitle=NULL;
1800 //      ofn.lpstrInitialDir=NULL;
1801 //      ofn.lpstrTitle=NULL;
1802         ofn.Flags=OFN_HIDEREADONLY;
1803 //      ofn.nFileOffset=0;
1804 //      ofn.nFileExtension=0;
1805         ofn.lpstrDefExt=TEXT("sif");
1806 //      ofn.lCustData = 0l;
1807         ofn.lpfnHook=NULL;
1808 //      ofn.lpTemplateName=NULL;
1809
1810         CHAR szFilename[MAX_PATH];
1811         CHAR szTitle[500];
1812         strcpy(szFilename,filename.c_str());
1813         strcpy(szTitle,title.c_str());
1814
1815         ofn.lpstrFile=szFilename;
1816         ofn.lpstrFileTitle=szTitle;
1817
1818         if(GetOpenFileName(&ofn))
1819         {
1820                 filename=szFilename;
1821                 return true;
1822         }
1823         return false;
1824
1825 #else
1826         synfig::String prev_path;
1827
1828         if(!_preferences.get_value(preference, prev_path))
1829                 prev_path = ".";
1830
1831         prev_path = absolute_path(prev_path);
1832
1833     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1834
1835     dialog->set_current_folder(prev_path);
1836     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1837     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1838
1839     if (filename.empty())
1840                 dialog->set_filename(prev_path);
1841         else if (is_absolute_path(filename))
1842                 dialog->set_filename(filename);
1843         else
1844                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1845
1846     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1847         filename = dialog->get_filename();
1848                 info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1849                 _preferences.set_value(preference, dirname(filename));
1850         delete dialog;
1851         return true;
1852     }
1853
1854     delete dialog;
1855     return false;
1856
1857     /*
1858
1859         GtkWidget *ok;
1860         GtkWidget *cancel;
1861         int val=0;
1862
1863         GtkWidget *fileselection;
1864         fileselection = gtk_file_selection_new(title.c_str());
1865
1866
1867         if(basename(filename)==filename)
1868         {
1869                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1870         }
1871         else
1872                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1873
1874         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1875
1876         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1877         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1878
1879         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1880         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1881
1882         gtk_widget_show(fileselection);
1883
1884         while(!val)
1885                 iteration();
1886
1887
1888         if(val==1)
1889         {
1890                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1891                 _preferences.set_value(preference,dirname(filename));
1892         }
1893         else
1894         {
1895                 gtk_widget_destroy(fileselection);
1896                 return false;
1897         }
1898         gtk_widget_destroy(fileselection);
1899         return true;
1900     */
1901 #endif
1902 }
1903
1904 bool
1905 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1906 {
1907         info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1908
1909 #if USE_WIN32_FILE_DIALOGS
1910         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1911
1912         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1913         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1914         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1915
1916         ofn.lStructSize=sizeof(OPENFILENAME);
1917         ofn.hwndOwner = hWnd;
1918         ofn.hInstance = hInstance;
1919         ofn.lpstrFilter = szFilter;
1920 //      ofn.lpstrCustomFilter=NULL;
1921 //      ofn.nMaxCustFilter=0;
1922 //      ofn.nFilterIndex=0;
1923 //      ofn.lpstrFile=NULL;
1924         ofn.nMaxFile=MAX_PATH;
1925 //      ofn.lpstrFileTitle=NULL;
1926 //      ofn.lpstrInitialDir=NULL;
1927 //      ofn.lpstrTitle=NULL;
1928         ofn.Flags=OFN_OVERWRITEPROMPT;
1929 //      ofn.nFileOffset=0;
1930 //      ofn.nFileExtension=0;
1931         ofn.lpstrDefExt=TEXT("sif");
1932 //      ofn.lCustData = 0l;
1933         ofn.lpfnHook=NULL;
1934 //      ofn.lpTemplateName=NULL;
1935
1936         CHAR szFilename[MAX_PATH];
1937         CHAR szTitle[500];
1938         strcpy(szFilename,filename.c_str());
1939         strcpy(szTitle,title.c_str());
1940
1941         ofn.lpstrFile=szFilename;
1942         ofn.lpstrFileTitle=szTitle;
1943
1944         if(GetSaveFileName(&ofn))
1945         {
1946                 filename=szFilename;
1947                 _preferences.set_value(preference,dirname(filename));
1948                 return true;
1949         }
1950         return false;
1951 #else
1952         synfig::String prev_path;
1953
1954         if(!_preferences.get_value(preference, prev_path))
1955                 prev_path=".";
1956
1957         prev_path = absolute_path(prev_path);
1958
1959     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
1960
1961     dialog->set_current_folder(prev_path);
1962     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1963     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1964
1965         Widget_Enum *file_type_enum = 0;
1966         if (preference == ANIMATION_DIR_PREFERENCE)
1967         {
1968                 file_type_enum = manage(new Widget_Enum());
1969                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
1970                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", strprintf("0.61.08 (%s)", _("current")))
1971                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
1972                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
1973                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
1974
1975                 Gtk::HBox *hbox = manage(new Gtk::HBox);
1976                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
1977                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
1978                 hbox->show_all();
1979
1980                 dialog->set_extra_widget(*hbox);
1981         }
1982
1983     if (filename.empty())
1984                 dialog->set_filename(prev_path);
1985     else
1986         {
1987                 std::string full_path;
1988                 if (is_absolute_path(filename))
1989                         full_path = filename;
1990                 else
1991                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
1992
1993                 // select the file if it exists
1994                 dialog->set_filename(full_path);
1995
1996                 // if the file doesn't exist, put its name into the filename box
1997                 struct stat s;
1998                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
1999                         dialog->set_current_name(basename(filename));
2000         }
2001
2002     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2003                 if (preference == ANIMATION_DIR_PREFERENCE)
2004                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2005         filename = dialog->get_filename();
2006                 info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2007                 _preferences.set_value(preference, dirname(filename));
2008         delete dialog;
2009         return true;
2010     }
2011
2012     delete dialog;
2013     return false;
2014 #endif
2015 }
2016
2017 void
2018 App::dialog_error_blocking(const std::string &title, const std::string &message)
2019 {
2020         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2021         dialog.set_title(title);
2022         dialog.show();
2023         dialog.run();
2024 }
2025
2026 void
2027 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2028 {
2029         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2030         dialog.set_title(title);
2031         dialog.show();
2032         dialog.run();
2033 }
2034
2035 bool
2036 App::dialog_yes_no(const std::string &title, const std::string &message)
2037 {
2038         Gtk::Dialog dialog(
2039                 title,          // Title
2040                 true,           // Modal
2041                 true            // use_separator
2042         );
2043         Gtk::Label label(message);
2044         label.show();
2045
2046         dialog.get_vbox()->pack_start(label);
2047         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2048         dialog.add_button(Gtk::StockID("gtk-no"),0);
2049         dialog.show();
2050         return dialog.run();
2051 }
2052
2053 int
2054 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2055 {
2056         Gtk::Dialog dialog(
2057                 title,          // Title
2058                 true,           // Modal
2059                 true            // use_separator
2060         );
2061         Gtk::Label label(message);
2062         label.show();
2063
2064         dialog.get_vbox()->pack_start(label);
2065         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2066         dialog.add_button(Gtk::StockID("gtk-no"),0);
2067         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2068         dialog.show();
2069         return dialog.run();
2070 }
2071
2072 void
2073 App::dialog_not_implemented()
2074 {
2075         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2076         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2077         dialog.run();
2078 }
2079
2080 static bool
2081 try_open_url(const std::string &url)
2082 {
2083 #ifdef WIN32
2084         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2085 #else // !WIN32
2086         std::vector<std::string> command_line;
2087         std::vector<std::string> browsers;
2088         browsers.reserve(23);
2089
2090         // Browser wrapper scripts
2091 #ifdef USE_OPEN_FOR_URLS
2092         browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
2093 #endif
2094         browsers.push_back("xdg-open");          // XDG wrapper
2095         browsers.push_back("sensible-browser");  // Debian wrapper
2096         browsers.push_back("gnome-open");        // GNOME wrapper
2097         browsers.push_back("kfmclient");         // KDE wrapper
2098         browsers.push_back("exo-open");          // XFCE wrapper
2099
2100         // Alternatives system
2101         browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2102         browsers.push_back("x-www-browser");     // Debian GUI alternative
2103
2104         // Individual browsers
2105         browsers.push_back("firefox");
2106         browsers.push_back("epiphany-browser");
2107         browsers.push_back("epiphany");
2108         browsers.push_back("konqueror");
2109         browsers.push_back("iceweasel");
2110         browsers.push_back("mozilla");
2111         browsers.push_back("netscape");
2112         browsers.push_back("icecat");
2113         browsers.push_back("galeon");
2114         browsers.push_back("midori");
2115         browsers.push_back("safari");
2116         browsers.push_back("opera");
2117         browsers.push_back("amaya");
2118         browsers.push_back("netsurf");
2119         browsers.push_back("dillo");
2120
2121         // Try the user-specified browser first
2122         command_line.push_back(App::browser_command);
2123         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2124         command_line.push_back(url);
2125
2126         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2127         catch( Glib::SpawnError& exception ){
2128
2129                 while ( !browsers.empty() )
2130                 {
2131                         // Skip the browser if we already tried it
2132                         if( browsers[0] == App::browser_command )
2133                                 continue;
2134
2135                         // Construct the command line
2136                         command_line.clear();
2137                         command_line.push_back(browsers[0]);
2138                         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2139                         command_line.push_back(url);
2140
2141                         // Remove the browser from the list
2142                         browsers.erase(browsers.begin());
2143
2144                         // Try to spawn the browser
2145                         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2146                         // Failed, move on to the next one
2147                         catch(Glib::SpawnError& exception){ continue; }
2148                         return true; // No exception means we succeeded!                        
2149                 }
2150         }
2151
2152         return false;
2153 #endif // !WIN32
2154 }
2155
2156 void
2157 App::dialog_help()
2158 {
2159         if (!try_open_url("http://synfig.org/Documentation"))
2160         {
2161                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2162                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2163                 dialog.set_title(_("Help"));
2164                 dialog.run();
2165         }
2166 }
2167
2168 void
2169 App::open_url(const std::string &url)
2170 {
2171         if(!try_open_url(url))
2172         {
2173                 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2174                 dialog.set_secondary_text(url);
2175                 dialog.set_title(_("No browser found"));
2176                 dialog.run();
2177         }
2178 }
2179
2180 bool
2181 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2182 {
2183         Gtk::Dialog dialog(
2184                 title,          // Title
2185                 true,           // Modal
2186                 true            // use_separator
2187         );
2188         Gtk::Label label(message);
2189         label.show();
2190         dialog.get_vbox()->pack_start(label);
2191
2192         Gtk::Entry entry;
2193         entry.set_text(text);
2194         entry.show();
2195         entry.set_activates_default(true);
2196         dialog.get_vbox()->pack_start(entry);
2197
2198         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2199         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2200         dialog.set_default_response(Gtk::RESPONSE_OK);
2201         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2202         dialog.show();
2203
2204         if(dialog.run()!=Gtk::RESPONSE_OK)
2205                 return false;
2206
2207         text=entry.get_text();
2208
2209         return true;
2210 }
2211
2212
2213
2214
2215 bool
2216 App::open(std::string filename)
2217 {
2218         return open_as(filename,filename);
2219 }
2220
2221 // this is called from autorecover.cpp:
2222 //   App::open_as(get_shadow_file_name(filename),filename)
2223 // other than that, 'filename' and 'as' are the same
2224 bool
2225 App::open_as(std::string filename,std::string as)
2226 {
2227 #ifdef WIN32
2228     char long_name[1024];
2229     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2230         // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2231         // don't use it if that's the case
2232         if (long_name[0] != '\0')
2233                 as=long_name;
2234 #endif
2235
2236         try
2237         {
2238                 OneMoment one_moment;
2239
2240                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2241                 if(canvas && get_instance(canvas))
2242                 {
2243                         get_instance(canvas)->find_canvas_view(canvas)->present();
2244                         info("%s is already open", filename.c_str());
2245                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2246                 }
2247                 else
2248                 {
2249                         if(!canvas)
2250                                 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2251
2252                         if (as.find(DEFAULT_FILENAME_PREFIX) != 0)
2253                                 add_recent_file(as);
2254
2255                         handle<Instance> instance(Instance::create(canvas));
2256
2257                         if(!instance)
2258                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2259
2260                         set_recent_file_window_size(instance);
2261
2262                         one_moment.hide();
2263
2264                         if(instance->is_updated() && App::dialog_yes_no(_("CVS Update"), _("There appears to be a newer version of this file available on the CVS repository.\nWould you like to update now? (It would probably be a good idea)")))
2265                                 instance->dialog_cvs_update();
2266                 }
2267         }
2268         catch(String x)
2269         {
2270                 dialog_error_blocking(_("Error"), x);
2271                 return false;
2272         }
2273         catch(...)
2274         {
2275                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2276                 return false;
2277         }
2278
2279         return true;
2280 }
2281
2282
2283 void
2284 App::new_instance()
2285 {
2286         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2287
2288         String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2289         canvas->set_name(file_name);
2290         file_name += ".sifz";
2291
2292         canvas->rend_desc().set_frame_rate(24.0);
2293         canvas->rend_desc().set_time_start(0.0);
2294         canvas->rend_desc().set_time_end(5.0);
2295         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2296         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2297         canvas->rend_desc().set_tl(Vector(-4,2.25));
2298         canvas->rend_desc().set_br(Vector(4,-2.25));
2299         canvas->rend_desc().set_w(480);
2300         canvas->rend_desc().set_h(270);
2301         canvas->rend_desc().set_antialias(1);
2302         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2303         canvas->set_file_name(file_name);
2304
2305         handle<Instance> instance = Instance::create(canvas);
2306
2307         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2308                 instance->find_canvas_view(canvas)->canvas_properties.present();
2309 }
2310
2311 void
2312 App::dialog_open(string filename)
2313 {
2314         if (filename.empty())
2315                 filename="*.sif";
2316
2317         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2318         {
2319                 // If the filename still has wildcards, then we should
2320                 // continue looking for the file we want
2321                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2322                         continue;
2323
2324                 if(open(filename))
2325                         break;
2326
2327                 get_ui_interface()->error(_("Unable to open file"));
2328         }
2329 }
2330
2331 void
2332 App::set_selected_instance(etl::loose_handle<Instance> instance)
2333 {
2334 /*      if(get_selected_instance()==instance)
2335         {
2336                 selected_instance=instance;
2337                 signal_instance_selected()(instance);
2338                 return;
2339         }
2340         else
2341         {
2342 */
2343                 selected_instance=instance;
2344                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2345                 {
2346                         if(instance)
2347                         {
2348                                 instance->focus(instance->get_canvas());
2349                         }
2350                         else
2351                                 set_selected_canvas_view(0);
2352                 }
2353                 signal_instance_selected()(instance);
2354 }
2355
2356 void
2357 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2358 {
2359         selected_canvas_view=canvas_view;
2360         signal_canvas_view_focus()(selected_canvas_view);
2361         if(canvas_view)
2362         {
2363                 selected_instance=canvas_view->get_instance();
2364                 signal_instance_selected()(canvas_view->get_instance());
2365         }
2366 /*
2367         if(get_selected_canvas_view()==canvas_view)
2368         {
2369                 signal_canvas_view_focus()(selected_canvas_view);
2370                 signal_instance_selected()(canvas_view->get_instance());
2371                 return;
2372         }
2373         selected_canvas_view=canvas_view;
2374         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2375                 set_selected_instance(canvas_view->get_instance());
2376         signal_canvas_view_focus()(selected_canvas_view);
2377 */
2378 }
2379
2380 etl::loose_handle<Instance>
2381 App::get_instance(etl::handle<synfig::Canvas> canvas)
2382 {
2383         if(!canvas) return 0;
2384         canvas=canvas->get_root();
2385
2386         std::list<etl::handle<Instance> >::iterator iter;
2387         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2388         {
2389                 if((*iter)->get_canvas()==canvas)
2390                         return *iter;
2391         }
2392         return 0;
2393 }
2394
2395 void
2396 App::dialog_about()
2397 {
2398         if(about)
2399                 about->show();
2400 }
2401
2402 void
2403 studio::App::undo()
2404 {
2405         if(selected_instance)
2406                 selected_instance->undo();
2407 }
2408
2409 void
2410 studio::App::redo()
2411 {
2412         if(selected_instance)
2413                 selected_instance->redo();
2414 }
2415
2416 synfig::String
2417 studio::App::get_base_path()
2418 {
2419         return app_base_path_;
2420 }