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