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