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