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