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