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