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