Enable Single threaded renderer by default for the windows version.
[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\n"
1323                                           "before you could save all your files.\n"
1324                                           "Recover unsaved changes?"),
1325                                         _("Recover"), _("Ignore"))
1326                                 == synfigapp::UIInterface::RESPONSE_OK)
1327                         {
1328                                 int number_recovered;
1329                                 if(!auto_recover->recover(number_recovered))
1330                                         if (number_recovered)
1331                                                 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1332                                         else
1333                                                 get_ui_interface()->error(_("Unable to recover from previous crash"));
1334                                 else
1335                                         get_ui_interface()->error(
1336                                                 _("Synfig Studio has attempted to recover\n"
1337                                                   "from a previous crash. The files that it has\n"
1338                                                   "recovered are NOT YET SAVED. It would be a good\n"
1339                                                   "idea to review them and save them now."));
1340
1341                                 if (number_recovered)
1342                                         opened_any = true;
1343                         }
1344                         splash_screen.show();
1345                 }
1346
1347                 // Look for any files given on the command line,
1348                 // and load them if found.
1349                 for(;*argc>=1;(*argc)--)
1350                         if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1351                         {
1352                                 studio_init_cb.task(_("Loading files..."));
1353                                 splash_screen.hide();
1354                                 open((*argv)[*argc]);
1355                                 opened_any = true;
1356                                 splash_screen.show();
1357                         }
1358
1359                 // if no file was specified to be opened, create a new document to help new users get started more easily
1360                 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1361                         new_instance();
1362
1363                 studio_init_cb.task(_("Done."));
1364                 studio_init_cb.amount_complete(10000,10000);
1365
1366                 // To avoid problems with some window managers and gtk >= 2.18
1367                 // we should show dock dialogs after the settings load.
1368                 // If dock dialogs are shown before the settings are loaded,
1369                 // the windows manager can act over it.
1370                 // See discussions here:
1371                 // * http://synfig.org/forums/viewtopic.php?f=1&t=1131&st=0&sk=t&sd=a&start=30
1372                 // * http://synfig.org/forums/viewtopic.php?f=15&t=1062
1373                 dock_manager->show_all_dock_dialogs();
1374
1375                 toolbox->present();
1376         }
1377         catch(String x)
1378         {
1379                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1380         }
1381         catch(...)
1382         {
1383                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1384         }
1385 }
1386
1387 StateManager* App::get_state_manager() { return state_manager; }
1388
1389 App::~App()
1390 {
1391         shutdown_in_progress=true;
1392
1393         save_settings();
1394
1395         synfigapp::Main::settings().remove_domain("pref");
1396
1397         selected_instance=0;
1398
1399         // Unload all of the modules
1400         for(;!module_list_.empty();module_list_.pop_back())
1401                 ;
1402
1403         delete state_manager;
1404
1405         delete ipc;
1406
1407         delete auto_recover;
1408
1409         delete about;
1410
1411         toolbox->hide();
1412
1413         delete toolbox;
1414
1415         delete dialog_setup;
1416
1417         delete dialog_gradient;
1418
1419         delete dialog_color;
1420
1421         delete dialog_input;
1422
1423         delete dock_manager;
1424
1425         instance_list.clear();
1426 }
1427
1428 String
1429 App::get_user_app_directory()
1430 {
1431 //! \todo do we need locale_from_utf8() on non-Windows boxes too?  (bug #1837445)
1432 #ifdef WIN32
1433         return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1434 #else
1435         return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1436 #endif
1437 }
1438
1439 synfig::String
1440 App::get_config_file(const synfig::String& file)
1441 {
1442         return Glib::build_filename(get_user_app_directory(),file);
1443 }
1444
1445 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1446 void
1447 App::set_recent_file_window_size(etl::handle<Instance> instance)
1448 {
1449
1450         const std::string &canvas_window_size = *recent_files_window_size.begin();
1451
1452         if(canvas_window_size.empty())
1453                 return;
1454
1455         synfig::String::size_type current=0;
1456         bool seen_root(false), shown_non_root(false);
1457
1458         while(current != synfig::String::npos)
1459         {
1460                 // find end of first field (canvas) or return
1461                 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1462                 if(separator == synfig::String::npos) break;
1463
1464                 // find the canvas
1465                 synfig::Canvas::Handle canvas;
1466                 try {
1467                         String warnings;
1468                         canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current), warnings);
1469                 }
1470                 catch(Exception::IDNotFound) {
1471                         // can't find the canvas; skip to the next canvas or return
1472                         separator = canvas_window_size.find_first_of('\t', current);
1473                         if(separator == synfig::String::npos) return;
1474                         current = separator+1;
1475                         continue;
1476                 }
1477
1478                 if (canvas->is_root())
1479                         seen_root = true;
1480                 else
1481                         shown_non_root = true;
1482
1483                 // check that we have the tab character the ends this canvas' data or return
1484                 current = separator+1;
1485                 separator = canvas_window_size.find_first_of('\t', current);
1486                 if(separator == synfig::String::npos) return;
1487
1488                 int x,y,w,h;
1489                 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1490                 {
1491                         current = separator+1;
1492                         continue;
1493                 }
1494                 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1495                 canvasview->move(x,y);
1496                 canvasview->resize(w,h);
1497                 canvasview->present();
1498
1499                 current = separator+1;
1500         }
1501
1502         if (shown_non_root && !seen_root)
1503                 instance->find_canvas_view(instance->get_canvas())->hide();
1504 }
1505
1506 void
1507 App::add_recent_file(const etl::handle<Instance> instance)
1508 {
1509
1510         std::string canvas_window_size;
1511
1512         const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1513         Instance::CanvasViewList::const_iterator iter;
1514
1515         for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1516         {
1517                 if( !((*iter)->is_visible()) )
1518                         continue;
1519
1520                 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1521                 int x_pos, y_pos, x_size, y_size;
1522                 (*iter)->get_position(x_pos,y_pos);
1523                 (*iter)->get_size(x_size,y_size);
1524
1525                 canvas_window_size += strprintf("%s %d %d %d %d\t",
1526                                                                                 canvas->get_relative_id(canvas->get_root()).c_str(),
1527                                                                                 x_pos,  y_pos,
1528                                                                                 x_size, y_size);
1529         }
1530
1531         add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1532 }
1533
1534 void
1535 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1536 {
1537         std::string filename(file_name);
1538
1539         assert(!filename.empty());
1540
1541         if(filename.empty())
1542                 return;
1543
1544         // Toss out any "hidden" files
1545         if(basename(filename)[0]=='.')
1546                 return;
1547
1548         // If we aren't an absolute path, turn ourselves into one
1549         if(!is_absolute_path(filename))
1550                 filename=absolute_path(filename);
1551
1552         std::string old_window_size;
1553
1554         list<string>::iterator iter;
1555         list<string>::iterator iter_wsize;
1556         // Check to see if the file is already on the list.
1557         // If it is, then remove it from the list
1558         for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1559                 if(*iter==filename)
1560                 {
1561                         recent_files.erase(iter);
1562                         old_window_size = *iter_wsize;
1563                         recent_files_window_size.erase(iter_wsize);
1564                         break;
1565                 }
1566
1567
1568         // Push the filename to the front of the list
1569         recent_files.push_front(filename);
1570         if(window_size.empty())
1571                 recent_files_window_size.push_front(old_window_size);
1572         else
1573                 recent_files_window_size.push_front(window_size);
1574
1575         // Clean out the files at the end of the list.
1576         while(recent_files.size()>(unsigned)get_max_recent_files())
1577         {
1578                 recent_files.pop_back();
1579                 recent_files_window_size.pop_back();
1580         }
1581
1582         signal_recent_files_changed_();
1583
1584         return;
1585 }
1586
1587 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1588
1589 Time::Format
1590 App::get_time_format()
1591 {
1592         return _App_time_format;
1593 }
1594
1595 void
1596 App::set_time_format(synfig::Time::Format x)
1597 {
1598         _App_time_format=x;
1599 }
1600
1601
1602 void
1603 App::save_settings()
1604 {
1605         char * old_locale;
1606         try
1607         {
1608         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1609         setlocale(LC_NUMERIC, "C");
1610                 {
1611                         std::string filename=get_config_file("accelrc");
1612                         Gtk::AccelMap::save(filename);
1613                 }
1614                 do{
1615                         std::string filename=get_config_file("recentfiles");
1616
1617                         std::ofstream file(filename.c_str());
1618
1619                         if(!file)
1620                         {
1621                                 synfig::warning("Unable to save %s",filename.c_str());
1622                                 break;
1623                         }
1624
1625                         list<string>::reverse_iterator iter;
1626
1627                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1628                                 file<<*iter<<endl;
1629                 }while(0);
1630                 do{
1631                         std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1632
1633                         std::ofstream file(filename.c_str());
1634
1635                         if(!file)
1636                         {
1637                                 synfig::warning("Unable to save %s",filename.c_str());
1638                                 break;
1639                         }
1640
1641                         list<string>::reverse_iterator iter;
1642
1643                         for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1644                                 file<<*iter<<endl;
1645
1646                 }while(0);
1647                 std::string filename=get_config_file("settings");
1648                 synfigapp::Main::settings().save_to_file(filename);
1649         setlocale(LC_NUMERIC,old_locale);
1650         }
1651         catch(...)
1652         {
1653                 synfig::warning("Caught exception when attempting to save settings.");
1654         }
1655 }
1656
1657 void
1658 App::load_settings()
1659 {
1660         char  * old_locale;
1661         try
1662         {
1663         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1664         setlocale(LC_NUMERIC, "C");
1665                 {
1666                         std::string filename=get_config_file("accelrc");
1667                         Gtk::AccelMap::load(filename);
1668                 }
1669                 {
1670                         bool window_size_broken = false;
1671
1672                         std::string filename=get_config_file("recentfiles");
1673                         std::string filename_window_size=filename+std::string("_window_size");
1674
1675                         std::ifstream file(filename.c_str());
1676                         std::ifstream file_window_size(filename_window_size.c_str());
1677
1678                         if(!file_window_size)
1679                                 window_size_broken = true;
1680
1681                         while(file)
1682                         {
1683                                 std::string recent_file;
1684                                 std::string recent_file_window_size;
1685                                 getline(file,recent_file);
1686                                 if(!window_size_broken)
1687                                         getline(file_window_size,recent_file_window_size);
1688                                 if(!recent_file.empty())
1689                                 {
1690                                         if(!window_size_broken && !file_window_size)
1691                                                 window_size_broken = true;
1692                                         if (std::ifstream(recent_file.c_str()))
1693                                         {
1694                                                 if(!window_size_broken)
1695                                                         add_recent_file(recent_file,recent_file_window_size);
1696                                                 else
1697                                                         add_recent_file(recent_file);
1698                                         }
1699                                 }
1700                         }
1701                         if(!window_size_broken && file_window_size)
1702                                 window_size_broken = true;
1703
1704                         if(window_size_broken)
1705                         {
1706                                 recent_files_window_size.clear();
1707                                 recent_files_window_size.resize(recent_files.size());
1708                         }
1709                 }
1710                 std::string filename=get_config_file("settings");
1711                 if(!synfigapp::Main::settings().load_from_file(filename))
1712                 {
1713                         //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1714                         //if(!synfigapp::Main::settings().load_from_file(filename))
1715                         {
1716                                 gamma.set_gamma(1.0/2.2);
1717                                 reset_initial_window_configuration();
1718                         }
1719                 }
1720         setlocale(LC_NUMERIC,old_locale);
1721         }
1722         catch(...)
1723         {
1724                 synfig::warning("Caught exception when attempting to load settings.");
1725         }
1726 }
1727
1728 void
1729 App::reset_initial_window_configuration()
1730 {
1731         Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
1732         Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
1733         Gdk::Rectangle rect;
1734         // A proper way to obtain the primary monitor is to use the
1735         // Gdk::Screen::get_primary_monitor () const member. But as it
1736         // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
1737         // primary one.
1738         screen->get_monitor_geometry(0,rect);
1739 #define hpanel_width 79.0f
1740 #define hpanel_height 25.0f
1741 #define vpanel_width 20.0f
1742 #define vpanel_height 100.0f
1743 #define vdock 20.0f
1744 #define hdock 20.0f
1745
1746 /* percentages referred to width or height of the screen
1747  *---------------------------------------------------------------------*
1748  *    t   |                                                |
1749  *    o   |                                                |
1750  *    o   |                                                |vdock%
1751  *    l   |                                                |
1752  *    b   |                                                |------------
1753  *    o   |                                                |
1754  *    x   |                                                |vdock%
1755  * --------                                                |
1756  *                                                         |
1757  *                                                         |------------
1758  *                                                         |
1759  *                                                         |vdock%
1760  *                                                         |
1761  *                                                         |
1762  *-----hdock%----------------------------------------------|------------
1763  *             |                                           |
1764  *             |                                           |vdock%
1765  *             |                                           |
1766  *             |                                           |
1767  * --------------------------------------------------------------------*
1768 */
1769 // Vertical Panel
1770         int v_xpos=rect.get_x() + rect.get_width()*(1.0-vpanel_width/100.0);
1771         int v_xsize=rect.get_width()*vpanel_width/100.0;
1772         int v_ypos=rect.get_y();
1773         int v_ysize=rect.get_height()*vpanel_height/100.0;
1774         std::string v_pos(strprintf("%d %d", v_xpos, v_ypos));
1775         std::string v_size(strprintf("%d %d", v_xsize, v_ysize));
1776 // Horizontal Panel
1777         int h_xpos=rect.get_x();
1778         int h_xsize=rect.get_width()*hpanel_width/100.0;
1779         int h_ypos=rect.get_y()+ rect.get_height()*(1.0-hpanel_height/100.0);;
1780         int h_ysize=rect.get_height()*hpanel_height/100.0;
1781         std::string h_pos(strprintf("%d %d", h_xpos, h_ypos));
1782         std::string h_size(strprintf("%d %d", h_xsize, h_ysize));
1783         int v_dock1 = rect.get_height()*vdock*0.8/100.0;
1784         int v_dock2 = rect.get_height()*vdock*0.6/100.0;
1785         int v_dock3 = rect.get_height()*vdock*1.1/100.0;
1786         int h_dock = rect.get_width()*hdock/100.0;
1787 //Contents size
1788         std::string v_contents(strprintf("%d %d %d", v_dock1, v_dock2, v_dock3));
1789         std::string h_contents(strprintf("%d", h_dock));
1790 // Tool Box position
1791         std::string tbox_pos(strprintf("%d %d", rect.get_x(), rect.get_y()));
1792 /*
1793         synfig::info("tool box pos: %s", tbox_pos.c_str());
1794         synfig::info("v_contents sizes: %s", v_contents.c_str());
1795         synfig::info("v_pos: %s", v_pos.c_str());
1796         synfig::info("v_sizes: %s", v_size.c_str());
1797         synfig::info("h_contents sizes: %s", h_contents.c_str());
1798         synfig::info("h_pos: %s", h_pos.c_str());
1799         synfig::info("h_sizes: %s", h_size.c_str());
1800 */
1801         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1802         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1803         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size",v_contents);
1804         synfigapp::Main::settings().set_value("dock.dialog.1.size",v_size);
1805         synfigapp::Main::settings().set_value("dock.dialog.1.pos",v_pos);
1806         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1807         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1808         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size",h_contents);
1809         synfigapp::Main::settings().set_value("dock.dialog.2.size",h_size);
1810         synfigapp::Main::settings().set_value("dock.dialog.2.pos",h_pos);
1811         synfigapp::Main::settings().set_value("window.toolbox.pos",tbox_pos);
1812
1813         dock_manager->show_all_dock_dialogs();
1814 }
1815
1816 void
1817 App::reset_initial_preferences()
1818 {
1819         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1820         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1821 #ifdef SINGLE_THREADED
1822         synfigapp::Main::settings().set_value("pref.single_threaded","1");
1823 #endif
1824         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1825         synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
1826         synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
1827         synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
1828         synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
1829         synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
1830         synfigapp::Main::settings().set_value("pref.preferred_fps","24.0");
1831         synfigapp::Main::settings().set_value("pref.predefined_fps",DEFAULT_PREDEFINED_FPS);
1832
1833 }
1834
1835 bool
1836 App::shutdown_request(GdkEventAny*)
1837 {
1838         quit();
1839         return true;
1840         //return !shutdown_in_progress;
1841 }
1842
1843 void
1844 App::quit()
1845 {
1846         if(shutdown_in_progress)return;
1847
1848
1849         get_ui_interface()->task(_("Quit Request"));
1850         if(Busy::count)
1851         {
1852                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1853                 return;
1854         }
1855
1856         std::list<etl::handle<Instance> >::iterator iter;
1857         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1858         {
1859                 if(!(*iter)->safe_close())
1860                         return;
1861
1862 /*
1863                 if((*iter)->synfigapp::Instance::get_action_count())
1864                 {
1865                         handle<synfigapp::UIInterface> uim;
1866                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1867                         assert(uim);
1868                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1869                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1870                         {
1871                                 case synfigapp::UIInterface::RESPONSE_NO:
1872                                         break;
1873                                 case synfigapp::UIInterface::RESPONSE_YES:
1874                                         (*iter)->save();
1875                                         break;
1876                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1877                                         return;
1878                                 default:
1879                                         assert(0);
1880                                         return;
1881                         }
1882                 }
1883
1884
1885                 if((*iter)->synfigapp::Instance::is_modified())
1886                 {
1887                         handle<synfigapp::UIInterface> uim;
1888                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1889                         assert(uim);
1890                         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() );
1891                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1892                         {
1893                                 case synfigapp::UIInterface::RESPONSE_NO:
1894                                         break;
1895                                 case synfigapp::UIInterface::RESPONSE_YES:
1896                                         (*iter)->dialog_cvs_commit();
1897                                         break;
1898                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1899                                         return;
1900                                 default:
1901                                         assert(0);
1902                                         return;
1903                         }
1904                 }
1905 */
1906
1907                 // This next line causes things to crash for some reason
1908                 //(*iter)->close();
1909         }
1910
1911         shutdown_in_progress=true;
1912
1913         instance_list.clear();
1914
1915         while(studio::App::events_pending())studio::App::iteration(false);
1916
1917         Gtk::Main::quit();
1918         auto_recover->normal_shutdown();
1919
1920         get_ui_interface()->task(_("Quit Request sent"));
1921 }
1922
1923 void
1924 App::show_setup()
1925 {
1926         dialog_setup->refresh();
1927         dialog_setup->show();
1928 }
1929
1930 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1931 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1932
1933 //#ifdef WIN32
1934 //#define USE_WIN32_FILE_DIALOGS 1
1935 //#endif
1936
1937 #ifdef USE_WIN32_FILE_DIALOGS
1938 static OPENFILENAME ofn={};
1939 #endif
1940
1941 #ifdef WIN32
1942 #include <gdk/gdkwin32.h>
1943 #endif
1944
1945 bool
1946 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1947 {
1948         // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1949
1950 #ifdef USE_WIN32_FILE_DIALOGS
1951         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1952
1953         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1954         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1955         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1956
1957         ofn.lStructSize=sizeof(OPENFILENAME);
1958         ofn.hwndOwner = hWnd;
1959         ofn.hInstance = hInstance;
1960         ofn.lpstrFilter = szFilter;
1961 //      ofn.lpstrCustomFilter=NULL;
1962 //      ofn.nMaxCustFilter=0;
1963 //      ofn.nFilterIndex=0;
1964 //      ofn.lpstrFile=NULL;
1965         ofn.nMaxFile=MAX_PATH;
1966 //      ofn.lpstrFileTitle=NULL;
1967 //      ofn.lpstrInitialDir=NULL;
1968 //      ofn.lpstrTitle=NULL;
1969         ofn.Flags=OFN_HIDEREADONLY;
1970 //      ofn.nFileOffset=0;
1971 //      ofn.nFileExtension=0;
1972         ofn.lpstrDefExt=TEXT("sif");
1973 //      ofn.lCustData = 0l;
1974         ofn.lpfnHook=NULL;
1975 //      ofn.lpTemplateName=NULL;
1976
1977         CHAR szFilename[MAX_PATH];
1978         CHAR szTitle[500];
1979         strcpy(szFilename,filename.c_str());
1980         strcpy(szTitle,title.c_str());
1981
1982         ofn.lpstrFile=szFilename;
1983         ofn.lpstrFileTitle=szTitle;
1984
1985         if(GetOpenFileName(&ofn))
1986         {
1987                 filename=szFilename;
1988                 return true;
1989         }
1990         return false;
1991
1992 #else   // not USE_WIN32_FILE_DIALOGS
1993         synfig::String prev_path;
1994
1995         if(!_preferences.get_value(preference, prev_path))
1996                 prev_path = ".";
1997
1998         prev_path = absolute_path(prev_path);
1999
2000     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2001
2002     dialog->set_current_folder(prev_path);
2003     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2004     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
2005
2006     if (filename.empty())
2007                 dialog->set_filename(prev_path);
2008         else if (is_absolute_path(filename))
2009                 dialog->set_filename(filename);
2010         else
2011                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2012
2013     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2014         filename = dialog->get_filename();
2015                 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2016                 _preferences.set_value(preference, dirname(filename));
2017         delete dialog;
2018         return true;
2019     }
2020
2021     delete dialog;
2022     return false;
2023 #endif   // not USE_WIN32_FILE_DIALOGS
2024 }
2025
2026 bool
2027 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
2028 {
2029         // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2030
2031 #if USE_WIN32_FILE_DIALOGS
2032         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
2033
2034         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2035         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2036         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2037
2038         ofn.lStructSize=sizeof(OPENFILENAME);
2039         ofn.hwndOwner = hWnd;
2040         ofn.hInstance = hInstance;
2041         ofn.lpstrFilter = szFilter;
2042 //      ofn.lpstrCustomFilter=NULL;
2043 //      ofn.nMaxCustFilter=0;
2044 //      ofn.nFilterIndex=0;
2045 //      ofn.lpstrFile=NULL;
2046         ofn.nMaxFile=MAX_PATH;
2047 //      ofn.lpstrFileTitle=NULL;
2048 //      ofn.lpstrInitialDir=NULL;
2049 //      ofn.lpstrTitle=NULL;
2050         ofn.Flags=OFN_OVERWRITEPROMPT;
2051 //      ofn.nFileOffset=0;
2052 //      ofn.nFileExtension=0;
2053         ofn.lpstrDefExt=TEXT("sif");
2054 //      ofn.lCustData = 0l;
2055         ofn.lpfnHook=NULL;
2056 //      ofn.lpTemplateName=NULL;
2057
2058         CHAR szFilename[MAX_PATH];
2059         CHAR szTitle[500];
2060         strcpy(szFilename,filename.c_str());
2061         strcpy(szTitle,title.c_str());
2062
2063         ofn.lpstrFile=szFilename;
2064         ofn.lpstrFileTitle=szTitle;
2065
2066         if(GetSaveFileName(&ofn))
2067         {
2068                 filename=szFilename;
2069                 _preferences.set_value(preference,dirname(filename));
2070                 return true;
2071         }
2072         return false;
2073 #else
2074         synfig::String prev_path;
2075
2076         if(!_preferences.get_value(preference, prev_path))
2077                 prev_path=".";
2078
2079         prev_path = absolute_path(prev_path);
2080
2081     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2082
2083     dialog->set_current_folder(prev_path);
2084     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2085     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
2086
2087         Widget_Enum *file_type_enum = 0;
2088         if (preference == ANIMATION_DIR_PREFERENCE)
2089         {
2090                 file_type_enum = manage(new Widget_Enum());
2091                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2092                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_62_01, "0.62.01", strprintf("0.62.01 (%s)", _("current")))
2093                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_62_00, "0.62.00", "0.61.00")
2094                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", "0.61.09")
2095                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2096                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2097                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2098                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2099
2100                 Gtk::HBox *hbox = manage(new Gtk::HBox);
2101                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2102                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2103                 hbox->show_all();
2104
2105                 dialog->set_extra_widget(*hbox);
2106         }
2107
2108     if (filename.empty())
2109                 dialog->set_filename(prev_path);
2110     else
2111         {
2112                 std::string full_path;
2113                 if (is_absolute_path(filename))
2114                         full_path = filename;
2115                 else
2116                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2117
2118                 // select the file if it exists
2119                 dialog->set_filename(full_path);
2120
2121                 // if the file doesn't exist, put its name into the filename box
2122                 struct stat s;
2123                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2124                         dialog->set_current_name(basename(filename));
2125         }
2126
2127     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2128                 if (preference == ANIMATION_DIR_PREFERENCE)
2129                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2130         filename = dialog->get_filename();
2131                 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2132                 _preferences.set_value(preference, dirname(filename));
2133         delete dialog;
2134         return true;
2135     }
2136
2137     delete dialog;
2138     return false;
2139 #endif
2140 }
2141
2142 void
2143 App::dialog_error_blocking(const std::string &title, const std::string &message)
2144 {
2145         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2146         dialog.set_title(title);
2147         dialog.show();
2148         dialog.run();
2149 }
2150
2151 void
2152 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2153 {
2154         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2155         dialog.set_title(title);
2156         dialog.show();
2157         dialog.run();
2158 }
2159
2160 bool
2161 App::dialog_yes_no(const std::string &title, const std::string &message)
2162 {
2163         Gtk::Dialog dialog(
2164                 title,          // Title
2165                 true,           // Modal
2166                 true            // use_separator
2167         );
2168         Gtk::Label label(message);
2169         label.show();
2170
2171         dialog.get_vbox()->pack_start(label);
2172         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2173         dialog.add_button(Gtk::StockID("gtk-no"),0);
2174         dialog.show();
2175         return dialog.run();
2176 }
2177
2178 int
2179 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2180 {
2181         Gtk::Dialog dialog(
2182                 title,          // Title
2183                 true,           // Modal
2184                 true            // use_separator
2185         );
2186         Gtk::Label label(message);
2187         label.show();
2188
2189         dialog.get_vbox()->pack_start(label);
2190         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2191         dialog.add_button(Gtk::StockID("gtk-no"),0);
2192         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2193         dialog.show();
2194         return dialog.run();
2195 }
2196
2197 void
2198 App::dialog_not_implemented()
2199 {
2200         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2201         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2202         dialog.run();
2203 }
2204
2205 static bool
2206 try_open_url(const std::string &url)
2207 {
2208 #ifdef WIN32
2209         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2210 #else // !WIN32
2211         std::vector<std::string> command_line;
2212         std::vector<std::string> browsers;
2213         browsers.reserve(23);
2214
2215         // Browser wrapper scripts
2216 #ifdef USE_OPEN_FOR_URLS
2217         browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
2218 #endif
2219         browsers.push_back("xdg-open");          // XDG wrapper
2220         browsers.push_back("sensible-browser");  // Debian wrapper
2221         browsers.push_back("gnome-open");        // GNOME wrapper
2222         browsers.push_back("kfmclient");         // KDE wrapper
2223         browsers.push_back("exo-open");          // XFCE wrapper
2224
2225         // Alternatives system
2226         browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2227         browsers.push_back("x-www-browser");     // Debian GUI alternative
2228
2229         // Individual browsers
2230         browsers.push_back("firefox");
2231         browsers.push_back("epiphany-browser");
2232         browsers.push_back("epiphany");
2233         browsers.push_back("konqueror");
2234         browsers.push_back("iceweasel");
2235         browsers.push_back("mozilla");
2236         browsers.push_back("netscape");
2237         browsers.push_back("icecat");
2238         browsers.push_back("galeon");
2239         browsers.push_back("midori");
2240         browsers.push_back("safari");
2241         browsers.push_back("opera");
2242         browsers.push_back("amaya");
2243         browsers.push_back("netsurf");
2244         browsers.push_back("dillo");
2245
2246         // Try the user-specified browser first
2247         command_line.push_back(App::browser_command);
2248         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2249         command_line.push_back(url);
2250
2251         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2252         catch( Glib::SpawnError& exception ){
2253
2254                 while ( !browsers.empty() )
2255                 {
2256                         // Skip the browser if we already tried it
2257                         if( browsers[0] == App::browser_command )
2258                                 continue;
2259
2260                         // Construct the command line
2261                         command_line.clear();
2262                         command_line.push_back(browsers[0]);
2263                         if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2264                         command_line.push_back(url);
2265
2266                         // Remove the browser from the list
2267                         browsers.erase(browsers.begin());
2268
2269                         // Try to spawn the browser
2270                         try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2271                         // Failed, move on to the next one
2272                         catch(Glib::SpawnError& exception){ continue; }
2273                         return true; // No exception means we succeeded!
2274                 }
2275         }
2276
2277         return false;
2278 #endif // !WIN32
2279 }
2280
2281 void
2282 App::dialog_help()
2283 {
2284         if (!try_open_url("http://synfig.org/wiki/Category:Manual"))
2285         {
2286                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2287                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://synfig.org/wiki/Category:Manual"));
2288                 dialog.set_title(_("Help"));
2289                 dialog.run();
2290         }
2291 }
2292
2293 void
2294 App::open_url(const std::string &url)
2295 {
2296         if(!try_open_url(url))
2297         {
2298                 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2299                 dialog.set_secondary_text(url);
2300                 dialog.set_title(_("No browser found"));
2301                 dialog.run();
2302         }
2303 }
2304
2305 bool
2306 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2307 {
2308         Gtk::Dialog dialog(
2309                 title,          // Title
2310                 true,           // Modal
2311                 true);          // use_separator
2312
2313         Gtk::Label label(message);
2314         label.show();
2315         dialog.get_vbox()->pack_start(label);
2316
2317         Gtk::Entry entry;
2318         entry.set_text(text);
2319         entry.show();
2320         entry.set_activates_default(true);
2321
2322         dialog.get_vbox()->pack_start(entry);
2323
2324         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2325         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2326         dialog.set_default_response(Gtk::RESPONSE_OK);
2327
2328         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2329         dialog.show();
2330
2331         if(dialog.run()!=Gtk::RESPONSE_OK)
2332                 return false;
2333
2334         text=entry.get_text();
2335
2336         return true;
2337 }
2338
2339 bool
2340 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2341 {
2342         Gtk::Dialog dialog(
2343                 title,          // Title
2344                 true,           // Modal
2345                 true);          // use_separator
2346
2347         Gtk::Label label(message);
2348         label.show();
2349         dialog.get_vbox()->pack_start(label);
2350
2351         Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2352         text_buffer->set_text(text);
2353         Gtk::TextView text_view(text_buffer);
2354         text_view.show();
2355
2356         dialog.get_vbox()->pack_start(text_view);
2357
2358         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2359         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2360         dialog.set_default_response(Gtk::RESPONSE_OK);
2361
2362         //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2363         dialog.show();
2364
2365         if(dialog.run()!=Gtk::RESPONSE_OK)
2366                 return false;
2367
2368         text=text_buffer->get_text();
2369
2370         return true;
2371 }
2372
2373 bool
2374 App::open(std::string filename)
2375 {
2376         return open_as(filename,filename);
2377 }
2378
2379 // this is called from autorecover.cpp:
2380 //   App::open_as(get_shadow_file_name(filename),filename)
2381 // other than that, 'filename' and 'as' are the same
2382 bool
2383 App::open_as(std::string filename,std::string as)
2384 {
2385 #ifdef WIN32
2386     char long_name[1024];
2387     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2388         // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2389         // don't use it if that's the case
2390         if (long_name[0] != '\0')
2391                 as=long_name;
2392 #endif
2393
2394         try
2395         {
2396                 OneMoment one_moment;
2397                 String errors, warnings;
2398
2399                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as,errors,warnings));
2400                 if(canvas && get_instance(canvas))
2401                 {
2402                         get_instance(canvas)->find_canvas_view(canvas)->present();
2403                         info("%s is already open", filename.c_str());
2404                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2405                 }
2406                 else
2407                 {
2408                         if(!canvas)
2409                                 throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
2410
2411                         if (warnings != "")
2412                                 dialog_warning_blocking(_("Warnings"), strprintf("%s:\n\n%s", _("Warnings"), warnings.c_str()));
2413
2414                         if (as.find(custom_filename_prefix.c_str()) != 0)
2415                                 add_recent_file(as);
2416
2417                         handle<Instance> instance(Instance::create(canvas));
2418
2419                         if(!instance)
2420                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2421
2422                         set_recent_file_window_size(instance);
2423
2424                         one_moment.hide();
2425
2426                         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)")))
2427                                 instance->dialog_cvs_update();
2428                 }
2429         }
2430         catch(String x)
2431         {
2432                 dialog_error_blocking(_("Error"), x);
2433                 return false;
2434         }
2435         catch(runtime_error x)
2436         {
2437                 dialog_error_blocking(_("Error"), x.what());
2438                 return false;
2439         }
2440         catch(...)
2441         {
2442                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2443                 return false;
2444         }
2445
2446         return true;
2447 }
2448
2449
2450 void
2451 App::new_instance()
2452 {
2453         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2454
2455         String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
2456         canvas->set_name(file_name);
2457         file_name += ".sifz";
2458
2459         canvas->rend_desc().set_frame_rate(preferred_fps);
2460         canvas->rend_desc().set_time_start(0.0);
2461         canvas->rend_desc().set_time_end(5.0);
2462         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2463         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2464         // The top left and botton right positions are expressed in units
2465         // Original convention is that 1 unit = 60 pixels
2466         canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
2467         canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
2468         canvas->rend_desc().set_w(preferred_x_size);
2469         canvas->rend_desc().set_h(preferred_y_size);
2470         canvas->rend_desc().set_antialias(1);
2471         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2472         canvas->set_file_name(file_name);
2473
2474         handle<Instance> instance = Instance::create(canvas);
2475
2476         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2477                 instance->find_canvas_view(canvas)->canvas_properties.present();
2478 }
2479
2480 void
2481 App::dialog_open(string filename)
2482 {
2483         if (filename.empty())
2484                 filename="*.sif";
2485
2486         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2487         {
2488                 // If the filename still has wildcards, then we should
2489                 // continue looking for the file we want
2490                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2491                         continue;
2492
2493                 if(open(filename))
2494                         break;
2495
2496                 get_ui_interface()->error(_("Unable to open file"));
2497         }
2498 }
2499
2500 void
2501 App::set_selected_instance(etl::loose_handle<Instance> instance)
2502 {
2503 /*      if(get_selected_instance()==instance)
2504         {
2505                 selected_instance=instance;
2506                 signal_instance_selected()(instance);
2507                 return;
2508         }
2509         else
2510         {
2511 */
2512                 selected_instance=instance;
2513                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2514                 {
2515                         if(instance)
2516                         {
2517                                 instance->focus(instance->get_canvas());
2518                         }
2519                         else
2520                                 set_selected_canvas_view(0);
2521                 }
2522                 signal_instance_selected()(instance);
2523 }
2524
2525 void
2526 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2527 {
2528         selected_canvas_view=canvas_view;
2529         signal_canvas_view_focus()(selected_canvas_view);
2530         if(canvas_view)
2531         {
2532                 selected_instance=canvas_view->get_instance();
2533                 signal_instance_selected()(canvas_view->get_instance());
2534         }
2535 /*
2536         if(get_selected_canvas_view()==canvas_view)
2537         {
2538                 signal_canvas_view_focus()(selected_canvas_view);
2539                 signal_instance_selected()(canvas_view->get_instance());
2540                 return;
2541         }
2542         selected_canvas_view=canvas_view;
2543         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2544                 set_selected_instance(canvas_view->get_instance());
2545         signal_canvas_view_focus()(selected_canvas_view);
2546 */
2547 }
2548
2549 etl::loose_handle<Instance>
2550 App::get_instance(etl::handle<synfig::Canvas> canvas)
2551 {
2552         if(!canvas) return 0;
2553         canvas=canvas->get_root();
2554
2555         std::list<etl::handle<Instance> >::iterator iter;
2556         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2557         {
2558                 if((*iter)->get_canvas()==canvas)
2559                         return *iter;
2560         }
2561         return 0;
2562 }
2563
2564 void
2565 App::dialog_about()
2566 {
2567         if(about)
2568                 about->show();
2569 }
2570
2571 void
2572 studio::App::undo()
2573 {
2574         if(selected_instance)
2575                 selected_instance->undo();
2576 }
2577
2578 void
2579 studio::App::redo()
2580 {
2581         if(selected_instance)
2582                 selected_instance->redo();
2583 }
2584
2585 synfig::String
2586 studio::App::get_base_path()
2587 {
2588         return app_base_path_;
2589 }