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