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