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