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