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