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