Use resize() to resize windows. set_default_size() doesn't work on the root window...
[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         bool seen_root(false), shown_non_root(false);
1354
1355         while(current != synfig::String::npos)
1356         {
1357                 // find end of first field (canvas) or return
1358                 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1359                 if(separator == synfig::String::npos) break;
1360
1361                 // find the canvas
1362                 synfig::Canvas::Handle canvas;
1363                 try {
1364                         canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current));
1365                 }
1366                 catch(Exception::IDNotFound) {
1367                         // can't find the canvas; skip to the next canvas or return
1368                         separator = canvas_window_size.find_first_of('\t', current);
1369                         if(separator == synfig::String::npos) return;
1370                         current = separator+1;
1371                         continue;
1372                 }
1373
1374                 if (canvas->is_root())
1375                         seen_root = true;
1376                 else
1377                         shown_non_root = true;
1378
1379                 // check that we have the tab character the ends this canvas' data or return
1380                 current = separator+1;
1381                 separator = canvas_window_size.find_first_of('\t', current);
1382                 if(separator == synfig::String::npos) return;
1383
1384                 int x,y,w,h;
1385                 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1386                 {
1387                         current = separator+1;
1388                         continue;
1389                 }
1390
1391                 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1392                 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1393                 x=x*screen_w/SCALE_FACTOR;
1394                 y=y*screen_h/SCALE_FACTOR;
1395                 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1396                         x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1397                 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1398                         y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1399
1400                 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1401                 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1402
1403                 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1404                 canvasview->move(x,y);
1405                 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1406                 canvasview->present();
1407
1408                 current = separator+1;
1409         }
1410
1411         if (shown_non_root && !seen_root)
1412                 instance->find_canvas_view(instance->get_canvas())->hide();
1413 }
1414
1415 void
1416 App::add_recent_file(const etl::handle<Instance> instance)
1417 {
1418         int screen_w(Gdk::screen_width());
1419         int screen_h(Gdk::screen_height());
1420
1421         std::string canvas_window_size;
1422
1423         const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1424         Instance::CanvasViewList::const_iterator iter;
1425
1426         for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1427         {
1428                 if( !((*iter)->is_visible()) )
1429                         continue;
1430
1431                 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1432                 int x_pos, y_pos, x_size, y_size;
1433                 (*iter)->get_position(x_pos,y_pos);
1434                 (*iter)->get_size(x_size,y_size);
1435
1436                 canvas_window_size += strprintf("%s %d %d %d %d\t",
1437                                                                                 canvas->get_relative_id(canvas->get_root()).c_str(),
1438                                                                                 x_pos*SCALE_FACTOR/screen_w,  y_pos*SCALE_FACTOR/screen_h,
1439                                                                                 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1440         }
1441
1442         add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1443 }
1444 #undef SCALE_FACTOR
1445
1446 void
1447 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1448 {
1449         std::string filename(file_name);
1450
1451         assert(!filename.empty());
1452
1453         if(filename.empty())
1454                 return;
1455
1456         // Toss out any "hidden" files
1457         if(basename(filename)[0]=='.')
1458                 return;
1459
1460         // If we aren't an absolute path, turn ourselves into one
1461         if(!is_absolute_path(filename))
1462                 filename=absolute_path(filename);
1463
1464         std::string old_window_size;
1465
1466         list<string>::iterator iter;
1467         list<string>::iterator iter_wsize;
1468         // Check to see if the file is already on the list.
1469         // If it is, then remove it from the list
1470         for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1471                 if(*iter==filename)
1472                 {
1473                         recent_files.erase(iter);
1474                         old_window_size = *iter_wsize;
1475                         recent_files_window_size.erase(iter_wsize);
1476                         break;
1477                 }
1478
1479
1480         // Push the filename to the front of the list
1481         recent_files.push_front(filename);
1482         if(window_size.empty())
1483                 recent_files_window_size.push_front(old_window_size);
1484         else
1485                 recent_files_window_size.push_front(window_size);
1486
1487         // Clean out the files at the end of the list.
1488         while(recent_files.size()>(unsigned)get_max_recent_files())
1489         {
1490                 recent_files.pop_back();
1491                 recent_files_window_size.pop_back();
1492         }
1493
1494         signal_recent_files_changed_();
1495
1496         return;
1497 }
1498
1499 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1500
1501 Time::Format
1502 App::get_time_format()
1503 {
1504         return _App_time_format;
1505 }
1506
1507 void
1508 App::set_time_format(synfig::Time::Format x)
1509 {
1510         _App_time_format=x;
1511 }
1512
1513
1514 void
1515 App::save_settings()
1516 {
1517         char * old_locale;
1518         try
1519         {
1520         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1521         setlocale(LC_NUMERIC, "C");
1522                 {
1523                         std::string filename=get_config_file("accelrc");
1524                         Gtk::AccelMap::save(filename);
1525                 }
1526                 do{
1527                         std::string filename=get_config_file("recentfiles");
1528
1529                         std::ofstream file(filename.c_str());
1530
1531                         if(!file)
1532                         {
1533                                 synfig::warning("Unable to save %s",filename.c_str());
1534                                 break;
1535                         }
1536
1537                         list<string>::reverse_iterator iter;
1538
1539                         for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1540                                 file<<*iter<<endl;
1541                 }while(0);
1542                 do{
1543                         std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1544
1545                         std::ofstream file(filename.c_str());
1546
1547                         if(!file)
1548                         {
1549                                 synfig::warning("Unable to save %s",filename.c_str());
1550                                 break;
1551                         }
1552
1553                         list<string>::reverse_iterator iter;
1554
1555                         for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1556                                 file<<*iter<<endl;
1557
1558                 }while(0);
1559                 std::string filename=get_config_file("settings");
1560                 synfigapp::Main::settings().save_to_file(filename);
1561         setlocale(LC_NUMERIC,old_locale);
1562         }
1563         catch(...)
1564         {
1565                 synfig::warning("Caught exception when attempting to save settings.");
1566         }
1567 }
1568
1569 void
1570 App::load_settings()
1571 {
1572         char  * old_locale;
1573         try
1574         {
1575         old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1576         setlocale(LC_NUMERIC, "C");
1577                 {
1578                         std::string filename=get_config_file("accelrc");
1579                         Gtk::AccelMap::load(filename);
1580                 }
1581                 {
1582                         bool window_size_broken = false;
1583
1584                         std::string filename=get_config_file("recentfiles");
1585                         std::string filename_window_size=filename+std::string("_window_size");
1586
1587                         std::ifstream file(filename.c_str());
1588                         std::ifstream file_window_size(filename_window_size.c_str());
1589
1590                         if(!file_window_size)
1591                                 window_size_broken = true;
1592
1593                         while(file)
1594                         {
1595                                 std::string recent_file;
1596                                 std::string recent_file_window_size;
1597                                 getline(file,recent_file);
1598                                 if(!window_size_broken)
1599                                         getline(file_window_size,recent_file_window_size);
1600                                 if(!recent_file.empty())
1601                                 {
1602                                         if(!window_size_broken && !file_window_size)
1603                                                 window_size_broken = true;
1604                                         if(!window_size_broken)
1605                                                 add_recent_file(recent_file,recent_file_window_size);
1606                                         else
1607                                                 add_recent_file(recent_file);
1608                                 }
1609                         }
1610                         if(!window_size_broken && file_window_size)
1611                                 window_size_broken = true;
1612
1613                         if(window_size_broken)
1614                         {
1615                                 recent_files_window_size.clear();
1616                                 recent_files_window_size.resize(recent_files.size());
1617                         }
1618                 }
1619                 std::string filename=get_config_file("settings");
1620                 if(!synfigapp::Main::settings().load_from_file(filename))
1621                 {
1622                         //std::string filename=Glib::build_filename(Glib::get_home_dir(),".synfigrc");
1623                         //if(!synfigapp::Main::settings().load_from_file(filename))
1624                         {
1625                                 gamma.set_gamma(1.0/2.2);
1626                                 reset_initial_window_configuration();
1627                         }
1628                 }
1629         setlocale(LC_NUMERIC,old_locale);
1630         }
1631         catch(...)
1632         {
1633                 synfig::warning("Caught exception when attempting to load settings.");
1634         }
1635 }
1636
1637 void
1638 App::reset_initial_window_configuration()
1639 {
1640         synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1641         synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1642         synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1643         synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1644         synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1645         synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1646         synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1647         synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1648         synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1649         synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1650         synfigapp::Main::settings().set_value("pref.distance_system","pt");
1651         synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1652 #ifdef SINGLE_THREADED
1653         synfigapp::Main::settings().set_value("pref.single_threaded","0");
1654 #endif
1655         synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1656         synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1657 }
1658
1659 bool
1660 App::shutdown_request(GdkEventAny*)
1661 {
1662         quit();
1663         return true;
1664         //return !shutdown_in_progress;
1665 }
1666
1667 void
1668 App::quit()
1669 {
1670         if(shutdown_in_progress)return;
1671
1672
1673         get_ui_interface()->task(_("Quit Request"));
1674         if(Busy::count)
1675         {
1676                 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1677                 return;
1678         }
1679
1680         std::list<etl::handle<Instance> >::iterator iter;
1681         for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1682         {
1683                 if(!(*iter)->safe_close())
1684                         return;
1685
1686 /*
1687                 if((*iter)->synfigapp::Instance::get_action_count())
1688                 {
1689                         handle<synfigapp::UIInterface> uim;
1690                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1691                         assert(uim);
1692                         string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1693                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1694                         {
1695                                 case synfigapp::UIInterface::RESPONSE_NO:
1696                                         break;
1697                                 case synfigapp::UIInterface::RESPONSE_YES:
1698                                         (*iter)->save();
1699                                         break;
1700                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1701                                         return;
1702                                 default:
1703                                         assert(0);
1704                                         return;
1705                         }
1706                 }
1707
1708
1709                 if((*iter)->synfigapp::Instance::is_modified())
1710                 {
1711                         handle<synfigapp::UIInterface> uim;
1712                         uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1713                         assert(uim);
1714                         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() );
1715                         switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1716                         {
1717                                 case synfigapp::UIInterface::RESPONSE_NO:
1718                                         break;
1719                                 case synfigapp::UIInterface::RESPONSE_YES:
1720                                         (*iter)->dialog_cvs_commit();
1721                                         break;
1722                                 case synfigapp::UIInterface::RESPONSE_CANCEL:
1723                                         return;
1724                                 default:
1725                                         assert(0);
1726                                         return;
1727                         }
1728                 }
1729 */
1730
1731                 // This next line causes things to crash for some reason
1732                 //(*iter)->close();
1733         }
1734
1735         shutdown_in_progress=true;
1736
1737         instance_list.clear();
1738
1739         while(studio::App::events_pending())studio::App::iteration(false);
1740
1741         Gtk::Main::quit();
1742         auto_recover->normal_shutdown();
1743
1744         get_ui_interface()->task(_("Quit Request sent"));
1745 }
1746
1747 void
1748 App::show_setup()
1749 {
1750         dialog_setup->refresh();
1751         dialog_setup->show();
1752 }
1753
1754 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1755 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1756
1757 //#ifdef WIN32
1758 //#define USE_WIN32_FILE_DIALOGS 1
1759 //#endif
1760
1761 #ifdef USE_WIN32_FILE_DIALOGS
1762 static OPENFILENAME ofn={};
1763 #endif
1764
1765 #ifdef WIN32
1766 #include <gdk/gdkwin32.h>
1767 #endif
1768
1769 bool
1770 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1771 {
1772         info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1773
1774 #ifdef USE_WIN32_FILE_DIALOGS
1775         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1776
1777         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1778         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1779         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1780
1781         ofn.lStructSize=sizeof(OPENFILENAME);
1782         ofn.hwndOwner = hWnd;
1783         ofn.hInstance = hInstance;
1784         ofn.lpstrFilter = szFilter;
1785 //      ofn.lpstrCustomFilter=NULL;
1786 //      ofn.nMaxCustFilter=0;
1787 //      ofn.nFilterIndex=0;
1788 //      ofn.lpstrFile=NULL;
1789         ofn.nMaxFile=MAX_PATH;
1790 //      ofn.lpstrFileTitle=NULL;
1791 //      ofn.lpstrInitialDir=NULL;
1792 //      ofn.lpstrTitle=NULL;
1793         ofn.Flags=OFN_HIDEREADONLY;
1794 //      ofn.nFileOffset=0;
1795 //      ofn.nFileExtension=0;
1796         ofn.lpstrDefExt=TEXT("sif");
1797 //      ofn.lCustData = 0l;
1798         ofn.lpfnHook=NULL;
1799 //      ofn.lpTemplateName=NULL;
1800
1801         CHAR szFilename[MAX_PATH];
1802         CHAR szTitle[500];
1803         strcpy(szFilename,filename.c_str());
1804         strcpy(szTitle,title.c_str());
1805
1806         ofn.lpstrFile=szFilename;
1807         ofn.lpstrFileTitle=szTitle;
1808
1809         if(GetOpenFileName(&ofn))
1810         {
1811                 filename=szFilename;
1812                 return true;
1813         }
1814         return false;
1815
1816 #else
1817         synfig::String prev_path;
1818
1819         if(!_preferences.get_value(preference, prev_path))
1820                 prev_path = ".";
1821
1822         prev_path = absolute_path(prev_path);
1823
1824     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1825
1826     dialog->set_current_folder(prev_path);
1827     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1828     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
1829
1830     if (filename.empty())
1831                 dialog->set_filename(prev_path);
1832         else if (is_absolute_path(filename))
1833                 dialog->set_filename(filename);
1834         else
1835                 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1836
1837     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1838         filename = dialog->get_filename();
1839                 info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1840                 _preferences.set_value(preference, dirname(filename));
1841         delete dialog;
1842         return true;
1843     }
1844
1845     delete dialog;
1846     return false;
1847
1848     /*
1849
1850         GtkWidget *ok;
1851         GtkWidget *cancel;
1852         int val=0;
1853
1854         GtkWidget *fileselection;
1855         fileselection = gtk_file_selection_new(title.c_str());
1856
1857
1858         if(basename(filename)==filename)
1859         {
1860                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1861         }
1862         else
1863                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1864
1865         gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1866
1867         ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1868         cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1869
1870         gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1871         gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1872
1873         gtk_widget_show(fileselection);
1874
1875         while(!val)
1876                 iteration();
1877
1878
1879         if(val==1)
1880         {
1881                 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1882                 _preferences.set_value(preference,dirname(filename));
1883         }
1884         else
1885         {
1886                 gtk_widget_destroy(fileselection);
1887                 return false;
1888         }
1889         gtk_widget_destroy(fileselection);
1890         return true;
1891     */
1892 #endif
1893 }
1894
1895 bool
1896 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1897 {
1898         info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1899
1900 #if USE_WIN32_FILE_DIALOGS
1901         static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1902
1903         GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1904         HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1905         HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1906
1907         ofn.lStructSize=sizeof(OPENFILENAME);
1908         ofn.hwndOwner = hWnd;
1909         ofn.hInstance = hInstance;
1910         ofn.lpstrFilter = szFilter;
1911 //      ofn.lpstrCustomFilter=NULL;
1912 //      ofn.nMaxCustFilter=0;
1913 //      ofn.nFilterIndex=0;
1914 //      ofn.lpstrFile=NULL;
1915         ofn.nMaxFile=MAX_PATH;
1916 //      ofn.lpstrFileTitle=NULL;
1917 //      ofn.lpstrInitialDir=NULL;
1918 //      ofn.lpstrTitle=NULL;
1919         ofn.Flags=OFN_OVERWRITEPROMPT;
1920 //      ofn.nFileOffset=0;
1921 //      ofn.nFileExtension=0;
1922         ofn.lpstrDefExt=TEXT("sif");
1923 //      ofn.lCustData = 0l;
1924         ofn.lpfnHook=NULL;
1925 //      ofn.lpTemplateName=NULL;
1926
1927         CHAR szFilename[MAX_PATH];
1928         CHAR szTitle[500];
1929         strcpy(szFilename,filename.c_str());
1930         strcpy(szTitle,title.c_str());
1931
1932         ofn.lpstrFile=szFilename;
1933         ofn.lpstrFileTitle=szTitle;
1934
1935         if(GetSaveFileName(&ofn))
1936         {
1937                 filename=szFilename;
1938                 _preferences.set_value(preference,dirname(filename));
1939                 return true;
1940         }
1941         return false;
1942 #else
1943         synfig::String prev_path;
1944
1945         if(!_preferences.get_value(preference, prev_path))
1946                 prev_path=".";
1947
1948         prev_path = absolute_path(prev_path);
1949
1950     Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
1951
1952     dialog->set_current_folder(prev_path);
1953     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1954     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
1955
1956         Widget_Enum *file_type_enum = 0;
1957         if (preference == ANIMATION_DIR_PREFERENCE)
1958         {
1959                 file_type_enum = manage(new Widget_Enum());
1960                 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
1961                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", strprintf("0.61.08 (%s)", _("current")))
1962                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
1963                                                                            .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
1964                 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
1965
1966                 Gtk::HBox *hbox = manage(new Gtk::HBox);
1967                 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
1968                 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
1969                 hbox->show_all();
1970
1971                 dialog->set_extra_widget(*hbox);
1972         }
1973
1974     if (filename.empty())
1975                 dialog->set_filename(prev_path);
1976     else
1977         {
1978                 std::string full_path;
1979                 if (is_absolute_path(filename))
1980                         full_path = filename;
1981                 else
1982                         full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
1983
1984                 // select the file if it exists
1985                 dialog->set_filename(full_path);
1986
1987                 // if the file doesn't exist, put its name into the filename box
1988                 struct stat s;
1989                 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
1990                         dialog->set_current_name(basename(filename));
1991         }
1992
1993     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1994                 if (preference == ANIMATION_DIR_PREFERENCE)
1995                         set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
1996         filename = dialog->get_filename();
1997                 info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
1998                 _preferences.set_value(preference, dirname(filename));
1999         delete dialog;
2000         return true;
2001     }
2002
2003     delete dialog;
2004     return false;
2005 #endif
2006 }
2007
2008 void
2009 App::dialog_error_blocking(const std::string &title, const std::string &message)
2010 {
2011         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2012         dialog.set_title(title);
2013         dialog.show();
2014         dialog.run();
2015 }
2016
2017 void
2018 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2019 {
2020         Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2021         dialog.set_title(title);
2022         dialog.show();
2023         dialog.run();
2024 }
2025
2026 bool
2027 App::dialog_yes_no(const std::string &title, const std::string &message)
2028 {
2029         Gtk::Dialog dialog(
2030                 title,          // Title
2031                 true,           // Modal
2032                 true            // use_separator
2033         );
2034         Gtk::Label label(message);
2035         label.show();
2036
2037         dialog.get_vbox()->pack_start(label);
2038         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2039         dialog.add_button(Gtk::StockID("gtk-no"),0);
2040         dialog.show();
2041         return dialog.run();
2042 }
2043
2044 int
2045 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2046 {
2047         Gtk::Dialog dialog(
2048                 title,          // Title
2049                 true,           // Modal
2050                 true            // use_separator
2051         );
2052         Gtk::Label label(message);
2053         label.show();
2054
2055         dialog.get_vbox()->pack_start(label);
2056         dialog.add_button(Gtk::StockID("gtk-yes"),1);
2057         dialog.add_button(Gtk::StockID("gtk-no"),0);
2058         dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2059         dialog.show();
2060         return dialog.run();
2061 }
2062
2063 void
2064 App::dialog_not_implemented()
2065 {
2066         Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2067         dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2068         dialog.run();
2069 }
2070
2071 static bool
2072 try_open_url(const std::string &url)
2073 {
2074 #ifdef WIN32
2075         return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2076 #else  // WIN32
2077         gchar* argv[3] = {strdup(App::browser_command.c_str()), strdup(url.c_str()), NULL};
2078
2079         GError* gerror = NULL;
2080         gboolean retval;
2081         retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &gerror);
2082         free(argv[0]);
2083         free(argv[1]);
2084
2085         if (!retval)
2086     {
2087                 error(_("Could not execute specified web browser: %s"), gerror->message);
2088                 g_error_free(gerror);
2089                 return false;
2090     }
2091
2092         return true;
2093 #endif  // WIN32
2094 }
2095
2096 void
2097 App::dialog_help()
2098 {
2099         if (!try_open_url("http://synfig.org/Documentation"))
2100         {
2101                 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2102                 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2103                 dialog.set_title(_("Help"));
2104                 dialog.run();
2105         }
2106 }
2107
2108 void
2109 App::open_url(const std::string &url)
2110 {
2111         try_open_url(url);
2112 }
2113
2114 bool
2115 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2116 {
2117         Gtk::Dialog dialog(
2118                 title,          // Title
2119                 true,           // Modal
2120                 true            // use_separator
2121         );
2122         Gtk::Label label(message);
2123         label.show();
2124         dialog.get_vbox()->pack_start(label);
2125
2126         Gtk::Entry entry;
2127         entry.set_text(text);
2128         entry.show();
2129         entry.set_activates_default(true);
2130         dialog.get_vbox()->pack_start(entry);
2131
2132         dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2133         dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2134         dialog.set_default_response(Gtk::RESPONSE_OK);
2135         entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2136         dialog.show();
2137
2138         if(dialog.run()!=Gtk::RESPONSE_OK)
2139                 return false;
2140
2141         text=entry.get_text();
2142
2143         return true;
2144 }
2145
2146
2147
2148
2149 bool
2150 App::open(std::string filename)
2151 {
2152         return open_as(filename,filename);
2153 }
2154
2155 // this is called from autorecover.cpp:
2156 //   App::open_as(get_shadow_file_name(filename),filename)
2157 // other than that, 'filename' and 'as' are the same
2158 bool
2159 App::open_as(std::string filename,std::string as)
2160 {
2161 #ifdef WIN32
2162     char long_name[1024];
2163     if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2164     as=long_name;
2165 #endif
2166
2167         try
2168         {
2169                 OneMoment one_moment;
2170
2171                 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2172                 if(canvas && get_instance(canvas))
2173                 {
2174                         get_instance(canvas)->find_canvas_view(canvas)->present();
2175                         info("%s is already open", filename.c_str());
2176                         // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2177                 }
2178                 else
2179                 {
2180                         if(!canvas)
2181                                 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2182
2183                         add_recent_file(as);
2184
2185                         handle<Instance> instance(Instance::create(canvas));
2186
2187                         if(!instance)
2188                                 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2189
2190                         set_recent_file_window_size(instance);
2191
2192                         one_moment.hide();
2193
2194                         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)")))
2195                                 instance->dialog_cvs_update();
2196                 }
2197         }
2198         catch(String x)
2199         {
2200                 dialog_error_blocking(_("Error"), x);
2201                 return false;
2202         }
2203         catch(...)
2204         {
2205                 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2206                 return false;
2207         }
2208
2209         return true;
2210 }
2211
2212
2213 void
2214 App::new_instance()
2215 {
2216         handle<synfig::Canvas> canvas=synfig::Canvas::create();
2217
2218         String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2219         canvas->set_name(file_name);
2220         file_name += ".sifz";
2221
2222         canvas->rend_desc().set_frame_rate(24.0);
2223         canvas->rend_desc().set_time_start(0.0);
2224         canvas->rend_desc().set_time_end(5.0);
2225         canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2226         canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2227         canvas->rend_desc().set_tl(Vector(-4,2.25));
2228         canvas->rend_desc().set_br(Vector(4,-2.25));
2229         canvas->rend_desc().set_w(480);
2230         canvas->rend_desc().set_h(270);
2231         canvas->rend_desc().set_antialias(1);
2232         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2233         canvas->set_file_name(file_name);
2234
2235         handle<Instance> instance = Instance::create(canvas);
2236
2237         if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2238                 instance->find_canvas_view(canvas)->canvas_properties.present();
2239 }
2240
2241 void
2242 App::dialog_open(string filename)
2243 {
2244         if (filename.empty())
2245                 filename="*.sif";
2246
2247         while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2248         {
2249                 // If the filename still has wildcards, then we should
2250                 // continue looking for the file we want
2251                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2252                         continue;
2253
2254                 if(open(filename))
2255                         break;
2256
2257                 get_ui_interface()->error(_("Unable to open file"));
2258         }
2259 }
2260
2261 void
2262 App::set_selected_instance(etl::loose_handle<Instance> instance)
2263 {
2264 /*      if(get_selected_instance()==instance)
2265         {
2266                 selected_instance=instance;
2267                 signal_instance_selected()(instance);
2268                 return;
2269         }
2270         else
2271         {
2272 */
2273                 selected_instance=instance;
2274                 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2275                 {
2276                         if(instance)
2277                         {
2278                                 instance->focus(instance->get_canvas());
2279                         }
2280                         else
2281                                 set_selected_canvas_view(0);
2282                 }
2283                 signal_instance_selected()(instance);
2284 }
2285
2286 void
2287 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2288 {
2289         selected_canvas_view=canvas_view;
2290         signal_canvas_view_focus()(selected_canvas_view);
2291         if(canvas_view)
2292         {
2293                 selected_instance=canvas_view->get_instance();
2294                 signal_instance_selected()(canvas_view->get_instance());
2295         }
2296 /*
2297         if(get_selected_canvas_view()==canvas_view)
2298         {
2299                 signal_canvas_view_focus()(selected_canvas_view);
2300                 signal_instance_selected()(canvas_view->get_instance());
2301                 return;
2302         }
2303         selected_canvas_view=canvas_view;
2304         if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2305                 set_selected_instance(canvas_view->get_instance());
2306         signal_canvas_view_focus()(selected_canvas_view);
2307 */
2308 }
2309
2310 etl::loose_handle<Instance>
2311 App::get_instance(etl::handle<synfig::Canvas> canvas)
2312 {
2313         if(!canvas) return 0;
2314         canvas=canvas->get_root();
2315
2316         std::list<etl::handle<Instance> >::iterator iter;
2317         for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2318         {
2319                 if((*iter)->get_canvas()==canvas)
2320                         return *iter;
2321         }
2322         return 0;
2323 }
2324
2325 void
2326 App::dialog_about()
2327 {
2328         if(about)
2329                 about->show();
2330 }
2331
2332 void
2333 studio::App::undo()
2334 {
2335         if(selected_instance)
2336                 selected_instance->undo();
2337 }
2338
2339 void
2340 studio::App::redo()
2341 {
2342         if(selected_instance)
2343                 selected_instance->redo();
2344 }
2345
2346 synfig::String
2347 studio::App::get_base_path()
2348 {
2349         return app_base_path_;
2350 }