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