1 /* === S Y N F I G ========================================================= */
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
10 ** Copyright (c) 2008 Gerald Young
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.
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.
23 /* ========================================================================= */
25 /* === H E A D E R S ======================================================= */
44 #ifdef HAVE_SYS_ERRNO_H
45 #include <sys/errno.h>
47 #include <gtkmm/fileselection.h>
48 #include <gtkmm/dialog.h>
49 #include <gtkmm/messagedialog.h>
50 #include <gtkmm/label.h>
51 #include <gtkmm/stock.h>
52 #include <gtkmm/stockitem.h>
53 #include <gtkmm/iconsource.h>
54 #include <gtkmm/inputdialog.h>
55 #include <gtkmm/accelmap.h>
56 #include <gtkmm/uimanager.h>
57 #include <gtkmm/textview.h>
61 #include <gdkmm/general.h>
63 #include <synfig/loadcanvas.h>
64 #include <synfig/savecanvas.h>
70 #include "canvasview.h"
71 #include "dialog_setup.h"
72 #include "dialog_gradient.h"
73 #include "dialog_color.h"
76 #include "onemoment.h"
78 #include "dockmanager.h"
80 #include "state_eyedrop.h"
81 #include "state_normal.h"
82 #include "state_draw.h"
83 #include "state_fill.h"
84 #include "state_bline.h"
85 #include "state_polygon.h"
86 #include "state_sketch.h"
87 #include "state_gradient.h"
88 #include "state_circle.h"
89 #include "state_rectangle.h"
90 #include "state_smoothmove.h"
91 #include "state_scale.h"
92 #include "state_star.h"
93 #include "state_text.h"
94 #include "state_width.h"
95 #include "state_rotate.h"
96 #include "state_zoom.h"
98 #include "devicetracker.h"
99 #include "dialog_tooloptions.h"
100 #include "widget_enum.h"
102 #include "autorecover.h"
104 #include <synfigapp/settings.h>
105 #include "dock_history.h"
106 #include "dock_canvases.h"
107 #include "dock_keyframes.h"
108 #include "dock_layers.h"
109 #include "dock_params.h"
110 #include "dock_metadata.h"
111 #include "dock_children.h"
112 #include "dock_info.h"
113 #include "dock_navigator.h"
114 #include "dock_layergroups.h"
115 #include "dock_timetrack.h"
116 #include "dock_curves.h"
118 #include "mod_palette/mod_palette.h"
119 #include "mod_mirror/mod_mirror.h"
121 #include <sys/stat.h>
127 #include "statemanager.h"
133 #include <gtkmm/accelmap.h>
134 #include <gtkmm/filechooser.h>
135 #include <gtkmm/filechooserdialog.h>
141 /* === U S I N G =========================================================== */
145 using namespace synfig;
146 using namespace studio;
148 /* === M A C R O S ========================================================= */
150 #ifndef SYNFIG_USER_APP_DIR
152 #define SYNFIG_USER_APP_DIR "Library/Synfig"
153 #elif defined(_WIN32)
154 #define SYNFIG_USER_APP_DIR "Synfig"
156 #define SYNFIG_USER_APP_DIR ".synfig"
161 #define DPM2DPI(x) (float(x)/39.3700787402f)
162 #define DPI2DPM(x) (float(x)*39.3700787402f)
168 # define IMAGE_DIR "share\\pixmaps"
173 # define IMAGE_DIR "/usr/local/share/pixmaps"
177 # define IMAGE_EXT "tif"
180 #include <synfigapp/main.h>
182 /* === S I G N A L S ======================================================= */
184 static sigc::signal<void> signal_present_all_;
186 App::signal_present_all() { return signal_present_all_; }
188 static sigc::signal<void> signal_recent_files_changed_;
190 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
192 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
193 sigc::signal<void,etl::loose_handle<CanvasView> >&
194 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
196 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
197 sigc::signal<void,etl::handle<Instance> >&
198 App::signal_instance_selected() { return signal_instance_selected_; }
200 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
201 sigc::signal<void,etl::handle<Instance> >&
202 App::signal_instance_created() { return signal_instance_created_; }
204 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
205 sigc::signal<void,etl::handle<Instance> >&
206 App::signal_instance_deleted() { return signal_instance_deleted_; }
208 /* === G L O B A L S ======================================================= */
210 static std::list<std::string> recent_files;
211 const std::list<std::string>& App::get_recent_files() { return recent_files; }
213 static std::list<std::string> recent_files_window_size;
215 int App::Busy::count;
216 bool App::shutdown_in_progress;
218 synfig::Gamma App::gamma;
220 Glib::RefPtr<studio::UIManager> App::ui_manager_;
222 synfig::Distance::System App::distance_system;
224 studio::Dialog_Setup* App::dialog_setup;
226 etl::handle< studio::ModPalette > mod_palette_;
227 //studio::Dialog_Palette* App::dialog_palette;
229 std::list<etl::handle<Instance> > App::instance_list;
231 static etl::handle<synfigapp::UIInterface> ui_interface_;
232 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
234 etl::handle<Instance> App::selected_instance;
235 etl::handle<CanvasView> App::selected_canvas_view;
237 studio::About *studio::App::about=NULL;
239 studio::Toolbox *studio::App::toolbox=NULL;
241 studio::AutoRecover *studio::App::auto_recover=NULL;
243 studio::IPC *ipc=NULL;
245 studio::DockManager* studio::App::dock_manager=0;
247 studio::DeviceTracker* studio::App::device_tracker=0;
249 studio::Dialog_Gradient* studio::App::dialog_gradient;
251 studio::Dialog_Color* studio::App::dialog_color;
253 Gtk::InputDialog* studio::App::dialog_input;
255 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
257 studio::Dock_History* dock_history;
258 studio::Dock_Canvases* dock_canvases;
259 studio::Dock_Keyframes* dock_keyframes;
260 studio::Dock_Layers* dock_layers;
261 studio::Dock_Params* dock_params;
262 studio::Dock_MetaData* dock_meta_data;
263 studio::Dock_Children* dock_children;
264 studio::Dock_Info* dock_info;
265 studio::Dock_LayerGroups* dock_layer_groups;
266 studio::Dock_Navigator* dock_navigator;
267 studio::Dock_Timetrack* dock_timetrack;
268 studio::Dock_Curves* dock_curves;
270 std::list< etl::handle< studio::Module > > module_list_;
272 bool studio::App::use_colorspace_gamma=true;
273 #ifdef SINGLE_THREADED
274 bool studio::App::single_threaded=false;
276 bool studio::App::restrict_radius_ducks=false;
277 int studio::App::preferred_x_size=480;
278 int studio::App::preferred_y_size=270;
279 #ifdef USE_OPEN_FOR_URLS
280 String studio::App::browser_command("open"); // MacOS only
282 String studio::App::browser_command("xdg-open"); // Linux XDG standard
285 static int max_recent_files_=25;
286 int studio::App::get_max_recent_files() { return max_recent_files_; }
287 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
289 static synfig::String app_base_path_;
294 really_delete_widget(Gtk::Widget *widget)
296 // synfig::info("really delete %p", (void*)widget);
301 // nasty workaround - when we've finished with a popup menu, we want to delete it
302 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
303 // so schedule the real delete to happen in 50ms, giving the action a chance to run
305 delete_widget(Gtk::Widget *widget)
307 // synfig::info("delete %p", (void*)widget);
308 Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
311 }; // END of namespace studio
312 studio::StateManager* state_manager;
317 class GlobalUIInterface : public synfigapp::UIInterface
321 virtual Response yes_no(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
326 true // use_separator
328 Gtk::Label label(message);
331 dialog.get_vbox()->pack_start(label);
332 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
333 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
335 dialog.set_default_response(dflt);
337 return (Response)dialog.run();
339 virtual Response yes_no_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
344 true // use_separator
346 Gtk::Label label(message);
349 dialog.get_vbox()->pack_start(label);
350 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
351 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
352 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
354 dialog.set_default_response(dflt);
356 return (Response)dialog.run();
358 virtual Response ok_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_OK)
363 true // use_separator
365 Gtk::Label label(message);
368 dialog.get_vbox()->pack_start(label);
369 dialog.add_button(Gtk::StockID("gtk-ok"),RESPONSE_OK);
370 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
372 dialog.set_default_response(dflt);
374 return (Response)dialog.run();
378 task(const std::string &task)
380 std::cerr<<task<<std::endl;
381 while(studio::App::events_pending())studio::App::iteration(false);
386 error(const std::string &err)
388 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
395 warning(const std::string &err)
397 std::cerr<<"warning: "<<err<<std::endl;
398 while(studio::App::events_pending())studio::App::iteration(false);
403 amount_complete(int /*current*/, int /*total*/)
405 while(studio::App::events_pending())studio::App::iteration(false);
410 /* === P R O C E D U R E S ================================================= */
414 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
416 action_group_list.push_back(action_group);
417 Gtk::UIManager::insert_action_group(action_group, pos);
421 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
423 std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
424 for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
425 if(*iter==action_group)
427 action_group_list.erase(iter);
428 Gtk::UIManager::remove_action_group(action_group);
431 synfig::error("Unable to find action group");
435 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
437 ui_manager->insert_action_group(group,0);
439 std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
440 std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
442 for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
444 if(*iter && (*iter)->get_name()!="menus")
446 synfig::info("Removing action group "+(*iter)->get_name());
447 ui_manager->remove_action_group(*iter);
450 ui_manager->insert_action_group(group,0);
452 for(;!prev_groups.empty();prev_groups.pop_front())
454 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
455 ui_manager->insert_action_group(prev_groups.front(),1);
459 class Preferences : public synfigapp::Settings
462 virtual bool get_value(const synfig::String& key, synfig::String& value)const
466 value=strprintf("%f %f %f %f",
467 App::gamma.get_gamma_r(),
468 App::gamma.get_gamma_g(),
469 App::gamma.get_gamma_b(),
470 App::gamma.get_black_level()
474 if(key=="time_format")
476 value=strprintf("%i",App::get_time_format());
479 if(key=="file_history.size")
481 value=strprintf("%i",App::get_max_recent_files());
484 if(key=="use_colorspace_gamma")
486 value=strprintf("%i",(int)App::use_colorspace_gamma);
489 if(key=="distance_system")
491 value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
494 #ifdef SINGLE_THREADED
495 if(key=="single_threaded")
497 value=strprintf("%i",(int)App::single_threaded);
501 if(key=="auto_recover_backup_interval")
503 value=strprintf("%i",App::auto_recover->get_timeout());
506 if(key=="restrict_radius_ducks")
508 value=strprintf("%i",(int)App::restrict_radius_ducks);
511 if(key=="browser_command")
513 value=App::browser_command;
516 if(key=="preferred_x_size")
518 value=strprintf("%i",App::preferred_x_size);
521 if(key=="preferred_y_size")
523 value=strprintf("%i",App::preferred_y_size);
526 return synfigapp::Settings::get_value(key,value);
529 virtual bool set_value(const synfig::String& key,const synfig::String& value)
535 strscanf(value,"%f %f %f %f",
542 App::gamma.set_all(r,g,b,blk);
546 if(key=="time_format")
548 int i(atoi(value.c_str()));
549 App::set_time_format(static_cast<synfig::Time::Format>(i));
552 if(key=="auto_recover_backup_interval")
554 int i(atoi(value.c_str()));
555 App::auto_recover->set_timeout(i);
558 if(key=="file_history.size")
560 int i(atoi(value.c_str()));
561 App::set_max_recent_files(i);
564 if(key=="use_colorspace_gamma")
566 int i(atoi(value.c_str()));
567 App::use_colorspace_gamma=i;
570 if(key=="distance_system")
572 App::distance_system=Distance::ident_system(value);;
575 #ifdef SINGLE_THREADED
576 if(key=="single_threaded")
578 int i(atoi(value.c_str()));
579 App::single_threaded=i;
583 if(key=="restrict_radius_ducks")
585 int i(atoi(value.c_str()));
586 App::restrict_radius_ducks=i;
589 if(key=="browser_command")
591 App::browser_command=value;
594 if(key=="preferred_x_size")
596 int i(atoi(value.c_str()));
597 App::preferred_x_size=i;
600 if(key=="preferred_y_size")
602 int i(atoi(value.c_str()));
603 App::preferred_y_size=i;
606 return synfigapp::Settings::set_value(key,value);
609 virtual KeyList get_key_list()const
611 KeyList ret(synfigapp::Settings::get_key_list());
612 ret.push_back("gamma");
613 ret.push_back("time_format");
614 ret.push_back("distance_system");
615 ret.push_back("file_history.size");
616 ret.push_back("use_colorspace_gamma");
617 #ifdef SINGLE_THREADED
618 ret.push_back("single_threaded");
620 ret.push_back("auto_recover_backup_interval");
621 ret.push_back("restrict_radius_ducks");
622 ret.push_back("browser_command");
623 ret.push_back("preferred_x_size");
624 ret.push_back("preferred_y_size");
629 static ::Preferences _preferences;
634 Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
636 Glib::RefPtr<Gtk::ActionGroup> toolbox_action_group = Gtk::ActionGroup::create("toolbox");
638 Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create();
640 menus_action_group->add( Gtk::Action::create("menu-file", _("_File")) );
641 menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")) );
642 menus_action_group->add( Gtk::Action::create("menu-view", _("_View")) );
643 menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")) );
644 menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
645 menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
646 menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
647 menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")) );
648 menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
649 menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
650 menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
651 menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
652 menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
654 // Add the synfigapp actions...
655 synfigapp::Action::Book::iterator iter;
656 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
658 actions_action_group->add(Gtk::Action::create(
659 "action-"+iter->second.name,
660 get_action_stock_id(iter->second),
661 iter->second.local_name,iter->second.local_name
665 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
666 #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); }
667 #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); }
669 DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
670 DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
671 DEFINE_ACTION("new", Gtk::Stock::NEW);
672 DEFINE_ACTION("open", Gtk::Stock::OPEN);
673 DEFINE_ACTION("save", Gtk::Stock::SAVE);
674 DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
675 DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
676 DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
677 DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
678 DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
679 DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
680 DEFINE_ACTION("import", _("Import"));
681 DEFINE_ACTION("render", _("Render"));
682 DEFINE_ACTION("preview", _("Preview"));
683 DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
684 DEFINE_ACTION("sound", _("Sound File"));
685 DEFINE_ACTION("options", _("Options"));
686 DEFINE_ACTION("close", _("Close View"));
687 DEFINE_ACTION("close-document", _("Close Document"));
688 DEFINE_ACTION("quit", Gtk::Stock::QUIT);
691 DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
692 DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
693 DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
694 DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
695 DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
696 DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
697 DEFINE_ACTION("unselect-all-ducks", _("Unselect All Ducks"));
698 DEFINE_ACTION("select-all-layers", _("Select All Layers"));
699 DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
700 DEFINE_ACTION("properties", _("Properties"));
702 DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
703 DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
704 DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
705 DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
706 DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
707 DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
708 DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
709 DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
710 DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
711 DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
712 DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
713 DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
714 DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
715 DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
716 DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
717 DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
718 DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
719 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
720 DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
721 DEFINE_ACTION("play", _("Play"));
722 // DEFINE_ACTION("pause", _("Pause"));
723 DEFINE_ACTION("stop", _("Stop"));
724 DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
725 DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
726 DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
727 DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
728 DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
729 DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
730 DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
731 DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
732 DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
733 DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
734 DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
735 DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
736 DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
737 DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
738 DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
739 DEFINE_ACTION("seek-next-frame", _("Next Frame"));
740 DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
741 DEFINE_ACTION("seek-next-second", _("Seek Forward"));
742 DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
743 DEFINE_ACTION("seek-begin", _("Seek to Begin"));
744 DEFINE_ACTION("seek-end", _("Seek to End"));
746 DEFINE_ACTION("action-group_add", _("Add group"));
748 DEFINE_ACTION("canvas-new", _("New Canvas"));
750 DEFINE_ACTION("amount-inc", _("Increase Amount"));
751 DEFINE_ACTION("amount-dec", _("Decrease Amount"));
756 // Set up synfigapp actions
758 synfigapp::Action::Book::iterator iter;
760 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
762 Gtk::StockID stock_id;
764 if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
766 //Gtk::Image* image(manage(new Gtk::Image()));
767 if(iter->second.task=="raise") stock_id=Gtk::Stock::GO_UP;
768 else if(iter->second.task=="lower") stock_id=Gtk::Stock::GO_DOWN;
769 else if(iter->second.task=="move_top") stock_id=Gtk::Stock::GOTO_TOP;
770 else if(iter->second.task=="move_bottom") stock_id=Gtk::Stock::GOTO_BOTTOM;
771 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
772 else if(iter->second.task=="set_on") stock_id=Gtk::Stock::YES;
773 else if(iter->second.task=="set_off") stock_id=Gtk::Stock::NO;
774 //else if(iter->second.task=="duplicate") stock_id=Gtk::Stock::COPY;
775 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
776 else stock_id=Gtk::StockID("synfig-"+iter->second.task);
778 actions_action_group->add(Gtk::Action::create(
779 "action-"+iter->second.name,
781 iter->second.local_name,iter->second.local_name
789 Glib::ustring ui_info =
791 " <popup name='menu-toolbox' action='menu-toolbox'>"
792 " <menu action='menu-file'>"
795 " <popup name='menu-main' action='menu-main'>"
796 " <menu action='menu-file'>"
797 " <menuitem action='new' />"
798 " <menuitem action='open' />"
799 " <menuitem action='save' />"
800 " <menuitem action='save-as' />"
801 " <menuitem action='revert' />"
802 " <separator name='bleh01'/>"
803 " <menuitem action='cvs-add' />"
804 " <menuitem action='cvs-update' />"
805 " <menuitem action='cvs-commit' />"
806 " <menuitem action='cvs-revert' />"
807 " <separator name='bleh02'/>"
808 " <menuitem action='import' />"
809 " <separator name='bleh03'/>"
810 " <menuitem action='render' />"
811 " <menuitem action='preview' />"
812 " <menuitem action='sound' />"
813 " <separator name='bleh04'/>"
814 " <menuitem action='options' />"
815 " <menuitem action='close' />"
816 " <menuitem action='close-document' />"
817 " <menuitem action='quit' />"
819 " <menu action='menu-edit'>"
820 " <menuitem action='undo'/>"
821 " <menuitem action='redo'/>"
822 " <separator name='bleh05'/>"
823 " <menuitem action='cut'/>"
824 " <menuitem action='copy'/>"
825 " <menuitem action='paste'/>"
826 " <separator name='bleh06'/>"
827 " <menuitem action='select-all-layers'/>"
828 " <menuitem action='unselect-all-layers'/>"
829 " <menuitem action='select-all-ducks'/>"
830 " <menuitem action='unselect-all-ducks'/>"
831 " <separator name='bleh07'/>"
832 " <menuitem action='properties'/>"
834 " <menu action='menu-view'>"
835 " <menu action='menu-duck-mask'>"
836 " <menuitem action='mask-position-ducks' />"
837 " <menuitem action='mask-vertex-ducks' />"
838 " <menuitem action='mask-tangent-ducks' />"
839 " <menuitem action='mask-radius-ducks' />"
840 " <menuitem action='mask-width-ducks' />"
841 " <menuitem action='mask-angle-ducks' />"
843 " <menu action='menu-preview-quality'>"
844 " <menuitem action='quality-00' />"
845 " <menuitem action='quality-01' />"
846 " <menuitem action='quality-02' />"
847 " <menuitem action='quality-03' />"
848 " <menuitem action='quality-04' />"
849 " <menuitem action='quality-05' />"
850 " <menuitem action='quality-06' />"
851 " <menuitem action='quality-07' />"
852 " <menuitem action='quality-08' />"
853 " <menuitem action='quality-09' />"
854 " <menuitem action='quality-10' />"
856 " <menu action='menu-lowres-pixel'>"
857 " <menuitem action='decrease-low-res-pixel-size'/>"
858 " <menuitem action='increase-low-res-pixel-size'/>"
859 " <separator name='pixel-size-separator'/>"
862 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
863 ui_info += strprintf(" <menuitem action='lowres-pixel-%d' />", *iter);
867 " <separator name='bleh08'/>"
868 " <menuitem action='play'/>"
869 //" <menuitem action='pause'/>"
870 " <menuitem action='stop'/>"
871 " <menuitem action='dialog-flipbook'/>"
872 " <separator name='bleh09'/>"
873 " <menuitem action='toggle-grid-show'/>"
874 " <menuitem action='toggle-grid-snap'/>"
875 " <menuitem action='toggle-guide-show'/>"
876 " <menuitem action='toggle-low-res'/>"
877 " <menuitem action='toggle-onion-skin'/>"
878 " <separator name='bleh10'/>"
879 " <menuitem action='canvas-zoom-in'/>"
880 " <menuitem action='canvas-zoom-out'/>"
881 " <menuitem action='canvas-zoom-fit'/>"
882 " <menuitem action='canvas-zoom-100'/>"
883 " <separator name='bleh11'/>"
884 " <menuitem action='time-zoom-in'/>"
885 " <menuitem action='time-zoom-out'/>"
886 " <separator name='bleh12'/>"
887 " <menuitem action='jump-next-keyframe'/>"
888 " <menuitem action='jump-prev-keyframe'/>"
889 " <menuitem action='seek-next-frame'/>"
890 " <menuitem action='seek-prev-frame'/>"
891 " <menuitem action='seek-next-second'/>"
892 " <menuitem action='seek-prev-second'/>"
893 " <menuitem action='seek-begin'/>"
894 " <menuitem action='seek-end'/>"
896 " <menu action='menu-canvas'>"
897 " <menuitem action='canvas-new'/>"
899 " <menu name='menu-state' action='menu-state'>"
901 " <menu action='menu-group'>"
902 " <menuitem action='action-group_add'/>"
904 " <menu action='menu-layer'>"
905 //" <menuitem action='cut'/>"
906 //" <menuitem action='copy'/>"
907 //" <menuitem action='paste'/>"
908 //" <separator name='bleh06'/>"
909 " <menu action='menu-layer-new'></menu>"
910 " <menuitem action='amount-inc'/>"
911 " <menuitem action='amount-dec'/>"
913 " <menu action='menu-keyframe'>"
914 " <menuitem action='keyframe-properties'/>"
921 " <menubar name='MenuBar'>"
922 " <menu action='MenuFile'>"
923 " <menuitem action='New'/>"
924 " <menuitem action='Open'/>"
926 " <menuitem action='Quit'/>"
928 " <menu action='MenuEdit'>"
929 " <menuitem action='Cut'/>"
930 " <menuitem action='Copy'/>"
931 " <menuitem action='Paste'/>"
934 " <toolbar name='ToolBar'>"
935 " <toolitem action='Open'/>"
936 " <toolitem action='Quit'/>"
942 actions_action_group->set_sensitive(false);
943 App::ui_manager()->set_add_tearoffs(true);
944 App::ui_manager()->insert_action_group(menus_action_group,1);
945 App::ui_manager()->insert_action_group(actions_action_group,1);
946 App::ui_manager()->add_ui_from_string(ui_info);
948 //App::ui_manager()->get_accel_group()->unlock();
950 catch(const Glib::Error& ex)
952 synfig::error("building menus and toolbars failed: " + ex.what());
955 // Add default keyboard accelerators
956 #define ACCEL(path,accel) \
958 Gtk::AccelKey accel_key(accel,path); \
959 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
962 #define ACCEL2(accel) \
964 Gtk::AccelKey accel_key(accel); \
965 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
968 ACCEL("<Actions>//select-all-ducks","<Control>a");
969 ACCEL("<Actions>//unselect-all-ducks","<Control>d");
970 ACCEL("<Actions>//select-all-layers","<Control><Shift>a");
971 ACCEL("<Actions>//unselect-all-layers","<Control><Shift>d");
972 ACCEL("<Actions>//render","F9");
973 ACCEL("<Actions>//preview","F11");
974 ACCEL("<Actions>//properties","F8");
975 ACCEL("<Actions>//options","F12");
976 ACCEL("<Actions>//import","<control>i");
977 ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
978 ACCEL("<Actions>//toggle-grid-show","<Control>g");
979 ACCEL("<Actions>//toggle-grid-snap","<Control>l");
980 ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
981 ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
982 ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
983 ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
984 ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
985 ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
986 ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
988 ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
989 ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
991 ACCEL("<Actions>//quality-01","<Control>1");
992 ACCEL("<Actions>//quality-02","<Control>2");
993 ACCEL("<Actions>//quality-03","<Control>3");
994 ACCEL("<Actions>//quality-04","<Control>4");
995 ACCEL("<Actions>//quality-05","<Control>5");
996 ACCEL("<Actions>//quality-06","<Control>6");
997 ACCEL("<Actions>//quality-07","<Control>7");
998 ACCEL("<Actions>//quality-08","<Control>8");
999 ACCEL("<Actions>//quality-09","<Control>9");
1000 ACCEL("<Actions>//quality-10","<Control>0");
1001 ACCEL("<Actions>//undo","<Control>z");
1002 ACCEL("<Actions>//redo","<Control>r");
1003 ACCEL("<Actions>//action-layer_remove","Delete");
1005 /* ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
1006 ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
1007 ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
1008 ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
1009 ACCEL("<Actions>//time-zoom-in","+");
1010 ACCEL("<Actions>//time-zoom-out","_");
1012 ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,"<Actions>//decrease-low-res-pixel-size"));
1013 ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,"<Actions>//increase-low-res-pixel-size"));
1015 ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
1016 ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
1018 ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
1019 ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
1020 ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
1021 ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
1022 ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
1023 ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
1024 ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
1025 ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
1026 ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
1027 ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
1028 ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
1029 ACCEL("<Actions>//play", "<Control>p");
1030 ACCEL("<Actions>//seek-begin","Home");
1031 ACCEL("<Actions>//seek-end","End");
1033 ACCEL("<Actions>//state-normal", "<Mod1>a");
1034 ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1035 ACCEL("<Actions>//state-scale", "<Mod1>s");
1036 ACCEL("<Actions>//state-rotate", "<Mod1>t");
1037 ACCEL("<Actions>//state-mirror", "<Mod1>m");
1039 ACCEL("<Actions>//state-circle", "<Mod1>c");
1040 ACCEL("<Actions>//state-rectangle", "<Mod1>r");
1041 ACCEL("<Actions>//state-star", "<Mod1>q");
1042 ACCEL("<Actions>//state-gradient", "<Mod1>g");
1043 ACCEL("<Actions>//state-polygon", "<Mod1>p");
1045 ACCEL("<Actions>//state-bline", "<Mod1>b");
1046 ACCEL("<Actions>//state-text", "<Mod1>x");
1047 ACCEL("<Actions>//state-fill", "<Mod1>f");
1048 ACCEL("<Actions>//state-eyedrop", "<Mod1>e");
1049 ACCEL("<Actions>//state-zoom", "<Mod1>z");
1051 ACCEL("<Actions>//state-draw", "<Mod1>d");
1052 ACCEL("<Actions>//state-sketch", "<Mod1>k");
1053 ACCEL("<Actions>//state-width", "<Mod1>w");
1055 ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1061 #define mkdir(x,y) mkdir(x)
1064 /* === M E T H O D S ======================================================= */
1066 App::App(int *argc, char ***argv):
1067 Gtk::Main(argc,argv),
1068 IconController(etl::dirname((*argv)[0]))
1070 app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1073 ui_interface_=new GlobalUIInterface();
1077 // don't call thread_init() if threads are already initialized
1078 // on some machines bonobo_init() initialized threads before we get here
1079 if (!g_thread_supported())
1080 Glib::thread_init();
1082 distance_system=Distance::SYSTEM_UNITS;
1084 if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1087 synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1091 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1097 if(!SYNFIG_CHECK_VERSION())
1099 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1100 dialog_error_blocking("Synfig Studio",
1101 "This copy of Synfig Studio was compiled against a\n"
1102 "different version of libsynfig than what is currently\n"
1103 "installed. Synfig Studio will now abort. Try downloading\n"
1104 "the latest version from the Synfig website at\n"
1105 "http://synfig.org/Download"
1109 Glib::set_application_name(_("Synfig Studio"));
1111 Splash splash_screen;
1112 splash_screen.show();
1114 shutdown_in_progress=false;
1115 SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1116 SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1118 // Initialize the Synfig library
1119 try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1122 get_ui_interface()->error(_("Failed to initialize synfig!"));
1126 // add the preferences to the settings
1127 synfigapp::Main::settings().add_domain(&_preferences,"pref");
1131 studio_init_cb.task(_("Init UI Manager..."));
1132 App::ui_manager_=studio::UIManager::create();
1135 studio_init_cb.task(_("Init Dock Manager..."));
1136 dock_manager=new studio::DockManager();
1138 studio_init_cb.task(_("Init State Manager..."));
1139 state_manager=new StateManager();
1141 studio_init_cb.task(_("Init Toolbox..."));
1142 toolbox=new studio::Toolbox();
1144 studio_init_cb.task(_("Init About Dialog..."));
1145 about=new studio::About();
1147 studio_init_cb.task(_("Init Tool Options..."));
1148 dialog_tool_options=new studio::Dialog_ToolOptions();
1149 dock_manager->register_dockable(*dialog_tool_options);
1151 studio_init_cb.task(_("Init History..."));
1152 dock_history=new studio::Dock_History();
1153 dock_manager->register_dockable(*dock_history);
1155 studio_init_cb.task(_("Init Canvases..."));
1156 dock_canvases=new studio::Dock_Canvases();
1157 dock_manager->register_dockable(*dock_canvases);
1159 studio_init_cb.task(_("Init Keyframes..."));
1160 dock_keyframes=new studio::Dock_Keyframes();
1161 dock_manager->register_dockable(*dock_keyframes);
1163 studio_init_cb.task(_("Init Layers..."));
1164 dock_layers=new studio::Dock_Layers();
1165 dock_manager->register_dockable(*dock_layers);
1167 studio_init_cb.task(_("Init Params..."));
1168 dock_params=new studio::Dock_Params();
1169 dock_manager->register_dockable(*dock_params);
1171 studio_init_cb.task(_("Init MetaData..."));
1172 dock_meta_data=new studio::Dock_MetaData();
1173 dock_manager->register_dockable(*dock_meta_data);
1175 studio_init_cb.task(_("Init Children..."));
1176 dock_children=new studio::Dock_Children();
1177 dock_manager->register_dockable(*dock_children);
1179 studio_init_cb.task(_("Init Info..."));
1180 dock_info = new studio::Dock_Info();
1181 dock_manager->register_dockable(*dock_info);
1183 studio_init_cb.task(_("Init Navigator..."));
1184 dock_navigator = new studio::Dock_Navigator();
1185 dock_manager->register_dockable(*dock_navigator);
1187 studio_init_cb.task(_("Init Timetrack..."));
1188 dock_timetrack = new studio::Dock_Timetrack();
1189 dock_manager->register_dockable(*dock_timetrack);
1191 studio_init_cb.task(_("Init Curve Editor..."));
1192 dock_curves = new studio::Dock_Curves();
1193 dock_manager->register_dockable(*dock_curves);
1195 studio_init_cb.task(_("Init Layer Groups..."));
1196 dock_layer_groups = new studio::Dock_LayerGroups();
1197 dock_manager->register_dockable(*dock_layer_groups);
1200 studio_init_cb.task(_("Init Color Dialog..."));
1201 dialog_color=new studio::Dialog_Color();
1203 studio_init_cb.task(_("Init Gradient Dialog..."));
1204 dialog_gradient=new studio::Dialog_Gradient();
1206 studio_init_cb.task(_("Init DeviceTracker..."));
1207 device_tracker=new studio::DeviceTracker();
1209 studio_init_cb.task(_("Init Tools..."));
1212 state_manager->add_state(&state_normal);
1213 state_manager->add_state(&state_smooth_move);
1214 state_manager->add_state(&state_scale);
1215 state_manager->add_state(&state_rotate);
1216 studio_init_cb.task(_("Init ModMirror...")); module_list_.push_back(new ModMirror()); module_list_.back()->start();
1219 state_manager->add_state(&state_circle);
1220 state_manager->add_state(&state_rectangle);
1221 state_manager->add_state(&state_star);
1222 state_manager->add_state(&state_gradient);
1223 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1226 state_manager->add_state(&state_bline);
1227 state_manager->add_state(&state_text);
1228 state_manager->add_state(&state_fill);
1229 state_manager->add_state(&state_eyedrop);
1230 state_manager->add_state(&state_zoom);
1232 if(!getenv("SYNFIG_DISABLE_DRAW" )) state_manager->add_state(&state_draw); // Enabled for now. Let's see whether they're good enough yet.
1233 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1235 // Disabled by default - it doesn't work properly?
1236 if(getenv("SYNFIG_ENABLE_WIDTH" )) state_manager->add_state(&state_width);
1238 studio_init_cb.task(_("Init ModPalette..."));
1239 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1241 studio_init_cb.task(_("Init Setup Dialog..."));
1242 dialog_setup=new studio::Dialog_Setup();
1244 studio_init_cb.task(_("Init Input Dialog..."));
1245 dialog_input=new Gtk::InputDialog();
1246 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1247 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1249 studio_init_cb.task(_("Init auto recovery..."));
1250 auto_recover=new AutoRecover();
1252 studio_init_cb.amount_complete(9250,10000);
1253 studio_init_cb.task(_("Loading Settings..."));
1255 studio_init_cb.task(_("Checking auto-recover..."));
1257 studio_init_cb.amount_complete(9900,10000);
1259 bool opened_any = false;
1260 if(auto_recover->recovery_needed())
1262 splash_screen.hide();
1263 if (get_ui_interface()->yes_no(_("Auto Recovery"),
1264 _("Synfig Studio seems to have crashed\n"
1265 "before you could save all your files.\n"
1266 "Would you like to re-open those files\n"
1267 "and recover your unsaved changes?")) ==
1268 synfigapp::UIInterface::RESPONSE_YES)
1270 int number_recovered;
1271 if(!auto_recover->recover(number_recovered))
1272 if (number_recovered)
1273 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1275 get_ui_interface()->error(_("Unable to recover from previous crash"));
1277 get_ui_interface()->error(
1278 _("Synfig Studio has attempted to recover\n"
1279 "from a previous crash. The files that it has\n"
1280 "recovered are NOT YET SAVED. It would be a good\n"
1281 "idea to review them and save them now."));
1283 if (number_recovered)
1286 splash_screen.show();
1289 // Look for any files given on the command line,
1290 // and load them if found.
1291 for(;*argc>=1;(*argc)--)
1292 if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1294 studio_init_cb.task(_("Loading files..."));
1295 splash_screen.hide();
1296 open((*argv)[*argc]);
1298 splash_screen.show();
1301 // if no file was specified to be opened, create a new document to help new users get started more easily
1302 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1305 studio_init_cb.task(_("Done."));
1306 studio_init_cb.amount_complete(10000,10000);
1312 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1316 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1320 StateManager* App::get_state_manager() { return state_manager; }
1324 shutdown_in_progress=true;
1328 synfigapp::Main::settings().remove_domain("pref");
1330 selected_instance=0;
1332 // Unload all of the modules
1333 for(;!module_list_.empty();module_list_.pop_back())
1336 delete state_manager;
1340 delete auto_recover;
1346 // studio::App::iteration(false);
1350 // studio::App::iteration(false);
1352 // studio::App::iteration(false);
1354 delete dialog_setup;
1356 delete dialog_gradient;
1358 delete dialog_color;
1360 delete dialog_input;
1362 delete dock_manager;
1364 instance_list.clear();
1366 // studio::App::iteration(false);
1370 App::get_user_app_directory()
1372 //! \todo do we need locale_from_utf8() on non-Windows boxes too? (bug #1837445)
1374 return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1376 return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1381 App::get_config_file(const synfig::String& file)
1383 return Glib::build_filename(get_user_app_directory(),file);
1386 #define SCALE_FACTOR (1280)
1387 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1389 App::set_recent_file_window_size(etl::handle<Instance> instance)
1391 int screen_w(Gdk::screen_width());
1392 int screen_h(Gdk::screen_height());
1394 const std::string &canvas_window_size = *recent_files_window_size.begin();
1396 if(canvas_window_size.empty())
1399 synfig::String::size_type current=0;
1400 bool seen_root(false), shown_non_root(false);
1402 while(current != synfig::String::npos)
1404 // find end of first field (canvas) or return
1405 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1406 if(separator == synfig::String::npos) break;
1409 synfig::Canvas::Handle canvas;
1411 canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current));
1413 catch(Exception::IDNotFound) {
1414 // can't find the canvas; skip to the next canvas or return
1415 separator = canvas_window_size.find_first_of('\t', current);
1416 if(separator == synfig::String::npos) return;
1417 current = separator+1;
1421 if (canvas->is_root())
1424 shown_non_root = true;
1426 // check that we have the tab character the ends this canvas' data or return
1427 current = separator+1;
1428 separator = canvas_window_size.find_first_of('\t', current);
1429 if(separator == synfig::String::npos) return;
1432 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1434 current = separator+1;
1438 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1439 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1440 x=x*screen_w/SCALE_FACTOR;
1441 y=y*screen_h/SCALE_FACTOR;
1442 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1443 x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1444 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1445 y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1447 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1448 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1450 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1451 canvasview->move(x,y);
1452 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1453 canvasview->present();
1455 current = separator+1;
1458 if (shown_non_root && !seen_root)
1459 instance->find_canvas_view(instance->get_canvas())->hide();
1463 App::add_recent_file(const etl::handle<Instance> instance)
1465 int screen_w(Gdk::screen_width());
1466 int screen_h(Gdk::screen_height());
1468 std::string canvas_window_size;
1470 const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1471 Instance::CanvasViewList::const_iterator iter;
1473 for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1475 if( !((*iter)->is_visible()) )
1478 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1479 int x_pos, y_pos, x_size, y_size;
1480 (*iter)->get_position(x_pos,y_pos);
1481 (*iter)->get_size(x_size,y_size);
1483 canvas_window_size += strprintf("%s %d %d %d %d\t",
1484 canvas->get_relative_id(canvas->get_root()).c_str(),
1485 x_pos*SCALE_FACTOR/screen_w, y_pos*SCALE_FACTOR/screen_h,
1486 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1489 add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1494 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1496 std::string filename(file_name);
1498 assert(!filename.empty());
1500 if(filename.empty())
1503 // Toss out any "hidden" files
1504 if(basename(filename)[0]=='.')
1507 // If we aren't an absolute path, turn ourselves into one
1508 if(!is_absolute_path(filename))
1509 filename=absolute_path(filename);
1511 std::string old_window_size;
1513 list<string>::iterator iter;
1514 list<string>::iterator iter_wsize;
1515 // Check to see if the file is already on the list.
1516 // If it is, then remove it from the list
1517 for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1520 recent_files.erase(iter);
1521 old_window_size = *iter_wsize;
1522 recent_files_window_size.erase(iter_wsize);
1527 // Push the filename to the front of the list
1528 recent_files.push_front(filename);
1529 if(window_size.empty())
1530 recent_files_window_size.push_front(old_window_size);
1532 recent_files_window_size.push_front(window_size);
1534 // Clean out the files at the end of the list.
1535 while(recent_files.size()>(unsigned)get_max_recent_files())
1537 recent_files.pop_back();
1538 recent_files_window_size.pop_back();
1541 signal_recent_files_changed_();
1546 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1549 App::get_time_format()
1551 return _App_time_format;
1555 App::set_time_format(synfig::Time::Format x)
1562 App::save_settings()
1567 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1568 setlocale(LC_NUMERIC, "C");
1570 std::string filename=get_config_file("accelrc");
1571 Gtk::AccelMap::save(filename);
1574 std::string filename=get_config_file("recentfiles");
1576 std::ofstream file(filename.c_str());
1580 synfig::warning("Unable to save %s",filename.c_str());
1584 list<string>::reverse_iterator iter;
1586 for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1590 std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1592 std::ofstream file(filename.c_str());
1596 synfig::warning("Unable to save %s",filename.c_str());
1600 list<string>::reverse_iterator iter;
1602 for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1606 std::string filename=get_config_file("settings");
1607 synfigapp::Main::settings().save_to_file(filename);
1608 setlocale(LC_NUMERIC,old_locale);
1612 synfig::warning("Caught exception when attempting to save settings.");
1617 App::load_settings()
1622 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1623 setlocale(LC_NUMERIC, "C");
1625 std::string filename=get_config_file("accelrc");
1626 Gtk::AccelMap::load(filename);
1629 bool window_size_broken = false;
1631 std::string filename=get_config_file("recentfiles");
1632 std::string filename_window_size=filename+std::string("_window_size");
1634 std::ifstream file(filename.c_str());
1635 std::ifstream file_window_size(filename_window_size.c_str());
1637 if(!file_window_size)
1638 window_size_broken = true;
1642 std::string recent_file;
1643 std::string recent_file_window_size;
1644 getline(file,recent_file);
1645 if(!window_size_broken)
1646 getline(file_window_size,recent_file_window_size);
1647 if(!recent_file.empty())
1649 if(!window_size_broken && !file_window_size)
1650 window_size_broken = true;
1651 if (std::ifstream(recent_file.c_str()))
1653 if(!window_size_broken)
1654 add_recent_file(recent_file,recent_file_window_size);
1656 add_recent_file(recent_file);
1660 if(!window_size_broken && file_window_size)
1661 window_size_broken = true;
1663 if(window_size_broken)
1665 recent_files_window_size.clear();
1666 recent_files_window_size.resize(recent_files.size());
1669 std::string filename=get_config_file("settings");
1670 if(!synfigapp::Main::settings().load_from_file(filename))
1672 //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1673 //if(!synfigapp::Main::settings().load_from_file(filename))
1675 gamma.set_gamma(1.0/2.2);
1676 reset_initial_window_configuration();
1679 setlocale(LC_NUMERIC,old_locale);
1683 synfig::warning("Caught exception when attempting to load settings.");
1688 App::reset_initial_window_configuration()
1690 synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1691 synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1692 synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1693 synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1694 synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1695 synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1696 synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1697 synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1698 synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1699 synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1700 synfigapp::Main::settings().set_value("pref.distance_system","pt");
1701 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1702 #ifdef SINGLE_THREADED
1703 synfigapp::Main::settings().set_value("pref.single_threaded","0");
1705 synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1706 synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
1707 synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
1708 synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1712 App::shutdown_request(GdkEventAny*)
1716 //return !shutdown_in_progress;
1722 if(shutdown_in_progress)return;
1725 get_ui_interface()->task(_("Quit Request"));
1728 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1732 std::list<etl::handle<Instance> >::iterator iter;
1733 for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1735 if(!(*iter)->safe_close())
1739 if((*iter)->synfigapp::Instance::get_action_count())
1741 handle<synfigapp::UIInterface> uim;
1742 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1744 string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1745 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1747 case synfigapp::UIInterface::RESPONSE_NO:
1749 case synfigapp::UIInterface::RESPONSE_YES:
1752 case synfigapp::UIInterface::RESPONSE_CANCEL:
1761 if((*iter)->synfigapp::Instance::is_modified())
1763 handle<synfigapp::UIInterface> uim;
1764 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1766 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() );
1767 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1769 case synfigapp::UIInterface::RESPONSE_NO:
1771 case synfigapp::UIInterface::RESPONSE_YES:
1772 (*iter)->dialog_cvs_commit();
1774 case synfigapp::UIInterface::RESPONSE_CANCEL:
1783 // This next line causes things to crash for some reason
1787 shutdown_in_progress=true;
1789 instance_list.clear();
1791 while(studio::App::events_pending())studio::App::iteration(false);
1794 auto_recover->normal_shutdown();
1796 get_ui_interface()->task(_("Quit Request sent"));
1802 dialog_setup->refresh();
1803 dialog_setup->show();
1806 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1807 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1810 //#define USE_WIN32_FILE_DIALOGS 1
1813 #ifdef USE_WIN32_FILE_DIALOGS
1814 static OPENFILENAME ofn={};
1818 #include <gdk/gdkwin32.h>
1822 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1824 // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1826 #ifdef USE_WIN32_FILE_DIALOGS
1827 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1829 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1830 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1831 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1833 ofn.lStructSize=sizeof(OPENFILENAME);
1834 ofn.hwndOwner = hWnd;
1835 ofn.hInstance = hInstance;
1836 ofn.lpstrFilter = szFilter;
1837 // ofn.lpstrCustomFilter=NULL;
1838 // ofn.nMaxCustFilter=0;
1839 // ofn.nFilterIndex=0;
1840 // ofn.lpstrFile=NULL;
1841 ofn.nMaxFile=MAX_PATH;
1842 // ofn.lpstrFileTitle=NULL;
1843 // ofn.lpstrInitialDir=NULL;
1844 // ofn.lpstrTitle=NULL;
1845 ofn.Flags=OFN_HIDEREADONLY;
1846 // ofn.nFileOffset=0;
1847 // ofn.nFileExtension=0;
1848 ofn.lpstrDefExt=TEXT("sif");
1849 // ofn.lCustData = 0l;
1851 // ofn.lpTemplateName=NULL;
1853 CHAR szFilename[MAX_PATH];
1855 strcpy(szFilename,filename.c_str());
1856 strcpy(szTitle,title.c_str());
1858 ofn.lpstrFile=szFilename;
1859 ofn.lpstrFileTitle=szTitle;
1861 if(GetOpenFileName(&ofn))
1863 filename=szFilename;
1869 synfig::String prev_path;
1871 if(!_preferences.get_value(preference, prev_path))
1874 prev_path = absolute_path(prev_path);
1876 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1878 dialog->set_current_folder(prev_path);
1879 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1880 dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1882 if (filename.empty())
1883 dialog->set_filename(prev_path);
1884 else if (is_absolute_path(filename))
1885 dialog->set_filename(filename);
1887 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1889 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1890 filename = dialog->get_filename();
1891 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1892 _preferences.set_value(preference, dirname(filename));
1906 GtkWidget *fileselection;
1907 fileselection = gtk_file_selection_new(title.c_str());
1910 if(basename(filename)==filename)
1912 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1915 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1917 gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1919 ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1920 cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1922 gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1923 gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1925 gtk_widget_show(fileselection);
1933 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1934 _preferences.set_value(preference,dirname(filename));
1938 gtk_widget_destroy(fileselection);
1941 gtk_widget_destroy(fileselection);
1948 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1950 // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1952 #if USE_WIN32_FILE_DIALOGS
1953 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1955 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1956 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1957 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1959 ofn.lStructSize=sizeof(OPENFILENAME);
1960 ofn.hwndOwner = hWnd;
1961 ofn.hInstance = hInstance;
1962 ofn.lpstrFilter = szFilter;
1963 // ofn.lpstrCustomFilter=NULL;
1964 // ofn.nMaxCustFilter=0;
1965 // ofn.nFilterIndex=0;
1966 // ofn.lpstrFile=NULL;
1967 ofn.nMaxFile=MAX_PATH;
1968 // ofn.lpstrFileTitle=NULL;
1969 // ofn.lpstrInitialDir=NULL;
1970 // ofn.lpstrTitle=NULL;
1971 ofn.Flags=OFN_OVERWRITEPROMPT;
1972 // ofn.nFileOffset=0;
1973 // ofn.nFileExtension=0;
1974 ofn.lpstrDefExt=TEXT("sif");
1975 // ofn.lCustData = 0l;
1977 // ofn.lpTemplateName=NULL;
1979 CHAR szFilename[MAX_PATH];
1981 strcpy(szFilename,filename.c_str());
1982 strcpy(szTitle,title.c_str());
1984 ofn.lpstrFile=szFilename;
1985 ofn.lpstrFileTitle=szTitle;
1987 if(GetSaveFileName(&ofn))
1989 filename=szFilename;
1990 _preferences.set_value(preference,dirname(filename));
1995 synfig::String prev_path;
1997 if(!_preferences.get_value(preference, prev_path))
2000 prev_path = absolute_path(prev_path);
2002 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2004 dialog->set_current_folder(prev_path);
2005 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2006 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2008 Widget_Enum *file_type_enum = 0;
2009 if (preference == ANIMATION_DIR_PREFERENCE)
2011 file_type_enum = manage(new Widget_Enum());
2012 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2013 .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
2014 .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2015 .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2016 .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2017 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2019 Gtk::HBox *hbox = manage(new Gtk::HBox);
2020 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
2021 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
2024 dialog->set_extra_widget(*hbox);
2027 if (filename.empty())
2028 dialog->set_filename(prev_path);
2031 std::string full_path;
2032 if (is_absolute_path(filename))
2033 full_path = filename;
2035 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2037 // select the file if it exists
2038 dialog->set_filename(full_path);
2040 // if the file doesn't exist, put its name into the filename box
2042 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2043 dialog->set_current_name(basename(filename));
2046 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2047 if (preference == ANIMATION_DIR_PREFERENCE)
2048 set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2049 filename = dialog->get_filename();
2050 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2051 _preferences.set_value(preference, dirname(filename));
2062 App::dialog_error_blocking(const std::string &title, const std::string &message)
2064 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2065 dialog.set_title(title);
2071 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2073 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2074 dialog.set_title(title);
2080 App::dialog_yes_no(const std::string &title, const std::string &message)
2085 true // use_separator
2087 Gtk::Label label(message);
2090 dialog.get_vbox()->pack_start(label);
2091 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2092 dialog.add_button(Gtk::StockID("gtk-no"),0);
2094 return dialog.run();
2098 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2103 true // use_separator
2105 Gtk::Label label(message);
2108 dialog.get_vbox()->pack_start(label);
2109 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2110 dialog.add_button(Gtk::StockID("gtk-no"),0);
2111 dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2113 return dialog.run();
2117 App::dialog_not_implemented()
2119 Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2120 dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2125 try_open_url(const std::string &url)
2128 return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2130 std::vector<std::string> command_line;
2131 std::vector<std::string> browsers;
2132 browsers.reserve(23);
2134 // Browser wrapper scripts
2135 #ifdef USE_OPEN_FOR_URLS
2136 browsers.push_back("open"); // Apple MacOS X wrapper, on Linux it opens a virtual console
2138 browsers.push_back("xdg-open"); // XDG wrapper
2139 browsers.push_back("sensible-browser"); // Debian wrapper
2140 browsers.push_back("gnome-open"); // GNOME wrapper
2141 browsers.push_back("kfmclient"); // KDE wrapper
2142 browsers.push_back("exo-open"); // XFCE wrapper
2144 // Alternatives system
2145 browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2146 browsers.push_back("x-www-browser"); // Debian GUI alternative
2148 // Individual browsers
2149 browsers.push_back("firefox");
2150 browsers.push_back("epiphany-browser");
2151 browsers.push_back("epiphany");
2152 browsers.push_back("konqueror");
2153 browsers.push_back("iceweasel");
2154 browsers.push_back("mozilla");
2155 browsers.push_back("netscape");
2156 browsers.push_back("icecat");
2157 browsers.push_back("galeon");
2158 browsers.push_back("midori");
2159 browsers.push_back("safari");
2160 browsers.push_back("opera");
2161 browsers.push_back("amaya");
2162 browsers.push_back("netsurf");
2163 browsers.push_back("dillo");
2165 // Try the user-specified browser first
2166 command_line.push_back(App::browser_command);
2167 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2168 command_line.push_back(url);
2170 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2171 catch( Glib::SpawnError& exception ){
2173 while ( !browsers.empty() )
2175 // Skip the browser if we already tried it
2176 if( browsers[0] == App::browser_command )
2179 // Construct the command line
2180 command_line.clear();
2181 command_line.push_back(browsers[0]);
2182 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2183 command_line.push_back(url);
2185 // Remove the browser from the list
2186 browsers.erase(browsers.begin());
2188 // Try to spawn the browser
2189 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2190 // Failed, move on to the next one
2191 catch(Glib::SpawnError& exception){ continue; }
2192 return true; // No exception means we succeeded!
2203 if (!try_open_url("http://synfig.org/Documentation"))
2205 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2206 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2207 dialog.set_title(_("Help"));
2213 App::open_url(const std::string &url)
2215 if(!try_open_url(url))
2217 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2218 dialog.set_secondary_text(url);
2219 dialog.set_title(_("No browser found"));
2225 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2230 true); // use_separator
2232 Gtk::Label label(message);
2234 dialog.get_vbox()->pack_start(label);
2237 entry.set_text(text);
2239 entry.set_activates_default(true);
2241 dialog.get_vbox()->pack_start(entry);
2243 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2244 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2245 dialog.set_default_response(Gtk::RESPONSE_OK);
2247 entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2250 if(dialog.run()!=Gtk::RESPONSE_OK)
2253 text=entry.get_text();
2259 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2264 true); // use_separator
2266 Gtk::Label label(message);
2268 dialog.get_vbox()->pack_start(label);
2270 Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2271 text_buffer->set_text(text);
2272 Gtk::TextView text_view(text_buffer);
2275 dialog.get_vbox()->pack_start(text_view);
2277 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2278 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2279 dialog.set_default_response(Gtk::RESPONSE_OK);
2281 //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2284 if(dialog.run()!=Gtk::RESPONSE_OK)
2287 text=text_buffer->get_text();
2293 App::open(std::string filename)
2295 return open_as(filename,filename);
2298 // this is called from autorecover.cpp:
2299 // App::open_as(get_shadow_file_name(filename),filename)
2300 // other than that, 'filename' and 'as' are the same
2302 App::open_as(std::string filename,std::string as)
2305 char long_name[1024];
2306 if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2307 // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2308 // don't use it if that's the case
2309 if (long_name[0] != '\0')
2315 OneMoment one_moment;
2317 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2318 if(canvas && get_instance(canvas))
2320 get_instance(canvas)->find_canvas_view(canvas)->present();
2321 info("%s is already open", filename.c_str());
2322 // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2327 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2329 if (as.find(DEFAULT_FILENAME_PREFIX) != 0)
2330 add_recent_file(as);
2332 handle<Instance> instance(Instance::create(canvas));
2335 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2337 set_recent_file_window_size(instance);
2341 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)")))
2342 instance->dialog_cvs_update();
2347 dialog_error_blocking(_("Error"), x);
2352 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2363 handle<synfig::Canvas> canvas=synfig::Canvas::create();
2365 String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2366 canvas->set_name(file_name);
2367 file_name += ".sifz";
2369 canvas->rend_desc().set_frame_rate(24.0);
2370 canvas->rend_desc().set_time_start(0.0);
2371 canvas->rend_desc().set_time_end(5.0);
2372 canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2373 canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2374 canvas->rend_desc().set_tl(Vector(-4,2.25));
2375 canvas->rend_desc().set_br(Vector(4,-2.25));
2376 canvas->rend_desc().set_w(preferred_x_size);
2377 canvas->rend_desc().set_h(preferred_y_size);
2378 canvas->rend_desc().set_antialias(1);
2379 canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2380 canvas->set_file_name(file_name);
2382 handle<Instance> instance = Instance::create(canvas);
2384 if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2385 instance->find_canvas_view(canvas)->canvas_properties.present();
2389 App::dialog_open(string filename)
2391 if (filename.empty())
2394 while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2396 // If the filename still has wildcards, then we should
2397 // continue looking for the file we want
2398 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2404 get_ui_interface()->error(_("Unable to open file"));
2409 App::set_selected_instance(etl::loose_handle<Instance> instance)
2411 /* if(get_selected_instance()==instance)
2413 selected_instance=instance;
2414 signal_instance_selected()(instance);
2420 selected_instance=instance;
2421 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2425 instance->focus(instance->get_canvas());
2428 set_selected_canvas_view(0);
2430 signal_instance_selected()(instance);
2434 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2436 selected_canvas_view=canvas_view;
2437 signal_canvas_view_focus()(selected_canvas_view);
2440 selected_instance=canvas_view->get_instance();
2441 signal_instance_selected()(canvas_view->get_instance());
2444 if(get_selected_canvas_view()==canvas_view)
2446 signal_canvas_view_focus()(selected_canvas_view);
2447 signal_instance_selected()(canvas_view->get_instance());
2450 selected_canvas_view=canvas_view;
2451 if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2452 set_selected_instance(canvas_view->get_instance());
2453 signal_canvas_view_focus()(selected_canvas_view);
2457 etl::loose_handle<Instance>
2458 App::get_instance(etl::handle<synfig::Canvas> canvas)
2460 if(!canvas) return 0;
2461 canvas=canvas->get_root();
2463 std::list<etl::handle<Instance> >::iterator iter;
2464 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2466 if((*iter)->get_canvas()==canvas)
2482 if(selected_instance)
2483 selected_instance->undo();
2489 if(selected_instance)
2490 selected_instance->redo();
2494 studio::App::get_base_path()
2496 return app_base_path_;