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