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