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