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