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