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