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