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