When loading .sif files, show any warnings in the GUI. When loading .sif files with...
[synfig.git] / synfig-studio / trunk / src / gtkmm / app.cpp
blob4447fc3f6c7418018d6af211544a70b44a1e9df4
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 ** Copyright (c) 2008 Carlos López
13 ** This package is free software; you can redistribute it and/or
14 ** modify it under the terms of the GNU General Public License as
15 ** published by the Free Software Foundation; either version 2 of
16 ** the License, or (at your option) any later version.
18 ** This package is distributed in the hope that it will be useful,
19 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ** General Public License for more details.
22 ** \endlegal
24 /* ========================================================================= */
26 /* === H E A D E R S ======================================================= */
28 #ifdef USING_PCH
29 # include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
35 #ifdef WIN32
36 #define WINVER 0x0500
37 #include <windows.h>
38 #endif
40 #include <fstream>
41 #include <iostream>
42 #include <locale>
43 #include <cstring>
45 #ifdef HAVE_SYS_ERRNO_H
46 #include <sys/errno.h>
47 #endif
48 #include <gtkmm/fileselection.h>
49 #include <gtkmm/dialog.h>
50 #include <gtkmm/messagedialog.h>
51 #include <gtkmm/label.h>
52 #include <gtkmm/stock.h>
53 #include <gtkmm/stockitem.h>
54 #include <gtkmm/iconsource.h>
55 #include <gtkmm/inputdialog.h>
56 #include <gtkmm/accelmap.h>
57 #include <gtkmm/uimanager.h>
58 #include <gtkmm/textview.h>
60 #include <gtk/gtk.h>
62 #include <gdkmm/general.h>
64 #include <synfig/loadcanvas.h>
65 #include <synfig/savecanvas.h>
67 #include "app.h"
68 #include "about.h"
69 #include "splash.h"
70 #include "instance.h"
71 #include "canvasview.h"
72 #include "dialog_setup.h"
73 #include "dialog_gradient.h"
74 #include "dialog_color.h"
75 #include "toolbox.h"
76 #include "compview.h"
77 #include "onemoment.h"
79 #include "dockmanager.h"
81 #include "state_eyedrop.h"
82 #include "state_normal.h"
83 #include "state_draw.h"
84 #include "state_fill.h"
85 #include "state_bline.h"
86 #include "state_polygon.h"
87 #include "state_sketch.h"
88 #include "state_gradient.h"
89 #include "state_circle.h"
90 #include "state_rectangle.h"
91 #include "state_smoothmove.h"
92 #include "state_scale.h"
93 #include "state_star.h"
94 #include "state_text.h"
95 #include "state_width.h"
96 #include "state_rotate.h"
97 #include "state_zoom.h"
99 #include "devicetracker.h"
100 #include "dialog_tooloptions.h"
101 #include "widget_enum.h"
103 #include "autorecover.h"
105 #include <synfigapp/settings.h>
106 #include "dock_history.h"
107 #include "dock_canvases.h"
108 #include "dock_keyframes.h"
109 #include "dock_layers.h"
110 #include "dock_params.h"
111 #include "dock_metadata.h"
112 #include "dock_children.h"
113 #include "dock_info.h"
114 #include "dock_navigator.h"
115 #include "dock_layergroups.h"
116 #include "dock_timetrack.h"
117 #include "dock_curves.h"
119 #include "mod_palette/mod_palette.h"
120 #include "mod_mirror/mod_mirror.h"
122 #include <sys/stat.h>
124 #include "ipc.h"
126 #include "module.h"
128 #include "statemanager.h"
130 #ifdef WITH_FMOD
131 #include <fmod.h>
132 #endif
134 #include <gtkmm/accelmap.h>
135 #include <gtkmm/filechooser.h>
136 #include <gtkmm/filechooserdialog.h>
138 #include "general.h"
140 #endif
142 /* === U S I N G =========================================================== */
144 using namespace std;
145 using namespace etl;
146 using namespace synfig;
147 using namespace studio;
149 /* === M A C R O S ========================================================= */
151 #ifndef SYNFIG_USER_APP_DIR
152 #ifdef __APPLE__
153 #define SYNFIG_USER_APP_DIR "Library/Synfig"
154 #elif defined(_WIN32)
155 #define SYNFIG_USER_APP_DIR "Synfig"
156 #else
157 #define SYNFIG_USER_APP_DIR ".synfig"
158 #endif
159 #endif
161 #ifndef DPM2DPI
162 #define DPM2DPI(x) (float(x)/39.3700787402f)
163 #define DPI2DPM(x) (float(x)*39.3700787402f)
164 #endif
166 #ifdef WIN32
167 # ifdef IMAGE_DIR
168 # undef IMAGE_DIR
169 # define IMAGE_DIR "share\\pixmaps"
170 # endif
171 #endif
173 #ifndef IMAGE_DIR
174 # define IMAGE_DIR "/usr/local/share/pixmaps"
175 #endif
177 #ifndef IMAGE_EXT
178 # define IMAGE_EXT "tif"
179 #endif
181 #include <synfigapp/main.h>
183 /* === S I G N A L S ======================================================= */
185 static sigc::signal<void> signal_present_all_;
186 sigc::signal<void>&
187 App::signal_present_all() { return signal_present_all_; }
189 static sigc::signal<void> signal_recent_files_changed_;
190 sigc::signal<void>&
191 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
193 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
194 sigc::signal<void,etl::loose_handle<CanvasView> >&
195 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
197 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
198 sigc::signal<void,etl::handle<Instance> >&
199 App::signal_instance_selected() { return signal_instance_selected_; }
201 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
202 sigc::signal<void,etl::handle<Instance> >&
203 App::signal_instance_created() { return signal_instance_created_; }
205 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
206 sigc::signal<void,etl::handle<Instance> >&
207 App::signal_instance_deleted() { return signal_instance_deleted_; }
209 /* === G L O B A L S ======================================================= */
211 static std::list<std::string> recent_files;
212 const std::list<std::string>& App::get_recent_files() { return recent_files; }
214 static std::list<std::string> recent_files_window_size;
216 int App::Busy::count;
217 bool App::shutdown_in_progress;
219 synfig::Gamma App::gamma;
221 Glib::RefPtr<studio::UIManager> App::ui_manager_;
223 synfig::Distance::System App::distance_system;
225 studio::Dialog_Setup* App::dialog_setup;
227 etl::handle< studio::ModPalette > mod_palette_;
228 //studio::Dialog_Palette* App::dialog_palette;
230 std::list<etl::handle<Instance> > App::instance_list;
232 static etl::handle<synfigapp::UIInterface> ui_interface_;
233 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
235 etl::handle<Instance> App::selected_instance;
236 etl::handle<CanvasView> App::selected_canvas_view;
238 studio::About *studio::App::about=NULL;
240 studio::Toolbox *studio::App::toolbox=NULL;
242 studio::AutoRecover *studio::App::auto_recover=NULL;
244 studio::IPC *ipc=NULL;
246 studio::DockManager* studio::App::dock_manager=0;
248 studio::DeviceTracker* studio::App::device_tracker=0;
250 studio::Dialog_Gradient* studio::App::dialog_gradient;
252 studio::Dialog_Color* studio::App::dialog_color;
254 Gtk::InputDialog* studio::App::dialog_input;
256 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
258 studio::Dock_History* dock_history;
259 studio::Dock_Canvases* dock_canvases;
260 studio::Dock_Keyframes* dock_keyframes;
261 studio::Dock_Layers* dock_layers;
262 studio::Dock_Params* dock_params;
263 studio::Dock_MetaData* dock_meta_data;
264 studio::Dock_Children* dock_children;
265 studio::Dock_Info* dock_info;
266 studio::Dock_LayerGroups* dock_layer_groups;
267 studio::Dock_Navigator* dock_navigator;
268 studio::Dock_Timetrack* dock_timetrack;
269 studio::Dock_Curves* dock_curves;
271 std::list< etl::handle< studio::Module > > module_list_;
273 bool studio::App::use_colorspace_gamma=true;
274 #ifdef SINGLE_THREADED
275 bool studio::App::single_threaded=false;
276 #endif
277 bool studio::App::restrict_radius_ducks=false;
278 bool studio::App::resize_imported_images=false;
279 String studio::App::custom_filename_prefix(DEFAULT_FILENAME_PREFIX);
280 int studio::App::preferred_x_size=480;
281 int studio::App::preferred_y_size=270;
282 String studio::App::predefined_size(DEFAULT_PREDEFINED_SIZE);
283 #ifdef USE_OPEN_FOR_URLS
284 String studio::App::browser_command("open"); // MacOS only
285 #else
286 String studio::App::browser_command("xdg-open"); // Linux XDG standard
287 #endif
289 static int max_recent_files_=25;
290 int studio::App::get_max_recent_files() { return max_recent_files_; }
291 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
293 static synfig::String app_base_path_;
295 namespace studio {
297 bool
298 really_delete_widget(Gtk::Widget *widget)
300 // synfig::info("really delete %p", (void*)widget);
301 delete widget;
302 return false;
305 // nasty workaround - when we've finished with a popup menu, we want to delete it
306 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
307 // so schedule the real delete to happen in 50ms, giving the action a chance to run
308 void
309 delete_widget(Gtk::Widget *widget)
311 // synfig::info("delete %p", (void*)widget);
312 Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
315 }; // END of namespace studio
316 studio::StateManager* state_manager;
321 class GlobalUIInterface : public synfigapp::UIInterface
323 public:
325 virtual Response yes_no(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
327 Gtk::Dialog dialog(
328 title, // Title
329 true, // Modal
330 true // use_separator
332 Gtk::Label label(message);
333 label.show();
335 dialog.get_vbox()->pack_start(label);
336 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
337 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
339 dialog.set_default_response(dflt);
340 dialog.show();
341 return (Response)dialog.run();
343 virtual Response yes_no_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
345 Gtk::Dialog dialog(
346 title, // Title
347 true, // Modal
348 true // use_separator
350 Gtk::Label label(message);
351 label.show();
353 dialog.get_vbox()->pack_start(label);
354 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
355 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
356 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
358 dialog.set_default_response(dflt);
359 dialog.show();
360 return (Response)dialog.run();
362 virtual Response ok_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_OK)
364 Gtk::Dialog dialog(
365 title, // Title
366 true, // Modal
367 true // use_separator
369 Gtk::Label label(message);
370 label.show();
372 dialog.get_vbox()->pack_start(label);
373 dialog.add_button(Gtk::StockID("gtk-ok"),RESPONSE_OK);
374 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
376 dialog.set_default_response(dflt);
377 dialog.show();
378 return (Response)dialog.run();
381 virtual bool
382 task(const std::string &task)
384 std::cerr<<task<<std::endl;
385 while(studio::App::events_pending())studio::App::iteration(false);
386 return true;
389 virtual bool
390 error(const std::string &err)
392 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
393 dialog.show();
394 dialog.run();
395 return true;
398 virtual bool
399 warning(const std::string &err)
401 std::cerr<<"warning: "<<err<<std::endl;
402 while(studio::App::events_pending())studio::App::iteration(false);
403 return true;
406 virtual bool
407 amount_complete(int /*current*/, int /*total*/)
409 while(studio::App::events_pending())studio::App::iteration(false);
410 return true;
414 /* === P R O C E D U R E S ================================================= */
417 void
418 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
420 action_group_list.push_back(action_group);
421 Gtk::UIManager::insert_action_group(action_group, pos);
424 void
425 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
427 std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
428 for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
429 if(*iter==action_group)
431 action_group_list.erase(iter);
432 Gtk::UIManager::remove_action_group(action_group);
433 return;
435 synfig::error("Unable to find action group");
438 void
439 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
441 ui_manager->insert_action_group(group,0);
442 return;
443 std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
444 std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
446 for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
448 if(*iter && (*iter)->get_name()!="menus")
450 synfig::info("Removing action group "+(*iter)->get_name());
451 ui_manager->remove_action_group(*iter);
454 ui_manager->insert_action_group(group,0);
456 for(;!prev_groups.empty();prev_groups.pop_front())
458 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
459 ui_manager->insert_action_group(prev_groups.front(),1);
463 class Preferences : public synfigapp::Settings
465 public:
466 virtual bool get_value(const synfig::String& key, synfig::String& value)const
468 if(key=="gamma")
470 value=strprintf("%f %f %f %f",
471 App::gamma.get_gamma_r(),
472 App::gamma.get_gamma_g(),
473 App::gamma.get_gamma_b(),
474 App::gamma.get_black_level()
476 return true;
478 if(key=="time_format")
480 value=strprintf("%i",App::get_time_format());
481 return true;
483 if(key=="file_history.size")
485 value=strprintf("%i",App::get_max_recent_files());
486 return true;
488 if(key=="use_colorspace_gamma")
490 value=strprintf("%i",(int)App::use_colorspace_gamma);
491 return true;
493 if(key=="distance_system")
495 value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
496 return true;
498 #ifdef SINGLE_THREADED
499 if(key=="single_threaded")
501 value=strprintf("%i",(int)App::single_threaded);
502 return true;
504 #endif
505 if(key=="auto_recover_backup_interval")
507 value=strprintf("%i",App::auto_recover->get_timeout());
508 return true;
510 if(key=="restrict_radius_ducks")
512 value=strprintf("%i",(int)App::restrict_radius_ducks);
513 return true;
515 if(key=="resize_imported_images")
517 value=strprintf("%i",(int)App::resize_imported_images);
518 return true;
520 if(key=="browser_command")
522 value=App::browser_command;
523 return true;
525 if(key=="custom_filename_prefix")
527 value=App::custom_filename_prefix;
528 return true;
530 if(key=="preferred_x_size")
532 value=strprintf("%i",App::preferred_x_size);
533 return true;
535 if(key=="preferred_y_size")
537 value=strprintf("%i",App::preferred_y_size);
538 return true;
540 if(key=="predefined_size")
542 value=strprintf("%s",App::predefined_size.c_str());
543 return true;
546 return synfigapp::Settings::get_value(key,value);
549 virtual bool set_value(const synfig::String& key,const synfig::String& value)
551 if(key=="gamma")
553 float r,g,b,blk;
555 strscanf(value,"%f %f %f %f",
559 &blk
562 App::gamma.set_all(r,g,b,blk);
564 return true;
566 if(key=="time_format")
568 int i(atoi(value.c_str()));
569 App::set_time_format(static_cast<synfig::Time::Format>(i));
570 return true;
572 if(key=="auto_recover_backup_interval")
574 int i(atoi(value.c_str()));
575 App::auto_recover->set_timeout(i);
576 return true;
578 if(key=="file_history.size")
580 int i(atoi(value.c_str()));
581 App::set_max_recent_files(i);
582 return true;
584 if(key=="use_colorspace_gamma")
586 int i(atoi(value.c_str()));
587 App::use_colorspace_gamma=i;
588 return true;
590 if(key=="distance_system")
592 App::distance_system=Distance::ident_system(value);;
593 return true;
595 #ifdef SINGLE_THREADED
596 if(key=="single_threaded")
598 int i(atoi(value.c_str()));
599 App::single_threaded=i;
600 return true;
602 #endif
603 if(key=="restrict_radius_ducks")
605 int i(atoi(value.c_str()));
606 App::restrict_radius_ducks=i;
607 return true;
609 if(key=="resize_imported_images")
611 int i(atoi(value.c_str()));
612 App::resize_imported_images=i;
613 return true;
615 if(key=="browser_command")
617 App::browser_command=value;
618 return true;
620 if(key=="custom_filename_prefix")
622 App::custom_filename_prefix=value;
623 return true;
625 if(key=="preferred_x_size")
627 int i(atoi(value.c_str()));
628 App::preferred_x_size=i;
629 return true;
631 if(key=="preferred_y_size")
633 int i(atoi(value.c_str()));
634 App::preferred_y_size=i;
635 return true;
637 if(key=="predefined_size")
639 App::predefined_size=value;
640 return true;
642 return synfigapp::Settings::set_value(key,value);
645 virtual KeyList get_key_list()const
647 KeyList ret(synfigapp::Settings::get_key_list());
648 ret.push_back("gamma");
649 ret.push_back("time_format");
650 ret.push_back("distance_system");
651 ret.push_back("file_history.size");
652 ret.push_back("use_colorspace_gamma");
653 #ifdef SINGLE_THREADED
654 ret.push_back("single_threaded");
655 #endif
656 ret.push_back("auto_recover_backup_interval");
657 ret.push_back("restrict_radius_ducks");
658 ret.push_back("resize_imported_images");
659 ret.push_back("browser_command");
660 ret.push_back("custom_filename_prefix");
661 ret.push_back("preferred_x_size");
662 ret.push_back("preferred_y_size");
663 ret.push_back("predefined_size");
664 return ret;
668 static ::Preferences _preferences;
670 void
671 init_ui_manager()
673 Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
675 Glib::RefPtr<Gtk::ActionGroup> toolbox_action_group = Gtk::ActionGroup::create("toolbox");
677 Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create();
679 menus_action_group->add( Gtk::Action::create("menu-file", _("_File")) );
680 menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")) );
681 menus_action_group->add( Gtk::Action::create("menu-view", _("_View")) );
682 menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")) );
683 menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
684 menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
685 menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
686 menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")) );
687 menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
688 menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
689 menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
690 menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
691 menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
693 // Add the synfigapp actions...
694 synfigapp::Action::Book::iterator iter;
695 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
697 actions_action_group->add(Gtk::Action::create(
698 "action-"+iter->second.name,
699 get_action_stock_id(iter->second),
700 iter->second.local_name,iter->second.local_name
704 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
705 #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); }
706 #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); }
708 DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
709 DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
710 DEFINE_ACTION("new", Gtk::Stock::NEW);
711 DEFINE_ACTION("open", Gtk::Stock::OPEN);
712 DEFINE_ACTION("save", Gtk::Stock::SAVE);
713 DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
714 DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
715 DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
716 DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
717 DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
718 DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
719 DEFINE_ACTION("import", _("Import"));
720 DEFINE_ACTION("render", _("Render"));
721 DEFINE_ACTION("preview", _("Preview"));
722 DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
723 DEFINE_ACTION("sound", _("Sound File"));
724 DEFINE_ACTION("options", _("Options"));
725 DEFINE_ACTION("close", _("Close View"));
726 DEFINE_ACTION("close-document", _("Close Document"));
727 DEFINE_ACTION("quit", Gtk::Stock::QUIT);
730 DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
731 DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
732 DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
733 DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
734 DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
735 DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
736 DEFINE_ACTION("unselect-all-ducks", _("Unselect All Ducks"));
737 DEFINE_ACTION("select-all-layers", _("Select All Layers"));
738 DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
739 DEFINE_ACTION("properties", _("Properties"));
741 DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
742 DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
743 DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
744 DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
745 DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
746 DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
747 DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
748 DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
749 DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
750 DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
751 DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
752 DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
753 DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
754 DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
755 DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
756 DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
757 DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
758 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
759 DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
760 DEFINE_ACTION("play", _("Play"));
761 // DEFINE_ACTION("pause", _("Pause"));
762 DEFINE_ACTION("stop", _("Stop"));
763 DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
764 DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
765 DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
766 DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
767 DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
768 DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
769 DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
770 DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
771 DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
772 DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
773 DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
774 DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
775 DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
776 DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
777 DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
778 DEFINE_ACTION("seek-next-frame", _("Next Frame"));
779 DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
780 DEFINE_ACTION("seek-next-second", _("Seek Forward"));
781 DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
782 DEFINE_ACTION("seek-begin", _("Seek to Begin"));
783 DEFINE_ACTION("seek-end", _("Seek to End"));
785 DEFINE_ACTION("action-group_add", _("Add group"));
787 DEFINE_ACTION("canvas-new", _("New Canvas"));
789 DEFINE_ACTION("amount-inc", _("Increase Amount"));
790 DEFINE_ACTION("amount-dec", _("Decrease Amount"));
792 #undef DEFINE_ACTION
795 // Set up synfigapp actions
797 synfigapp::Action::Book::iterator iter;
799 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
801 Gtk::StockID stock_id;
803 if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
805 //Gtk::Image* image(manage(new Gtk::Image()));
806 if(iter->second.task=="raise") stock_id=Gtk::Stock::GO_UP;
807 else if(iter->second.task=="lower") stock_id=Gtk::Stock::GO_DOWN;
808 else if(iter->second.task=="move_top") stock_id=Gtk::Stock::GOTO_TOP;
809 else if(iter->second.task=="move_bottom") stock_id=Gtk::Stock::GOTO_BOTTOM;
810 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
811 else if(iter->second.task=="set_on") stock_id=Gtk::Stock::YES;
812 else if(iter->second.task=="set_off") stock_id=Gtk::Stock::NO;
813 //else if(iter->second.task=="duplicate") stock_id=Gtk::Stock::COPY;
814 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
815 else stock_id=Gtk::StockID("synfig-"+iter->second.task);
817 actions_action_group->add(Gtk::Action::create(
818 "action-"+iter->second.name,
819 stock_id,
820 iter->second.local_name,iter->second.local_name
828 Glib::ustring ui_info =
829 "<ui>"
830 " <popup name='menu-toolbox' action='menu-toolbox'>"
831 " <menu action='menu-file'>"
832 " </menu>"
833 " </popup>"
834 " <popup name='menu-main' action='menu-main'>"
835 " <menu action='menu-file'>"
836 " <menuitem action='new' />"
837 " <menuitem action='open' />"
838 " <menuitem action='save' />"
839 " <menuitem action='save-as' />"
840 " <menuitem action='revert' />"
841 " <separator name='bleh01'/>"
842 " <menuitem action='cvs-add' />"
843 " <menuitem action='cvs-update' />"
844 " <menuitem action='cvs-commit' />"
845 " <menuitem action='cvs-revert' />"
846 " <separator name='bleh02'/>"
847 " <menuitem action='import' />"
848 " <separator name='bleh03'/>"
849 " <menuitem action='render' />"
850 " <menuitem action='preview' />"
851 " <menuitem action='sound' />"
852 " <separator name='bleh04'/>"
853 " <menuitem action='options' />"
854 " <menuitem action='close' />"
855 " <menuitem action='close-document' />"
856 " <menuitem action='quit' />"
857 " </menu>"
858 " <menu action='menu-edit'>"
859 " <menuitem action='undo'/>"
860 " <menuitem action='redo'/>"
861 " <separator name='bleh05'/>"
862 " <menuitem action='cut'/>"
863 " <menuitem action='copy'/>"
864 " <menuitem action='paste'/>"
865 " <separator name='bleh06'/>"
866 " <menuitem action='select-all-layers'/>"
867 " <menuitem action='unselect-all-layers'/>"
868 " <menuitem action='select-all-ducks'/>"
869 " <menuitem action='unselect-all-ducks'/>"
870 " <separator name='bleh07'/>"
871 " <menuitem action='properties'/>"
872 " </menu>"
873 " <menu action='menu-view'>"
874 " <menu action='menu-duck-mask'>"
875 " <menuitem action='mask-position-ducks' />"
876 " <menuitem action='mask-vertex-ducks' />"
877 " <menuitem action='mask-tangent-ducks' />"
878 " <menuitem action='mask-radius-ducks' />"
879 " <menuitem action='mask-width-ducks' />"
880 " <menuitem action='mask-angle-ducks' />"
881 " </menu>"
882 " <menu action='menu-preview-quality'>"
883 " <menuitem action='quality-00' />"
884 " <menuitem action='quality-01' />"
885 " <menuitem action='quality-02' />"
886 " <menuitem action='quality-03' />"
887 " <menuitem action='quality-04' />"
888 " <menuitem action='quality-05' />"
889 " <menuitem action='quality-06' />"
890 " <menuitem action='quality-07' />"
891 " <menuitem action='quality-08' />"
892 " <menuitem action='quality-09' />"
893 " <menuitem action='quality-10' />"
894 " </menu>"
895 " <menu action='menu-lowres-pixel'>"
896 " <menuitem action='decrease-low-res-pixel-size'/>"
897 " <menuitem action='increase-low-res-pixel-size'/>"
898 " <separator name='pixel-size-separator'/>"
901 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
902 ui_info += strprintf(" <menuitem action='lowres-pixel-%d' />", *iter);
904 ui_info +=
905 " </menu>"
906 " <separator name='bleh08'/>"
907 " <menuitem action='play'/>"
908 //" <menuitem action='pause'/>"
909 " <menuitem action='stop'/>"
910 " <menuitem action='dialog-flipbook'/>"
911 " <separator name='bleh09'/>"
912 " <menuitem action='toggle-grid-show'/>"
913 " <menuitem action='toggle-grid-snap'/>"
914 " <menuitem action='toggle-guide-show'/>"
915 " <menuitem action='toggle-low-res'/>"
916 " <menuitem action='toggle-onion-skin'/>"
917 " <separator name='bleh10'/>"
918 " <menuitem action='canvas-zoom-in'/>"
919 " <menuitem action='canvas-zoom-out'/>"
920 " <menuitem action='canvas-zoom-fit'/>"
921 " <menuitem action='canvas-zoom-100'/>"
922 " <separator name='bleh11'/>"
923 " <menuitem action='time-zoom-in'/>"
924 " <menuitem action='time-zoom-out'/>"
925 " <separator name='bleh12'/>"
926 " <menuitem action='jump-next-keyframe'/>"
927 " <menuitem action='jump-prev-keyframe'/>"
928 " <menuitem action='seek-next-frame'/>"
929 " <menuitem action='seek-prev-frame'/>"
930 " <menuitem action='seek-next-second'/>"
931 " <menuitem action='seek-prev-second'/>"
932 " <menuitem action='seek-begin'/>"
933 " <menuitem action='seek-end'/>"
934 " </menu>"
935 " <menu action='menu-canvas'>"
936 " <menuitem action='canvas-new'/>"
937 " </menu>"
938 " <menu name='menu-state' action='menu-state'>"
939 " </menu>"
940 " <menu action='menu-group'>"
941 " <menuitem action='action-group_add'/>"
942 " </menu>"
943 " <menu action='menu-layer'>"
944 //" <menuitem action='cut'/>"
945 //" <menuitem action='copy'/>"
946 //" <menuitem action='paste'/>"
947 //" <separator name='bleh06'/>"
948 " <menu action='menu-layer-new'></menu>"
949 " <menuitem action='amount-inc'/>"
950 " <menuitem action='amount-dec'/>"
951 " </menu>"
952 " <menu action='menu-keyframe'>"
953 " <menuitem action='keyframe-properties'/>"
954 " </menu>"
955 " </popup>"
957 "</ui>"
959 /* "<ui>"
960 " <menubar name='MenuBar'>"
961 " <menu action='MenuFile'>"
962 " <menuitem action='New'/>"
963 " <menuitem action='Open'/>"
964 " <separator/>"
965 " <menuitem action='Quit'/>"
966 " </menu>"
967 " <menu action='MenuEdit'>"
968 " <menuitem action='Cut'/>"
969 " <menuitem action='Copy'/>"
970 " <menuitem action='Paste'/>"
971 " </menu>"
972 " </menubar>"
973 " <toolbar name='ToolBar'>"
974 " <toolitem action='Open'/>"
975 " <toolitem action='Quit'/>"
976 " </toolbar>"
977 "</ui>";
981 actions_action_group->set_sensitive(false);
982 App::ui_manager()->set_add_tearoffs(true);
983 App::ui_manager()->insert_action_group(menus_action_group,1);
984 App::ui_manager()->insert_action_group(actions_action_group,1);
985 App::ui_manager()->add_ui_from_string(ui_info);
987 //App::ui_manager()->get_accel_group()->unlock();
989 catch(const Glib::Error& ex)
991 synfig::error("building menus and toolbars failed: " + ex.what());
994 // Add default keyboard accelerators
995 #define ACCEL(path,accel) \
997 Gtk::AccelKey accel_key(accel,path); \
998 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1001 #define ACCEL2(accel) \
1003 Gtk::AccelKey accel_key(accel); \
1004 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1007 ACCEL("<Actions>//select-all-ducks","<Control>a");
1008 ACCEL("<Actions>//unselect-all-ducks","<Control>d");
1009 ACCEL("<Actions>//select-all-layers","<Control><Shift>a");
1010 ACCEL("<Actions>//unselect-all-layers","<Control><Shift>d");
1011 ACCEL("<Actions>//render","F9");
1012 ACCEL("<Actions>//preview","F11");
1013 ACCEL("<Actions>//properties","F8");
1014 ACCEL("<Actions>//options","F12");
1015 ACCEL("<Actions>//import","<control>i");
1016 ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
1017 ACCEL("<Actions>//toggle-grid-show","<Control>g");
1018 ACCEL("<Actions>//toggle-grid-snap","<Control>l");
1019 ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
1020 ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
1021 ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
1022 ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
1023 ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
1024 ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
1025 ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
1027 ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
1028 ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
1030 ACCEL("<Actions>//quality-01","<Control>1");
1031 ACCEL("<Actions>//quality-02","<Control>2");
1032 ACCEL("<Actions>//quality-03","<Control>3");
1033 ACCEL("<Actions>//quality-04","<Control>4");
1034 ACCEL("<Actions>//quality-05","<Control>5");
1035 ACCEL("<Actions>//quality-06","<Control>6");
1036 ACCEL("<Actions>//quality-07","<Control>7");
1037 ACCEL("<Actions>//quality-08","<Control>8");
1038 ACCEL("<Actions>//quality-09","<Control>9");
1039 ACCEL("<Actions>//quality-10","<Control>0");
1040 ACCEL("<Actions>//undo","<Control>z");
1041 ACCEL("<Actions>//redo","<Control>r");
1042 ACCEL("<Actions>//action-layer_remove","Delete");
1044 /* ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
1045 ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
1046 ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
1047 ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
1048 ACCEL("<Actions>//time-zoom-in","+");
1049 ACCEL("<Actions>//time-zoom-out","_");
1051 ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,"<Actions>//decrease-low-res-pixel-size"));
1052 ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,"<Actions>//increase-low-res-pixel-size"));
1054 ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
1055 ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
1057 ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
1058 ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
1059 ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
1060 ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
1061 ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
1062 ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
1063 ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
1064 ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
1065 ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
1066 ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
1067 ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
1068 ACCEL("<Actions>//play", "<Control>p");
1069 ACCEL("<Actions>//seek-begin","Home");
1070 ACCEL("<Actions>//seek-end","End");
1072 ACCEL("<Actions>//state-normal", "<Mod1>a");
1073 ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1074 ACCEL("<Actions>//state-scale", "<Mod1>s");
1075 ACCEL("<Actions>//state-rotate", "<Mod1>t");
1076 ACCEL("<Actions>//state-mirror", "<Mod1>m");
1078 ACCEL("<Actions>//state-circle", "<Mod1>c");
1079 ACCEL("<Actions>//state-rectangle", "<Mod1>r");
1080 ACCEL("<Actions>//state-star", "<Mod1>q");
1081 ACCEL("<Actions>//state-gradient", "<Mod1>g");
1082 ACCEL("<Actions>//state-polygon", "<Mod1>p");
1084 ACCEL("<Actions>//state-bline", "<Mod1>b");
1085 ACCEL("<Actions>//state-text", "<Mod1>x");
1086 ACCEL("<Actions>//state-fill", "<Mod1>f");
1087 ACCEL("<Actions>//state-eyedrop", "<Mod1>e");
1088 ACCEL("<Actions>//state-zoom", "<Mod1>z");
1090 ACCEL("<Actions>//state-draw", "<Mod1>d");
1091 ACCEL("<Actions>//state-sketch", "<Mod1>k");
1092 ACCEL("<Actions>//state-width", "<Mod1>w");
1094 ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1096 #undef ACCEL
1099 #ifdef _WIN32
1100 #define mkdir(x,y) mkdir(x)
1101 #endif
1103 /* === M E T H O D S ======================================================= */
1105 App::App(int *argc, char ***argv):
1106 Gtk::Main(argc,argv),
1107 IconController(etl::dirname((*argv)[0]))
1109 app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1112 ui_interface_=new GlobalUIInterface();
1114 gdk_rgb_init();
1116 // don't call thread_init() if threads are already initialized
1117 // on some machines bonobo_init() initialized threads before we get here
1118 if (!g_thread_supported())
1119 Glib::thread_init();
1121 distance_system=Distance::SYSTEM_UNITS;
1123 if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1125 if(errno!=EEXIST)
1126 synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1128 else
1130 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1134 ipc=new IPC();
1136 if(!SYNFIG_CHECK_VERSION())
1138 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1139 dialog_error_blocking("Synfig Studio",
1140 "This copy of Synfig Studio was compiled against a\n"
1141 "different version of libsynfig than what is currently\n"
1142 "installed. Synfig Studio will now abort. Try downloading\n"
1143 "the latest version from the Synfig website at\n"
1144 "http://synfig.org/Download"
1146 throw 40;
1148 Glib::set_application_name(_("Synfig Studio"));
1150 Splash splash_screen;
1151 splash_screen.show();
1153 shutdown_in_progress=false;
1154 SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1155 SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1157 // Initialize the Synfig library
1158 try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1159 catch(std::runtime_error x)
1161 get_ui_interface()->error(strprintf("%s\n\n%s", _("Failed to initialize synfig!"), x.what()));
1162 throw;
1164 catch(...)
1166 get_ui_interface()->error(_("Failed to initialize synfig!"));
1167 throw;
1170 // add the preferences to the settings
1171 synfigapp::Main::settings().add_domain(&_preferences,"pref");
1175 studio_init_cb.task(_("Init UI Manager..."));
1176 App::ui_manager_=studio::UIManager::create();
1177 init_ui_manager();
1179 studio_init_cb.task(_("Init Dock Manager..."));
1180 dock_manager=new studio::DockManager();
1182 studio_init_cb.task(_("Init State Manager..."));
1183 state_manager=new StateManager();
1185 studio_init_cb.task(_("Init Toolbox..."));
1186 toolbox=new studio::Toolbox();
1188 studio_init_cb.task(_("Init About Dialog..."));
1189 about=new studio::About();
1191 studio_init_cb.task(_("Init Tool Options..."));
1192 dialog_tool_options=new studio::Dialog_ToolOptions();
1193 dock_manager->register_dockable(*dialog_tool_options);
1195 studio_init_cb.task(_("Init History..."));
1196 dock_history=new studio::Dock_History();
1197 dock_manager->register_dockable(*dock_history);
1199 studio_init_cb.task(_("Init Canvases..."));
1200 dock_canvases=new studio::Dock_Canvases();
1201 dock_manager->register_dockable(*dock_canvases);
1203 studio_init_cb.task(_("Init Keyframes..."));
1204 dock_keyframes=new studio::Dock_Keyframes();
1205 dock_manager->register_dockable(*dock_keyframes);
1207 studio_init_cb.task(_("Init Layers..."));
1208 dock_layers=new studio::Dock_Layers();
1209 dock_manager->register_dockable(*dock_layers);
1211 studio_init_cb.task(_("Init Params..."));
1212 dock_params=new studio::Dock_Params();
1213 dock_manager->register_dockable(*dock_params);
1215 studio_init_cb.task(_("Init MetaData..."));
1216 dock_meta_data=new studio::Dock_MetaData();
1217 dock_manager->register_dockable(*dock_meta_data);
1219 studio_init_cb.task(_("Init Children..."));
1220 dock_children=new studio::Dock_Children();
1221 dock_manager->register_dockable(*dock_children);
1223 studio_init_cb.task(_("Init Info..."));
1224 dock_info = new studio::Dock_Info();
1225 dock_manager->register_dockable(*dock_info);
1227 studio_init_cb.task(_("Init Navigator..."));
1228 dock_navigator = new studio::Dock_Navigator();
1229 dock_manager->register_dockable(*dock_navigator);
1231 studio_init_cb.task(_("Init Timetrack..."));
1232 dock_timetrack = new studio::Dock_Timetrack();
1233 dock_manager->register_dockable(*dock_timetrack);
1235 studio_init_cb.task(_("Init Curve Editor..."));
1236 dock_curves = new studio::Dock_Curves();
1237 dock_manager->register_dockable(*dock_curves);
1239 studio_init_cb.task(_("Init Layer Groups..."));
1240 dock_layer_groups = new studio::Dock_LayerGroups();
1241 dock_manager->register_dockable(*dock_layer_groups);
1244 studio_init_cb.task(_("Init Color Dialog..."));
1245 dialog_color=new studio::Dialog_Color();
1247 studio_init_cb.task(_("Init Gradient Dialog..."));
1248 dialog_gradient=new studio::Dialog_Gradient();
1250 studio_init_cb.task(_("Init DeviceTracker..."));
1251 device_tracker=new studio::DeviceTracker();
1253 studio_init_cb.task(_("Init Tools..."));
1255 /* row 1 */
1256 state_manager->add_state(&state_normal);
1257 state_manager->add_state(&state_smooth_move);
1258 state_manager->add_state(&state_scale);
1259 state_manager->add_state(&state_rotate);
1260 studio_init_cb.task(_("Init ModMirror...")); module_list_.push_back(new ModMirror()); module_list_.back()->start();
1262 /* row 2 */
1263 state_manager->add_state(&state_circle);
1264 state_manager->add_state(&state_rectangle);
1265 state_manager->add_state(&state_star);
1266 state_manager->add_state(&state_gradient);
1267 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1269 /* row 3 */
1270 state_manager->add_state(&state_bline);
1271 state_manager->add_state(&state_text);
1272 state_manager->add_state(&state_fill);
1273 state_manager->add_state(&state_eyedrop);
1274 state_manager->add_state(&state_zoom);
1276 if(!getenv("SYNFIG_DISABLE_DRAW" )) state_manager->add_state(&state_draw); // Enabled for now. Let's see whether they're good enough yet.
1277 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1278 if(!getenv("SYNFIG_DISABLE_WIDTH" )) state_manager->add_state(&state_width); // Enabled since 0.61.09
1280 studio_init_cb.task(_("Init ModPalette..."));
1281 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1283 studio_init_cb.task(_("Init Setup Dialog..."));
1284 dialog_setup=new studio::Dialog_Setup();
1286 studio_init_cb.task(_("Init Input Dialog..."));
1287 dialog_input=new Gtk::InputDialog();
1288 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1289 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1291 studio_init_cb.task(_("Init auto recovery..."));
1292 auto_recover=new AutoRecover();
1294 studio_init_cb.amount_complete(9250,10000);
1295 studio_init_cb.task(_("Loading Settings..."));
1296 load_settings();
1297 studio_init_cb.task(_("Checking auto-recover..."));
1299 studio_init_cb.amount_complete(9900,10000);
1301 bool opened_any = false;
1302 if(auto_recover->recovery_needed())
1304 splash_screen.hide();
1305 if (get_ui_interface()->yes_no(_("Auto Recovery"),
1306 _("Synfig Studio seems to have crashed\n"
1307 "before you could save all your files.\n"
1308 "Would you like to re-open those files\n"
1309 "and recover your unsaved changes?")) ==
1310 synfigapp::UIInterface::RESPONSE_YES)
1312 int number_recovered;
1313 if(!auto_recover->recover(number_recovered))
1314 if (number_recovered)
1315 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1316 else
1317 get_ui_interface()->error(_("Unable to recover from previous crash"));
1318 else
1319 get_ui_interface()->error(
1320 _("Synfig Studio has attempted to recover\n"
1321 "from a previous crash. The files that it has\n"
1322 "recovered are NOT YET SAVED. It would be a good\n"
1323 "idea to review them and save them now."));
1325 if (number_recovered)
1326 opened_any = true;
1328 splash_screen.show();
1331 // Look for any files given on the command line,
1332 // and load them if found.
1333 for(;*argc>=1;(*argc)--)
1334 if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1336 studio_init_cb.task(_("Loading files..."));
1337 splash_screen.hide();
1338 open((*argv)[*argc]);
1339 opened_any = true;
1340 splash_screen.show();
1343 // if no file was specified to be opened, create a new document to help new users get started more easily
1344 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1345 new_instance();
1347 studio_init_cb.task(_("Done."));
1348 studio_init_cb.amount_complete(10000,10000);
1350 toolbox->present();
1352 catch(String x)
1354 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1356 catch(...)
1358 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1362 StateManager* App::get_state_manager() { return state_manager; }
1364 App::~App()
1366 shutdown_in_progress=true;
1368 save_settings();
1370 synfigapp::Main::settings().remove_domain("pref");
1372 selected_instance=0;
1374 // Unload all of the modules
1375 for(;!module_list_.empty();module_list_.pop_back())
1378 delete state_manager;
1380 delete ipc;
1382 delete auto_recover;
1384 delete about;
1386 toolbox->hide();
1388 // studio::App::iteration(false);
1390 delete toolbox;
1392 // studio::App::iteration(false);
1394 // studio::App::iteration(false);
1396 delete dialog_setup;
1398 delete dialog_gradient;
1400 delete dialog_color;
1402 delete dialog_input;
1404 delete dock_manager;
1406 instance_list.clear();
1408 // studio::App::iteration(false);
1411 String
1412 App::get_user_app_directory()
1414 //! \todo do we need locale_from_utf8() on non-Windows boxes too? (bug #1837445)
1415 #ifdef WIN32
1416 return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1417 #else
1418 return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1419 #endif
1422 synfig::String
1423 App::get_config_file(const synfig::String& file)
1425 return Glib::build_filename(get_user_app_directory(),file);
1428 #define SCALE_FACTOR (1280)
1429 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1430 void
1431 App::set_recent_file_window_size(etl::handle<Instance> instance)
1433 int screen_w(Gdk::screen_width());
1434 int screen_h(Gdk::screen_height());
1436 const std::string &canvas_window_size = *recent_files_window_size.begin();
1438 if(canvas_window_size.empty())
1439 return;
1441 synfig::String::size_type current=0;
1442 bool seen_root(false), shown_non_root(false);
1444 while(current != synfig::String::npos)
1446 // find end of first field (canvas) or return
1447 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1448 if(separator == synfig::String::npos) break;
1450 // find the canvas
1451 synfig::Canvas::Handle canvas;
1452 try {
1453 String warnings;
1454 canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current), warnings);
1456 catch(Exception::IDNotFound) {
1457 // can't find the canvas; skip to the next canvas or return
1458 separator = canvas_window_size.find_first_of('\t', current);
1459 if(separator == synfig::String::npos) return;
1460 current = separator+1;
1461 continue;
1464 if (canvas->is_root())
1465 seen_root = true;
1466 else
1467 shown_non_root = true;
1469 // check that we have the tab character the ends this canvas' data or return
1470 current = separator+1;
1471 separator = canvas_window_size.find_first_of('\t', current);
1472 if(separator == synfig::String::npos) return;
1474 int x,y,w,h;
1475 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1477 current = separator+1;
1478 continue;
1481 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1482 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1483 x=x*screen_w/SCALE_FACTOR;
1484 y=y*screen_h/SCALE_FACTOR;
1485 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1486 x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1487 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1488 y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1490 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1491 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1493 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1494 canvasview->move(x,y);
1495 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1496 canvasview->present();
1498 current = separator+1;
1501 if (shown_non_root && !seen_root)
1502 instance->find_canvas_view(instance->get_canvas())->hide();
1505 void
1506 App::add_recent_file(const etl::handle<Instance> instance)
1508 int screen_w(Gdk::screen_width());
1509 int screen_h(Gdk::screen_height());
1511 std::string canvas_window_size;
1513 const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1514 Instance::CanvasViewList::const_iterator iter;
1516 for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1518 if( !((*iter)->is_visible()) )
1519 continue;
1521 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1522 int x_pos, y_pos, x_size, y_size;
1523 (*iter)->get_position(x_pos,y_pos);
1524 (*iter)->get_size(x_size,y_size);
1526 canvas_window_size += strprintf("%s %d %d %d %d\t",
1527 canvas->get_relative_id(canvas->get_root()).c_str(),
1528 x_pos*SCALE_FACTOR/screen_w, y_pos*SCALE_FACTOR/screen_h,
1529 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1532 add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1534 #undef SCALE_FACTOR
1536 void
1537 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1539 std::string filename(file_name);
1541 assert(!filename.empty());
1543 if(filename.empty())
1544 return;
1546 // Toss out any "hidden" files
1547 if(basename(filename)[0]=='.')
1548 return;
1550 // If we aren't an absolute path, turn ourselves into one
1551 if(!is_absolute_path(filename))
1552 filename=absolute_path(filename);
1554 std::string old_window_size;
1556 list<string>::iterator iter;
1557 list<string>::iterator iter_wsize;
1558 // Check to see if the file is already on the list.
1559 // If it is, then remove it from the list
1560 for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1561 if(*iter==filename)
1563 recent_files.erase(iter);
1564 old_window_size = *iter_wsize;
1565 recent_files_window_size.erase(iter_wsize);
1566 break;
1570 // Push the filename to the front of the list
1571 recent_files.push_front(filename);
1572 if(window_size.empty())
1573 recent_files_window_size.push_front(old_window_size);
1574 else
1575 recent_files_window_size.push_front(window_size);
1577 // Clean out the files at the end of the list.
1578 while(recent_files.size()>(unsigned)get_max_recent_files())
1580 recent_files.pop_back();
1581 recent_files_window_size.pop_back();
1584 signal_recent_files_changed_();
1586 return;
1589 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1591 Time::Format
1592 App::get_time_format()
1594 return _App_time_format;
1597 void
1598 App::set_time_format(synfig::Time::Format x)
1600 _App_time_format=x;
1604 void
1605 App::save_settings()
1607 char * old_locale;
1610 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1611 setlocale(LC_NUMERIC, "C");
1613 std::string filename=get_config_file("accelrc");
1614 Gtk::AccelMap::save(filename);
1617 std::string filename=get_config_file("recentfiles");
1619 std::ofstream file(filename.c_str());
1621 if(!file)
1623 synfig::warning("Unable to save %s",filename.c_str());
1624 break;
1627 list<string>::reverse_iterator iter;
1629 for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1630 file<<*iter<<endl;
1631 }while(0);
1633 std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1635 std::ofstream file(filename.c_str());
1637 if(!file)
1639 synfig::warning("Unable to save %s",filename.c_str());
1640 break;
1643 list<string>::reverse_iterator iter;
1645 for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1646 file<<*iter<<endl;
1648 }while(0);
1649 std::string filename=get_config_file("settings");
1650 synfigapp::Main::settings().save_to_file(filename);
1651 setlocale(LC_NUMERIC,old_locale);
1653 catch(...)
1655 synfig::warning("Caught exception when attempting to save settings.");
1659 void
1660 App::load_settings()
1662 char * old_locale;
1665 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1666 setlocale(LC_NUMERIC, "C");
1668 std::string filename=get_config_file("accelrc");
1669 Gtk::AccelMap::load(filename);
1672 bool window_size_broken = false;
1674 std::string filename=get_config_file("recentfiles");
1675 std::string filename_window_size=filename+std::string("_window_size");
1677 std::ifstream file(filename.c_str());
1678 std::ifstream file_window_size(filename_window_size.c_str());
1680 if(!file_window_size)
1681 window_size_broken = true;
1683 while(file)
1685 std::string recent_file;
1686 std::string recent_file_window_size;
1687 getline(file,recent_file);
1688 if(!window_size_broken)
1689 getline(file_window_size,recent_file_window_size);
1690 if(!recent_file.empty())
1692 if(!window_size_broken && !file_window_size)
1693 window_size_broken = true;
1694 if (std::ifstream(recent_file.c_str()))
1696 if(!window_size_broken)
1697 add_recent_file(recent_file,recent_file_window_size);
1698 else
1699 add_recent_file(recent_file);
1703 if(!window_size_broken && file_window_size)
1704 window_size_broken = true;
1706 if(window_size_broken)
1708 recent_files_window_size.clear();
1709 recent_files_window_size.resize(recent_files.size());
1712 std::string filename=get_config_file("settings");
1713 if(!synfigapp::Main::settings().load_from_file(filename))
1715 //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1716 //if(!synfigapp::Main::settings().load_from_file(filename))
1718 gamma.set_gamma(1.0/2.2);
1719 reset_initial_window_configuration();
1722 setlocale(LC_NUMERIC,old_locale);
1724 catch(...)
1726 synfig::warning("Caught exception when attempting to load settings.");
1730 void
1731 App::reset_initial_window_configuration()
1733 synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1734 synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1735 synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1736 synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1737 synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1738 synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1739 synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1740 synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1741 synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1742 synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1743 synfigapp::Main::settings().set_value("pref.distance_system","pt");
1744 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1745 #ifdef SINGLE_THREADED
1746 synfigapp::Main::settings().set_value("pref.single_threaded","0");
1747 #endif
1748 synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1749 synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
1750 synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
1751 synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
1752 synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
1753 synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
1754 synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1757 bool
1758 App::shutdown_request(GdkEventAny*)
1760 quit();
1761 return true;
1762 //return !shutdown_in_progress;
1765 void
1766 App::quit()
1768 if(shutdown_in_progress)return;
1771 get_ui_interface()->task(_("Quit Request"));
1772 if(Busy::count)
1774 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1775 return;
1778 std::list<etl::handle<Instance> >::iterator iter;
1779 for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1781 if(!(*iter)->safe_close())
1782 return;
1785 if((*iter)->synfigapp::Instance::get_action_count())
1787 handle<synfigapp::UIInterface> uim;
1788 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1789 assert(uim);
1790 string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1791 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1793 case synfigapp::UIInterface::RESPONSE_NO:
1794 break;
1795 case synfigapp::UIInterface::RESPONSE_YES:
1796 (*iter)->save();
1797 break;
1798 case synfigapp::UIInterface::RESPONSE_CANCEL:
1799 return;
1800 default:
1801 assert(0);
1802 return;
1807 if((*iter)->synfigapp::Instance::is_modified())
1809 handle<synfigapp::UIInterface> uim;
1810 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1811 assert(uim);
1812 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() );
1813 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1815 case synfigapp::UIInterface::RESPONSE_NO:
1816 break;
1817 case synfigapp::UIInterface::RESPONSE_YES:
1818 (*iter)->dialog_cvs_commit();
1819 break;
1820 case synfigapp::UIInterface::RESPONSE_CANCEL:
1821 return;
1822 default:
1823 assert(0);
1824 return;
1829 // This next line causes things to crash for some reason
1830 //(*iter)->close();
1833 shutdown_in_progress=true;
1835 instance_list.clear();
1837 while(studio::App::events_pending())studio::App::iteration(false);
1839 Gtk::Main::quit();
1840 auto_recover->normal_shutdown();
1842 get_ui_interface()->task(_("Quit Request sent"));
1845 void
1846 App::show_setup()
1848 dialog_setup->refresh();
1849 dialog_setup->show();
1852 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1853 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1855 //#ifdef WIN32
1856 //#define USE_WIN32_FILE_DIALOGS 1
1857 //#endif
1859 #ifdef USE_WIN32_FILE_DIALOGS
1860 static OPENFILENAME ofn={};
1861 #endif
1863 #ifdef WIN32
1864 #include <gdk/gdkwin32.h>
1865 #endif
1867 bool
1868 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1870 // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1872 #ifdef USE_WIN32_FILE_DIALOGS
1873 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1875 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1876 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1877 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1879 ofn.lStructSize=sizeof(OPENFILENAME);
1880 ofn.hwndOwner = hWnd;
1881 ofn.hInstance = hInstance;
1882 ofn.lpstrFilter = szFilter;
1883 // ofn.lpstrCustomFilter=NULL;
1884 // ofn.nMaxCustFilter=0;
1885 // ofn.nFilterIndex=0;
1886 // ofn.lpstrFile=NULL;
1887 ofn.nMaxFile=MAX_PATH;
1888 // ofn.lpstrFileTitle=NULL;
1889 // ofn.lpstrInitialDir=NULL;
1890 // ofn.lpstrTitle=NULL;
1891 ofn.Flags=OFN_HIDEREADONLY;
1892 // ofn.nFileOffset=0;
1893 // ofn.nFileExtension=0;
1894 ofn.lpstrDefExt=TEXT("sif");
1895 // ofn.lCustData = 0l;
1896 ofn.lpfnHook=NULL;
1897 // ofn.lpTemplateName=NULL;
1899 CHAR szFilename[MAX_PATH];
1900 CHAR szTitle[500];
1901 strcpy(szFilename,filename.c_str());
1902 strcpy(szTitle,title.c_str());
1904 ofn.lpstrFile=szFilename;
1905 ofn.lpstrFileTitle=szTitle;
1907 if(GetOpenFileName(&ofn))
1909 filename=szFilename;
1910 return true;
1912 return false;
1914 #else
1915 synfig::String prev_path;
1917 if(!_preferences.get_value(preference, prev_path))
1918 prev_path = ".";
1920 prev_path = absolute_path(prev_path);
1922 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1924 dialog->set_current_folder(prev_path);
1925 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1926 dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1928 if (filename.empty())
1929 dialog->set_filename(prev_path);
1930 else if (is_absolute_path(filename))
1931 dialog->set_filename(filename);
1932 else
1933 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1935 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1936 filename = dialog->get_filename();
1937 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1938 _preferences.set_value(preference, dirname(filename));
1939 delete dialog;
1940 return true;
1943 delete dialog;
1944 return false;
1948 GtkWidget *ok;
1949 GtkWidget *cancel;
1950 int val=0;
1952 GtkWidget *fileselection;
1953 fileselection = gtk_file_selection_new(title.c_str());
1956 if(basename(filename)==filename)
1958 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1960 else
1961 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1963 gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1965 ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1966 cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1968 gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1969 gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1971 gtk_widget_show(fileselection);
1973 while(!val)
1974 iteration();
1977 if(val==1)
1979 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1980 _preferences.set_value(preference,dirname(filename));
1982 else
1984 gtk_widget_destroy(fileselection);
1985 return false;
1987 gtk_widget_destroy(fileselection);
1988 return true;
1990 #endif
1993 bool
1994 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1996 // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1998 #if USE_WIN32_FILE_DIALOGS
1999 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
2001 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2002 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2003 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2005 ofn.lStructSize=sizeof(OPENFILENAME);
2006 ofn.hwndOwner = hWnd;
2007 ofn.hInstance = hInstance;
2008 ofn.lpstrFilter = szFilter;
2009 // ofn.lpstrCustomFilter=NULL;
2010 // ofn.nMaxCustFilter=0;
2011 // ofn.nFilterIndex=0;
2012 // ofn.lpstrFile=NULL;
2013 ofn.nMaxFile=MAX_PATH;
2014 // ofn.lpstrFileTitle=NULL;
2015 // ofn.lpstrInitialDir=NULL;
2016 // ofn.lpstrTitle=NULL;
2017 ofn.Flags=OFN_OVERWRITEPROMPT;
2018 // ofn.nFileOffset=0;
2019 // ofn.nFileExtension=0;
2020 ofn.lpstrDefExt=TEXT("sif");
2021 // ofn.lCustData = 0l;
2022 ofn.lpfnHook=NULL;
2023 // ofn.lpTemplateName=NULL;
2025 CHAR szFilename[MAX_PATH];
2026 CHAR szTitle[500];
2027 strcpy(szFilename,filename.c_str());
2028 strcpy(szTitle,title.c_str());
2030 ofn.lpstrFile=szFilename;
2031 ofn.lpstrFileTitle=szTitle;
2033 if(GetSaveFileName(&ofn))
2035 filename=szFilename;
2036 _preferences.set_value(preference,dirname(filename));
2037 return true;
2039 return false;
2040 #else
2041 synfig::String prev_path;
2043 if(!_preferences.get_value(preference, prev_path))
2044 prev_path=".";
2046 prev_path = absolute_path(prev_path);
2048 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2050 dialog->set_current_folder(prev_path);
2051 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2052 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2054 Widget_Enum *file_type_enum = 0;
2055 if (preference == ANIMATION_DIR_PREFERENCE)
2057 file_type_enum = manage(new Widget_Enum());
2058 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2059 .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
2060 .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2061 .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2062 .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2063 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2065 Gtk::HBox *hbox = manage(new Gtk::HBox);
2066 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2067 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2068 hbox->show_all();
2070 dialog->set_extra_widget(*hbox);
2073 if (filename.empty())
2074 dialog->set_filename(prev_path);
2075 else
2077 std::string full_path;
2078 if (is_absolute_path(filename))
2079 full_path = filename;
2080 else
2081 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2083 // select the file if it exists
2084 dialog->set_filename(full_path);
2086 // if the file doesn't exist, put its name into the filename box
2087 struct stat s;
2088 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2089 dialog->set_current_name(basename(filename));
2092 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2093 if (preference == ANIMATION_DIR_PREFERENCE)
2094 set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2095 filename = dialog->get_filename();
2096 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2097 _preferences.set_value(preference, dirname(filename));
2098 delete dialog;
2099 return true;
2102 delete dialog;
2103 return false;
2104 #endif
2107 void
2108 App::dialog_error_blocking(const std::string &title, const std::string &message)
2110 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2111 dialog.set_title(title);
2112 dialog.show();
2113 dialog.run();
2116 void
2117 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2119 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2120 dialog.set_title(title);
2121 dialog.show();
2122 dialog.run();
2125 bool
2126 App::dialog_yes_no(const std::string &title, const std::string &message)
2128 Gtk::Dialog dialog(
2129 title, // Title
2130 true, // Modal
2131 true // use_separator
2133 Gtk::Label label(message);
2134 label.show();
2136 dialog.get_vbox()->pack_start(label);
2137 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2138 dialog.add_button(Gtk::StockID("gtk-no"),0);
2139 dialog.show();
2140 return dialog.run();
2144 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2146 Gtk::Dialog dialog(
2147 title, // Title
2148 true, // Modal
2149 true // use_separator
2151 Gtk::Label label(message);
2152 label.show();
2154 dialog.get_vbox()->pack_start(label);
2155 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2156 dialog.add_button(Gtk::StockID("gtk-no"),0);
2157 dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2158 dialog.show();
2159 return dialog.run();
2162 void
2163 App::dialog_not_implemented()
2165 Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2166 dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2167 dialog.run();
2170 static bool
2171 try_open_url(const std::string &url)
2173 #ifdef WIN32
2174 return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2175 #else // !WIN32
2176 std::vector<std::string> command_line;
2177 std::vector<std::string> browsers;
2178 browsers.reserve(23);
2180 // Browser wrapper scripts
2181 #ifdef USE_OPEN_FOR_URLS
2182 browsers.push_back("open"); // Apple MacOS X wrapper, on Linux it opens a virtual console
2183 #endif
2184 browsers.push_back("xdg-open"); // XDG wrapper
2185 browsers.push_back("sensible-browser"); // Debian wrapper
2186 browsers.push_back("gnome-open"); // GNOME wrapper
2187 browsers.push_back("kfmclient"); // KDE wrapper
2188 browsers.push_back("exo-open"); // XFCE wrapper
2190 // Alternatives system
2191 browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2192 browsers.push_back("x-www-browser"); // Debian GUI alternative
2194 // Individual browsers
2195 browsers.push_back("firefox");
2196 browsers.push_back("epiphany-browser");
2197 browsers.push_back("epiphany");
2198 browsers.push_back("konqueror");
2199 browsers.push_back("iceweasel");
2200 browsers.push_back("mozilla");
2201 browsers.push_back("netscape");
2202 browsers.push_back("icecat");
2203 browsers.push_back("galeon");
2204 browsers.push_back("midori");
2205 browsers.push_back("safari");
2206 browsers.push_back("opera");
2207 browsers.push_back("amaya");
2208 browsers.push_back("netsurf");
2209 browsers.push_back("dillo");
2211 // Try the user-specified browser first
2212 command_line.push_back(App::browser_command);
2213 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2214 command_line.push_back(url);
2216 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2217 catch( Glib::SpawnError& exception ){
2219 while ( !browsers.empty() )
2221 // Skip the browser if we already tried it
2222 if( browsers[0] == App::browser_command )
2223 continue;
2225 // Construct the command line
2226 command_line.clear();
2227 command_line.push_back(browsers[0]);
2228 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2229 command_line.push_back(url);
2231 // Remove the browser from the list
2232 browsers.erase(browsers.begin());
2234 // Try to spawn the browser
2235 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2236 // Failed, move on to the next one
2237 catch(Glib::SpawnError& exception){ continue; }
2238 return true; // No exception means we succeeded!
2242 return false;
2243 #endif // !WIN32
2246 void
2247 App::dialog_help()
2249 if (!try_open_url("http://synfig.org/Documentation"))
2251 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2252 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2253 dialog.set_title(_("Help"));
2254 dialog.run();
2258 void
2259 App::open_url(const std::string &url)
2261 if(!try_open_url(url))
2263 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2264 dialog.set_secondary_text(url);
2265 dialog.set_title(_("No browser found"));
2266 dialog.run();
2270 bool
2271 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2273 Gtk::Dialog dialog(
2274 title, // Title
2275 true, // Modal
2276 true); // use_separator
2278 Gtk::Label label(message);
2279 label.show();
2280 dialog.get_vbox()->pack_start(label);
2282 Gtk::Entry entry;
2283 entry.set_text(text);
2284 entry.show();
2285 entry.set_activates_default(true);
2287 dialog.get_vbox()->pack_start(entry);
2289 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2290 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2291 dialog.set_default_response(Gtk::RESPONSE_OK);
2293 entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2294 dialog.show();
2296 if(dialog.run()!=Gtk::RESPONSE_OK)
2297 return false;
2299 text=entry.get_text();
2301 return true;
2304 bool
2305 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2307 Gtk::Dialog dialog(
2308 title, // Title
2309 true, // Modal
2310 true); // use_separator
2312 Gtk::Label label(message);
2313 label.show();
2314 dialog.get_vbox()->pack_start(label);
2316 Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2317 text_buffer->set_text(text);
2318 Gtk::TextView text_view(text_buffer);
2319 text_view.show();
2321 dialog.get_vbox()->pack_start(text_view);
2323 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2324 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2325 dialog.set_default_response(Gtk::RESPONSE_OK);
2327 //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2328 dialog.show();
2330 if(dialog.run()!=Gtk::RESPONSE_OK)
2331 return false;
2333 text=text_buffer->get_text();
2335 return true;
2338 bool
2339 App::open(std::string filename)
2341 return open_as(filename,filename);
2344 // this is called from autorecover.cpp:
2345 // App::open_as(get_shadow_file_name(filename),filename)
2346 // other than that, 'filename' and 'as' are the same
2347 bool
2348 App::open_as(std::string filename,std::string as)
2350 #ifdef WIN32
2351 char long_name[1024];
2352 if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2353 // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2354 // don't use it if that's the case
2355 if (long_name[0] != '\0')
2356 as=long_name;
2357 #endif
2361 OneMoment one_moment;
2362 String errors, warnings;
2364 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as,errors,warnings));
2365 if(canvas && get_instance(canvas))
2367 get_instance(canvas)->find_canvas_view(canvas)->present();
2368 info("%s is already open", filename.c_str());
2369 // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2371 else
2373 if(!canvas)
2374 throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
2376 if (warnings != "")
2377 dialog_warning_blocking(_("Warnings"), strprintf("%s:\n\n%s", _("Warnings"), warnings.c_str()));
2379 if (as.find(custom_filename_prefix.c_str()) != 0)
2380 add_recent_file(as);
2382 handle<Instance> instance(Instance::create(canvas));
2384 if(!instance)
2385 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2387 set_recent_file_window_size(instance);
2389 one_moment.hide();
2391 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)")))
2392 instance->dialog_cvs_update();
2395 catch(String x)
2397 dialog_error_blocking(_("Error"), x);
2398 return false;
2400 catch(...)
2402 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2403 return false;
2406 return true;
2410 void
2411 App::new_instance()
2413 handle<synfig::Canvas> canvas=synfig::Canvas::create();
2415 String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
2416 canvas->set_name(file_name);
2417 file_name += ".sifz";
2419 canvas->rend_desc().set_frame_rate(24.0);
2420 canvas->rend_desc().set_time_start(0.0);
2421 canvas->rend_desc().set_time_end(5.0);
2422 canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2423 canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2424 // The top left and botton right positions are expressed in units
2425 // Original convention is that 1 unit = 60 pixels
2426 canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
2427 canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
2428 canvas->rend_desc().set_w(preferred_x_size);
2429 canvas->rend_desc().set_h(preferred_y_size);
2430 canvas->rend_desc().set_antialias(1);
2431 canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2432 canvas->set_file_name(file_name);
2434 handle<Instance> instance = Instance::create(canvas);
2436 if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2437 instance->find_canvas_view(canvas)->canvas_properties.present();
2440 void
2441 App::dialog_open(string filename)
2443 if (filename.empty())
2444 filename="*.sif";
2446 while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2448 // If the filename still has wildcards, then we should
2449 // continue looking for the file we want
2450 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2451 continue;
2453 if(open(filename))
2454 break;
2456 get_ui_interface()->error(_("Unable to open file"));
2460 void
2461 App::set_selected_instance(etl::loose_handle<Instance> instance)
2463 /* if(get_selected_instance()==instance)
2465 selected_instance=instance;
2466 signal_instance_selected()(instance);
2467 return;
2469 else
2472 selected_instance=instance;
2473 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2475 if(instance)
2477 instance->focus(instance->get_canvas());
2479 else
2480 set_selected_canvas_view(0);
2482 signal_instance_selected()(instance);
2485 void
2486 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2488 selected_canvas_view=canvas_view;
2489 signal_canvas_view_focus()(selected_canvas_view);
2490 if(canvas_view)
2492 selected_instance=canvas_view->get_instance();
2493 signal_instance_selected()(canvas_view->get_instance());
2496 if(get_selected_canvas_view()==canvas_view)
2498 signal_canvas_view_focus()(selected_canvas_view);
2499 signal_instance_selected()(canvas_view->get_instance());
2500 return;
2502 selected_canvas_view=canvas_view;
2503 if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2504 set_selected_instance(canvas_view->get_instance());
2505 signal_canvas_view_focus()(selected_canvas_view);
2509 etl::loose_handle<Instance>
2510 App::get_instance(etl::handle<synfig::Canvas> canvas)
2512 if(!canvas) return 0;
2513 canvas=canvas->get_root();
2515 std::list<etl::handle<Instance> >::iterator iter;
2516 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2518 if((*iter)->get_canvas()==canvas)
2519 return *iter;
2521 return 0;
2524 void
2525 App::dialog_about()
2527 if(about)
2528 about->show();
2531 void
2532 studio::App::undo()
2534 if(selected_instance)
2535 selected_instance->undo();
2538 void
2539 studio::App::redo()
2541 if(selected_instance)
2542 selected_instance->redo();
2545 synfig::String
2546 studio::App::get_base_path()
2548 return app_base_path_;