Applied patch from IL'dar AKHmetgaleev aka AkhIL to allow different qualities of...
[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-lowres-pixel", _("Low-Res Pixel Size")) );
573         menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
574         menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
575         menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
576         menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
577         menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
578
579         // Add the synfigapp actions...
580         synfigapp::Action::Book::iterator iter;
581         for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
582         {
583                 actions_action_group->add(Gtk::Action::create(
584                         "action-"+iter->second.name,
585                         get_action_stock_id(iter->second),
586                         iter->second.local_name,iter->second.local_name
587                 ));
588         }
589
590 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
591 #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); }
592 #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); }
593
594         DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
595         DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
596         DEFINE_ACTION("new", Gtk::Stock::NEW);
597         DEFINE_ACTION("open", Gtk::Stock::OPEN);
598         DEFINE_ACTION("save", Gtk::Stock::SAVE);
599         DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
600         DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
601         DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
602         DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
603         DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
604         DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
605         DEFINE_ACTION("import", _("Import"));
606         DEFINE_ACTION("render", _("Render"));
607         DEFINE_ACTION("preview", _("Preview"));
608         DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
609         DEFINE_ACTION("sound", _("Sound File"));
610         DEFINE_ACTION("options", _("Options"));
611         DEFINE_ACTION("close", _("Close View"));
612         DEFINE_ACTION("close-document", _("Close Document"));
613         DEFINE_ACTION("quit", Gtk::Stock::QUIT);
614
615
616         DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
617         DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
618         DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
619         DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
620         DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
621         DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
622         DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
623         DEFINE_ACTION("properties", _("Properties"));
624
625         DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
626         DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
627         DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
628         DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
629         DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
630         DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
631         DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
632         DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
633         DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
634         DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
635         DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
636         DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
637         DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
638         DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
639         DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
640         DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
641         DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
642         DEFINE_ACTION("lowres-pixel-01", _("Set Low-Res pixel size to 2^1"));
643         DEFINE_ACTION("lowres-pixel-02", _("Set Low-Res pixel size to 2^2"));
644         DEFINE_ACTION("lowres-pixel-03", _("Set Low-Res pixel size to 2^3"));
645         DEFINE_ACTION("lowres-pixel-04", _("Set Low-Res pixel size to 2^4"));
646         DEFINE_ACTION("lowres-pixel-05", _("Set Low-Res pixel size to 2^5"));
647         DEFINE_ACTION("lowres-pixel-06", _("Set Low-Res pixel size to 2^6"));
648         DEFINE_ACTION("play", _("Play"));
649         // DEFINE_ACTION("pause", _("Pause"));
650         DEFINE_ACTION("stop", _("Stop"));
651         DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
652         DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
653         DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
654         DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
655         DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
656         DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
657         DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
658         DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
659         DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
660         DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
661         DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
662         DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
663         DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
664         DEFINE_ACTION("seek-next-frame", _("Next Frame"));
665         DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
666         DEFINE_ACTION("seek-next-second", _("Seek Forward"));
667         DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
668         DEFINE_ACTION("seek-begin", _("Seek to Begin"));
669         DEFINE_ACTION("seek-end", _("Seek to End"));
670
671         DEFINE_ACTION("action-group_add", _("Add group"));
672
673         DEFINE_ACTION("canvas-new", _("New Canvas"));
674
675         DEFINE_ACTION("amount-inc", _("Increase Amount"));
676         DEFINE_ACTION("amount-dec", _("Decrease Amount"));
677
678 #undef DEFINE_ACTION
679
680
681 // Set up synfigapp actions
682         /*{
683                 synfigapp::Action::Book::iterator iter;
684
685                 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
686                 {
687                         Gtk::StockID stock_id;
688
689                         if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
690                         {
691                                 //Gtk::Image* image(manage(new Gtk::Image()));
692                                 if(iter->second.task=="raise")                  stock_id=Gtk::Stock::GO_UP;
693                                 else if(iter->second.task=="lower")             stock_id=Gtk::Stock::GO_DOWN;
694                                 else if(iter->second.task=="move_top")  stock_id=Gtk::Stock::GOTO_TOP;
695                                 else if(iter->second.task=="move_bottom")       stock_id=Gtk::Stock::GOTO_BOTTOM;
696                                 else if(iter->second.task=="remove")    stock_id=Gtk::Stock::DELETE;
697                                 else if(iter->second.task=="set_on")    stock_id=Gtk::Stock::YES;
698                                 else if(iter->second.task=="set_off")   stock_id=Gtk::Stock::NO;
699                                 //else if(iter->second.task=="duplicate")       stock_id=Gtk::Stock::COPY;
700                                 else if(iter->second.task=="remove")    stock_id=Gtk::Stock::DELETE;
701                                 else                                                                    stock_id=Gtk::StockID("synfig-"+iter->second.task);
702
703                                 actions_action_group->add(Gtk::Action::create(
704                                         "action-"+iter->second.name,
705                                         stock_id,
706                                         iter->second.local_name,iter->second.local_name
707                                 ));
708                         }
709                 }
710         }
711 */
712
713
714     Glib::ustring ui_info =
715 "<ui>"
716 "       <popup name='menu-toolbox' action='menu-toolbox'>"
717 "       <menu action='menu-file'>"
718 "       </menu>"
719 "       </popup>"
720 "       <popup name='menu-main' action='menu-main'>"
721 "       <menu action='menu-file'>"
722 "               <menuitem action='new' />"
723 "               <menuitem action='open' />"
724 "               <menuitem action='save' />"
725 "               <menuitem action='save-as' />"
726 "               <menuitem action='revert' />"
727 "               <separator name='bleh01'/>"
728 "               <menuitem action='cvs-add' />"
729 "               <menuitem action='cvs-update' />"
730 "               <menuitem action='cvs-commit' />"
731 "               <menuitem action='cvs-revert' />"
732 "               <separator name='bleh02'/>"
733 "               <menuitem action='import' />"
734 "               <separator name='bleh03'/>"
735 "               <menuitem action='render' />"
736 "               <menuitem action='preview' />"
737 "               <menuitem action='sound' />"
738 "               <separator name='bleh04'/>"
739 "               <menuitem action='options' />"
740 "               <menuitem action='close' />"
741 "               <menuitem action='close-document' />"
742 "               <menuitem action='quit' />"
743 "       </menu>"
744 "       <menu action='menu-edit'>"
745 "               <menuitem action='undo'/>"
746 "               <menuitem action='redo'/>"
747 "               <separator name='bleh05'/>"
748 "               <menuitem action='cut'/>"
749 "               <menuitem action='copy'/>"
750 "               <menuitem action='paste'/>"
751 "               <separator name='bleh06'/>"
752 "               <menuitem action='select-all-ducks'/>"
753 "               <menuitem action='unselect-all-layers'/>"
754 "               <separator name='bleh07'/>"
755 "               <menuitem action='properties'/>"
756 "       </menu>"
757 "       <menu action='menu-view'>"
758 "               <menu action='menu-duck-mask'>"
759 "                       <menuitem action='mask-position-ducks' />"
760 "                       <menuitem action='mask-vertex-ducks' />"
761 "                       <menuitem action='mask-tangent-ducks' />"
762 "                       <menuitem action='mask-radius-ducks' />"
763 "                       <menuitem action='mask-width-ducks' />"
764 "                       <menuitem action='mask-angle-ducks' />"
765 "               </menu>"
766 "               <menu action='menu-preview-quality'>"
767 "                       <menuitem action='quality-00' />"
768 "                       <menuitem action='quality-01' />"
769 "                       <menuitem action='quality-02' />"
770 "                       <menuitem action='quality-03' />"
771 "                       <menuitem action='quality-04' />"
772 "                       <menuitem action='quality-05' />"
773 "                       <menuitem action='quality-06' />"
774 "                       <menuitem action='quality-07' />"
775 "                       <menuitem action='quality-08' />"
776 "                       <menuitem action='quality-09' />"
777 "                       <menuitem action='quality-10' />"
778 "               </menu>"
779 "               <menu action='menu-lowres-pixel'>"
780 "                       <menuitem action='lowres-pixel-01' />"
781 "                       <menuitem action='lowres-pixel-02' />"
782 "                       <menuitem action='lowres-pixel-03' />"
783 "                       <menuitem action='lowres-pixel-04' />"
784 "                       <menuitem action='lowres-pixel-05' />"
785 "                       <menuitem action='lowres-pixel-06' />"
786 "               </menu>"
787 "               <separator name='bleh08'/>"
788 "               <menuitem action='play'/>"
789 //"             <menuitem action='pause'/>"
790 "               <menuitem action='stop'/>"
791 "               <menuitem action='dialog-flipbook'/>"
792 "               <separator name='bleh09'/>"
793 "               <menuitem action='toggle-grid-show'/>"
794 "               <menuitem action='toggle-grid-snap'/>"
795 "               <menuitem action='toggle-guide-show'/>"
796 "               <menuitem action='toggle-low-res'/>"
797 "               <menuitem action='toggle-onion-skin'/>"
798 "               <separator name='bleh10'/>"
799 "               <menuitem action='canvas-zoom-in'/>"
800 "               <menuitem action='canvas-zoom-out'/>"
801 "               <menuitem action='canvas-zoom-fit'/>"
802 "               <menuitem action='canvas-zoom-100'/>"
803 "               <separator name='bleh11'/>"
804 "               <menuitem action='time-zoom-in'/>"
805 "               <menuitem action='time-zoom-out'/>"
806 "               <separator name='bleh12'/>"
807 "               <menuitem action='jump-next-keyframe'/>"
808 "               <menuitem action='jump-prev-keyframe'/>"
809 "               <menuitem action='seek-next-frame'/>"
810 "               <menuitem action='seek-prev-frame'/>"
811 "               <menuitem action='seek-next-second'/>"
812 "               <menuitem action='seek-prev-second'/>"
813 "               <menuitem action='seek-begin'/>"
814 "               <menuitem action='seek-end'/>"
815 "       </menu>"
816 "       <menu action='menu-canvas'>"
817 "               <menuitem action='canvas-new'/>"
818 "       </menu>"
819 "       <menu name='menu-state' action='menu-state'>"
820 "       </menu>"
821 "       <menu action='menu-group'>"
822 "               <menuitem action='action-group_add'/>"
823 "       </menu>"
824 "       <menu action='menu-layer'>"
825 //"             <menuitem action='cut'/>"
826 //"             <menuitem action='copy'/>"
827 //"             <menuitem action='paste'/>"
828 //"             <separator name='bleh06'/>"
829 "               <menu action='menu-layer-new'></menu>"
830 "               <menuitem action='amount-inc'/>"
831 "               <menuitem action='amount-dec'/>"
832 "       </menu>"
833 "       <menu action='menu-keyframe'>"
834 "               <menuitem action='keyframe-properties'/>"
835 "       </menu>"
836 "       </popup>"
837
838 "</ui>"
839 ;
840 /*              "<ui>"
841         "  <menubar name='MenuBar'>"
842         "    <menu action='MenuFile'>"
843         "      <menuitem action='New'/>"
844         "      <menuitem action='Open'/>"
845         "      <separator/>"
846         "      <menuitem action='Quit'/>"
847         "    </menu>"
848         "    <menu action='MenuEdit'>"
849         "      <menuitem action='Cut'/>"
850         "      <menuitem action='Copy'/>"
851         "      <menuitem action='Paste'/>"
852         "    </menu>"
853         "  </menubar>"
854         "  <toolbar  name='ToolBar'>"
855         "    <toolitem action='Open'/>"
856         "    <toolitem action='Quit'/>"
857         "  </toolbar>"
858         "</ui>";
859 */
860         try
861         {
862                 actions_action_group->set_sensitive(false);
863                 App::ui_manager()->set_add_tearoffs(true);
864                 App::ui_manager()->insert_action_group(menus_action_group,1);
865                 App::ui_manager()->insert_action_group(actions_action_group,1);
866                 App::ui_manager()->add_ui_from_string(ui_info);
867
868                 //App::ui_manager()->get_accel_group()->unlock();
869         }
870         catch(const Glib::Error& ex)
871         {
872                 synfig::error("building menus and toolbars failed: " + ex.what());
873         }
874
875         // Add default keyboard accelerators
876 #define ACCEL(path,accel)                                               \
877         {                                                                                       \
878                 Gtk::AccelKey accel_key(accel,path);    \
879                 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
880         }
881
882 #define ACCEL2(accel)                                                   \
883         {                                                                                       \
884                 Gtk::AccelKey accel_key(accel);                 \
885                 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod());       \
886         }
887
888         ACCEL("<Actions>//select-all-ducks","<Control>a");
889         ACCEL("<Actions>//unselect-all-layers","<Control>d");
890         ACCEL("<Actions>//render","F9");
891         ACCEL("<Actions>//preview","F11");
892         ACCEL("<Actions>//properties","F8");
893         ACCEL("<Actions>//options","F12");
894         ACCEL("<Actions>//import","<control>i");
895         ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
896         ACCEL("<Actions>//toggle-grid-show","<Control>g");
897         ACCEL("<Actions>//toggle-grid-snap","<Control>l");
898         ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
899         ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
900         ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
901         ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
902         ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
903         ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
904         ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
905
906         ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
907         ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
908
909         ACCEL("<Actions>//quality-01","<Control>1");
910         ACCEL("<Actions>//quality-02","<Control>2");
911         ACCEL("<Actions>//quality-03","<Control>3");
912         ACCEL("<Actions>//quality-04","<Control>4");
913         ACCEL("<Actions>//quality-05","<Control>5");
914         ACCEL("<Actions>//quality-06","<Control>6");
915         ACCEL("<Actions>//quality-07","<Control>7");
916         ACCEL("<Actions>//quality-08","<Control>8");
917         ACCEL("<Actions>//quality-09","<Control>9");
918         ACCEL("<Actions>//quality-10","<Control>0");
919         ACCEL("<Actions>//undo","<Control>z");
920         ACCEL("<Actions>//redo","<Control>r");
921         ACCEL("<Actions>//action-layer_remove","Delete");
922
923 /*      ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
924         ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
925         ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
926         ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
927         ACCEL("<Actions>//time-zoom-in","+");
928         ACCEL("<Actions>//time-zoom-out","_");
929 */
930         ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
931         ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
932
933         ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
934         ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
935         ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
936         ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
937         ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
938         ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
939         ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
940         ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
941         ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
942         ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
943         ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
944         ACCEL("<Actions>//play",              "<Control>p");
945         ACCEL("<Actions>//seek-begin","Home");
946         ACCEL("<Actions>//seek-end","End");
947
948         ACCEL("<Actions>//state-normal",      "<Mod1>a");
949         ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
950         ACCEL("<Actions>//state-scale",       "<Mod1>d");
951         ACCEL("<Actions>//state-rotate",      "<Mod1>s");
952
953         ACCEL("<Actions>//state-bline",       "<Mod1>b");
954         ACCEL("<Actions>//state-circle",      "<Mod1>c");
955         ACCEL("<Actions>//state-rectangle",   "<Mod1>r");
956         ACCEL("<Actions>//state-gradient",    "<Mod1>g");
957
958         ACCEL("<Actions>//state-eyedrop",     "<Mod1>e");
959         ACCEL("<Actions>//state-fill",        "<Mod1>f");
960         ACCEL("<Actions>//state-zoom",        "<Mod1>z");
961         ACCEL("<Actions>//state-polygon",     "<Mod1>p");
962
963         ACCEL("<Actions>//state-draw",        "<Mod1>w");
964         ACCEL("<Actions>//state-sketch",      "<Mod1>k");
965         ACCEL("<Actions>//state-width",       "<Mod1>t");
966         ACCEL("<Actions>//state-mirror",      "<Mod1>m");
967
968         ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
969
970 #undef ACCEL
971 }
972
973 #ifdef _WIN32
974 #define mkdir(x,y) mkdir(x)
975 #endif
976
977 /* === M E T H O D S ======================================================= */
978
979 App::App(int *argc, char ***argv):
980         Gtk::Main(argc,argv),
981         IconController(etl::dirname((*argv)[0]))
982 {
983         app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
984
985
986         ui_interface_=new GlobalUIInterface();
987
988         gdk_rgb_init();
989
990         // don't call thread_init() if threads are already initialized
991         // on some machines bonobo_init() initialized threads before we get here
992         if (!g_thread_supported())
993                 Glib::thread_init();
994
995         distance_system=Distance::SYSTEM_UNITS;
996
997         if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
998         {
999                 if(errno!=EEXIST)
1000                         synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1001         }
1002         else
1003         {
1004                 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1005         }
1006
1007
1008         ipc=new IPC();
1009
1010         if(!SYNFIG_CHECK_VERSION())
1011         {
1012                 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1013                 dialog_error_blocking("Synfig Studio",
1014                         "This copy of Synfig Studio was compiled against a\n"
1015                         "different version of libsynfig than what is currently\n"
1016                         "installed. Synfig Studio will now abort. Try downloading\n"
1017                         "the latest version from the Synfig website at\n"
1018                         "http://synfig.org/Download"
1019                 );
1020                 throw 40;
1021         }
1022         Glib::set_application_name(_("Synfig Studio"));
1023
1024         About about_window;
1025         about_window.set_can_self_destruct(false);
1026         about_window.show();
1027
1028         shutdown_in_progress=false;
1029         SuperCallback synfig_init_cb(about_window.get_callback(),0,9000,10000);
1030         SuperCallback studio_init_cb(about_window.get_callback(),9000,10000,10000);
1031
1032         // Initialize the Synfig library
1033         try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1034         catch(...)
1035         {
1036                 get_ui_interface()->error(_("Failed to initialize synfig!"));
1037                 throw;
1038         }
1039
1040         // add the preferences to the settings
1041         synfigapp::Main::settings().add_domain(&_preferences,"pref");
1042
1043         try
1044         {
1045                 studio_init_cb.task(_("Init UI Manager..."));
1046                 App::ui_manager_=studio::UIManager::create();
1047                 init_ui_manager();
1048
1049                 studio_init_cb.task(_("Init Dock Manager..."));
1050                 dock_manager=new studio::DockManager();
1051
1052                 studio_init_cb.task(_("Init State Manager..."));
1053                 state_manager=new StateManager();
1054
1055                 studio_init_cb.task(_("Init Toolbox..."));
1056                 toolbox=new studio::Toolbox();
1057
1058                 studio_init_cb.task(_("Init Tool Options..."));
1059                 dialog_tool_options=new studio::Dialog_ToolOptions();
1060                 dock_manager->register_dockable(*dialog_tool_options);
1061
1062                 studio_init_cb.task(_("Init History..."));
1063                 dock_history=new studio::Dock_History();
1064                 dock_manager->register_dockable(*dock_history);
1065
1066                 studio_init_cb.task(_("Init Canvases..."));
1067                 dock_canvases=new studio::Dock_Canvases();
1068                 dock_manager->register_dockable(*dock_canvases);
1069
1070                 studio_init_cb.task(_("Init Keyframes..."));
1071                 dock_keyframes=new studio::Dock_Keyframes();
1072                 dock_manager->register_dockable(*dock_keyframes);
1073
1074                 studio_init_cb.task(_("Init Layers..."));
1075                 dock_layers=new studio::Dock_Layers();
1076                 dock_manager->register_dockable(*dock_layers);
1077
1078                 studio_init_cb.task(_("Init Params..."));
1079                 dock_params=new studio::Dock_Params();
1080                 dock_manager->register_dockable(*dock_params);
1081
1082                 studio_init_cb.task(_("Init MetaData..."));
1083                 dock_meta_data=new studio::Dock_MetaData();
1084                 dock_manager->register_dockable(*dock_meta_data);
1085
1086                 studio_init_cb.task(_("Init Children..."));
1087                 dock_children=new studio::Dock_Children();
1088                 dock_manager->register_dockable(*dock_children);
1089
1090                 studio_init_cb.task(_("Init Info..."));
1091                 dock_info = new studio::Dock_Info();
1092                 dock_manager->register_dockable(*dock_info);
1093
1094                 studio_init_cb.task(_("Init Navigator..."));
1095                 dock_navigator = new studio::Dock_Navigator();
1096                 dock_manager->register_dockable(*dock_navigator);
1097
1098                 studio_init_cb.task(_("Init Timetrack..."));
1099                 dock_timetrack = new studio::Dock_Timetrack();
1100                 dock_manager->register_dockable(*dock_timetrack);
1101
1102                 studio_init_cb.task(_("Init Curve Editor..."));
1103                 dock_curves = new studio::Dock_Curves();
1104                 dock_manager->register_dockable(*dock_curves);
1105
1106                 studio_init_cb.task(_("Init Layer Groups..."));
1107                 dock_layer_groups = new studio::Dock_LayerGroups();
1108                 dock_manager->register_dockable(*dock_layer_groups);
1109
1110
1111                 studio_init_cb.task(_("Init Color Dialog..."));
1112                 dialog_color=new studio::Dialog_Color();
1113
1114                 studio_init_cb.task(_("Init Gradient Dialog..."));
1115                 dialog_gradient=new studio::Dialog_Gradient();
1116
1117                 studio_init_cb.task(_("Init DeviceTracker..."));
1118                 device_tracker=new studio::DeviceTracker();
1119
1120                 studio_init_cb.task(_("Init Tools..."));
1121                 state_manager->add_state(&state_normal);
1122                 state_manager->add_state(&state_smooth_move);
1123                 state_manager->add_state(&state_scale);
1124                 state_manager->add_state(&state_rotate);
1125
1126                 state_manager->add_state(&state_bline);
1127
1128
1129                 state_manager->add_state(&state_circle);
1130                 state_manager->add_state(&state_rectangle);
1131
1132                 state_manager->add_state(&state_gradient);
1133                 state_manager->add_state(&state_eyedrop);
1134                 state_manager->add_state(&state_fill);
1135
1136                 state_manager->add_state(&state_zoom);
1137
1138                 // Enabled - it's useful to be able to work with polygons without tangent ducks getting in the way.
1139                 // I know we can switch tangent ducks off, but why not allow this kind of layer as well?
1140                 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon);
1141
1142                 // Enabled for now.  Let's see whether they're good enough yet.
1143                 if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw);
1144                 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1145
1146                 // Disabled by default - it doesn't work properly?
1147                 if(getenv("SYNFIG_ENABLE_WIDTH"    )) state_manager->add_state(&state_width);
1148
1149                 studio_init_cb.task(_("Init ModPalette..."));
1150                 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1151
1152                 studio_init_cb.task(_("Init ModMirror..."));
1153                 module_list_.push_back(new ModMirror()); module_list_.back()->start();
1154
1155
1156                 studio_init_cb.task(_("Init Setup Dialog..."));
1157                 dialog_setup=new studio::Dialog_Setup();
1158
1159                 studio_init_cb.task(_("Init Input Dialog..."));
1160                 dialog_input=new Gtk::InputDialog();
1161                 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1162                 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1163
1164                 studio_init_cb.task(_("Init auto recovery..."));
1165                 auto_recover=new AutoRecover();
1166
1167                 studio_init_cb.amount_complete(9250,10000);
1168                 studio_init_cb.task(_("Loading Settings..."));
1169                 load_settings();
1170                 studio_init_cb.task(_("Checking auto-recover..."));
1171
1172                 studio_init_cb.amount_complete(9900,10000);
1173
1174                 if(auto_recover->recovery_needed())
1175                 {
1176                         about_window.hide();
1177                         if(
1178                                 get_ui_interface()->yes_no(
1179                                         _("Auto Recovery"),
1180                                         _("Synfig Studio seems to have crashed\n"
1181                                         "before you could save all your files.\n"
1182                                         "Would you like to re-open those files\n"
1183                                         "and recover your unsaved changes?")
1184                                 )==synfigapp::UIInterface::RESPONSE_YES
1185                         )
1186                         {
1187                                 if(!auto_recover->recover())
1188                                 {
1189                                         get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1190                                 }
1191                                 else
1192                                 get_ui_interface()->error(
1193                                         _("Synfig Studio has attempted to recover\n"
1194                                         "from a previous crash. The files that it has\n"
1195                                         "recovered are NOT YET SAVED. It would be a good\n"
1196                                         "idea to review them and save them now.")
1197                                 );
1198                         }
1199                         about_window.show();
1200                 }
1201
1202                 // Look for any files given on the command line,
1203                 // and load them if found.
1204                 bool opened_any = false;
1205                 for(;*argc>=1;(*argc)--)
1206                         if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1207                         {
1208                                 studio_init_cb.task(_("Loading files..."));
1209                                 about_window.hide();
1210                                 open((*argv)[*argc]);
1211                                 opened_any = true;
1212                                 about_window.show();
1213                         }
1214
1215                 // if no file was specified to be opened, create a new document to help new users get started more easily
1216                 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1217                         new_instance();
1218
1219                 studio_init_cb.task(_("Done."));
1220                 studio_init_cb.amount_complete(10000,10000);
1221
1222                 toolbox->present();
1223         }
1224         catch(String x)
1225         {
1226                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1227         }
1228         catch(...)
1229         {
1230                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1231         }
1232 }
1233
1234 StateManager* App::get_state_manager() { return state_manager; }
1235
1236 App::~App()
1237 {
1238         shutdown_in_progress=true;
1239
1240         save_settings();
1241
1242         synfigapp::Main::settings().remove_domain("pref");
1243
1244         selected_instance=0;
1245
1246         // Unload all of the modules
1247         for(;!module_list_.empty();module_list_.pop_back())
1248                 ;
1249
1250         delete state_manager;
1251
1252         delete ipc;
1253
1254         delete auto_recover;
1255
1256         toolbox->hide();
1257
1258 //      studio::App::iteration(false);
1259
1260         delete toolbox;
1261
1262 //      studio::App::iteration(false);
1263
1264 //      studio::App::iteration(false);
1265
1266         delete dialog_setup;
1267
1268         delete dialog_gradient;
1269
1270         delete dialog_color;
1271
1272         delete dialog_input;
1273
1274         delete dock_manager;
1275
1276         instance_list.clear();
1277
1278 //      studio::App::iteration(false);
1279 }
1280
1281 String
1282 App::get_user_app_directory()
1283 {
1284         return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1285 }
1286
1287 synfig::String
1288 App::get_config_file(const synfig::String& file)
1289 {
1290         return Glib::build_filename(get_user_app_directory(),file);
1291 }
1292
1293 void
1294 App::add_recent_file(const std::string &file_name)
1295 {
1296         std::string filename(file_name);
1297
1298         assert(!filename.empty());
1299
1300         if(filename.empty())
1301                 return;
1302
1303         // Toss out any "hidden" files
1304         if(basename(filename)[0]=='.')
1305                 return;
1306
1307         // If we aren't an absolute path, turn ourselves into one
1308         if(!is_absolute_path(filename))
1309                 filename=absolute_path(filename);
1310
1311         list<string>::iterator iter;
1312         // Check to see if the file is already on the list.
1313         // If it is, then remove it from the list
1314         for(iter=recent_files.begin();iter!=recent_files.end();iter++)
1315                 if(*iter==filename)
1316                 {
1317                         recent_files.erase(iter);
1318                         break;
1319                 }
1320
1321
1322         // Push the filename to the front of the list
1323         recent_files.push_front(filename);
1324
1325         // Clean out the files at the end of the list.
1326         while(recent_files.size()>(unsigned)get_max_recent_files())
1327                 recent_files.pop_back();
1328
1329         signal_recent_files_changed_();
1330
1331         return;
1332 }
1333
1334 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1335
1336 Time::Format
1337 App::get_time_format()
1338 {
1339         return _App_time_format;
1340 }
1341
1342 void
1343 App::set_time_format(synfig::Time::Format x)
1344 {
1345         _App_time_format=x;
1346 }
1347
1348
1349 void
1350 App::save_settings()
1351 {
1352         char * old_locale;
1353         try
1354         {
1355         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1356         setlocale(LC_NUMERIC, "C");
1357                 {
1358                         std::string filename=get_config_file("accelrc");
1359                         Gtk::AccelMap::save(filename);
1360                 }
1361                 do{
1362                         std::string filename=get_config_file("recentfiles");
1363
1364                         std::ofstream file(filename.c_str());
1365
1366                         if(!file)
1367                         {
1368                                 synfig::warning("Unable to save %s",filename.c_str());
1369                                 break;
1370                         }
1371
1372                         list<string>::reverse_iterator iter;
1373
1374                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1375                                 file<<*iter<<endl;
1376                 }while(0);
1377
1378                 std::string filename=get_config_file("settings");
1379                 synfigapp::Main::settings().save_to_file(filename);
1380         setlocale(LC_NUMERIC,old_locale);
1381         }
1382         catch(...)
1383         {
1384                 synfig::warning("Caught exception when attempting to save settings.");
1385         }
1386 }
1387
1388 void
1389 App::load_settings()
1390 {
1391         char  * old_locale;
1392         try
1393         {
1394         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1395         setlocale(LC_NUMERIC, "C");
1396                 {
1397                         std::string filename=get_config_file("accelrc");
1398                         Gtk::AccelMap::load(filename);
1399                 }
1400                 {
1401                         std::string filename=get_config_file("recentfiles");
1402
1403                         std::ifstream file(filename.c_str());
1404
1405                         while(file)
1406                         {
1407                                 std::string recent_file;
1408                                 getline(file,recent_file);
1409                                 if(!recent_file.empty())
1410                                         add_recent_file(recent_file);
1411                         }
1412                 }
1413                 std::string filename=get_config_file("settings");
1414                 if(!synfigapp::Main::settings().load_from_file(filename))
1415                 {
1416                         //std::string filename=Glib::build_filename(Glib::get_home_dir(),".synfigrc");
1417                         //if(!synfigapp::Main::settings().load_from_file(filename))
1418                         {
1419                                 gamma.set_gamma(1.0/2.2);
1420                                 reset_initial_window_configuration();
1421                         }
1422                 }
1423         setlocale(LC_NUMERIC,old_locale);
1424         }
1425         catch(...)
1426         {
1427                 synfig::warning("Caught exception when attempting to load settings.");
1428         }
1429 }
1430
1431 void
1432 App::reset_initial_window_configuration()
1433 {
1434         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1435         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1436         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1437         synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1438         synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1439         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1440         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1441         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1442         synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1443         synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1444         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1445         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1446         synfigapp::Main::settings().set_value("pref.single_threaded","0");
1447         synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1448 }
1449
1450 bool
1451 App::shutdown_request(GdkEventAny*)
1452 {
1453         quit();
1454         return true;
1455         //return !shutdown_in_progress;
1456 }
1457
1458 void
1459 App::quit()
1460 {
1461         if(shutdown_in_progress)return;
1462
1463
1464         get_ui_interface()->task(_("Quit Request"));
1465         if(Busy::count)
1466         {
1467                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1468                 return;
1469         }
1470
1471         std::list<etl::handle<Instance> >::iterator iter;
1472         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1473         {
1474                 if(!(*iter)->safe_close())
1475                         return;
1476
1477 /*
1478                 if((*iter)->synfigapp::Instance::get_action_count())
1479                 {
1480                         handle<synfigapp::UIInterface> uim;
1481                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1482                         assert(uim);
1483                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1484                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1485                         {
1486                                 case synfigapp::UIInterface::RESPONSE_NO:
1487                                         break;
1488                                 case synfigapp::UIInterface::RESPONSE_YES:
1489                                         (*iter)->save();
1490                                         break;
1491                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1492                                         return;
1493                                 default:
1494                                         assert(0);
1495                                         return;
1496                         }
1497                 }
1498
1499
1500                 if((*iter)->synfigapp::Instance::is_modified())
1501                 {
1502                         handle<synfigapp::UIInterface> uim;
1503                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1504                         assert(uim);
1505                         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() );
1506                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1507                         {
1508                                 case synfigapp::UIInterface::RESPONSE_NO:
1509                                         break;
1510                                 case synfigapp::UIInterface::RESPONSE_YES:
1511                                         (*iter)->dialog_cvs_commit();
1512                                         break;
1513                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1514                                         return;
1515                                 default:
1516                                         assert(0);
1517                                         return;
1518                         }
1519                 }
1520 */
1521
1522                 // This next line causes things to crash for some reason
1523                 //(*iter)->close();
1524         }
1525
1526         shutdown_in_progress=true;
1527
1528         instance_list.clear();
1529
1530         while(studio::App::events_pending())studio::App::iteration(false);
1531
1532         Gtk::Main::quit();
1533         auto_recover->normal_shutdown();
1534
1535         get_ui_interface()->task(_("Quit Request sent"));
1536 }
1537
1538 void
1539 App::show_setup()
1540 {
1541         dialog_setup->refresh();
1542         dialog_setup->show();
1543 }
1544
1545 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1546 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1547
1548 //#ifdef WIN32
1549 //#define USE_WIN32_FILE_DIALOGS 1
1550 //#endif
1551
1552 #ifdef USE_WIN32_FILE_DIALOGS
1553 static OPENFILENAME ofn={};
1554 #endif
1555
1556 #ifdef WIN32
1557 #include <gdk/gdkwin32.h>
1558 #endif
1559
1560 bool
1561 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1562 {
1563         info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1564
1565 #ifdef USE_WIN32_FILE_DIALOGS
1566         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1567
1568         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1569         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1570         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1571
1572         ofn.lStructSize=sizeof(OPENFILENAME);
1573         ofn.hwndOwner = hWnd;
1574         ofn.hInstance = hInstance;
1575         ofn.lpstrFilter = szFilter;
1576 //      ofn.lpstrCustomFilter=NULL;
1577 //      ofn.nMaxCustFilter=0;
1578 //      ofn.nFilterIndex=0;
1579 //      ofn.lpstrFile=NULL;
1580         ofn.nMaxFile=MAX_PATH;
1581 //      ofn.lpstrFileTitle=NULL;
1582 //      ofn.lpstrInitialDir=NULL;
1583 //      ofn.lpstrTitle=NULL;
1584         ofn.Flags=OFN_HIDEREADONLY;
1585 //      ofn.nFileOffset=0;
1586 //      ofn.nFileExtension=0;
1587         ofn.lpstrDefExt=TEXT("sif");
1588 //      ofn.lCustData = 0l;
1589         ofn.lpfnHook=NULL;
1590 //      ofn.lpTemplateName=NULL;
1591
1592         CHAR szFilename[MAX_PATH];
1593         CHAR szTitle[500];
1594         strcpy(szFilename,filename.c_str());
1595         strcpy(szTitle,title.c_str());
1596
1597         ofn.lpstrFile=szFilename;
1598         ofn.lpstrFileTitle=szTitle;
1599
1600         if(GetOpenFileName(&ofn))
1601         {
1602                 filename=szFilename;
1603                 return true;
1604         }
1605         return false;
1606
1607 #else
1608         synfig::String prev_path;
1609
1610         if(!_preferences.get_value(preference, prev_path))
1611                 prev_path = ".";
1612
1613         prev_path = absolute_path(prev_path);
1614
1615     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1616
1617     dialog->set_current_folder(prev_path);
1618     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1619     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1620
1621     if (filename.empty())
1622                 dialog->set_filename(prev_path);
1623         else if (is_absolute_path(filename))
1624                 dialog->set_filename(filename);
1625         else
1626                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1627
1628     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1629         filename = dialog->get_filename();
1630                 info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1631                 _preferences.set_value(preference, dirname(filename));
1632         delete dialog;
1633         return true;
1634     }
1635
1636     delete dialog;
1637     return false;
1638
1639     /*
1640
1641         GtkWidget *ok;
1642         GtkWidget *cancel;
1643         int val=0;
1644
1645         GtkWidget *fileselection;
1646         fileselection = gtk_file_selection_new(title.c_str());
1647
1648
1649         if(basename(filename)==filename)
1650         {
1651                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1652         }
1653         else
1654                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1655
1656         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1657
1658         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1659         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1660
1661         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1662         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1663
1664         gtk_widget_show(fileselection);
1665
1666         while(!val)
1667                 iteration();
1668
1669
1670         if(val==1)
1671         {
1672                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1673                 _preferences.set_value(preference,dirname(filename));
1674         }
1675         else
1676         {
1677                 gtk_widget_destroy(fileselection);
1678                 return false;
1679         }
1680         gtk_widget_destroy(fileselection);
1681         return true;
1682     */
1683 #endif
1684 }
1685
1686 bool
1687 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1688 {
1689         info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1690
1691 #if USE_WIN32_FILE_DIALOGS
1692         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1693
1694         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1695         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1696         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1697
1698         ofn.lStructSize=sizeof(OPENFILENAME);
1699         ofn.hwndOwner = hWnd;
1700         ofn.hInstance = hInstance;
1701         ofn.lpstrFilter = szFilter;
1702 //      ofn.lpstrCustomFilter=NULL;
1703 //      ofn.nMaxCustFilter=0;
1704 //      ofn.nFilterIndex=0;
1705 //      ofn.lpstrFile=NULL;
1706         ofn.nMaxFile=MAX_PATH;
1707 //      ofn.lpstrFileTitle=NULL;
1708 //      ofn.lpstrInitialDir=NULL;
1709 //      ofn.lpstrTitle=NULL;
1710         ofn.Flags=OFN_OVERWRITEPROMPT;
1711 //      ofn.nFileOffset=0;
1712 //      ofn.nFileExtension=0;
1713         ofn.lpstrDefExt=TEXT("sif");
1714 //      ofn.lCustData = 0l;
1715         ofn.lpfnHook=NULL;
1716 //      ofn.lpTemplateName=NULL;
1717
1718         CHAR szFilename[MAX_PATH];
1719         CHAR szTitle[500];
1720         strcpy(szFilename,filename.c_str());
1721         strcpy(szTitle,title.c_str());
1722
1723         ofn.lpstrFile=szFilename;
1724         ofn.lpstrFileTitle=szTitle;
1725
1726         if(GetSaveFileName(&ofn))
1727         {
1728                 filename=szFilename;
1729                 _preferences.set_value(preference,dirname(filename));
1730                 return true;
1731         }
1732         return false;
1733 #else
1734         synfig::String prev_path;
1735
1736         if(!_preferences.get_value(preference, prev_path))
1737                 prev_path=".";
1738
1739         prev_path = absolute_path(prev_path);
1740
1741     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
1742
1743     dialog->set_current_folder(prev_path);
1744     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1745     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1746
1747     if (filename.empty())
1748                 dialog->set_filename(prev_path);
1749     else
1750         {
1751                 std::string full_path;
1752                 if (is_absolute_path(filename))
1753                         full_path = filename;
1754                 else
1755                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
1756
1757                 // select the file if it exists
1758                 dialog->set_filename(full_path);
1759
1760                 // if the file doesn't exist, put its name into the filename box
1761                 struct stat s;
1762                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
1763                         dialog->set_current_name(basename(filename));
1764         }
1765
1766     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1767         filename = dialog->get_filename();
1768                 info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
1769                 _preferences.set_value(preference, dirname(filename));
1770         delete dialog;
1771         return true;
1772     }
1773
1774     delete dialog;
1775     return false;
1776 #endif
1777 }
1778
1779 void
1780 App::dialog_error_blocking(const std::string &title, const std::string &message)
1781 {
1782         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1783         dialog.set_title(title);
1784         dialog.show();
1785         dialog.run();
1786 }
1787
1788 void
1789 App::dialog_warning_blocking(const std::string &title, const std::string &message)
1790 {
1791         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
1792         dialog.set_title(title);
1793         dialog.show();
1794         dialog.run();
1795 }
1796
1797 bool
1798 App::dialog_yes_no(const std::string &title, const std::string &message)
1799 {
1800         Gtk::Dialog dialog(
1801                 title,          // Title
1802                 true,           // Modal
1803                 true            // use_separator
1804         );
1805         Gtk::Label label(message);
1806         label.show();
1807
1808         dialog.get_vbox()->pack_start(label);
1809         dialog.add_button(Gtk::StockID("gtk-yes"),1);
1810         dialog.add_button(Gtk::StockID("gtk-no"),0);
1811         dialog.show();
1812         return dialog.run();
1813 }
1814
1815 int
1816 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
1817 {
1818         Gtk::Dialog dialog(
1819                 title,          // Title
1820                 true,           // Modal
1821                 true            // use_separator
1822         );
1823         Gtk::Label label(message);
1824         label.show();
1825
1826         dialog.get_vbox()->pack_start(label);
1827         dialog.add_button(Gtk::StockID("gtk-yes"),1);
1828         dialog.add_button(Gtk::StockID("gtk-no"),0);
1829         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
1830         dialog.show();
1831         return dialog.run();
1832 }
1833
1834 void
1835 App::dialog_not_implemented()
1836 {
1837         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1838         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
1839         dialog.run();
1840 }
1841
1842 bool
1843 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
1844 {
1845         Gtk::Dialog dialog(
1846                 title,          // Title
1847                 true,           // Modal
1848                 true            // use_separator
1849         );
1850         Gtk::Label label(message);
1851         label.show();
1852         dialog.get_vbox()->pack_start(label);
1853
1854         Gtk::Entry entry;
1855         entry.set_text(text);
1856         entry.show();
1857         entry.set_activates_default(true);
1858         dialog.get_vbox()->pack_start(entry);
1859
1860         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
1861         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
1862         dialog.set_default_response(Gtk::RESPONSE_OK);
1863         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
1864         dialog.show();
1865
1866         if(dialog.run()!=Gtk::RESPONSE_OK)
1867                 return false;
1868
1869         text=entry.get_text();
1870
1871         return true;
1872 }
1873
1874
1875
1876
1877 bool
1878 App::open(std::string filename)
1879 {
1880         return open_as(filename,filename);
1881 }
1882
1883 // this is called from autorecover.cpp:
1884 //   App::open_as(get_shadow_file_name(filename),filename)
1885 // other than that, 'filename' and 'as' are the same
1886 bool
1887 App::open_as(std::string filename,std::string as)
1888 {
1889 #ifdef WIN32
1890     char long_name[1024];
1891     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
1892     as=long_name;
1893 #endif
1894
1895         try
1896         {
1897                 OneMoment one_moment;
1898
1899                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
1900                 if(canvas && get_instance(canvas))
1901                 {
1902                         get_instance(canvas)->find_canvas_view(canvas)->present();
1903                         throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
1904                 }
1905                 if(!canvas)
1906                         throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
1907
1908                 add_recent_file(as);
1909
1910                 handle<Instance> instance(Instance::create(canvas));
1911
1912                 if(!instance)
1913                         throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
1914
1915                 one_moment.hide();
1916
1917                 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)")))
1918                         instance->dialog_cvs_update();
1919         }
1920         catch(String x)
1921         {
1922                 dialog_error_blocking(_("Error"), x);
1923                 return false;
1924         }
1925         catch(...)
1926         {
1927                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
1928                 return false;
1929         }
1930
1931         return true;
1932 }
1933
1934
1935 void
1936 App::new_instance()
1937 {
1938         handle<synfig::Canvas> canvas=synfig::Canvas::create();
1939         canvas->set_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
1940
1941         String file_name(strprintf("%s%d.sifz", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
1942
1943         canvas->rend_desc().set_frame_rate(24.0);
1944         canvas->rend_desc().set_time_start(0.0);
1945         canvas->rend_desc().set_time_end(5.0);
1946         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
1947         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
1948         canvas->rend_desc().set_tl(Vector(-4,2.25));
1949         canvas->rend_desc().set_br(Vector(4,-2.25));
1950         canvas->rend_desc().set_w(480);
1951         canvas->rend_desc().set_h(270);
1952         canvas->rend_desc().set_antialias(1);
1953         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
1954         canvas->set_file_name(file_name);
1955
1956         handle<Instance> instance = Instance::create(canvas);
1957
1958         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
1959                 instance->find_canvas_view(canvas)->canvas_properties.present();
1960 }
1961
1962 void
1963 App::dialog_open()
1964 {
1965         string filename="*.sif";
1966
1967         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
1968         {
1969                 // If the filename still has wildcards, then we should
1970                 // continue looking for the file we want
1971                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
1972                         continue;
1973
1974                 if(open(filename))
1975                         break;
1976
1977                 get_ui_interface()->error(_("Unable to open file"));
1978         }
1979 }
1980
1981 void
1982 App::set_selected_instance(etl::loose_handle<Instance> instance)
1983 {
1984 /*      if(get_selected_instance()==instance)
1985         {
1986                 selected_instance=instance;
1987                 signal_instance_selected()(instance);
1988                 return;
1989         }
1990         else
1991         {
1992 */
1993                 selected_instance=instance;
1994                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
1995                 {
1996                         if(instance)
1997                         {
1998                                 instance->focus(instance->get_canvas());
1999                         }
2000                         else
2001                                 set_selected_canvas_view(0);
2002                 }
2003                 signal_instance_selected()(instance);
2004 }
2005
2006 void
2007 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2008 {
2009         selected_canvas_view=canvas_view;
2010         signal_canvas_view_focus()(selected_canvas_view);
2011         if(canvas_view)
2012         {
2013                 selected_instance=canvas_view->get_instance();
2014                 signal_instance_selected()(canvas_view->get_instance());
2015         }
2016 /*
2017         if(get_selected_canvas_view()==canvas_view)
2018         {
2019                 signal_canvas_view_focus()(selected_canvas_view);
2020                 signal_instance_selected()(canvas_view->get_instance());
2021                 return;
2022         }
2023         selected_canvas_view=canvas_view;
2024         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2025                 set_selected_instance(canvas_view->get_instance());
2026         signal_canvas_view_focus()(selected_canvas_view);
2027 */
2028 }
2029
2030 etl::loose_handle<Instance>
2031 App::get_instance(etl::handle<synfig::Canvas> canvas)
2032 {
2033         if(!canvas) return 0;
2034         canvas=canvas->get_root();
2035
2036         std::list<etl::handle<Instance> >::iterator iter;
2037         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2038         {
2039                 if((*iter)->get_canvas()==canvas)
2040                         return *iter;
2041         }
2042         return 0;
2043 }
2044
2045 void
2046 App::dialog_about()
2047 {
2048         (new class About())->show();
2049 }
2050
2051 void
2052 studio::App::undo()
2053 {
2054         if(selected_instance)
2055                 selected_instance->undo();
2056 }
2057
2058 void
2059 studio::App::redo()
2060 {
2061         if(selected_instance)
2062                 selected_instance->redo();
2063 }
2064
2065 synfig::String
2066 studio::App::get_base_path()
2067 {
2068         return app_base_path_;
2069 }