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