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