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