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