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