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