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