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