Since the last change will require a recompilation of pretty much everything, I took...
[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>//seek-begin","Home");
1044         ACCEL("<Actions>//seek-end","End");
1045
1046         ACCEL("<Actions>//state-normal",      "<Mod1>a");
1047         ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1048         ACCEL("<Actions>//state-scale",       "<Mod1>d");
1049         ACCEL("<Actions>//state-rotate",      "<Mod1>s");
1050
1051         ACCEL("<Actions>//state-bline",       "<Mod1>b");
1052         ACCEL("<Actions>//state-circle",      "<Mod1>c");
1053         ACCEL("<Actions>//state-rectangle",   "<Mod1>r");
1054         ACCEL("<Actions>//state-gradient",    "<Mod1>g");
1055
1056         ACCEL("<Actions>//state-eyedrop",     "<Mod1>e");
1057         ACCEL("<Actions>//state-fill",        "<Mod1>f");
1058         ACCEL("<Actions>//state-zoom",        "<Mod1>z");
1059         ACCEL("<Actions>//state-polygon",     "<Mod1>p");
1060
1061         ACCEL("<Actions>//state-draw",        "<Mod1>w");
1062         ACCEL("<Actions>//state-sketch",      "<Mod1>k");
1063         ACCEL("<Actions>//state-width",       "<Mod1>t");
1064         ACCEL("<Actions>//state-mirror",      "<Mod1>m");
1065
1066         ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1067
1068 #undef ACCEL
1069 }
1070
1071 #ifdef _WIN32
1072 #define mkdir(x,y) mkdir(x)
1073 #endif
1074
1075 /* === M E T H O D S ======================================================= */
1076
1077 App::App(int *argc, char ***argv):
1078         Gtk::Main(argc,argv),
1079         IconControler(etl::dirname((*argv)[0]))
1080 {
1081         app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1082
1083         int serial_;
1084         serial_=check_license(app_base_path_);
1085
1086
1087         ui_interface_=new GlobalUIInterface();
1088
1089         gdk_rgb_init();
1090
1091         Glib::thread_init();
1092
1093         distance_system=Distance::SYSTEM_UNITS;
1094
1095         if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1096         {
1097                 if(errno!=EEXIST)
1098                         synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1099         }
1100         else
1101         {
1102                 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1103         }
1104
1105
1106         ipc=new IPC();
1107
1108         try
1109         {
1110                 if(!SYNFIG_CHECK_VERSION())
1111                 {
1112                 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1113                 dialog_error_blocking("Synfig Studio",
1114                         "This copy of Synfig Studio was compiled against a\n"
1115                         "different version of libsynfig than what is currently\n"
1116                         "installed. Synfig Studio will now abort. Try downloading\n"
1117                         "the latest version from the Synfig website at\n"
1118                         "http://www.synfig.com/ "
1119                 );
1120                 throw 40;
1121                 }
1122         }
1123         catch(synfig::SoftwareExpired)
1124         {
1125                 cerr<<"FATAL: Software Expired"<<endl;
1126                 dialog_error_blocking("Synfig Studio",
1127                         "This copy of Synfig Studio has expired.\n"
1128                         "Please erase this copy, or download and\n"
1129                         "install the latest copy from the Synfig\n"
1130                         "website at http://www.synfig.com/ ."
1131                 );
1132                 throw 39;
1133         }
1134         Glib::set_application_name(_("Synfig Studio"));
1135
1136         About about_window;
1137         about_window.set_can_self_destruct(false);
1138         about_window.show();
1139
1140         shutdown_in_progress=false;
1141         SuperCallback synfig_init_cb(about_window.get_callback(),0,9000,10000);
1142         SuperCallback studio_init_cb(about_window.get_callback(),9000,10000,10000);
1143
1144         // Initialize the Synfig library
1145         try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1146         catch(...)
1147         {
1148                 get_ui_interface()->error("Failed to initialize synfig!");
1149                 throw;
1150         }
1151
1152         // add the preferences to the settings
1153         synfigapp::Main::settings().add_domain(&_preferences,"pref");
1154
1155         try
1156         {
1157                 studio_init_cb.task("Init UI Manager...");
1158                 App::ui_manager_=studio::UIManager::create();
1159                 init_ui_manager();
1160
1161                 studio_init_cb.task("Init Dock Manager...");
1162                 dock_manager=new studio::DockManager();
1163
1164                 studio_init_cb.task("Init State Manager...");
1165                 state_manager=new StateManager();
1166
1167                 studio_init_cb.task("Init Toolbox...");
1168                 toolbox=new studio::Toolbox();
1169
1170                 studio_init_cb.task("Init Tool Options...");
1171                 dialog_tool_options=new studio::Dialog_ToolOptions();
1172                 dock_manager->register_dockable(*dialog_tool_options);
1173
1174                 studio_init_cb.task("Init History...");
1175                 dock_history=new studio::Dock_History();
1176                 dock_manager->register_dockable(*dock_history);
1177
1178                 studio_init_cb.task("Init Canvases...");
1179                 dock_canvases=new studio::Dock_Canvases();
1180                 dock_manager->register_dockable(*dock_canvases);
1181
1182                 studio_init_cb.task("Init Keyframes...");
1183                 dock_keyframes=new studio::Dock_Keyframes();
1184                 dock_manager->register_dockable(*dock_keyframes);
1185
1186                 studio_init_cb.task("Init Layers...");
1187                 dock_layers=new studio::Dock_Layers();
1188                 dock_manager->register_dockable(*dock_layers);
1189
1190                 studio_init_cb.task("Init Params...");
1191                 dock_params=new studio::Dock_Params();
1192                 dock_manager->register_dockable(*dock_params);
1193
1194                 studio_init_cb.task("Init MetaData...");
1195                 dock_meta_data=new studio::Dock_MetaData();
1196                 dock_manager->register_dockable(*dock_meta_data);
1197
1198                 studio_init_cb.task("Init Children...");
1199                 dock_children=new studio::Dock_Children();
1200                 dock_manager->register_dockable(*dock_children);
1201
1202                 studio_init_cb.task("Init Info...");
1203                 dock_info = new studio::Dock_Info();
1204                 dock_manager->register_dockable(*dock_info);
1205
1206                 studio_init_cb.task("Init Navigator...");
1207                 dock_navigator = new studio::Dock_Navigator();
1208                 dock_manager->register_dockable(*dock_navigator);
1209
1210                 studio_init_cb.task("Init Timetrack...");
1211                 dock_timetrack = new studio::Dock_Timetrack();
1212                 dock_manager->register_dockable(*dock_timetrack);
1213
1214                 studio_init_cb.task("Init Curve Editor...");
1215                 dock_curves = new studio::Dock_Curves();
1216                 dock_manager->register_dockable(*dock_curves);
1217
1218                 studio_init_cb.task("Init Layer Groups...");
1219                 dock_layer_groups = new studio::Dock_LayerGroups();
1220                 dock_manager->register_dockable(*dock_layer_groups);
1221
1222
1223                 studio_init_cb.task("Init Color Dialog...");
1224                 dialog_color=new studio::Dialog_Color();
1225
1226                 studio_init_cb.task("Init Gradient Dialog...");
1227                 dialog_gradient=new studio::Dialog_Gradient();
1228
1229                 studio_init_cb.task("Init DeviceTracker...");
1230                 device_tracker=new studio::DeviceTracker();
1231
1232                 studio_init_cb.task("Init Tools...");
1233                 state_manager->add_state(&state_normal);
1234                 state_manager->add_state(&state_smooth_move);
1235                 state_manager->add_state(&state_scale);
1236                 state_manager->add_state(&state_rotate);
1237
1238                 state_manager->add_state(&state_bline);
1239
1240
1241                 state_manager->add_state(&state_circle);
1242                 state_manager->add_state(&state_rectangle);
1243
1244                 state_manager->add_state(&state_gradient);
1245                 state_manager->add_state(&state_eyedrop);
1246                 state_manager->add_state(&state_fill);
1247
1248                 state_manager->add_state(&state_zoom);
1249
1250                 // Enabled - it's useful to be able to work with polygons without tangent ducks getting in the way.
1251                 // I know we can switch tangent ducks off, but why not allow this kind of layer as well?
1252                 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon);
1253
1254                 // Enabled for now.  Let's see whether they're good enough yet.
1255                 if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw);
1256                 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1257
1258                 // Disabled by default - it doesn't work properly?
1259                 if(getenv("SYNFIG_ENABLE_WIDTH"    )) state_manager->add_state(&state_width);
1260
1261                 studio_init_cb.task("Init ModPalette...");
1262                 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1263
1264                 studio_init_cb.task("Init ModMirror...");
1265                 module_list_.push_back(new ModMirror()); module_list_.back()->start();
1266
1267
1268                 studio_init_cb.task("Init Setup Dialog...");
1269                 dialog_setup=new studio::Dialog_Setup();
1270
1271                 studio_init_cb.task("Init Input Dialog...");
1272                 dialog_input=new Gtk::InputDialog();
1273                 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1274                 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1275
1276                 studio_init_cb.task("Init auto recovery...");
1277                 auto_recover=new AutoRecover();
1278
1279                 studio_init_cb.amount_complete(9250,10000);
1280                 studio_init_cb.task("Loading Settings...");
1281                 load_settings();
1282                 studio_init_cb.task("Checking auto-recover...");
1283
1284                 studio_init_cb.amount_complete(9900,10000);
1285
1286                 if(auto_recover->recovery_needed())
1287                 {
1288                         about_window.hide();
1289                         if(
1290                                 get_ui_interface()->yes_no(
1291                                         "Auto Recovery",
1292                                         "Synfig Studio seems to have crashed\n"
1293                                         "before you could save all your files.\n"
1294                                         "Would you like to re-open those files\n"
1295                                         "and recover your unsaved changes?"
1296                                 )==synfigapp::UIInterface::RESPONSE_YES
1297                         )
1298                         {
1299                                 if(!auto_recover->recover())
1300                                 {
1301                                         get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1302                                 }
1303                                 else
1304                                 get_ui_interface()->error(
1305                                         _("Synfig Studio has attempted to recover\n"
1306                                         "from a previous crash. The files that it has\n"
1307                                         "recovered are NOT YET SAVED. It would be a good\n"
1308                                         "idea to review them and save them now.")
1309                                 );
1310                         }
1311                         about_window.show();
1312                 }
1313
1314                 // Look for any files given on the command line,
1315                 // and load them if found.
1316                 for(;*argc>=1;(*argc)--)
1317                         if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1318                         {
1319                                 studio_init_cb.task("Loading files...");
1320                                 about_window.hide();
1321                                 open((*argv)[*argc]);
1322                                 about_window.show();
1323                         }
1324
1325                 studio_init_cb.task("Done.");
1326                 studio_init_cb.amount_complete(10000,10000);
1327
1328                 toolbox->present();
1329         }
1330         catch(...)
1331         {
1332                 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1333         }
1334 }
1335
1336 StateManager* App::get_state_manager() { return state_manager; }
1337
1338 App::~App()
1339 {
1340         shutdown_in_progress=true;
1341
1342         save_settings();
1343
1344         synfigapp::Main::settings().remove_domain("pref");
1345
1346         selected_instance=0;
1347
1348         // Unload all of the modules
1349         for(;!module_list_.empty();module_list_.pop_back());
1350
1351         delete state_manager;
1352
1353         delete ipc;
1354
1355         delete auto_recover;
1356
1357         toolbox->hide();
1358
1359 //      studio::App::iteration(false);
1360
1361         delete toolbox;
1362
1363 //      studio::App::iteration(false);
1364
1365 //      studio::App::iteration(false);
1366
1367         delete dialog_setup;
1368
1369         delete dialog_gradient;
1370
1371         delete dialog_color;
1372
1373         delete dialog_input;
1374
1375         delete dock_manager;
1376
1377         instance_list.clear();
1378
1379 //      studio::App::iteration(false);
1380 }
1381
1382 String
1383 App::get_user_app_directory()
1384 {
1385         return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1386 }
1387
1388 synfig::String
1389 App::get_config_file(const synfig::String& file)
1390 {
1391         return Glib::build_filename(get_user_app_directory(),file);
1392 }
1393
1394 void
1395 App::add_recent_file(const std::string &file_name)
1396 {
1397         std::string filename(file_name);
1398
1399         assert(!filename.empty());
1400
1401         if(filename.empty())
1402                 return;
1403
1404         // Toss out any "hidden" files
1405         if(basename(filename)[0]=='.')
1406                 return;
1407
1408         // If we aren't an absolute path, turn outselves into one
1409         if(!is_absolute_path(filename))
1410                 filename=absolute_path(filename);
1411
1412         list<string>::iterator iter;
1413         // Check to see if the file is already on the list.
1414         // If it is, then remove it from the list
1415         for(iter=recent_files.begin();iter!=recent_files.end();iter++)
1416                 if(*iter==filename)
1417                 {
1418                         recent_files.erase(iter);
1419                         break;
1420                 }
1421
1422
1423         // Push the filename to the front of the list
1424         recent_files.push_front(filename);
1425
1426         // Clean out the files at the end of the list.
1427         while(recent_files.size()>(unsigned)get_max_recent_files())
1428                 recent_files.pop_back();
1429
1430         signal_recent_files_changed_();
1431
1432         return;
1433 }
1434
1435 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1436
1437 Time::Format
1438 App::get_time_format()
1439 {
1440         return _App_time_format;
1441 }
1442
1443 void
1444 App::set_time_format(synfig::Time::Format x)
1445 {
1446         _App_time_format=x;
1447 }
1448
1449
1450 void
1451 App::save_settings()
1452 {
1453         char * old_locale;
1454         try
1455         {
1456         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1457         setlocale(LC_NUMERIC, "C");
1458                 {
1459                         std::string filename=get_config_file("accelrc");
1460                         Gtk::AccelMap::save(filename);
1461                 }
1462                 do{
1463                         std::string filename=get_config_file("recentfiles");
1464
1465                         std::ofstream file(filename.c_str());
1466
1467                         if(!file)
1468                         {
1469                                 synfig::warning("Unable to save %s",filename.c_str());
1470                                 break;
1471                         }
1472
1473                         list<string>::reverse_iterator iter;
1474
1475                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1476                                 file<<*iter<<endl;
1477                 }while(0);
1478
1479                 std::string filename=get_config_file("settings");
1480                 synfigapp::Main::settings().save_to_file(filename);
1481         setlocale(LC_NUMERIC,old_locale);
1482         }
1483         catch(...)
1484         {
1485                 synfig::warning("Caught exception when attempting to save settings.");
1486         }
1487 }
1488
1489 void
1490 App::load_settings()
1491 {
1492         char  * old_locale;
1493         try
1494         {
1495         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1496         setlocale(LC_NUMERIC, "C");
1497                 {
1498                         std::string filename=get_config_file("accelrc");
1499                         Gtk::AccelMap::load(filename);
1500                 }
1501                 {
1502                         std::string filename=get_config_file("recentfiles");
1503
1504                         std::ifstream file(filename.c_str());
1505
1506                         while(file)
1507                         {
1508                                 std::string recent_file;
1509                                 getline(file,recent_file);
1510                                 if(!recent_file.empty())
1511                                         add_recent_file(recent_file);
1512                         }
1513                 }
1514                 std::string filename=get_config_file("settings");
1515                 if(!synfigapp::Main::settings().load_from_file(filename))
1516                 {
1517                         //std::string filename=Glib::build_filename(Glib::get_home_dir(),".synfigrc");
1518                         //if(!synfigapp::Main::settings().load_from_file(filename))
1519                         {
1520                                 gamma.set_gamma(1.0/2.2);
1521                                 synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1522                                 synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1523                                 synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1524                                 synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1525                                 synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1526                                 synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1527                                 synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1528                                 synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1529                                 synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1530                                 synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1531                                 synfigapp::Main::settings().set_value("pref.distance_system","pt");
1532                                 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1533                                 synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1534                         }
1535                 }
1536         setlocale(LC_NUMERIC,old_locale);
1537         }
1538         catch(...)
1539         {
1540                 synfig::warning("Caught exception when attempting to load settings.");
1541         }
1542 }
1543
1544 bool
1545 App::shutdown_request(GdkEventAny*)
1546 {
1547         quit();
1548         return true;
1549         //return !shutdown_in_progress;
1550 }
1551
1552 void
1553 App::quit()
1554 {
1555         if(shutdown_in_progress)return;
1556
1557
1558         get_ui_interface()->task("Quit Request");
1559         if(Busy::count)
1560         {
1561                 dialog_error_blocking("Cannot quit!","Tasks are currently running.\nPlease cancel the current tasks and try again");
1562                 return;
1563         }
1564
1565         std::list<etl::handle<Instance> >::iterator iter;
1566         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1567         {
1568                 if(!(*iter)->safe_close())
1569                         return;
1570
1571 /*
1572                 if((*iter)->synfigapp::Instance::get_action_count())
1573                 {
1574                         handle<synfigapp::UIInterface> uim;
1575                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1576                         assert(uim);
1577                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1578                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1579                         {
1580                                 case synfigapp::UIInterface::RESPONSE_NO:
1581                                         break;
1582                                 case synfigapp::UIInterface::RESPONSE_YES:
1583                                         (*iter)->save();
1584                                         break;
1585                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1586                                         return;
1587                                 default:
1588                                         assert(0);
1589                                         return;
1590                         }
1591                 }
1592
1593
1594                 if((*iter)->synfigapp::Instance::is_modified())
1595                 {
1596                         handle<synfigapp::UIInterface> uim;
1597                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1598                         assert(uim);
1599                         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() );
1600                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1601                         {
1602                                 case synfigapp::UIInterface::RESPONSE_NO:
1603                                         break;
1604                                 case synfigapp::UIInterface::RESPONSE_YES:
1605                                         (*iter)->dialog_cvs_commit();
1606                                         break;
1607                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1608                                         return;
1609                                 default:
1610                                         assert(0);
1611                                         return;
1612                         }
1613                 }
1614 */
1615
1616                 // This next line causes things to crash for some reason
1617                 //(*iter)->close();
1618         }
1619
1620         shutdown_in_progress=true;
1621
1622         instance_list.clear();
1623
1624         while(studio::App::events_pending())studio::App::iteration(false);
1625
1626         Gtk::Main::quit();
1627         auto_recover->normal_shutdown();
1628
1629         get_ui_interface()->task("Quit Request sent");
1630 }
1631
1632 void
1633 App::show_setup()
1634 {
1635         dialog_setup->refresh();
1636         dialog_setup->show();
1637 }
1638
1639 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1640 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1641
1642 //#ifdef WIN32
1643 //#define USE_WIN32_FILE_DIALOGS 1
1644 //#endif
1645
1646 #ifdef USE_WIN32_FILE_DIALOGS
1647 static OPENFILENAME ofn={};
1648 #endif
1649
1650 #ifdef WIN32
1651 #include <gdk/gdkwin32.h>
1652 #endif
1653
1654 bool
1655 App::dialog_open_file(const std::string &title, std::string &filename)
1656 {
1657 #ifdef USE_WIN32_FILE_DIALOGS
1658         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1659
1660         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1661         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1662         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1663
1664         ofn.lStructSize=sizeof(OPENFILENAME);
1665         ofn.hwndOwner = hWnd;
1666         ofn.hInstance = hInstance;
1667         ofn.lpstrFilter = szFilter;
1668 //      ofn.lpstrCustomFilter=NULL;
1669 //      ofn.nMaxCustFilter=0;
1670 //      ofn.nFilterIndex=0;
1671 //      ofn.lpstrFile=NULL;
1672         ofn.nMaxFile=MAX_PATH;
1673 //      ofn.lpstrFileTitle=NULL;
1674 //      ofn.lpstrInitialDir=NULL;
1675 //      ofn.lpstrTitle=NULL;
1676         ofn.Flags=OFN_HIDEREADONLY;
1677 //      ofn.nFileOffset=0;
1678 //      ofn.nFileExtension=0;
1679         ofn.lpstrDefExt=TEXT("sif");
1680 //      ofn.lCustData = 0l;
1681         ofn.lpfnHook=NULL;
1682 //      ofn.lpTemplateName=NULL;
1683
1684         CHAR szFilename[MAX_PATH];
1685         CHAR szTitle[500];
1686         strcpy(szFilename,filename.c_str());
1687         strcpy(szTitle,title.c_str());
1688
1689         ofn.lpstrFile=szFilename;
1690         ofn.lpstrFileTitle=szTitle;
1691
1692         if(GetOpenFileName(&ofn))
1693         {
1694                 filename=szFilename;
1695                 return true;
1696         }
1697         return false;
1698
1699 #else
1700         synfig::String prev_path;
1701         if(!_preferences.get_value("curr_path",prev_path))
1702                 prev_path=".";
1703
1704     Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_OPEN);
1705     dialog->set_current_folder(prev_path);
1706     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1707     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1708     if(!filename.empty())
1709         dialog->set_filename(filename);
1710     if(dialog->run()==GTK_RESPONSE_ACCEPT) {
1711         filename=dialog->get_filename();
1712         delete dialog;
1713         return true;
1714     }
1715     delete dialog;
1716     return false;
1717     /*
1718
1719         GtkWidget *ok;
1720         GtkWidget *cancel;
1721         int val=0;
1722
1723         GtkWidget *fileselection;
1724         fileselection = gtk_file_selection_new(title.c_str());
1725
1726
1727         if(basename(filename)==filename)
1728         {
1729                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1730         }
1731         else
1732                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1733
1734         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1735
1736         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1737         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1738
1739         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1740         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1741
1742         gtk_widget_show(fileselection);
1743
1744         while(!val)
1745                 iteration();
1746
1747
1748         if(val==1)
1749         {
1750                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1751                 _preferences.set_value("curr_path",dirname(filename));
1752         }
1753         else
1754         {
1755                 gtk_widget_destroy(fileselection);
1756                 return false;
1757         }
1758         gtk_widget_destroy(fileselection);
1759         return true;
1760     */
1761 #endif
1762 }
1763
1764 bool
1765 App::dialog_save_file(const std::string &title, std::string &filename)
1766 {
1767 #ifdef USE_WIN32_FILE_DIALOGS
1768         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1769
1770         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1771         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1772         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1773
1774         ofn.lStructSize=sizeof(OPENFILENAME);
1775         ofn.hwndOwner = hWnd;
1776         ofn.hInstance = hInstance;
1777         ofn.lpstrFilter = szFilter;
1778 //      ofn.lpstrCustomFilter=NULL;
1779 //      ofn.nMaxCustFilter=0;
1780 //      ofn.nFilterIndex=0;
1781 //      ofn.lpstrFile=NULL;
1782         ofn.nMaxFile=MAX_PATH;
1783 //      ofn.lpstrFileTitle=NULL;
1784 //      ofn.lpstrInitialDir=NULL;
1785 //      ofn.lpstrTitle=NULL;
1786         ofn.Flags=OFN_OVERWRITEPROMPT;
1787 //      ofn.nFileOffset=0;
1788 //      ofn.nFileExtension=0;
1789         ofn.lpstrDefExt=TEXT("sif");
1790 //      ofn.lCustData = 0l;
1791         ofn.lpfnHook=NULL;
1792 //      ofn.lpTemplateName=NULL;
1793
1794         CHAR szFilename[MAX_PATH];
1795         CHAR szTitle[500];
1796         strcpy(szFilename,filename.c_str());
1797         strcpy(szTitle,title.c_str());
1798
1799         ofn.lpstrFile=szFilename;
1800         ofn.lpstrFileTitle=szTitle;
1801
1802         if(GetSaveFileName(&ofn))
1803         {
1804                 filename=szFilename;
1805                 return true;
1806         }
1807         return false;
1808 #else
1809         synfig::String prev_path;
1810         if(!_preferences.get_value("curr_path",prev_path))
1811                 prev_path=".";
1812
1813     Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_SAVE);
1814     dialog->set_current_folder(prev_path);
1815     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1816     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1817     if(!filename.empty())
1818         dialog->set_filename(filename);
1819     if(dialog->run()==GTK_RESPONSE_ACCEPT) {
1820         filename=dialog->get_filename();
1821         delete dialog;
1822         return true;
1823     }
1824     delete dialog;
1825     return false;
1826 //      return dialog_open_file(title, filename);
1827 #endif
1828 }
1829
1830 bool
1831 App::dialog_saveas_file(const std::string &title, std::string &filename)
1832 {
1833 #if USE_WIN32_FILE_DIALOGS
1834         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1835
1836         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1837         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1838         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1839
1840         ofn.lStructSize=sizeof(OPENFILENAME);
1841         ofn.hwndOwner = hWnd;
1842         ofn.hInstance = hInstance;
1843         ofn.lpstrFilter = szFilter;
1844 //      ofn.lpstrCustomFilter=NULL;
1845 //      ofn.nMaxCustFilter=0;
1846 //      ofn.nFilterIndex=0;
1847 //      ofn.lpstrFile=NULL;
1848         ofn.nMaxFile=MAX_PATH;
1849 //      ofn.lpstrFileTitle=NULL;
1850 //      ofn.lpstrInitialDir=NULL;
1851 //      ofn.lpstrTitle=NULL;
1852         ofn.Flags=OFN_OVERWRITEPROMPT;
1853 //      ofn.nFileOffset=0;
1854 //      ofn.nFileExtension=0;
1855         ofn.lpstrDefExt=TEXT("sif");
1856 //      ofn.lCustData = 0l;
1857         ofn.lpfnHook=NULL;
1858 //      ofn.lpTemplateName=NULL;
1859
1860         CHAR szFilename[MAX_PATH];
1861         CHAR szTitle[500];
1862         strcpy(szFilename,filename.c_str());
1863         strcpy(szTitle,title.c_str());
1864
1865         ofn.lpstrFile=szFilename;
1866         ofn.lpstrFileTitle=szTitle;
1867
1868         if(GetSaveFileName(&ofn))
1869         {
1870                 filename=szFilename;
1871                 return true;
1872         }
1873         return false;
1874 #else
1875         synfig::String prev_path;
1876         if(!_preferences.get_value("curr_path",prev_path))
1877                 prev_path=".";
1878
1879     Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_SAVE);
1880     dialog->set_current_folder(prev_path);
1881     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1882     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1883     if(!filename.empty())
1884         dialog->set_filename(filename);
1885     if(dialog->run()==GTK_RESPONSE_ACCEPT) {
1886         filename=dialog->get_filename();
1887         delete dialog;
1888         return true;
1889     }
1890     delete dialog;
1891     return false;
1892 //      return dialog_open_file(title, filename);
1893 #endif
1894 }
1895
1896 void
1897 App::dialog_error_blocking(const std::string &title, const std::string &message)
1898 {
1899         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1900         dialog.set_title(title);
1901         dialog.show();
1902         dialog.run();
1903 }
1904
1905 void
1906 App::dialog_warning_blocking(const std::string &title, const std::string &message)
1907 {
1908         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
1909         dialog.set_title(title);
1910         dialog.show();
1911         dialog.run();
1912 }
1913
1914 bool
1915 App::dialog_yes_no(const std::string &title, const std::string &message)
1916 {
1917         Gtk::Dialog dialog(
1918                 title,          // Title
1919                 true,           // Modal
1920                 true            // use_separator
1921         );
1922         Gtk::Label label(message);
1923         label.show();
1924
1925         dialog.get_vbox()->pack_start(label);
1926         dialog.add_button(Gtk::StockID("gtk-yes"),1);
1927         dialog.add_button(Gtk::StockID("gtk-no"),0);
1928         dialog.show();
1929         return dialog.run();
1930 }
1931
1932 int
1933 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
1934 {
1935         Gtk::Dialog dialog(
1936                 title,          // Title
1937                 true,           // Modal
1938                 true            // use_separator
1939         );
1940         Gtk::Label label(message);
1941         label.show();
1942
1943         dialog.get_vbox()->pack_start(label);
1944         dialog.add_button(Gtk::StockID("gtk-yes"),1);
1945         dialog.add_button(Gtk::StockID("gtk-no"),0);
1946         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
1947         dialog.show();
1948         return dialog.run();
1949 }
1950
1951 void
1952 App::dialog_not_implemented()
1953 {
1954         Gtk::MessageDialog dialog("Feature not available", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1955         dialog.set_secondary_text("Sorry, this feature has not yet been implemented.");
1956         dialog.run();
1957 }
1958
1959 bool
1960 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
1961 {
1962         Gtk::Dialog dialog(
1963                 title,          // Title
1964                 true,           // Modal
1965                 true            // use_separator
1966         );
1967         Gtk::Label label(message);
1968         label.show();
1969         dialog.get_vbox()->pack_start(label);
1970
1971         Gtk::Entry entry;
1972         entry.set_text(text);
1973         entry.show();
1974         entry.set_activates_default(true);
1975         dialog.get_vbox()->pack_start(entry);
1976
1977         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
1978         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
1979         dialog.set_default_response(Gtk::RESPONSE_OK);
1980         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
1981         dialog.show();
1982
1983         if(dialog.run()!=Gtk::RESPONSE_OK)
1984                 return false;
1985
1986         text=entry.get_text();
1987
1988         return true;
1989 }
1990
1991
1992
1993
1994 bool
1995 App::open(std::string filename)
1996 {
1997         return open_as(filename,filename);
1998 }
1999
2000 bool
2001 App::open_as(std::string filename,std::string as)
2002 {
2003 #ifdef WIN32
2004     char long_name[1024];
2005     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2006     as=long_name;
2007 #endif
2008
2009         try
2010         {
2011                 OneMoment one_moment;
2012
2013                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2014                 if(canvas && get_instance(canvas))
2015                 {
2016                         get_instance(canvas)->find_canvas_view(canvas)->present();
2017                         throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2018                 }
2019                 if(!canvas)
2020                         throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2021
2022                 add_recent_file(as);
2023
2024                 handle<Instance> instance(Instance::create(canvas));
2025
2026                 if(!instance)
2027                         throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2028
2029                 one_moment.hide();
2030
2031                 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)")))
2032                         instance->dialog_cvs_update();
2033         }
2034         catch(String x)
2035         {
2036                 dialog_error_blocking(_("Error"), x);
2037                 return false;
2038         }
2039         catch(...)
2040         {
2041                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2042                 return false;
2043         }
2044
2045         _preferences.set_value("curr_path",dirname(as));
2046
2047         return true;
2048 }
2049
2050
2051 void
2052 App::new_instance()
2053 {
2054         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2055         canvas->set_name(strprintf("Untitled%d",Instance::get_count()));
2056
2057         String file_name(strprintf("untitled%d.sif",Instance::get_count()));
2058
2059         canvas->rend_desc().set_frame_rate(24.0);
2060         canvas->rend_desc().set_time_start(0.0);
2061         canvas->rend_desc().set_time_end(00.0);
2062         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2063         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2064         canvas->rend_desc().set_tl(Vector(-4,2.25));
2065         canvas->rend_desc().set_br(Vector(4,-2.25));
2066         canvas->rend_desc().set_w(480);
2067         canvas->rend_desc().set_h(270);
2068         canvas->rend_desc().set_antialias(1);
2069         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2070         canvas->set_file_name(file_name);
2071
2072         Instance::create(canvas)->find_canvas_view(canvas)->canvas_properties.present();
2073 }
2074
2075 void
2076 App::dialog_open()
2077 {
2078         string filename="*.sif";
2079
2080         while(dialog_open_file("Open", filename))
2081         {
2082                 // If the filename still has wildcards, then we should
2083                 // continue looking for the file we want
2084                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2085                         continue;
2086
2087                 if(open(filename))
2088                         break;
2089
2090                 get_ui_interface()->error(_("Unable to open file"));
2091         }
2092 }
2093
2094 void
2095 App::set_selected_instance(etl::loose_handle<Instance> instance)
2096 {
2097 /*      if(get_selected_instance()==instance)
2098         {
2099                 selected_instance=instance;
2100                 signal_instance_selected()(instance);
2101                 return;
2102         }
2103         else
2104         {
2105 */
2106                 selected_instance=instance;
2107                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2108                 {
2109                         if(instance)
2110                         {
2111                                 instance->focus(instance->get_canvas());
2112                         }
2113                         else
2114                                 set_selected_canvas_view(0);
2115                 }
2116                 signal_instance_selected()(instance);
2117 }
2118
2119 void
2120 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2121 {
2122         selected_canvas_view=canvas_view;
2123         signal_canvas_view_focus()(selected_canvas_view);
2124         if(canvas_view)
2125         {
2126                 selected_instance=canvas_view->get_instance();
2127                 signal_instance_selected()(canvas_view->get_instance());
2128         }
2129 /*
2130         if(get_selected_canvas_view()==canvas_view)
2131         {
2132                 signal_canvas_view_focus()(selected_canvas_view);
2133                 signal_instance_selected()(canvas_view->get_instance());
2134                 return;
2135         }
2136         selected_canvas_view=canvas_view;
2137         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2138                 set_selected_instance(canvas_view->get_instance());
2139         signal_canvas_view_focus()(selected_canvas_view);
2140 */
2141 }
2142
2143 etl::loose_handle<Instance>
2144 App::get_instance(Canvas::Handle canvas)
2145 {
2146         if(!canvas) return 0;
2147         canvas=canvas->get_root();
2148
2149         std::list<etl::handle<Instance> >::iterator iter;
2150         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2151         {
2152                 if((*iter)->get_canvas()==canvas)
2153                         return *iter;
2154         }
2155         return 0;
2156 }
2157
2158 void
2159 App::dialog_about()
2160 {
2161         (new class About())->show();
2162 }
2163
2164 void
2165 studio::App::undo()
2166 {
2167         if(selected_instance)
2168                 selected_instance->undo();
2169 }
2170
2171 void
2172 studio::App::redo()
2173 {
2174         if(selected_instance)
2175                 selected_instance->redo();
2176 }
2177
2178 synfig::String
2179 studio::App::get_base_path()
2180 {
2181         return app_base_path_;
2182 }