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