Add "select all layers" (S-C-a) and "unselect all ducks" (C-d). Move "unselect all...
[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(!window_size_broken)
1628                                                 add_recent_file(recent_file,recent_file_window_size);
1629                                         else
1630                                                 add_recent_file(recent_file);
1631                                 }
1632                         }
1633                         if(!window_size_broken && file_window_size)
1634                                 window_size_broken = true;
1635
1636                         if(window_size_broken)
1637                         {
1638                                 recent_files_window_size.clear();
1639                                 recent_files_window_size.resize(recent_files.size());
1640                         }
1641                 }
1642                 std::string filename=get_config_file("settings");
1643                 if(!synfigapp::Main::settings().load_from_file(filename))
1644                 {
1645                         //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1646                         //if(!synfigapp::Main::settings().load_from_file(filename))
1647                         {
1648                                 gamma.set_gamma(1.0/2.2);
1649                                 reset_initial_window_configuration();
1650                         }
1651                 }
1652         setlocale(LC_NUMERIC,old_locale);
1653         }
1654         catch(...)
1655         {
1656                 synfig::warning("Caught exception when attempting to load settings.");
1657         }
1658 }
1659
1660 void
1661 App::reset_initial_window_configuration()
1662 {
1663         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1664         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1665         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1666         synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1667         synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1668         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1669         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1670         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1671         synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1672         synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1673         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1674         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1675 #ifdef SINGLE_THREADED
1676         synfigapp::Main::settings().set_value("pref.single_threaded","0");
1677 #endif
1678         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1679         synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1680 }
1681
1682 bool
1683 App::shutdown_request(GdkEventAny*)
1684 {
1685         quit();
1686         return true;
1687         //return !shutdown_in_progress;
1688 }
1689
1690 void
1691 App::quit()
1692 {
1693         if(shutdown_in_progress)return;
1694
1695
1696         get_ui_interface()->task(_("Quit Request"));
1697         if(Busy::count)
1698         {
1699                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1700                 return;
1701         }
1702
1703         std::list<etl::handle<Instance> >::iterator iter;
1704         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1705         {
1706                 if(!(*iter)->safe_close())
1707                         return;
1708
1709 /*
1710                 if((*iter)->synfigapp::Instance::get_action_count())
1711                 {
1712                         handle<synfigapp::UIInterface> uim;
1713                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1714                         assert(uim);
1715                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1716                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1717                         {
1718                                 case synfigapp::UIInterface::RESPONSE_NO:
1719                                         break;
1720                                 case synfigapp::UIInterface::RESPONSE_YES:
1721                                         (*iter)->save();
1722                                         break;
1723                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1724                                         return;
1725                                 default:
1726                                         assert(0);
1727                                         return;
1728                         }
1729                 }
1730
1731
1732                 if((*iter)->synfigapp::Instance::is_modified())
1733                 {
1734                         handle<synfigapp::UIInterface> uim;
1735                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1736                         assert(uim);
1737                         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() );
1738                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1739                         {
1740                                 case synfigapp::UIInterface::RESPONSE_NO:
1741                                         break;
1742                                 case synfigapp::UIInterface::RESPONSE_YES:
1743                                         (*iter)->dialog_cvs_commit();
1744                                         break;
1745                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1746                                         return;
1747                                 default:
1748                                         assert(0);
1749                                         return;
1750                         }
1751                 }
1752 */
1753
1754                 // This next line causes things to crash for some reason
1755                 //(*iter)->close();
1756         }
1757
1758         shutdown_in_progress=true;
1759
1760         instance_list.clear();
1761
1762         while(studio::App::events_pending())studio::App::iteration(false);
1763
1764         Gtk::Main::quit();
1765         auto_recover->normal_shutdown();
1766
1767         get_ui_interface()->task(_("Quit Request sent"));
1768 }
1769
1770 void
1771 App::show_setup()
1772 {
1773         dialog_setup->refresh();
1774         dialog_setup->show();
1775 }
1776
1777 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1778 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1779
1780 //#ifdef WIN32
1781 //#define USE_WIN32_FILE_DIALOGS 1
1782 //#endif
1783
1784 #ifdef USE_WIN32_FILE_DIALOGS
1785 static OPENFILENAME ofn={};
1786 #endif
1787
1788 #ifdef WIN32
1789 #include <gdk/gdkwin32.h>
1790 #endif
1791
1792 bool
1793 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1794 {
1795         // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1796
1797 #ifdef USE_WIN32_FILE_DIALOGS
1798         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1799
1800         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1801         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1802         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1803
1804         ofn.lStructSize=sizeof(OPENFILENAME);
1805         ofn.hwndOwner = hWnd;
1806         ofn.hInstance = hInstance;
1807         ofn.lpstrFilter = szFilter;
1808 //      ofn.lpstrCustomFilter=NULL;
1809 //      ofn.nMaxCustFilter=0;
1810 //      ofn.nFilterIndex=0;
1811 //      ofn.lpstrFile=NULL;
1812         ofn.nMaxFile=MAX_PATH;
1813 //      ofn.lpstrFileTitle=NULL;
1814 //      ofn.lpstrInitialDir=NULL;
1815 //      ofn.lpstrTitle=NULL;
1816         ofn.Flags=OFN_HIDEREADONLY;
1817 //      ofn.nFileOffset=0;
1818 //      ofn.nFileExtension=0;
1819         ofn.lpstrDefExt=TEXT("sif");
1820 //      ofn.lCustData = 0l;
1821         ofn.lpfnHook=NULL;
1822 //      ofn.lpTemplateName=NULL;
1823
1824         CHAR szFilename[MAX_PATH];
1825         CHAR szTitle[500];
1826         strcpy(szFilename,filename.c_str());
1827         strcpy(szTitle,title.c_str());
1828
1829         ofn.lpstrFile=szFilename;
1830         ofn.lpstrFileTitle=szTitle;
1831
1832         if(GetOpenFileName(&ofn))
1833         {
1834                 filename=szFilename;
1835                 return true;
1836         }
1837         return false;
1838
1839 #else
1840         synfig::String prev_path;
1841
1842         if(!_preferences.get_value(preference, prev_path))
1843                 prev_path = ".";
1844
1845         prev_path = absolute_path(prev_path);
1846
1847     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1848
1849     dialog->set_current_folder(prev_path);
1850     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1851     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1852
1853     if (filename.empty())
1854                 dialog->set_filename(prev_path);
1855         else if (is_absolute_path(filename))
1856                 dialog->set_filename(filename);
1857         else
1858                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1859
1860     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1861         filename = dialog->get_filename();
1862                 info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1863                 _preferences.set_value(preference, dirname(filename));
1864         delete dialog;
1865         return true;
1866     }
1867
1868     delete dialog;
1869     return false;
1870
1871     /*
1872
1873         GtkWidget *ok;
1874         GtkWidget *cancel;
1875         int val=0;
1876
1877         GtkWidget *fileselection;
1878         fileselection = gtk_file_selection_new(title.c_str());
1879
1880
1881         if(basename(filename)==filename)
1882         {
1883                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1884         }
1885         else
1886                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1887
1888         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1889
1890         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1891         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1892
1893         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1894         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1895
1896         gtk_widget_show(fileselection);
1897
1898         while(!val)
1899                 iteration();
1900
1901
1902         if(val==1)
1903         {
1904                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1905                 _preferences.set_value(preference,dirname(filename));
1906         }
1907         else
1908         {
1909                 gtk_widget_destroy(fileselection);
1910                 return false;
1911         }
1912         gtk_widget_destroy(fileselection);
1913         return true;
1914     */
1915 #endif
1916 }
1917
1918 bool
1919 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1920 {
1921         // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1922
1923 #if USE_WIN32_FILE_DIALOGS
1924         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1925
1926         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1927         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1928         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1929
1930         ofn.lStructSize=sizeof(OPENFILENAME);
1931         ofn.hwndOwner = hWnd;
1932         ofn.hInstance = hInstance;
1933         ofn.lpstrFilter = szFilter;
1934 //      ofn.lpstrCustomFilter=NULL;
1935 //      ofn.nMaxCustFilter=0;
1936 //      ofn.nFilterIndex=0;
1937 //      ofn.lpstrFile=NULL;
1938         ofn.nMaxFile=MAX_PATH;
1939 //      ofn.lpstrFileTitle=NULL;
1940 //      ofn.lpstrInitialDir=NULL;
1941 //      ofn.lpstrTitle=NULL;
1942         ofn.Flags=OFN_OVERWRITEPROMPT;
1943 //      ofn.nFileOffset=0;
1944 //      ofn.nFileExtension=0;
1945         ofn.lpstrDefExt=TEXT("sif");
1946 //      ofn.lCustData = 0l;
1947         ofn.lpfnHook=NULL;
1948 //      ofn.lpTemplateName=NULL;
1949
1950         CHAR szFilename[MAX_PATH];
1951         CHAR szTitle[500];
1952         strcpy(szFilename,filename.c_str());
1953         strcpy(szTitle,title.c_str());
1954
1955         ofn.lpstrFile=szFilename;
1956         ofn.lpstrFileTitle=szTitle;
1957
1958         if(GetSaveFileName(&ofn))
1959         {
1960                 filename=szFilename;
1961                 _preferences.set_value(preference,dirname(filename));
1962                 return true;
1963         }
1964         return false;
1965 #else
1966         synfig::String prev_path;
1967
1968         if(!_preferences.get_value(preference, prev_path))
1969                 prev_path=".";
1970
1971         prev_path = absolute_path(prev_path);
1972
1973     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
1974
1975     dialog->set_current_folder(prev_path);
1976     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1977     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1978
1979         Widget_Enum *file_type_enum = 0;
1980         if (preference == ANIMATION_DIR_PREFERENCE)
1981         {
1982                 file_type_enum = manage(new Widget_Enum());
1983                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
1984                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
1985                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
1986                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
1987                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
1988                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
1989
1990                 Gtk::HBox *hbox = manage(new Gtk::HBox);
1991                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
1992                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
1993                 hbox->show_all();
1994
1995                 dialog->set_extra_widget(*hbox);
1996         }
1997
1998     if (filename.empty())
1999                 dialog->set_filename(prev_path);
2000     else
2001         {
2002                 std::string full_path;
2003                 if (is_absolute_path(filename))
2004                         full_path = filename;
2005                 else
2006                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2007
2008                 // select the file if it exists
2009                 dialog->set_filename(full_path);
2010
2011                 // if the file doesn't exist, put its name into the filename box
2012                 struct stat s;
2013                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2014                         dialog->set_current_name(basename(filename));
2015         }
2016
2017     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2018                 if (preference == ANIMATION_DIR_PREFERENCE)
2019                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2020         filename = dialog->get_filename();
2021                 info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2022                 _preferences.set_value(preference, dirname(filename));
2023         delete dialog;
2024         return true;
2025     }
2026
2027     delete dialog;
2028     return false;
2029 #endif
2030 }
2031
2032 void
2033 App::dialog_error_blocking(const std::string &title, const std::string &message)
2034 {
2035         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2036         dialog.set_title(title);
2037         dialog.show();
2038         dialog.run();
2039 }
2040
2041 void
2042 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2043 {
2044         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2045         dialog.set_title(title);
2046         dialog.show();
2047         dialog.run();
2048 }
2049
2050 bool
2051 App::dialog_yes_no(const std::string &title, const std::string &message)
2052 {
2053         Gtk::Dialog dialog(
2054                 title,          // Title
2055                 true,           // Modal
2056                 true            // use_separator
2057         );
2058         Gtk::Label label(message);
2059         label.show();
2060
2061         dialog.get_vbox()->pack_start(label);
2062         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2063         dialog.add_button(Gtk::StockID("gtk-no"),0);
2064         dialog.show();
2065         return dialog.run();
2066 }
2067
2068 int
2069 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2070 {
2071         Gtk::Dialog dialog(
2072                 title,          // Title
2073                 true,           // Modal
2074                 true            // use_separator
2075         );
2076         Gtk::Label label(message);
2077         label.show();
2078
2079         dialog.get_vbox()->pack_start(label);
2080         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2081         dialog.add_button(Gtk::StockID("gtk-no"),0);
2082         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2083         dialog.show();
2084         return dialog.run();
2085 }
2086
2087 void
2088 App::dialog_not_implemented()
2089 {
2090         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2091         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2092         dialog.run();
2093 }
2094
2095 static bool
2096 try_open_url(const std::string &url)
2097 {
2098 #ifdef WIN32
2099         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2100 #else // !WIN32
2101         std::vector<std::string> command_line;
2102         std::vector<std::string> browsers;
2103         browsers.reserve(23);
2104
2105         // Browser wrapper scripts
2106 #ifdef USE_OPEN_FOR_URLS
2107         browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
2108 #endif
2109         browsers.push_back("xdg-open");          // XDG wrapper
2110         browsers.push_back("sensible-browser");  // Debian wrapper
2111         browsers.push_back("gnome-open");        // GNOME wrapper
2112         browsers.push_back("kfmclient");         // KDE wrapper
2113         browsers.push_back("exo-open");          // XFCE wrapper
2114
2115         // Alternatives system
2116         browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2117         browsers.push_back("x-www-browser");     // Debian GUI alternative
2118
2119         // Individual browsers
2120         browsers.push_back("firefox");
2121         browsers.push_back("epiphany-browser");
2122         browsers.push_back("epiphany");
2123         browsers.push_back("konqueror");
2124         browsers.push_back("iceweasel");
2125         browsers.push_back("mozilla");
2126         browsers.push_back("netscape");
2127         browsers.push_back("icecat");
2128         browsers.push_back("galeon");
2129         browsers.push_back("midori");
2130         browsers.push_back("safari");
2131         browsers.push_back("opera");
2132         browsers.push_back("amaya");
2133         browsers.push_back("netsurf");
2134         browsers.push_back("dillo");
2135
2136         // Try the user-specified browser first
2137         command_line.push_back(App::browser_command);
2138         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2139         command_line.push_back(url);
2140
2141         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2142         catch( Glib::SpawnError& exception ){
2143
2144                 while ( !browsers.empty() )
2145                 {
2146                         // Skip the browser if we already tried it
2147                         if( browsers[0] == App::browser_command )
2148                                 continue;
2149
2150                         // Construct the command line
2151                         command_line.clear();
2152                         command_line.push_back(browsers[0]);
2153                         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2154                         command_line.push_back(url);
2155
2156                         // Remove the browser from the list
2157                         browsers.erase(browsers.begin());
2158
2159                         // Try to spawn the browser
2160                         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2161                         // Failed, move on to the next one
2162                         catch(Glib::SpawnError& exception){ continue; }
2163                         return true; // No exception means we succeeded!                        
2164                 }
2165         }
2166
2167         return false;
2168 #endif // !WIN32
2169 }
2170
2171 void
2172 App::dialog_help()
2173 {
2174         if (!try_open_url("http://synfig.org/Documentation"))
2175         {
2176                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2177                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2178                 dialog.set_title(_("Help"));
2179                 dialog.run();
2180         }
2181 }
2182
2183 void
2184 App::open_url(const std::string &url)
2185 {
2186         if(!try_open_url(url))
2187         {
2188                 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2189                 dialog.set_secondary_text(url);
2190                 dialog.set_title(_("No browser found"));
2191                 dialog.run();
2192         }
2193 }
2194
2195 bool
2196 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2197 {
2198         Gtk::Dialog dialog(
2199                 title,          // Title
2200                 true,           // Modal
2201                 true);          // use_separator
2202
2203         Gtk::Label label(message);
2204         label.show();
2205         dialog.get_vbox()->pack_start(label);
2206
2207         Gtk::Entry entry;
2208         entry.set_text(text);
2209         entry.show();
2210         entry.set_activates_default(true);
2211
2212         dialog.get_vbox()->pack_start(entry);
2213
2214         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2215         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2216         dialog.set_default_response(Gtk::RESPONSE_OK);
2217
2218         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2219         dialog.show();
2220
2221         if(dialog.run()!=Gtk::RESPONSE_OK)
2222                 return false;
2223
2224         text=entry.get_text();
2225
2226         return true;
2227 }
2228
2229 bool
2230 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2231 {
2232         Gtk::Dialog dialog(
2233                 title,          // Title
2234                 true,           // Modal
2235                 true);          // use_separator
2236
2237         Gtk::Label label(message);
2238         label.show();
2239         dialog.get_vbox()->pack_start(label);
2240
2241         Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2242         text_buffer->set_text(text);
2243         Gtk::TextView text_view(text_buffer);
2244         text_view.show();
2245
2246         dialog.get_vbox()->pack_start(text_view);
2247
2248         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2249         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2250         dialog.set_default_response(Gtk::RESPONSE_OK);
2251
2252         //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2253         dialog.show();
2254
2255         if(dialog.run()!=Gtk::RESPONSE_OK)
2256                 return false;
2257
2258         text=text_buffer->get_text();
2259
2260         return true;
2261 }
2262
2263 bool
2264 App::open(std::string filename)
2265 {
2266         return open_as(filename,filename);
2267 }
2268
2269 // this is called from autorecover.cpp:
2270 //   App::open_as(get_shadow_file_name(filename),filename)
2271 // other than that, 'filename' and 'as' are the same
2272 bool
2273 App::open_as(std::string filename,std::string as)
2274 {
2275 #ifdef WIN32
2276     char long_name[1024];
2277     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2278         // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2279         // don't use it if that's the case
2280         if (long_name[0] != '\0')
2281                 as=long_name;
2282 #endif
2283
2284         try
2285         {
2286                 OneMoment one_moment;
2287
2288                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2289                 if(canvas && get_instance(canvas))
2290                 {
2291                         get_instance(canvas)->find_canvas_view(canvas)->present();
2292                         info("%s is already open", filename.c_str());
2293                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2294                 }
2295                 else
2296                 {
2297                         if(!canvas)
2298                                 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2299
2300                         if (as.find(DEFAULT_FILENAME_PREFIX) != 0)
2301                                 add_recent_file(as);
2302
2303                         handle<Instance> instance(Instance::create(canvas));
2304
2305                         if(!instance)
2306                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2307
2308                         set_recent_file_window_size(instance);
2309
2310                         one_moment.hide();
2311
2312                         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)")))
2313                                 instance->dialog_cvs_update();
2314                 }
2315         }
2316         catch(String x)
2317         {
2318                 dialog_error_blocking(_("Error"), x);
2319                 return false;
2320         }
2321         catch(...)
2322         {
2323                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2324                 return false;
2325         }
2326
2327         return true;
2328 }
2329
2330
2331 void
2332 App::new_instance()
2333 {
2334         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2335
2336         String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2337         canvas->set_name(file_name);
2338         file_name += ".sifz";
2339
2340         canvas->rend_desc().set_frame_rate(24.0);
2341         canvas->rend_desc().set_time_start(0.0);
2342         canvas->rend_desc().set_time_end(5.0);
2343         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2344         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2345         canvas->rend_desc().set_tl(Vector(-4,2.25));
2346         canvas->rend_desc().set_br(Vector(4,-2.25));
2347         canvas->rend_desc().set_w(480);
2348         canvas->rend_desc().set_h(270);
2349         canvas->rend_desc().set_antialias(1);
2350         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2351         canvas->set_file_name(file_name);
2352
2353         handle<Instance> instance = Instance::create(canvas);
2354
2355         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2356                 instance->find_canvas_view(canvas)->canvas_properties.present();
2357 }
2358
2359 void
2360 App::dialog_open(string filename)
2361 {
2362         if (filename.empty())
2363                 filename="*.sif";
2364
2365         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2366         {
2367                 // If the filename still has wildcards, then we should
2368                 // continue looking for the file we want
2369                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2370                         continue;
2371
2372                 if(open(filename))
2373                         break;
2374
2375                 get_ui_interface()->error(_("Unable to open file"));
2376         }
2377 }
2378
2379 void
2380 App::set_selected_instance(etl::loose_handle<Instance> instance)
2381 {
2382 /*      if(get_selected_instance()==instance)
2383         {
2384                 selected_instance=instance;
2385                 signal_instance_selected()(instance);
2386                 return;
2387         }
2388         else
2389         {
2390 */
2391                 selected_instance=instance;
2392                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2393                 {
2394                         if(instance)
2395                         {
2396                                 instance->focus(instance->get_canvas());
2397                         }
2398                         else
2399                                 set_selected_canvas_view(0);
2400                 }
2401                 signal_instance_selected()(instance);
2402 }
2403
2404 void
2405 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2406 {
2407         selected_canvas_view=canvas_view;
2408         signal_canvas_view_focus()(selected_canvas_view);
2409         if(canvas_view)
2410         {
2411                 selected_instance=canvas_view->get_instance();
2412                 signal_instance_selected()(canvas_view->get_instance());
2413         }
2414 /*
2415         if(get_selected_canvas_view()==canvas_view)
2416         {
2417                 signal_canvas_view_focus()(selected_canvas_view);
2418                 signal_instance_selected()(canvas_view->get_instance());
2419                 return;
2420         }
2421         selected_canvas_view=canvas_view;
2422         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2423                 set_selected_instance(canvas_view->get_instance());
2424         signal_canvas_view_focus()(selected_canvas_view);
2425 */
2426 }
2427
2428 etl::loose_handle<Instance>
2429 App::get_instance(etl::handle<synfig::Canvas> canvas)
2430 {
2431         if(!canvas) return 0;
2432         canvas=canvas->get_root();
2433
2434         std::list<etl::handle<Instance> >::iterator iter;
2435         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2436         {
2437                 if((*iter)->get_canvas()==canvas)
2438                         return *iter;
2439         }
2440         return 0;
2441 }
2442
2443 void
2444 App::dialog_about()
2445 {
2446         if(about)
2447                 about->show();
2448 }
2449
2450 void
2451 studio::App::undo()
2452 {
2453         if(selected_instance)
2454                 selected_instance->undo();
2455 }
2456
2457 void
2458 studio::App::redo()
2459 {
2460         if(selected_instance)
2461                 selected_instance->redo();
2462 }
2463
2464 synfig::String
2465 studio::App::get_base_path()
2466 {
2467         return app_base_path_;
2468 }