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