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