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