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