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