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