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