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