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