Add an option to the setup dialog "Scale New Imported Images to Fit Canvas". Previou...
[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                         canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current));
1454                 }
1455                 catch(Exception::IDNotFound) {
1456                         // can't find the canvas; skip to the next canvas or return
1457                         separator = canvas_window_size.find_first_of('\t', current);
1458                         if(separator == synfig::String::npos) return;
1459                         current = separator+1;
1460                         continue;
1461                 }
1462
1463                 if (canvas->is_root())
1464                         seen_root = true;
1465                 else
1466                         shown_non_root = true;
1467
1468                 // check that we have the tab character the ends this canvas' data or return
1469                 current = separator+1;
1470                 separator = canvas_window_size.find_first_of('\t', current);
1471                 if(separator == synfig::String::npos) return;
1472
1473                 int x,y,w,h;
1474                 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1475                 {
1476                         current = separator+1;
1477                         continue;
1478                 }
1479
1480                 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1481                 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1482                 x=x*screen_w/SCALE_FACTOR;
1483                 y=y*screen_h/SCALE_FACTOR;
1484                 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1485                         x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1486                 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1487                         y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1488
1489                 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1490                 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1491
1492                 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1493                 canvasview->move(x,y);
1494                 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1495                 canvasview->present();
1496
1497                 current = separator+1;
1498         }
1499
1500         if (shown_non_root && !seen_root)
1501                 instance->find_canvas_view(instance->get_canvas())->hide();
1502 }
1503
1504 void
1505 App::add_recent_file(const etl::handle<Instance> instance)
1506 {
1507         int screen_w(Gdk::screen_width());
1508         int screen_h(Gdk::screen_height());
1509
1510         std::string canvas_window_size;
1511
1512         const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1513         Instance::CanvasViewList::const_iterator iter;
1514
1515         for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1516         {
1517                 if( !((*iter)->is_visible()) )
1518                         continue;
1519
1520                 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1521                 int x_pos, y_pos, x_size, y_size;
1522                 (*iter)->get_position(x_pos,y_pos);
1523                 (*iter)->get_size(x_size,y_size);
1524
1525                 canvas_window_size += strprintf("%s %d %d %d %d\t",
1526                                                                                 canvas->get_relative_id(canvas->get_root()).c_str(),
1527                                                                                 x_pos*SCALE_FACTOR/screen_w,  y_pos*SCALE_FACTOR/screen_h,
1528                                                                                 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1529         }
1530
1531         add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1532 }
1533 #undef SCALE_FACTOR
1534
1535 void
1536 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1537 {
1538         std::string filename(file_name);
1539
1540         assert(!filename.empty());
1541
1542         if(filename.empty())
1543                 return;
1544
1545         // Toss out any "hidden" files
1546         if(basename(filename)[0]=='.')
1547                 return;
1548
1549         // If we aren't an absolute path, turn ourselves into one
1550         if(!is_absolute_path(filename))
1551                 filename=absolute_path(filename);
1552
1553         std::string old_window_size;
1554
1555         list<string>::iterator iter;
1556         list<string>::iterator iter_wsize;
1557         // Check to see if the file is already on the list.
1558         // If it is, then remove it from the list
1559         for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1560                 if(*iter==filename)
1561                 {
1562                         recent_files.erase(iter);
1563                         old_window_size = *iter_wsize;
1564                         recent_files_window_size.erase(iter_wsize);
1565                         break;
1566                 }
1567
1568
1569         // Push the filename to the front of the list
1570         recent_files.push_front(filename);
1571         if(window_size.empty())
1572                 recent_files_window_size.push_front(old_window_size);
1573         else
1574                 recent_files_window_size.push_front(window_size);
1575
1576         // Clean out the files at the end of the list.
1577         while(recent_files.size()>(unsigned)get_max_recent_files())
1578         {
1579                 recent_files.pop_back();
1580                 recent_files_window_size.pop_back();
1581         }
1582
1583         signal_recent_files_changed_();
1584
1585         return;
1586 }
1587
1588 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1589
1590 Time::Format
1591 App::get_time_format()
1592 {
1593         return _App_time_format;
1594 }
1595
1596 void
1597 App::set_time_format(synfig::Time::Format x)
1598 {
1599         _App_time_format=x;
1600 }
1601
1602
1603 void
1604 App::save_settings()
1605 {
1606         char * old_locale;
1607         try
1608         {
1609         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1610         setlocale(LC_NUMERIC, "C");
1611                 {
1612                         std::string filename=get_config_file("accelrc");
1613                         Gtk::AccelMap::save(filename);
1614                 }
1615                 do{
1616                         std::string filename=get_config_file("recentfiles");
1617
1618                         std::ofstream file(filename.c_str());
1619
1620                         if(!file)
1621                         {
1622                                 synfig::warning("Unable to save %s",filename.c_str());
1623                                 break;
1624                         }
1625
1626                         list<string>::reverse_iterator iter;
1627
1628                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1629                                 file<<*iter<<endl;
1630                 }while(0);
1631                 do{
1632                         std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1633
1634                         std::ofstream file(filename.c_str());
1635
1636                         if(!file)
1637                         {
1638                                 synfig::warning("Unable to save %s",filename.c_str());
1639                                 break;
1640                         }
1641
1642                         list<string>::reverse_iterator iter;
1643
1644                         for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1645                                 file<<*iter<<endl;
1646
1647                 }while(0);
1648                 std::string filename=get_config_file("settings");
1649                 synfigapp::Main::settings().save_to_file(filename);
1650         setlocale(LC_NUMERIC,old_locale);
1651         }
1652         catch(...)
1653         {
1654                 synfig::warning("Caught exception when attempting to save settings.");
1655         }
1656 }
1657
1658 void
1659 App::load_settings()
1660 {
1661         char  * old_locale;
1662         try
1663         {
1664         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1665         setlocale(LC_NUMERIC, "C");
1666                 {
1667                         std::string filename=get_config_file("accelrc");
1668                         Gtk::AccelMap::load(filename);
1669                 }
1670                 {
1671                         bool window_size_broken = false;
1672
1673                         std::string filename=get_config_file("recentfiles");
1674                         std::string filename_window_size=filename+std::string("_window_size");
1675
1676                         std::ifstream file(filename.c_str());
1677                         std::ifstream file_window_size(filename_window_size.c_str());
1678
1679                         if(!file_window_size)
1680                                 window_size_broken = true;
1681
1682                         while(file)
1683                         {
1684                                 std::string recent_file;
1685                                 std::string recent_file_window_size;
1686                                 getline(file,recent_file);
1687                                 if(!window_size_broken)
1688                                         getline(file_window_size,recent_file_window_size);
1689                                 if(!recent_file.empty())
1690                                 {
1691                                         if(!window_size_broken && !file_window_size)
1692                                                 window_size_broken = true;
1693                                         if (std::ifstream(recent_file.c_str()))
1694                                         {
1695                                                 if(!window_size_broken)
1696                                                         add_recent_file(recent_file,recent_file_window_size);
1697                                                 else
1698                                                         add_recent_file(recent_file);
1699                                         }
1700                                 }
1701                         }
1702                         if(!window_size_broken && file_window_size)
1703                                 window_size_broken = true;
1704
1705                         if(window_size_broken)
1706                         {
1707                                 recent_files_window_size.clear();
1708                                 recent_files_window_size.resize(recent_files.size());
1709                         }
1710                 }
1711                 std::string filename=get_config_file("settings");
1712                 if(!synfigapp::Main::settings().load_from_file(filename))
1713                 {
1714                         //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1715                         //if(!synfigapp::Main::settings().load_from_file(filename))
1716                         {
1717                                 gamma.set_gamma(1.0/2.2);
1718                                 reset_initial_window_configuration();
1719                         }
1720                 }
1721         setlocale(LC_NUMERIC,old_locale);
1722         }
1723         catch(...)
1724         {
1725                 synfig::warning("Caught exception when attempting to load settings.");
1726         }
1727 }
1728
1729 void
1730 App::reset_initial_window_configuration()
1731 {
1732         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1733         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1734         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1735         synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1736         synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1737         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1738         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1739         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1740         synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1741         synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1742         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1743         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1744 #ifdef SINGLE_THREADED
1745         synfigapp::Main::settings().set_value("pref.single_threaded","0");
1746 #endif
1747         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1748         synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
1749         synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
1750         synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
1751         synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
1752         synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
1753         synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1754 }
1755
1756 bool
1757 App::shutdown_request(GdkEventAny*)
1758 {
1759         quit();
1760         return true;
1761         //return !shutdown_in_progress;
1762 }
1763
1764 void
1765 App::quit()
1766 {
1767         if(shutdown_in_progress)return;
1768
1769
1770         get_ui_interface()->task(_("Quit Request"));
1771         if(Busy::count)
1772         {
1773                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1774                 return;
1775         }
1776
1777         std::list<etl::handle<Instance> >::iterator iter;
1778         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1779         {
1780                 if(!(*iter)->safe_close())
1781                         return;
1782
1783 /*
1784                 if((*iter)->synfigapp::Instance::get_action_count())
1785                 {
1786                         handle<synfigapp::UIInterface> uim;
1787                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1788                         assert(uim);
1789                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1790                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1791                         {
1792                                 case synfigapp::UIInterface::RESPONSE_NO:
1793                                         break;
1794                                 case synfigapp::UIInterface::RESPONSE_YES:
1795                                         (*iter)->save();
1796                                         break;
1797                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1798                                         return;
1799                                 default:
1800                                         assert(0);
1801                                         return;
1802                         }
1803                 }
1804
1805
1806                 if((*iter)->synfigapp::Instance::is_modified())
1807                 {
1808                         handle<synfigapp::UIInterface> uim;
1809                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1810                         assert(uim);
1811                         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() );
1812                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1813                         {
1814                                 case synfigapp::UIInterface::RESPONSE_NO:
1815                                         break;
1816                                 case synfigapp::UIInterface::RESPONSE_YES:
1817                                         (*iter)->dialog_cvs_commit();
1818                                         break;
1819                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1820                                         return;
1821                                 default:
1822                                         assert(0);
1823                                         return;
1824                         }
1825                 }
1826 */
1827
1828                 // This next line causes things to crash for some reason
1829                 //(*iter)->close();
1830         }
1831
1832         shutdown_in_progress=true;
1833
1834         instance_list.clear();
1835
1836         while(studio::App::events_pending())studio::App::iteration(false);
1837
1838         Gtk::Main::quit();
1839         auto_recover->normal_shutdown();
1840
1841         get_ui_interface()->task(_("Quit Request sent"));
1842 }
1843
1844 void
1845 App::show_setup()
1846 {
1847         dialog_setup->refresh();
1848         dialog_setup->show();
1849 }
1850
1851 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1852 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1853
1854 //#ifdef WIN32
1855 //#define USE_WIN32_FILE_DIALOGS 1
1856 //#endif
1857
1858 #ifdef USE_WIN32_FILE_DIALOGS
1859 static OPENFILENAME ofn={};
1860 #endif
1861
1862 #ifdef WIN32
1863 #include <gdk/gdkwin32.h>
1864 #endif
1865
1866 bool
1867 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1868 {
1869         // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1870
1871 #ifdef USE_WIN32_FILE_DIALOGS
1872         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1873
1874         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1875         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1876         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1877
1878         ofn.lStructSize=sizeof(OPENFILENAME);
1879         ofn.hwndOwner = hWnd;
1880         ofn.hInstance = hInstance;
1881         ofn.lpstrFilter = szFilter;
1882 //      ofn.lpstrCustomFilter=NULL;
1883 //      ofn.nMaxCustFilter=0;
1884 //      ofn.nFilterIndex=0;
1885 //      ofn.lpstrFile=NULL;
1886         ofn.nMaxFile=MAX_PATH;
1887 //      ofn.lpstrFileTitle=NULL;
1888 //      ofn.lpstrInitialDir=NULL;
1889 //      ofn.lpstrTitle=NULL;
1890         ofn.Flags=OFN_HIDEREADONLY;
1891 //      ofn.nFileOffset=0;
1892 //      ofn.nFileExtension=0;
1893         ofn.lpstrDefExt=TEXT("sif");
1894 //      ofn.lCustData = 0l;
1895         ofn.lpfnHook=NULL;
1896 //      ofn.lpTemplateName=NULL;
1897
1898         CHAR szFilename[MAX_PATH];
1899         CHAR szTitle[500];
1900         strcpy(szFilename,filename.c_str());
1901         strcpy(szTitle,title.c_str());
1902
1903         ofn.lpstrFile=szFilename;
1904         ofn.lpstrFileTitle=szTitle;
1905
1906         if(GetOpenFileName(&ofn))
1907         {
1908                 filename=szFilename;
1909                 return true;
1910         }
1911         return false;
1912
1913 #else
1914         synfig::String prev_path;
1915
1916         if(!_preferences.get_value(preference, prev_path))
1917                 prev_path = ".";
1918
1919         prev_path = absolute_path(prev_path);
1920
1921     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1922
1923     dialog->set_current_folder(prev_path);
1924     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1925     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1926
1927     if (filename.empty())
1928                 dialog->set_filename(prev_path);
1929         else if (is_absolute_path(filename))
1930                 dialog->set_filename(filename);
1931         else
1932                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1933
1934     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1935         filename = dialog->get_filename();
1936                 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1937                 _preferences.set_value(preference, dirname(filename));
1938         delete dialog;
1939         return true;
1940     }
1941
1942     delete dialog;
1943     return false;
1944
1945     /*
1946
1947         GtkWidget *ok;
1948         GtkWidget *cancel;
1949         int val=0;
1950
1951         GtkWidget *fileselection;
1952         fileselection = gtk_file_selection_new(title.c_str());
1953
1954
1955         if(basename(filename)==filename)
1956         {
1957                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1958         }
1959         else
1960                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1961
1962         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1963
1964         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1965         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1966
1967         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1968         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1969
1970         gtk_widget_show(fileselection);
1971
1972         while(!val)
1973                 iteration();
1974
1975
1976         if(val==1)
1977         {
1978                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1979                 _preferences.set_value(preference,dirname(filename));
1980         }
1981         else
1982         {
1983                 gtk_widget_destroy(fileselection);
1984                 return false;
1985         }
1986         gtk_widget_destroy(fileselection);
1987         return true;
1988     */
1989 #endif
1990 }
1991
1992 bool
1993 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1994 {
1995         // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1996
1997 #if USE_WIN32_FILE_DIALOGS
1998         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1999
2000         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2001         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2002         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2003
2004         ofn.lStructSize=sizeof(OPENFILENAME);
2005         ofn.hwndOwner = hWnd;
2006         ofn.hInstance = hInstance;
2007         ofn.lpstrFilter = szFilter;
2008 //      ofn.lpstrCustomFilter=NULL;
2009 //      ofn.nMaxCustFilter=0;
2010 //      ofn.nFilterIndex=0;
2011 //      ofn.lpstrFile=NULL;
2012         ofn.nMaxFile=MAX_PATH;
2013 //      ofn.lpstrFileTitle=NULL;
2014 //      ofn.lpstrInitialDir=NULL;
2015 //      ofn.lpstrTitle=NULL;
2016         ofn.Flags=OFN_OVERWRITEPROMPT;
2017 //      ofn.nFileOffset=0;
2018 //      ofn.nFileExtension=0;
2019         ofn.lpstrDefExt=TEXT("sif");
2020 //      ofn.lCustData = 0l;
2021         ofn.lpfnHook=NULL;
2022 //      ofn.lpTemplateName=NULL;
2023
2024         CHAR szFilename[MAX_PATH];
2025         CHAR szTitle[500];
2026         strcpy(szFilename,filename.c_str());
2027         strcpy(szTitle,title.c_str());
2028
2029         ofn.lpstrFile=szFilename;
2030         ofn.lpstrFileTitle=szTitle;
2031
2032         if(GetSaveFileName(&ofn))
2033         {
2034                 filename=szFilename;
2035                 _preferences.set_value(preference,dirname(filename));
2036                 return true;
2037         }
2038         return false;
2039 #else
2040         synfig::String prev_path;
2041
2042         if(!_preferences.get_value(preference, prev_path))
2043                 prev_path=".";
2044
2045         prev_path = absolute_path(prev_path);
2046
2047     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2048
2049     dialog->set_current_folder(prev_path);
2050     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2051     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
2052
2053         Widget_Enum *file_type_enum = 0;
2054         if (preference == ANIMATION_DIR_PREFERENCE)
2055         {
2056                 file_type_enum = manage(new Widget_Enum());
2057                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2058                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
2059                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2060                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2061                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2062                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2063
2064                 Gtk::HBox *hbox = manage(new Gtk::HBox);
2065                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2066                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2067                 hbox->show_all();
2068
2069                 dialog->set_extra_widget(*hbox);
2070         }
2071
2072     if (filename.empty())
2073                 dialog->set_filename(prev_path);
2074     else
2075         {
2076                 std::string full_path;
2077                 if (is_absolute_path(filename))
2078                         full_path = filename;
2079                 else
2080                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2081
2082                 // select the file if it exists
2083                 dialog->set_filename(full_path);
2084
2085                 // if the file doesn't exist, put its name into the filename box
2086                 struct stat s;
2087                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2088                         dialog->set_current_name(basename(filename));
2089         }
2090
2091     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2092                 if (preference == ANIMATION_DIR_PREFERENCE)
2093                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2094         filename = dialog->get_filename();
2095                 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2096                 _preferences.set_value(preference, dirname(filename));
2097         delete dialog;
2098         return true;
2099     }
2100
2101     delete dialog;
2102     return false;
2103 #endif
2104 }
2105
2106 void
2107 App::dialog_error_blocking(const std::string &title, const std::string &message)
2108 {
2109         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2110         dialog.set_title(title);
2111         dialog.show();
2112         dialog.run();
2113 }
2114
2115 void
2116 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2117 {
2118         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2119         dialog.set_title(title);
2120         dialog.show();
2121         dialog.run();
2122 }
2123
2124 bool
2125 App::dialog_yes_no(const std::string &title, const std::string &message)
2126 {
2127         Gtk::Dialog dialog(
2128                 title,          // Title
2129                 true,           // Modal
2130                 true            // use_separator
2131         );
2132         Gtk::Label label(message);
2133         label.show();
2134
2135         dialog.get_vbox()->pack_start(label);
2136         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2137         dialog.add_button(Gtk::StockID("gtk-no"),0);
2138         dialog.show();
2139         return dialog.run();
2140 }
2141
2142 int
2143 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2144 {
2145         Gtk::Dialog dialog(
2146                 title,          // Title
2147                 true,           // Modal
2148                 true            // use_separator
2149         );
2150         Gtk::Label label(message);
2151         label.show();
2152
2153         dialog.get_vbox()->pack_start(label);
2154         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2155         dialog.add_button(Gtk::StockID("gtk-no"),0);
2156         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2157         dialog.show();
2158         return dialog.run();
2159 }
2160
2161 void
2162 App::dialog_not_implemented()
2163 {
2164         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2165         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2166         dialog.run();
2167 }
2168
2169 static bool
2170 try_open_url(const std::string &url)
2171 {
2172 #ifdef WIN32
2173         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2174 #else // !WIN32
2175         std::vector<std::string> command_line;
2176         std::vector<std::string> browsers;
2177         browsers.reserve(23);
2178
2179         // Browser wrapper scripts
2180 #ifdef USE_OPEN_FOR_URLS
2181         browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
2182 #endif
2183         browsers.push_back("xdg-open");          // XDG wrapper
2184         browsers.push_back("sensible-browser");  // Debian wrapper
2185         browsers.push_back("gnome-open");        // GNOME wrapper
2186         browsers.push_back("kfmclient");         // KDE wrapper
2187         browsers.push_back("exo-open");          // XFCE wrapper
2188
2189         // Alternatives system
2190         browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2191         browsers.push_back("x-www-browser");     // Debian GUI alternative
2192
2193         // Individual browsers
2194         browsers.push_back("firefox");
2195         browsers.push_back("epiphany-browser");
2196         browsers.push_back("epiphany");
2197         browsers.push_back("konqueror");
2198         browsers.push_back("iceweasel");
2199         browsers.push_back("mozilla");
2200         browsers.push_back("netscape");
2201         browsers.push_back("icecat");
2202         browsers.push_back("galeon");
2203         browsers.push_back("midori");
2204         browsers.push_back("safari");
2205         browsers.push_back("opera");
2206         browsers.push_back("amaya");
2207         browsers.push_back("netsurf");
2208         browsers.push_back("dillo");
2209
2210         // Try the user-specified browser first
2211         command_line.push_back(App::browser_command);
2212         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2213         command_line.push_back(url);
2214
2215         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2216         catch( Glib::SpawnError& exception ){
2217
2218                 while ( !browsers.empty() )
2219                 {
2220                         // Skip the browser if we already tried it
2221                         if( browsers[0] == App::browser_command )
2222                                 continue;
2223
2224                         // Construct the command line
2225                         command_line.clear();
2226                         command_line.push_back(browsers[0]);
2227                         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2228                         command_line.push_back(url);
2229
2230                         // Remove the browser from the list
2231                         browsers.erase(browsers.begin());
2232
2233                         // Try to spawn the browser
2234                         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2235                         // Failed, move on to the next one
2236                         catch(Glib::SpawnError& exception){ continue; }
2237                         return true; // No exception means we succeeded!                        
2238                 }
2239         }
2240
2241         return false;
2242 #endif // !WIN32
2243 }
2244
2245 void
2246 App::dialog_help()
2247 {
2248         if (!try_open_url("http://synfig.org/Documentation"))
2249         {
2250                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2251                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2252                 dialog.set_title(_("Help"));
2253                 dialog.run();
2254         }
2255 }
2256
2257 void
2258 App::open_url(const std::string &url)
2259 {
2260         if(!try_open_url(url))
2261         {
2262                 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2263                 dialog.set_secondary_text(url);
2264                 dialog.set_title(_("No browser found"));
2265                 dialog.run();
2266         }
2267 }
2268
2269 bool
2270 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2271 {
2272         Gtk::Dialog dialog(
2273                 title,          // Title
2274                 true,           // Modal
2275                 true);          // use_separator
2276
2277         Gtk::Label label(message);
2278         label.show();
2279         dialog.get_vbox()->pack_start(label);
2280
2281         Gtk::Entry entry;
2282         entry.set_text(text);
2283         entry.show();
2284         entry.set_activates_default(true);
2285
2286         dialog.get_vbox()->pack_start(entry);
2287
2288         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2289         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2290         dialog.set_default_response(Gtk::RESPONSE_OK);
2291
2292         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2293         dialog.show();
2294
2295         if(dialog.run()!=Gtk::RESPONSE_OK)
2296                 return false;
2297
2298         text=entry.get_text();
2299
2300         return true;
2301 }
2302
2303 bool
2304 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2305 {
2306         Gtk::Dialog dialog(
2307                 title,          // Title
2308                 true,           // Modal
2309                 true);          // use_separator
2310
2311         Gtk::Label label(message);
2312         label.show();
2313         dialog.get_vbox()->pack_start(label);
2314
2315         Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2316         text_buffer->set_text(text);
2317         Gtk::TextView text_view(text_buffer);
2318         text_view.show();
2319
2320         dialog.get_vbox()->pack_start(text_view);
2321
2322         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2323         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2324         dialog.set_default_response(Gtk::RESPONSE_OK);
2325
2326         //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2327         dialog.show();
2328
2329         if(dialog.run()!=Gtk::RESPONSE_OK)
2330                 return false;
2331
2332         text=text_buffer->get_text();
2333
2334         return true;
2335 }
2336
2337 bool
2338 App::open(std::string filename)
2339 {
2340         return open_as(filename,filename);
2341 }
2342
2343 // this is called from autorecover.cpp:
2344 //   App::open_as(get_shadow_file_name(filename),filename)
2345 // other than that, 'filename' and 'as' are the same
2346 bool
2347 App::open_as(std::string filename,std::string as)
2348 {
2349 #ifdef WIN32
2350     char long_name[1024];
2351     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2352         // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2353         // don't use it if that's the case
2354         if (long_name[0] != '\0')
2355                 as=long_name;
2356 #endif
2357
2358         try
2359         {
2360                 OneMoment one_moment;
2361                 String errors;
2362
2363                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as,errors));
2364                 if(canvas && get_instance(canvas))
2365                 {
2366                         get_instance(canvas)->find_canvas_view(canvas)->present();
2367                         info("%s is already open", filename.c_str());
2368                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2369                 }
2370                 else
2371                 {
2372                         if(!canvas)
2373                                 throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
2374
2375                         if (as.find(custom_filename_prefix.c_str()) != 0)
2376                                 add_recent_file(as);
2377
2378                         handle<Instance> instance(Instance::create(canvas));
2379
2380                         if(!instance)
2381                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2382
2383                         set_recent_file_window_size(instance);
2384
2385                         one_moment.hide();
2386
2387                         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)")))
2388                                 instance->dialog_cvs_update();
2389                 }
2390         }
2391         catch(String x)
2392         {
2393                 dialog_error_blocking(_("Error"), x);
2394                 return false;
2395         }
2396         catch(...)
2397         {
2398                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2399                 return false;
2400         }
2401
2402         return true;
2403 }
2404
2405
2406 void
2407 App::new_instance()
2408 {
2409         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2410
2411         String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
2412         canvas->set_name(file_name);
2413         file_name += ".sifz";
2414
2415         canvas->rend_desc().set_frame_rate(24.0);
2416         canvas->rend_desc().set_time_start(0.0);
2417         canvas->rend_desc().set_time_end(5.0);
2418         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2419         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2420         // The top left and botton right positions are expressed in units
2421         // Original convention is that 1 unit = 60 pixels
2422         canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
2423         canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
2424         canvas->rend_desc().set_w(preferred_x_size);
2425         canvas->rend_desc().set_h(preferred_y_size);
2426         canvas->rend_desc().set_antialias(1);
2427         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2428         canvas->set_file_name(file_name);
2429
2430         handle<Instance> instance = Instance::create(canvas);
2431
2432         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2433                 instance->find_canvas_view(canvas)->canvas_properties.present();
2434 }
2435
2436 void
2437 App::dialog_open(string filename)
2438 {
2439         if (filename.empty())
2440                 filename="*.sif";
2441
2442         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2443         {
2444                 // If the filename still has wildcards, then we should
2445                 // continue looking for the file we want
2446                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2447                         continue;
2448
2449                 if(open(filename))
2450                         break;
2451
2452                 get_ui_interface()->error(_("Unable to open file"));
2453         }
2454 }
2455
2456 void
2457 App::set_selected_instance(etl::loose_handle<Instance> instance)
2458 {
2459 /*      if(get_selected_instance()==instance)
2460         {
2461                 selected_instance=instance;
2462                 signal_instance_selected()(instance);
2463                 return;
2464         }
2465         else
2466         {
2467 */
2468                 selected_instance=instance;
2469                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2470                 {
2471                         if(instance)
2472                         {
2473                                 instance->focus(instance->get_canvas());
2474                         }
2475                         else
2476                                 set_selected_canvas_view(0);
2477                 }
2478                 signal_instance_selected()(instance);
2479 }
2480
2481 void
2482 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2483 {
2484         selected_canvas_view=canvas_view;
2485         signal_canvas_view_focus()(selected_canvas_view);
2486         if(canvas_view)
2487         {
2488                 selected_instance=canvas_view->get_instance();
2489                 signal_instance_selected()(canvas_view->get_instance());
2490         }
2491 /*
2492         if(get_selected_canvas_view()==canvas_view)
2493         {
2494                 signal_canvas_view_focus()(selected_canvas_view);
2495                 signal_instance_selected()(canvas_view->get_instance());
2496                 return;
2497         }
2498         selected_canvas_view=canvas_view;
2499         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2500                 set_selected_instance(canvas_view->get_instance());
2501         signal_canvas_view_focus()(selected_canvas_view);
2502 */
2503 }
2504
2505 etl::loose_handle<Instance>
2506 App::get_instance(etl::handle<synfig::Canvas> canvas)
2507 {
2508         if(!canvas) return 0;
2509         canvas=canvas->get_root();
2510
2511         std::list<etl::handle<Instance> >::iterator iter;
2512         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2513         {
2514                 if((*iter)->get_canvas()==canvas)
2515                         return *iter;
2516         }
2517         return 0;
2518 }
2519
2520 void
2521 App::dialog_about()
2522 {
2523         if(about)
2524                 about->show();
2525 }
2526
2527 void
2528 studio::App::undo()
2529 {
2530         if(selected_instance)
2531                 selected_instance->undo();
2532 }
2533
2534 void
2535 studio::App::redo()
2536 {
2537         if(selected_instance)
2538                 selected_instance->redo();
2539 }
2540
2541 synfig::String
2542 studio::App::get_base_path()
2543 {
2544         return app_base_path_;
2545 }