Fix bug: Change default bline width spin button without first focus a canvas window...
[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                 // If the default bline width is modified before focus a canvas
1326                 // window, the Distance widget doesn't understand the given value
1327                 // and produces this message:
1328                 // Distance::ident_system(): Unknown distance system ".00pt"
1329                 // setting the default bline width to 1 unit.
1330                 // This line fixes that.
1331                 synfigapp::Main::set_bline_width(synfigapp::Main::get_selected_input_device()->get_bline_width());
1332
1333                 studio_init_cb.task(_("Checking auto-recover..."));
1334
1335                 studio_init_cb.amount_complete(9900,10000);
1336
1337                 bool opened_any = false;
1338                 if(auto_recover->recovery_needed())
1339                 {
1340                         splash_screen.hide();
1341                         if (get_ui_interface()->confirmation(_("Crash Recovery"),
1342                                         _("Auto recovery file found"),
1343                                         _("Synfig Studio seems to have crashed before you could save all your files. "
1344                                           "Recover unsaved changes?"),
1345                                         _("Recover"), _("Ignore"))
1346                                 == synfigapp::UIInterface::RESPONSE_OK)
1347                         {
1348                                 int number_recovered;
1349                                 if(!auto_recover->recover(number_recovered))
1350                                         if (number_recovered)
1351                                                 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1352                                         else
1353                                                 get_ui_interface()->error(_("Unable to recover from previous crash"));
1354                                 else
1355                                         dialog_warning_blocking(_("Warning"),
1356                                                 _("Synfig Studio has attempted to recover from a previous crash. "
1357                                                 "The files that it has recovered are NOT YET SAVED. It would be a "
1358                                                 "good idea to review them and save them now."));
1359
1360                                 if (number_recovered)
1361                                         opened_any = true;
1362                         }
1363                         splash_screen.show();
1364                 }
1365
1366                 // Look for any files given on the command line,
1367                 // and load them if found.
1368                 for(;*argc>=1;(*argc)--)
1369                         if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1370                         {
1371                                 studio_init_cb.task(_("Loading files..."));
1372                                 splash_screen.hide();
1373                                 open((*argv)[*argc]);
1374                                 opened_any = true;
1375                                 splash_screen.show();
1376                         }
1377
1378                 // if no file was specified to be opened, create a new document to help new users get started more easily
1379                 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1380                         new_instance();
1381
1382                 studio_init_cb.task(_("Done."));
1383                 studio_init_cb.amount_complete(10000,10000);
1384
1385                 // To avoid problems with some window managers and gtk >= 2.18
1386                 // we should show dock dialogs after the settings load.
1387                 // If dock dialogs are shown before the settings are loaded,
1388                 // the windows manager can act over it.
1389                 // See discussions here:
1390                 // * http://synfig.org/forums/viewtopic.php?f=1&t=1131&st=0&sk=t&sd=a&start=30
1391                 // * http://synfig.org/forums/viewtopic.php?f=15&t=1062
1392                 dock_manager->show_all_dock_dialogs();
1393
1394                 toolbox->present();
1395         }
1396         catch(String x)
1397         {
1398                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1399         }
1400         catch(...)
1401         {
1402                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1403         }
1404 }
1405
1406 StateManager* App::get_state_manager() { return state_manager; }
1407
1408 App::~App()
1409 {
1410         shutdown_in_progress=true;
1411
1412         save_settings();
1413
1414         synfigapp::Main::settings().remove_domain("pref");
1415
1416         selected_instance=0;
1417
1418         // Unload all of the modules
1419         for(;!module_list_.empty();module_list_.pop_back())
1420                 ;
1421
1422         delete state_manager;
1423
1424         delete ipc;
1425
1426         delete auto_recover;
1427
1428         delete about;
1429
1430         toolbox->hide();
1431
1432         delete toolbox;
1433
1434         delete dialog_setup;
1435
1436         delete dialog_gradient;
1437
1438         delete dialog_color;
1439
1440         delete dialog_input;
1441
1442         delete dock_manager;
1443
1444         instance_list.clear();
1445 }
1446
1447 String
1448 App::get_user_app_directory()
1449 {
1450 //! \todo do we need locale_from_utf8() on non-Windows boxes too?  (bug #1837445)
1451 #ifdef WIN32
1452         return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1453 #else
1454         return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1455 #endif
1456 }
1457
1458 synfig::String
1459 App::get_config_file(const synfig::String& file)
1460 {
1461         return Glib::build_filename(get_user_app_directory(),file);
1462 }
1463
1464 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1465 void
1466 App::set_recent_file_window_size(etl::handle<Instance> instance)
1467 {
1468
1469         const std::string &canvas_window_size = *recent_files_window_size.begin();
1470
1471         if(canvas_window_size.empty())
1472                 return;
1473
1474         synfig::String::size_type current=0;
1475         bool seen_root(false), shown_non_root(false);
1476
1477         while(current != synfig::String::npos)
1478         {
1479                 // find end of first field (canvas) or return
1480                 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1481                 if(separator == synfig::String::npos) break;
1482
1483                 // find the canvas
1484                 synfig::Canvas::Handle canvas;
1485                 try {
1486                         String warnings;
1487                         canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current), warnings);
1488                 }
1489                 catch(Exception::IDNotFound) {
1490                         // can't find the canvas; skip to the next canvas or return
1491                         separator = canvas_window_size.find_first_of('\t', current);
1492                         if(separator == synfig::String::npos) return;
1493                         current = separator+1;
1494                         continue;
1495                 }
1496
1497                 if (canvas->is_root())
1498                         seen_root = true;
1499                 else
1500                         shown_non_root = true;
1501
1502                 // check that we have the tab character the ends this canvas' data or return
1503                 current = separator+1;
1504                 separator = canvas_window_size.find_first_of('\t', current);
1505                 if(separator == synfig::String::npos) return;
1506
1507                 int x,y,w,h;
1508                 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1509                 {
1510                         current = separator+1;
1511                         continue;
1512                 }
1513                 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1514                 canvasview->move(x,y);
1515                 canvasview->resize(w,h);
1516                 canvasview->present();
1517
1518                 current = separator+1;
1519         }
1520
1521         if (shown_non_root && !seen_root)
1522                 instance->find_canvas_view(instance->get_canvas())->hide();
1523 }
1524
1525 void
1526 App::add_recent_file(const etl::handle<Instance> instance)
1527 {
1528
1529         std::string canvas_window_size;
1530
1531         const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1532         Instance::CanvasViewList::const_iterator iter;
1533
1534         for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1535         {
1536                 if( !((*iter)->is_visible()) )
1537                         continue;
1538
1539                 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1540                 int x_pos, y_pos, x_size, y_size;
1541                 (*iter)->get_position(x_pos,y_pos);
1542                 (*iter)->get_size(x_size,y_size);
1543
1544                 canvas_window_size += strprintf("%s %d %d %d %d\t",
1545                                                                                 canvas->get_relative_id(canvas->get_root()).c_str(),
1546                                                                                 x_pos,  y_pos,
1547                                                                                 x_size, y_size);
1548         }
1549
1550         add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1551 }
1552
1553 void
1554 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1555 {
1556         std::string filename(file_name);
1557
1558         assert(!filename.empty());
1559
1560         if(filename.empty())
1561                 return;
1562
1563         // Toss out any "hidden" files
1564         if(basename(filename)[0]=='.')
1565                 return;
1566
1567         // If we aren't an absolute path, turn ourselves into one
1568         if(!is_absolute_path(filename))
1569                 filename=absolute_path(filename);
1570
1571         std::string old_window_size;
1572
1573         list<string>::iterator iter;
1574         list<string>::iterator iter_wsize;
1575         // Check to see if the file is already on the list.
1576         // If it is, then remove it from the list
1577         for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1578                 if(*iter==filename)
1579                 {
1580                         recent_files.erase(iter);
1581                         old_window_size = *iter_wsize;
1582                         recent_files_window_size.erase(iter_wsize);
1583                         break;
1584                 }
1585
1586
1587         // Push the filename to the front of the list
1588         recent_files.push_front(filename);
1589         if(window_size.empty())
1590                 recent_files_window_size.push_front(old_window_size);
1591         else
1592                 recent_files_window_size.push_front(window_size);
1593
1594         // Clean out the files at the end of the list.
1595         while(recent_files.size()>(unsigned)get_max_recent_files())
1596         {
1597                 recent_files.pop_back();
1598                 recent_files_window_size.pop_back();
1599         }
1600
1601         signal_recent_files_changed_();
1602
1603         return;
1604 }
1605
1606 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1607
1608 Time::Format
1609 App::get_time_format()
1610 {
1611         return _App_time_format;
1612 }
1613
1614 void
1615 App::set_time_format(synfig::Time::Format x)
1616 {
1617         _App_time_format=x;
1618 }
1619
1620
1621 void
1622 App::save_settings()
1623 {
1624         try
1625         {
1626                 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1627                 {
1628                         std::string filename=get_config_file("accelrc");
1629                         Gtk::AccelMap::save(filename);
1630                 }
1631                 do{
1632                         std::string filename=get_config_file("recentfiles");
1633
1634                         std::ofstream file(filename.c_str());
1635
1636                         if(!file)
1637                         {
1638                                 synfig::warning("Unable to save %s",filename.c_str());
1639                                 break;
1640                         }
1641
1642                         list<string>::reverse_iterator iter;
1643
1644                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1645                                 file<<*iter<<endl;
1646                 }while(0);
1647                 do{
1648                         std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1649
1650                         std::ofstream file(filename.c_str());
1651
1652                         if(!file)
1653                         {
1654                                 synfig::warning("Unable to save %s",filename.c_str());
1655                                 break;
1656                         }
1657
1658                         list<string>::reverse_iterator iter;
1659
1660                         for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1661                                 file<<*iter<<endl;
1662
1663                 }while(0);
1664                 std::string filename=get_config_file("settings");
1665                 synfigapp::Main::settings().save_to_file(filename);
1666
1667         }
1668         catch(...)
1669         {
1670                 synfig::warning("Caught exception when attempting to save settings.");
1671         }
1672 }
1673
1674 void
1675 App::load_settings()
1676 {
1677         try
1678         {
1679                 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1680                 {
1681                         std::string filename=get_config_file("accelrc");
1682                         Gtk::AccelMap::load(filename);
1683                 }
1684                 {
1685                         bool window_size_broken = false;
1686
1687                         std::string filename=get_config_file("recentfiles");
1688                         std::string filename_window_size=filename+std::string("_window_size");
1689
1690                         std::ifstream file(filename.c_str());
1691                         std::ifstream file_window_size(filename_window_size.c_str());
1692
1693                         if(!file_window_size)
1694                                 window_size_broken = true;
1695
1696                         while(file)
1697                         {
1698                                 std::string recent_file;
1699                                 std::string recent_file_window_size;
1700                                 getline(file,recent_file);
1701                                 if(!window_size_broken)
1702                                         getline(file_window_size,recent_file_window_size);
1703                                 if(!recent_file.empty())
1704                                 {
1705                                         if(!window_size_broken && !file_window_size)
1706                                                 window_size_broken = true;
1707                                         if (std::ifstream(recent_file.c_str()))
1708                                         {
1709                                                 if(!window_size_broken)
1710                                                         add_recent_file(recent_file,recent_file_window_size);
1711                                                 else
1712                                                         add_recent_file(recent_file);
1713                                         }
1714                                 }
1715                         }
1716                         if(!window_size_broken && file_window_size)
1717                                 window_size_broken = true;
1718
1719                         if(window_size_broken)
1720                         {
1721                                 recent_files_window_size.clear();
1722                                 recent_files_window_size.resize(recent_files.size());
1723                         }
1724                 }
1725                 std::string filename=get_config_file("settings");
1726                 if(!synfigapp::Main::settings().load_from_file(filename))
1727                 {
1728                         //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1729                         //if(!synfigapp::Main::settings().load_from_file(filename))
1730                         {
1731                                 gamma.set_gamma(1.0/2.2);
1732                                 reset_initial_window_configuration();
1733                         }
1734                 }
1735
1736         }
1737         catch(...)
1738         {
1739                 synfig::warning("Caught exception when attempting to load settings.");
1740         }
1741 }
1742
1743 void
1744 App::reset_initial_window_configuration()
1745 {
1746         Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
1747         Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
1748         Gdk::Rectangle rect;
1749         // A proper way to obtain the primary monitor is to use the
1750         // Gdk::Screen::get_primary_monitor () const member. But as it
1751         // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
1752         // primary one.
1753         screen->get_monitor_geometry(0,rect);
1754 #define hpanel_width 79.0f
1755 #define hpanel_height 25.0f
1756 #define vpanel_width 20.0f
1757 #define vpanel_height 100.0f
1758 #define vdock 20.0f
1759 #define hdock 20.0f
1760
1761 /* percentages referred to width or height of the screen
1762  *---------------------------------------------------------------------*
1763  *    t   |                                                |
1764  *    o   |                                                |
1765  *    o   |                                                |vdock%
1766  *    l   |                                                |
1767  *    b   |                                                |------------
1768  *    o   |                                                |
1769  *    x   |                                                |vdock%
1770  * --------                                                |
1771  *                                                         |
1772  *                                                         |------------
1773  *                                                         |
1774  *                                                         |vdock%
1775  *                                                         |
1776  *                                                         |
1777  *-----hdock%----------------------------------------------|------------
1778  *             |                                           |
1779  *             |                                           |vdock%
1780  *             |                                           |
1781  *             |                                           |
1782  * --------------------------------------------------------------------*
1783 */
1784 // Vertical Panel
1785         int v_xpos=rect.get_x() + rect.get_width()*(1.0-vpanel_width/100.0);
1786         int v_xsize=rect.get_width()*vpanel_width/100.0;
1787         int v_ypos=rect.get_y();
1788         int v_ysize=rect.get_height()*vpanel_height/100.0;
1789         std::string v_pos(strprintf("%d %d", v_xpos, v_ypos));
1790         std::string v_size(strprintf("%d %d", v_xsize, v_ysize));
1791 // Horizontal Panel
1792         int h_xpos=rect.get_x();
1793         int h_xsize=rect.get_width()*hpanel_width/100.0;
1794         int h_ypos=rect.get_y()+ rect.get_height()*(1.0-hpanel_height/100.0);;
1795         int h_ysize=rect.get_height()*hpanel_height/100.0;
1796         std::string h_pos(strprintf("%d %d", h_xpos, h_ypos));
1797         std::string h_size(strprintf("%d %d", h_xsize, h_ysize));
1798         int v_dock1 = rect.get_height()*vdock*0.8/100.0;
1799         int v_dock2 = rect.get_height()*vdock*0.6/100.0;
1800         int v_dock3 = rect.get_height()*vdock*1.1/100.0;
1801         int h_dock = rect.get_width()*hdock/100.0;
1802 //Contents size
1803         std::string v_contents(strprintf("%d %d %d", v_dock1, v_dock2, v_dock3));
1804         std::string h_contents(strprintf("%d", h_dock));
1805 // Tool Box position
1806         std::string tbox_pos(strprintf("%d %d", rect.get_x(), rect.get_y()));
1807 /*
1808         synfig::info("tool box pos: %s", tbox_pos.c_str());
1809         synfig::info("v_contents sizes: %s", v_contents.c_str());
1810         synfig::info("v_pos: %s", v_pos.c_str());
1811         synfig::info("v_sizes: %s", v_size.c_str());
1812         synfig::info("h_contents sizes: %s", h_contents.c_str());
1813         synfig::info("h_pos: %s", h_pos.c_str());
1814         synfig::info("h_sizes: %s", h_size.c_str());
1815 */
1816         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1817         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1818         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size",v_contents);
1819         synfigapp::Main::settings().set_value("dock.dialog.1.size",v_size);
1820         synfigapp::Main::settings().set_value("dock.dialog.1.pos",v_pos);
1821         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1822         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1823         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size",h_contents);
1824         synfigapp::Main::settings().set_value("dock.dialog.2.size",h_size);
1825         synfigapp::Main::settings().set_value("dock.dialog.2.pos",h_pos);
1826         synfigapp::Main::settings().set_value("window.toolbox.pos",tbox_pos);
1827
1828         dock_manager->show_all_dock_dialogs();
1829 }
1830
1831 void
1832 App::reset_initial_preferences()
1833 {
1834         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1835         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1836 #ifdef SINGLE_THREADED
1837         synfigapp::Main::settings().set_value("pref.single_threaded","1");
1838 #endif
1839         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1840         synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
1841         synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
1842         synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
1843         synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
1844         synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
1845         synfigapp::Main::settings().set_value("pref.preferred_fps","24.0");
1846         synfigapp::Main::settings().set_value("pref.predefined_fps",DEFAULT_PREDEFINED_FPS);
1847
1848 }
1849
1850 bool
1851 App::shutdown_request(GdkEventAny*)
1852 {
1853         quit();
1854         return true;
1855         //return !shutdown_in_progress;
1856 }
1857
1858 void
1859 App::quit()
1860 {
1861         if(shutdown_in_progress)return;
1862
1863
1864         get_ui_interface()->task(_("Quit Request"));
1865         if(Busy::count)
1866         {
1867                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1868                 return;
1869         }
1870
1871         std::list<etl::handle<Instance> >::iterator iter;
1872         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1873         {
1874                 if(!(*iter)->safe_close())
1875                         return;
1876
1877 /*
1878                 if((*iter)->synfigapp::Instance::get_action_count())
1879                 {
1880                         handle<synfigapp::UIInterface> uim;
1881                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1882                         assert(uim);
1883                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1884                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1885                         {
1886                                 case synfigapp::UIInterface::RESPONSE_NO:
1887                                         break;
1888                                 case synfigapp::UIInterface::RESPONSE_YES:
1889                                         (*iter)->save();
1890                                         break;
1891                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1892                                         return;
1893                                 default:
1894                                         assert(0);
1895                                         return;
1896                         }
1897                 }
1898
1899
1900                 if((*iter)->synfigapp::Instance::is_modified())
1901                 {
1902                         handle<synfigapp::UIInterface> uim;
1903                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1904                         assert(uim);
1905                         string str=strprintf(_("%s has changes not yet on the CVS repository.\nWould you like to commit these changes?"),(*iter)->get_file_name().c_str() );
1906                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1907                         {
1908                                 case synfigapp::UIInterface::RESPONSE_NO:
1909                                         break;
1910                                 case synfigapp::UIInterface::RESPONSE_YES:
1911                                         (*iter)->dialog_cvs_commit();
1912                                         break;
1913                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1914                                         return;
1915                                 default:
1916                                         assert(0);
1917                                         return;
1918                         }
1919                 }
1920 */
1921
1922                 // This next line causes things to crash for some reason
1923                 //(*iter)->close();
1924         }
1925
1926         shutdown_in_progress=true;
1927
1928         instance_list.clear();
1929
1930         while(studio::App::events_pending())studio::App::iteration(false);
1931
1932         Gtk::Main::quit();
1933         auto_recover->normal_shutdown();
1934
1935         get_ui_interface()->task(_("Quit Request sent"));
1936 }
1937
1938 void
1939 App::show_setup()
1940 {
1941         dialog_setup->refresh();
1942         dialog_setup->show();
1943 }
1944
1945 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1946 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1947
1948 //#ifdef WIN32
1949 //#define USE_WIN32_FILE_DIALOGS 1
1950 //#endif
1951
1952 #ifdef USE_WIN32_FILE_DIALOGS
1953 static OPENFILENAME ofn={};
1954 #endif
1955
1956 #ifdef WIN32
1957 #include <gdk/gdkwin32.h>
1958 #endif
1959
1960 bool
1961 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1962 {
1963         // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1964
1965 #ifdef USE_WIN32_FILE_DIALOGS
1966         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1967
1968         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1969         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1970         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1971
1972         ofn.lStructSize=sizeof(OPENFILENAME);
1973         ofn.hwndOwner = hWnd;
1974         ofn.hInstance = hInstance;
1975         ofn.lpstrFilter = szFilter;
1976 //      ofn.lpstrCustomFilter=NULL;
1977 //      ofn.nMaxCustFilter=0;
1978 //      ofn.nFilterIndex=0;
1979 //      ofn.lpstrFile=NULL;
1980         ofn.nMaxFile=MAX_PATH;
1981 //      ofn.lpstrFileTitle=NULL;
1982 //      ofn.lpstrInitialDir=NULL;
1983 //      ofn.lpstrTitle=NULL;
1984         ofn.Flags=OFN_HIDEREADONLY;
1985 //      ofn.nFileOffset=0;
1986 //      ofn.nFileExtension=0;
1987         ofn.lpstrDefExt=TEXT("sif");
1988 //      ofn.lCustData = 0l;
1989         ofn.lpfnHook=NULL;
1990 //      ofn.lpTemplateName=NULL;
1991
1992         CHAR szFilename[MAX_PATH];
1993         CHAR szTitle[500];
1994         strcpy(szFilename,filename.c_str());
1995         strcpy(szTitle,title.c_str());
1996
1997         ofn.lpstrFile=szFilename;
1998         ofn.lpstrFileTitle=szTitle;
1999
2000         if(GetOpenFileName(&ofn))
2001         {
2002                 filename=szFilename;
2003                 return true;
2004         }
2005         return false;
2006
2007 #else   // not USE_WIN32_FILE_DIALOGS
2008         synfig::String prev_path;
2009
2010         if(!_preferences.get_value(preference, prev_path))
2011                 prev_path = ".";
2012
2013         prev_path = absolute_path(prev_path);
2014
2015     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2016
2017     dialog->set_current_folder(prev_path);
2018     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2019     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
2020
2021     if (filename.empty())
2022                 dialog->set_filename(prev_path);
2023         else if (is_absolute_path(filename))
2024                 dialog->set_filename(filename);
2025         else
2026                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2027
2028     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2029         filename = dialog->get_filename();
2030                 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2031                 _preferences.set_value(preference, dirname(filename));
2032         delete dialog;
2033         return true;
2034     }
2035
2036     delete dialog;
2037     return false;
2038 #endif   // not USE_WIN32_FILE_DIALOGS
2039 }
2040
2041 bool
2042 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
2043 {
2044         // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2045
2046 #if USE_WIN32_FILE_DIALOGS
2047         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
2048
2049         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2050         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2051         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2052
2053         ofn.lStructSize=sizeof(OPENFILENAME);
2054         ofn.hwndOwner = hWnd;
2055         ofn.hInstance = hInstance;
2056         ofn.lpstrFilter = szFilter;
2057 //      ofn.lpstrCustomFilter=NULL;
2058 //      ofn.nMaxCustFilter=0;
2059 //      ofn.nFilterIndex=0;
2060 //      ofn.lpstrFile=NULL;
2061         ofn.nMaxFile=MAX_PATH;
2062 //      ofn.lpstrFileTitle=NULL;
2063 //      ofn.lpstrInitialDir=NULL;
2064 //      ofn.lpstrTitle=NULL;
2065         ofn.Flags=OFN_OVERWRITEPROMPT;
2066 //      ofn.nFileOffset=0;
2067 //      ofn.nFileExtension=0;
2068         ofn.lpstrDefExt=TEXT("sif");
2069 //      ofn.lCustData = 0l;
2070         ofn.lpfnHook=NULL;
2071 //      ofn.lpTemplateName=NULL;
2072
2073         CHAR szFilename[MAX_PATH];
2074         CHAR szTitle[500];
2075         strcpy(szFilename,filename.c_str());
2076         strcpy(szTitle,title.c_str());
2077
2078         ofn.lpstrFile=szFilename;
2079         ofn.lpstrFileTitle=szTitle;
2080
2081         if(GetSaveFileName(&ofn))
2082         {
2083                 filename=szFilename;
2084                 _preferences.set_value(preference,dirname(filename));
2085                 return true;
2086         }
2087         return false;
2088 #else
2089         synfig::String prev_path;
2090
2091         if(!_preferences.get_value(preference, prev_path))
2092                 prev_path=".";
2093
2094         prev_path = absolute_path(prev_path);
2095
2096     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2097
2098     dialog->set_current_folder(prev_path);
2099     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2100     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
2101
2102         Widget_Enum *file_type_enum = 0;
2103         if (preference == ANIMATION_DIR_PREFERENCE)
2104         {
2105                 file_type_enum = manage(new Widget_Enum());
2106                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2107                                 .add_enum_value(synfig::RELEASE_VERSION_0_62_02, "0.62.02", strprintf("0.62.02 (%s)", _("current")))
2108                                 .add_enum_value(synfig::RELEASE_VERSION_0_62_01, "0.62.01", "0.62.01")
2109                                 .add_enum_value(synfig::RELEASE_VERSION_0_62_00, "0.62.00", "0.61.00")
2110                                 .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", "0.61.09")
2111                                 .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2112                                 .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2113                                 .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2114                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2115
2116                 Gtk::HBox *hbox = manage(new Gtk::HBox);
2117                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2118                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2119                 hbox->show_all();
2120
2121                 dialog->set_extra_widget(*hbox);
2122         }
2123
2124     if (filename.empty())
2125                 dialog->set_filename(prev_path);
2126     else
2127         {
2128                 std::string full_path;
2129                 if (is_absolute_path(filename))
2130                         full_path = filename;
2131                 else
2132                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2133
2134                 // select the file if it exists
2135                 dialog->set_filename(full_path);
2136
2137                 // if the file doesn't exist, put its name into the filename box
2138                 struct stat s;
2139                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2140                         dialog->set_current_name(basename(filename));
2141         }
2142
2143     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2144                 if (preference == ANIMATION_DIR_PREFERENCE)
2145                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2146         filename = dialog->get_filename();
2147                 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2148                 _preferences.set_value(preference, dirname(filename));
2149         delete dialog;
2150         return true;
2151     }
2152
2153     delete dialog;
2154     return false;
2155 #endif
2156 }
2157
2158 void
2159 App::dialog_error_blocking(const std::string &title, const std::string &message)
2160 {
2161         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2162         dialog.set_title(title);
2163         dialog.show();
2164         dialog.run();
2165 }
2166
2167 void
2168 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2169 {
2170         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2171         dialog.set_title(title);
2172         dialog.show();
2173         dialog.run();
2174 }
2175
2176 bool
2177 App::dialog_yes_no(const std::string &title, const std::string &message)
2178 {
2179         Gtk::Dialog dialog(
2180                 title,          // Title
2181                 true,           // Modal
2182                 true            // use_separator
2183         );
2184         Gtk::Label label(message);
2185         label.show();
2186
2187         dialog.get_vbox()->pack_start(label);
2188         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2189         dialog.add_button(Gtk::StockID("gtk-no"),0);
2190         dialog.show();
2191         return dialog.run();
2192 }
2193
2194 int
2195 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2196 {
2197         Gtk::Dialog dialog(
2198                 title,          // Title
2199                 true,           // Modal
2200                 true            // use_separator
2201         );
2202         Gtk::Label label(message);
2203         label.show();
2204
2205         dialog.get_vbox()->pack_start(label);
2206         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2207         dialog.add_button(Gtk::StockID("gtk-no"),0);
2208         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2209         dialog.show();
2210         return dialog.run();
2211 }
2212
2213 void
2214 App::dialog_not_implemented()
2215 {
2216         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2217         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2218         dialog.run();
2219 }
2220
2221 static bool
2222 try_open_url(const std::string &url)
2223 {
2224 #ifdef WIN32
2225         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2226 #else // !WIN32
2227         std::vector<std::string> command_line;
2228         std::vector<std::string> browsers;
2229         browsers.reserve(23);
2230
2231         // Browser wrapper scripts
2232 #ifdef USE_OPEN_FOR_URLS
2233         browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
2234 #endif
2235         browsers.push_back("xdg-open");          // XDG wrapper
2236         browsers.push_back("sensible-browser");  // Debian wrapper
2237         browsers.push_back("gnome-open");        // GNOME wrapper
2238         browsers.push_back("kfmclient");         // KDE wrapper
2239         browsers.push_back("exo-open");          // XFCE wrapper
2240
2241         // Alternatives system
2242         browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2243         browsers.push_back("x-www-browser");     // Debian GUI alternative
2244
2245         // Individual browsers
2246         browsers.push_back("firefox");
2247         browsers.push_back("epiphany-browser");
2248         browsers.push_back("epiphany");
2249         browsers.push_back("konqueror");
2250         browsers.push_back("iceweasel");
2251         browsers.push_back("mozilla");
2252         browsers.push_back("netscape");
2253         browsers.push_back("icecat");
2254         browsers.push_back("galeon");
2255         browsers.push_back("midori");
2256         browsers.push_back("safari");
2257         browsers.push_back("opera");
2258         browsers.push_back("amaya");
2259         browsers.push_back("netsurf");
2260         browsers.push_back("dillo");
2261
2262         // Try the user-specified browser first
2263         command_line.push_back(App::browser_command);
2264         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2265         command_line.push_back(url);
2266
2267         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2268         catch( Glib::SpawnError& exception ){
2269
2270                 while ( !browsers.empty() )
2271                 {
2272                         // Skip the browser if we already tried it
2273                         if( browsers[0] == App::browser_command )
2274                                 continue;
2275
2276                         // Construct the command line
2277                         command_line.clear();
2278                         command_line.push_back(browsers[0]);
2279                         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2280                         command_line.push_back(url);
2281
2282                         // Remove the browser from the list
2283                         browsers.erase(browsers.begin());
2284
2285                         // Try to spawn the browser
2286                         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2287                         // Failed, move on to the next one
2288                         catch(Glib::SpawnError& exception){ continue; }
2289                         return true; // No exception means we succeeded!
2290                 }
2291         }
2292
2293         return false;
2294 #endif // !WIN32
2295 }
2296
2297 void
2298 App::dialog_help()
2299 {
2300         if (!try_open_url("http://synfig.org/wiki/Category:Manual"))
2301         {
2302                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2303                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://synfig.org/wiki/Category:Manual"));
2304                 dialog.set_title(_("Help"));
2305                 dialog.run();
2306         }
2307 }
2308
2309 void
2310 App::open_url(const std::string &url)
2311 {
2312         if(!try_open_url(url))
2313         {
2314                 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2315                 dialog.set_secondary_text(url);
2316                 dialog.set_title(_("No browser found"));
2317                 dialog.run();
2318         }
2319 }
2320
2321 bool
2322 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2323 {
2324         Gtk::Dialog dialog(
2325                 title,          // Title
2326                 true,           // Modal
2327                 true);          // use_separator
2328
2329         Gtk::Label label(message);
2330         label.show();
2331         dialog.get_vbox()->pack_start(label);
2332
2333         Gtk::Entry entry;
2334         entry.set_text(text);
2335         entry.show();
2336         entry.set_activates_default(true);
2337
2338         dialog.get_vbox()->pack_start(entry);
2339
2340         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2341         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2342         dialog.set_default_response(Gtk::RESPONSE_OK);
2343
2344         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2345         dialog.show();
2346
2347         if(dialog.run()!=Gtk::RESPONSE_OK)
2348                 return false;
2349
2350         text=entry.get_text();
2351
2352         return true;
2353 }
2354
2355 bool
2356 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2357 {
2358         Gtk::Dialog dialog(
2359                 title,          // Title
2360                 true,           // Modal
2361                 true);          // use_separator
2362
2363         Gtk::Label label(message);
2364         label.show();
2365         dialog.get_vbox()->pack_start(label);
2366
2367         Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2368         text_buffer->set_text(text);
2369         Gtk::TextView text_view(text_buffer);
2370         text_view.show();
2371
2372         dialog.get_vbox()->pack_start(text_view);
2373
2374         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2375         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2376         dialog.set_default_response(Gtk::RESPONSE_OK);
2377
2378         //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2379         dialog.show();
2380
2381         if(dialog.run()!=Gtk::RESPONSE_OK)
2382                 return false;
2383
2384         text=text_buffer->get_text();
2385
2386         return true;
2387 }
2388
2389 bool
2390 App::open(std::string filename)
2391 {
2392         return open_as(filename,filename);
2393 }
2394
2395 // this is called from autorecover.cpp:
2396 //   App::open_as(get_shadow_file_name(filename),filename)
2397 // other than that, 'filename' and 'as' are the same
2398 bool
2399 App::open_as(std::string filename,std::string as)
2400 {
2401 #ifdef WIN32
2402     char long_name[1024];
2403     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2404         // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2405         // don't use it if that's the case
2406         if (long_name[0] != '\0')
2407                 as=long_name;
2408 #endif
2409
2410         try
2411         {
2412                 OneMoment one_moment;
2413                 String errors, warnings;
2414
2415                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as,errors,warnings));
2416                 if(canvas && get_instance(canvas))
2417                 {
2418                         get_instance(canvas)->find_canvas_view(canvas)->present();
2419                         info("%s is already open", filename.c_str());
2420                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2421                 }
2422                 else
2423                 {
2424                         if(!canvas)
2425                                 throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
2426
2427                         if (warnings != "")
2428                                 dialog_warning_blocking(_("Warnings"), strprintf("%s:\n\n%s", _("Warnings"), warnings.c_str()));
2429
2430                         if (as.find(custom_filename_prefix.c_str()) != 0)
2431                                 add_recent_file(as);
2432
2433                         handle<Instance> instance(Instance::create(canvas));
2434
2435                         if(!instance)
2436                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2437
2438                         set_recent_file_window_size(instance);
2439
2440                         one_moment.hide();
2441
2442                         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)")))
2443                                 instance->dialog_cvs_update();
2444                 }
2445         }
2446         catch(String x)
2447         {
2448                 dialog_error_blocking(_("Error"), x);
2449                 return false;
2450         }
2451         catch(runtime_error x)
2452         {
2453                 dialog_error_blocking(_("Error"), x.what());
2454                 return false;
2455         }
2456         catch(...)
2457         {
2458                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2459                 return false;
2460         }
2461
2462         return true;
2463 }
2464
2465
2466 void
2467 App::new_instance()
2468 {
2469         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2470
2471         String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
2472         canvas->set_name(file_name);
2473         file_name += ".sifz";
2474
2475         canvas->rend_desc().set_frame_rate(preferred_fps);
2476         canvas->rend_desc().set_time_start(0.0);
2477         canvas->rend_desc().set_time_end(5.0);
2478         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2479         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2480         // The top left and botton right positions are expressed in units
2481         // Original convention is that 1 unit = 60 pixels
2482         canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
2483         canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
2484         canvas->rend_desc().set_w(preferred_x_size);
2485         canvas->rend_desc().set_h(preferred_y_size);
2486         canvas->rend_desc().set_antialias(1);
2487         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2488         canvas->set_file_name(file_name);
2489
2490         handle<Instance> instance = Instance::create(canvas);
2491
2492         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2493                 instance->find_canvas_view(canvas)->canvas_properties.present();
2494 }
2495
2496 void
2497 App::dialog_open(string filename)
2498 {
2499         if (filename.empty())
2500                 filename="*.sif";
2501
2502         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2503         {
2504                 // If the filename still has wildcards, then we should
2505                 // continue looking for the file we want
2506                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2507                         continue;
2508
2509                 if(open(filename))
2510                         break;
2511
2512                 get_ui_interface()->error(_("Unable to open file"));
2513         }
2514 }
2515
2516 void
2517 App::set_selected_instance(etl::loose_handle<Instance> instance)
2518 {
2519 /*      if(get_selected_instance()==instance)
2520         {
2521                 selected_instance=instance;
2522                 signal_instance_selected()(instance);
2523                 return;
2524         }
2525         else
2526         {
2527 */
2528                 selected_instance=instance;
2529                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2530                 {
2531                         if(instance)
2532                         {
2533                                 instance->focus(instance->get_canvas());
2534                         }
2535                         else
2536                                 set_selected_canvas_view(0);
2537                 }
2538                 signal_instance_selected()(instance);
2539 }
2540
2541 void
2542 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2543 {
2544         selected_canvas_view=canvas_view;
2545         signal_canvas_view_focus()(selected_canvas_view);
2546         if(canvas_view)
2547         {
2548                 selected_instance=canvas_view->get_instance();
2549                 signal_instance_selected()(canvas_view->get_instance());
2550         }
2551 /*
2552         if(get_selected_canvas_view()==canvas_view)
2553         {
2554                 signal_canvas_view_focus()(selected_canvas_view);
2555                 signal_instance_selected()(canvas_view->get_instance());
2556                 return;
2557         }
2558         selected_canvas_view=canvas_view;
2559         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2560                 set_selected_instance(canvas_view->get_instance());
2561         signal_canvas_view_focus()(selected_canvas_view);
2562 */
2563 }
2564
2565 etl::loose_handle<Instance>
2566 App::get_instance(etl::handle<synfig::Canvas> canvas)
2567 {
2568         if(!canvas) return 0;
2569         canvas=canvas->get_root();
2570
2571         std::list<etl::handle<Instance> >::iterator iter;
2572         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2573         {
2574                 if((*iter)->get_canvas()==canvas)
2575                         return *iter;
2576         }
2577         return 0;
2578 }
2579
2580 void
2581 App::dialog_about()
2582 {
2583         if(about)
2584                 about->show();
2585 }
2586
2587 void
2588 studio::App::undo()
2589 {
2590         if(selected_instance)
2591                 selected_instance->undo();
2592 }
2593
2594 void
2595 studio::App::redo()
2596 {
2597         if(selected_instance)
2598                 selected_instance->redo();
2599 }
2600
2601 synfig::String
2602 studio::App::get_base_path()
2603 {
2604         return app_base_path_;
2605 }