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_text.h"
93 #include "state_width.h"
94 #include "state_rotate.h"
95 #include "state_zoom.h"
97 #include "devicetracker.h"
98 #include "dialog_tooloptions.h"
99 #include "widget_enum.h"
101 #include "autorecover.h"
103 #include <synfigapp/settings.h>
104 #include "dock_history.h"
105 #include "dock_canvases.h"
106 #include "dock_keyframes.h"
107 #include "dock_layers.h"
108 #include "dock_params.h"
109 #include "dock_metadata.h"
110 #include "dock_children.h"
111 #include "dock_info.h"
112 #include "dock_navigator.h"
113 #include "dock_layergroups.h"
114 #include "dock_timetrack.h"
115 #include "dock_curves.h"
117 #include "mod_palette/mod_palette.h"
118 #include "mod_mirror/mod_mirror.h"
120 #include <sys/stat.h>
126 #include "statemanager.h"
132 #include <gtkmm/accelmap.h>
133 #include <gtkmm/filechooser.h>
134 #include <gtkmm/filechooserdialog.h>
140 /* === U S I N G =========================================================== */
144 using namespace synfig;
145 using namespace studio;
147 /* === M A C R O S ========================================================= */
149 #ifndef SYNFIG_USER_APP_DIR
151 #define SYNFIG_USER_APP_DIR "Library/Synfig"
152 #elif defined(_WIN32)
153 #define SYNFIG_USER_APP_DIR "Synfig"
155 #define SYNFIG_USER_APP_DIR ".synfig"
160 #define DPM2DPI(x) (float(x)/39.3700787402f)
161 #define DPI2DPM(x) (float(x)*39.3700787402f)
167 # define IMAGE_DIR "share\\pixmaps"
172 # define IMAGE_DIR "/usr/local/share/pixmaps"
176 # define IMAGE_EXT "tif"
179 #include <synfigapp/main.h>
181 /* === S I G N A L S ======================================================= */
183 static sigc::signal<void> signal_present_all_;
185 App::signal_present_all() { return signal_present_all_; }
187 static sigc::signal<void> signal_recent_files_changed_;
189 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
191 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
192 sigc::signal<void,etl::loose_handle<CanvasView> >&
193 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
195 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
196 sigc::signal<void,etl::handle<Instance> >&
197 App::signal_instance_selected() { return signal_instance_selected_; }
199 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
200 sigc::signal<void,etl::handle<Instance> >&
201 App::signal_instance_created() { return signal_instance_created_; }
203 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
204 sigc::signal<void,etl::handle<Instance> >&
205 App::signal_instance_deleted() { return signal_instance_deleted_; }
207 /* === G L O B A L S ======================================================= */
209 static std::list<std::string> recent_files;
210 const std::list<std::string>& App::get_recent_files() { return recent_files; }
212 static std::list<std::string> recent_files_window_size;
214 int App::Busy::count;
215 bool App::shutdown_in_progress;
217 synfig::Gamma App::gamma;
219 Glib::RefPtr<studio::UIManager> App::ui_manager_;
221 synfig::Distance::System App::distance_system;
223 studio::Dialog_Setup* App::dialog_setup;
225 etl::handle< studio::ModPalette > mod_palette_;
226 //studio::Dialog_Palette* App::dialog_palette;
228 std::list<etl::handle<Instance> > App::instance_list;
230 static etl::handle<synfigapp::UIInterface> ui_interface_;
231 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
233 etl::handle<Instance> App::selected_instance;
234 etl::handle<CanvasView> App::selected_canvas_view;
236 studio::About *studio::App::about=NULL;
238 studio::Toolbox *studio::App::toolbox=NULL;
240 studio::AutoRecover *studio::App::auto_recover=NULL;
242 studio::IPC *ipc=NULL;
244 studio::DockManager* studio::App::dock_manager=0;
246 studio::DeviceTracker* studio::App::device_tracker=0;
248 studio::Dialog_Gradient* studio::App::dialog_gradient;
250 studio::Dialog_Color* studio::App::dialog_color;
252 Gtk::InputDialog* studio::App::dialog_input;
254 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
256 studio::Dock_History* dock_history;
257 studio::Dock_Canvases* dock_canvases;
258 studio::Dock_Keyframes* dock_keyframes;
259 studio::Dock_Layers* dock_layers;
260 studio::Dock_Params* dock_params;
261 studio::Dock_MetaData* dock_meta_data;
262 studio::Dock_Children* dock_children;
263 studio::Dock_Info* dock_info;
264 studio::Dock_LayerGroups* dock_layer_groups;
265 studio::Dock_Navigator* dock_navigator;
266 studio::Dock_Timetrack* dock_timetrack;
267 studio::Dock_Curves* dock_curves;
269 std::list< etl::handle< studio::Module > > module_list_;
271 bool studio::App::use_colorspace_gamma=true;
272 #ifdef SINGLE_THREADED
273 bool studio::App::single_threaded=false;
275 bool studio::App::restrict_radius_ducks=false;
276 #ifdef USE_OPEN_FOR_URLS
277 String studio::App::browser_command("open"); // MacOS only
279 String studio::App::browser_command("xdg-open"); // Linux XDG standard
282 static int max_recent_files_=25;
283 int studio::App::get_max_recent_files() { return max_recent_files_; }
284 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
286 static synfig::String app_base_path_;
291 really_delete_widget(Gtk::Widget *widget)
293 // synfig::info("really delete %p", (void*)widget);
298 // nasty workaround - when we've finished with a popup menu, we want to delete it
299 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
300 // so schedule the real delete to happen in 50ms, giving the action a chance to run
302 delete_widget(Gtk::Widget *widget)
304 // synfig::info("delete %p", (void*)widget);
305 Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
308 }; // END of namespace studio
309 studio::StateManager* state_manager;
314 class GlobalUIInterface : public synfigapp::UIInterface
318 virtual Response yes_no(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
323 true // use_separator
325 Gtk::Label label(message);
328 dialog.get_vbox()->pack_start(label);
329 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
330 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
332 dialog.set_default_response(dflt);
334 return (Response)dialog.run();
336 virtual Response yes_no_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
341 true // use_separator
343 Gtk::Label label(message);
346 dialog.get_vbox()->pack_start(label);
347 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
348 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
349 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
351 dialog.set_default_response(dflt);
353 return (Response)dialog.run();
355 virtual Response ok_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_OK)
360 true // use_separator
362 Gtk::Label label(message);
365 dialog.get_vbox()->pack_start(label);
366 dialog.add_button(Gtk::StockID("gtk-ok"),RESPONSE_OK);
367 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
369 dialog.set_default_response(dflt);
371 return (Response)dialog.run();
375 task(const std::string &task)
377 std::cerr<<task<<std::endl;
378 while(studio::App::events_pending())studio::App::iteration(false);
383 error(const std::string &err)
385 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
392 warning(const std::string &err)
394 std::cerr<<"warning: "<<err<<std::endl;
395 while(studio::App::events_pending())studio::App::iteration(false);
400 amount_complete(int /*current*/, int /*total*/)
402 while(studio::App::events_pending())studio::App::iteration(false);
407 /* === P R O C E D U R E S ================================================= */
411 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
413 action_group_list.push_back(action_group);
414 Gtk::UIManager::insert_action_group(action_group, pos);
418 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
420 std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
421 for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
422 if(*iter==action_group)
424 action_group_list.erase(iter);
425 Gtk::UIManager::remove_action_group(action_group);
428 synfig::error("Unable to find action group");
432 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
434 ui_manager->insert_action_group(group,0);
436 std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
437 std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
439 for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
441 if(*iter && (*iter)->get_name()!="menus")
443 synfig::info("Removing action group "+(*iter)->get_name());
444 ui_manager->remove_action_group(*iter);
447 ui_manager->insert_action_group(group,0);
449 for(;!prev_groups.empty();prev_groups.pop_front())
451 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
452 ui_manager->insert_action_group(prev_groups.front(),1);
456 class Preferences : public synfigapp::Settings
459 virtual bool get_value(const synfig::String& key, synfig::String& value)const
463 value=strprintf("%f %f %f %f",
464 App::gamma.get_gamma_r(),
465 App::gamma.get_gamma_g(),
466 App::gamma.get_gamma_b(),
467 App::gamma.get_black_level()
471 if(key=="time_format")
473 value=strprintf("%i",App::get_time_format());
476 if(key=="file_history.size")
478 value=strprintf("%i",App::get_max_recent_files());
481 if(key=="use_colorspace_gamma")
483 value=strprintf("%i",(int)App::use_colorspace_gamma);
486 if(key=="distance_system")
488 value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
491 #ifdef SINGLE_THREADED
492 if(key=="single_threaded")
494 value=strprintf("%i",(int)App::single_threaded);
498 if(key=="auto_recover_backup_interval")
500 value=strprintf("%i",App::auto_recover->get_timeout());
503 if(key=="restrict_radius_ducks")
505 value=strprintf("%i",(int)App::restrict_radius_ducks);
508 if(key=="browser_command")
510 value=App::browser_command;
514 return synfigapp::Settings::get_value(key,value);
517 virtual bool set_value(const synfig::String& key,const synfig::String& value)
523 strscanf(value,"%f %f %f %f",
530 App::gamma.set_all(r,g,b,blk);
534 if(key=="time_format")
536 int i(atoi(value.c_str()));
537 App::set_time_format(static_cast<synfig::Time::Format>(i));
540 if(key=="auto_recover_backup_interval")
542 int i(atoi(value.c_str()));
543 App::auto_recover->set_timeout(i);
546 if(key=="file_history.size")
548 int i(atoi(value.c_str()));
549 App::set_max_recent_files(i);
552 if(key=="use_colorspace_gamma")
554 int i(atoi(value.c_str()));
555 App::use_colorspace_gamma=i;
558 if(key=="distance_system")
560 App::distance_system=Distance::ident_system(value);;
563 #ifdef SINGLE_THREADED
564 if(key=="single_threaded")
566 int i(atoi(value.c_str()));
567 App::single_threaded=i;
571 if(key=="restrict_radius_ducks")
573 int i(atoi(value.c_str()));
574 App::restrict_radius_ducks=i;
577 if(key=="browser_command")
579 App::browser_command=value;
583 return synfigapp::Settings::set_value(key,value);
586 virtual KeyList get_key_list()const
588 KeyList ret(synfigapp::Settings::get_key_list());
589 ret.push_back("gamma");
590 ret.push_back("time_format");
591 ret.push_back("distance_system");
592 ret.push_back("file_history.size");
593 ret.push_back("use_colorspace_gamma");
594 #ifdef SINGLE_THREADED
595 ret.push_back("single_threaded");
597 ret.push_back("auto_recover_backup_interval");
598 ret.push_back("restrict_radius_ducks");
599 ret.push_back("browser_command");
604 static ::Preferences _preferences;
609 Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
611 Glib::RefPtr<Gtk::ActionGroup> toolbox_action_group = Gtk::ActionGroup::create("toolbox");
613 Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create();
615 menus_action_group->add( Gtk::Action::create("menu-file", _("_File")) );
616 menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")) );
617 menus_action_group->add( Gtk::Action::create("menu-view", _("_View")) );
618 menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")) );
619 menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
620 menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
621 menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
622 menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")) );
623 menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
624 menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
625 menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
626 menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
627 menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
629 // Add the synfigapp actions...
630 synfigapp::Action::Book::iterator iter;
631 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
633 actions_action_group->add(Gtk::Action::create(
634 "action-"+iter->second.name,
635 get_action_stock_id(iter->second),
636 iter->second.local_name,iter->second.local_name
640 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
641 #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); }
642 #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); }
644 DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
645 DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
646 DEFINE_ACTION("new", Gtk::Stock::NEW);
647 DEFINE_ACTION("open", Gtk::Stock::OPEN);
648 DEFINE_ACTION("save", Gtk::Stock::SAVE);
649 DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
650 DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
651 DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
652 DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
653 DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
654 DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
655 DEFINE_ACTION("import", _("Import"));
656 DEFINE_ACTION("render", _("Render"));
657 DEFINE_ACTION("preview", _("Preview"));
658 DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
659 DEFINE_ACTION("sound", _("Sound File"));
660 DEFINE_ACTION("options", _("Options"));
661 DEFINE_ACTION("close", _("Close View"));
662 DEFINE_ACTION("close-document", _("Close Document"));
663 DEFINE_ACTION("quit", Gtk::Stock::QUIT);
666 DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
667 DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
668 DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
669 DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
670 DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
671 DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
672 DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
673 DEFINE_ACTION("properties", _("Properties"));
675 DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
676 DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
677 DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
678 DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
679 DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
680 DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
681 DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
682 DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
683 DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
684 DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
685 DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
686 DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
687 DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
688 DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
689 DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
690 DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
691 DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
692 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
693 DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
694 DEFINE_ACTION("play", _("Play"));
695 // DEFINE_ACTION("pause", _("Pause"));
696 DEFINE_ACTION("stop", _("Stop"));
697 DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
698 DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
699 DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
700 DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
701 DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
702 DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
703 DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
704 DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
705 DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
706 DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
707 DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
708 DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
709 DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
710 DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
711 DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
712 DEFINE_ACTION("seek-next-frame", _("Next Frame"));
713 DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
714 DEFINE_ACTION("seek-next-second", _("Seek Forward"));
715 DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
716 DEFINE_ACTION("seek-begin", _("Seek to Begin"));
717 DEFINE_ACTION("seek-end", _("Seek to End"));
719 DEFINE_ACTION("action-group_add", _("Add group"));
721 DEFINE_ACTION("canvas-new", _("New Canvas"));
723 DEFINE_ACTION("amount-inc", _("Increase Amount"));
724 DEFINE_ACTION("amount-dec", _("Decrease Amount"));
729 // Set up synfigapp actions
731 synfigapp::Action::Book::iterator iter;
733 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
735 Gtk::StockID stock_id;
737 if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
739 //Gtk::Image* image(manage(new Gtk::Image()));
740 if(iter->second.task=="raise") stock_id=Gtk::Stock::GO_UP;
741 else if(iter->second.task=="lower") stock_id=Gtk::Stock::GO_DOWN;
742 else if(iter->second.task=="move_top") stock_id=Gtk::Stock::GOTO_TOP;
743 else if(iter->second.task=="move_bottom") stock_id=Gtk::Stock::GOTO_BOTTOM;
744 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
745 else if(iter->second.task=="set_on") stock_id=Gtk::Stock::YES;
746 else if(iter->second.task=="set_off") stock_id=Gtk::Stock::NO;
747 //else if(iter->second.task=="duplicate") stock_id=Gtk::Stock::COPY;
748 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
749 else stock_id=Gtk::StockID("synfig-"+iter->second.task);
751 actions_action_group->add(Gtk::Action::create(
752 "action-"+iter->second.name,
754 iter->second.local_name,iter->second.local_name
762 Glib::ustring ui_info =
764 " <popup name='menu-toolbox' action='menu-toolbox'>"
765 " <menu action='menu-file'>"
768 " <popup name='menu-main' action='menu-main'>"
769 " <menu action='menu-file'>"
770 " <menuitem action='new' />"
771 " <menuitem action='open' />"
772 " <menuitem action='save' />"
773 " <menuitem action='save-as' />"
774 " <menuitem action='revert' />"
775 " <separator name='bleh01'/>"
776 " <menuitem action='cvs-add' />"
777 " <menuitem action='cvs-update' />"
778 " <menuitem action='cvs-commit' />"
779 " <menuitem action='cvs-revert' />"
780 " <separator name='bleh02'/>"
781 " <menuitem action='import' />"
782 " <separator name='bleh03'/>"
783 " <menuitem action='render' />"
784 " <menuitem action='preview' />"
785 " <menuitem action='sound' />"
786 " <separator name='bleh04'/>"
787 " <menuitem action='options' />"
788 " <menuitem action='close' />"
789 " <menuitem action='close-document' />"
790 " <menuitem action='quit' />"
792 " <menu action='menu-edit'>"
793 " <menuitem action='undo'/>"
794 " <menuitem action='redo'/>"
795 " <separator name='bleh05'/>"
796 " <menuitem action='cut'/>"
797 " <menuitem action='copy'/>"
798 " <menuitem action='paste'/>"
799 " <separator name='bleh06'/>"
800 " <menuitem action='select-all-ducks'/>"
801 " <menuitem action='unselect-all-layers'/>"
802 " <separator name='bleh07'/>"
803 " <menuitem action='properties'/>"
805 " <menu action='menu-view'>"
806 " <menu action='menu-duck-mask'>"
807 " <menuitem action='mask-position-ducks' />"
808 " <menuitem action='mask-vertex-ducks' />"
809 " <menuitem action='mask-tangent-ducks' />"
810 " <menuitem action='mask-radius-ducks' />"
811 " <menuitem action='mask-width-ducks' />"
812 " <menuitem action='mask-angle-ducks' />"
814 " <menu action='menu-preview-quality'>"
815 " <menuitem action='quality-00' />"
816 " <menuitem action='quality-01' />"
817 " <menuitem action='quality-02' />"
818 " <menuitem action='quality-03' />"
819 " <menuitem action='quality-04' />"
820 " <menuitem action='quality-05' />"
821 " <menuitem action='quality-06' />"
822 " <menuitem action='quality-07' />"
823 " <menuitem action='quality-08' />"
824 " <menuitem action='quality-09' />"
825 " <menuitem action='quality-10' />"
827 " <menu action='menu-lowres-pixel'>"
828 " <menuitem action='decrease-low-res-pixel-size'/>"
829 " <menuitem action='increase-low-res-pixel-size'/>"
830 " <separator name='pixel-size-separator'/>"
833 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
834 ui_info += strprintf(" <menuitem action='lowres-pixel-%d' />", *iter);
838 " <separator name='bleh08'/>"
839 " <menuitem action='play'/>"
840 //" <menuitem action='pause'/>"
841 " <menuitem action='stop'/>"
842 " <menuitem action='dialog-flipbook'/>"
843 " <separator name='bleh09'/>"
844 " <menuitem action='toggle-grid-show'/>"
845 " <menuitem action='toggle-grid-snap'/>"
846 " <menuitem action='toggle-guide-show'/>"
847 " <menuitem action='toggle-low-res'/>"
848 " <menuitem action='toggle-onion-skin'/>"
849 " <separator name='bleh10'/>"
850 " <menuitem action='canvas-zoom-in'/>"
851 " <menuitem action='canvas-zoom-out'/>"
852 " <menuitem action='canvas-zoom-fit'/>"
853 " <menuitem action='canvas-zoom-100'/>"
854 " <separator name='bleh11'/>"
855 " <menuitem action='time-zoom-in'/>"
856 " <menuitem action='time-zoom-out'/>"
857 " <separator name='bleh12'/>"
858 " <menuitem action='jump-next-keyframe'/>"
859 " <menuitem action='jump-prev-keyframe'/>"
860 " <menuitem action='seek-next-frame'/>"
861 " <menuitem action='seek-prev-frame'/>"
862 " <menuitem action='seek-next-second'/>"
863 " <menuitem action='seek-prev-second'/>"
864 " <menuitem action='seek-begin'/>"
865 " <menuitem action='seek-end'/>"
867 " <menu action='menu-canvas'>"
868 " <menuitem action='canvas-new'/>"
870 " <menu name='menu-state' action='menu-state'>"
872 " <menu action='menu-group'>"
873 " <menuitem action='action-group_add'/>"
875 " <menu action='menu-layer'>"
876 //" <menuitem action='cut'/>"
877 //" <menuitem action='copy'/>"
878 //" <menuitem action='paste'/>"
879 //" <separator name='bleh06'/>"
880 " <menu action='menu-layer-new'></menu>"
881 " <menuitem action='amount-inc'/>"
882 " <menuitem action='amount-dec'/>"
884 " <menu action='menu-keyframe'>"
885 " <menuitem action='keyframe-properties'/>"
892 " <menubar name='MenuBar'>"
893 " <menu action='MenuFile'>"
894 " <menuitem action='New'/>"
895 " <menuitem action='Open'/>"
897 " <menuitem action='Quit'/>"
899 " <menu action='MenuEdit'>"
900 " <menuitem action='Cut'/>"
901 " <menuitem action='Copy'/>"
902 " <menuitem action='Paste'/>"
905 " <toolbar name='ToolBar'>"
906 " <toolitem action='Open'/>"
907 " <toolitem action='Quit'/>"
913 actions_action_group->set_sensitive(false);
914 App::ui_manager()->set_add_tearoffs(true);
915 App::ui_manager()->insert_action_group(menus_action_group,1);
916 App::ui_manager()->insert_action_group(actions_action_group,1);
917 App::ui_manager()->add_ui_from_string(ui_info);
919 //App::ui_manager()->get_accel_group()->unlock();
921 catch(const Glib::Error& ex)
923 synfig::error("building menus and toolbars failed: " + ex.what());
926 // Add default keyboard accelerators
927 #define ACCEL(path,accel) \
929 Gtk::AccelKey accel_key(accel,path); \
930 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
933 #define ACCEL2(accel) \
935 Gtk::AccelKey accel_key(accel); \
936 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
939 ACCEL("<Actions>//select-all-ducks","<Control>a");
940 ACCEL("<Actions>//unselect-all-layers","<Control>d");
941 ACCEL("<Actions>//render","F9");
942 ACCEL("<Actions>//preview","F11");
943 ACCEL("<Actions>//properties","F8");
944 ACCEL("<Actions>//options","F12");
945 ACCEL("<Actions>//import","<control>i");
946 ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
947 ACCEL("<Actions>//toggle-grid-show","<Control>g");
948 ACCEL("<Actions>//toggle-grid-snap","<Control>l");
949 ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
950 ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
951 ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
952 ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
953 ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
954 ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
955 ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
957 ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
958 ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
960 ACCEL("<Actions>//quality-01","<Control>1");
961 ACCEL("<Actions>//quality-02","<Control>2");
962 ACCEL("<Actions>//quality-03","<Control>3");
963 ACCEL("<Actions>//quality-04","<Control>4");
964 ACCEL("<Actions>//quality-05","<Control>5");
965 ACCEL("<Actions>//quality-06","<Control>6");
966 ACCEL("<Actions>//quality-07","<Control>7");
967 ACCEL("<Actions>//quality-08","<Control>8");
968 ACCEL("<Actions>//quality-09","<Control>9");
969 ACCEL("<Actions>//quality-10","<Control>0");
970 ACCEL("<Actions>//undo","<Control>z");
971 ACCEL("<Actions>//redo","<Control>r");
972 ACCEL("<Actions>//action-layer_remove","Delete");
974 /* ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
975 ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
976 ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
977 ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
978 ACCEL("<Actions>//time-zoom-in","+");
979 ACCEL("<Actions>//time-zoom-out","_");
981 ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,"<Actions>//decrease-low-res-pixel-size"));
982 ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,"<Actions>//increase-low-res-pixel-size"));
984 ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
985 ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
987 ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
988 ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
989 ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
990 ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
991 ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
992 ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
993 ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
994 ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
995 ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
996 ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
997 ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
998 ACCEL("<Actions>//play", "<Control>p");
999 ACCEL("<Actions>//seek-begin","Home");
1000 ACCEL("<Actions>//seek-end","End");
1002 ACCEL("<Actions>//state-normal", "<Mod1>a");
1003 ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1004 ACCEL("<Actions>//state-scale", "<Mod1>d");
1005 ACCEL("<Actions>//state-rotate", "<Mod1>s");
1007 ACCEL("<Actions>//state-bline", "<Mod1>b");
1008 ACCEL("<Actions>//state-circle", "<Mod1>c");
1009 ACCEL("<Actions>//state-rectangle", "<Mod1>r");
1010 ACCEL("<Actions>//state-gradient", "<Mod1>g");
1012 ACCEL("<Actions>//state-eyedrop", "<Mod1>e");
1013 ACCEL("<Actions>//state-fill", "<Mod1>f");
1014 ACCEL("<Actions>//state-zoom", "<Mod1>z");
1015 ACCEL("<Actions>//state-polygon", "<Mod1>p");
1017 ACCEL("<Actions>//state-draw", "<Mod1>w");
1018 ACCEL("<Actions>//state-sketch", "<Mod1>k");
1019 ACCEL("<Actions>//state-width", "<Mod1>t");
1020 ACCEL("<Actions>//state-mirror", "<Mod1>m");
1022 ACCEL("<Actions>//state-text", "<Mod1>x");
1024 ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1030 #define mkdir(x,y) mkdir(x)
1033 /* === M E T H O D S ======================================================= */
1035 App::App(int *argc, char ***argv):
1036 Gtk::Main(argc,argv),
1037 IconController(etl::dirname((*argv)[0]))
1039 app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1042 ui_interface_=new GlobalUIInterface();
1046 // don't call thread_init() if threads are already initialized
1047 // on some machines bonobo_init() initialized threads before we get here
1048 if (!g_thread_supported())
1049 Glib::thread_init();
1051 distance_system=Distance::SYSTEM_UNITS;
1053 if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1056 synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1060 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1066 if(!SYNFIG_CHECK_VERSION())
1068 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1069 dialog_error_blocking("Synfig Studio",
1070 "This copy of Synfig Studio was compiled against a\n"
1071 "different version of libsynfig than what is currently\n"
1072 "installed. Synfig Studio will now abort. Try downloading\n"
1073 "the latest version from the Synfig website at\n"
1074 "http://synfig.org/Download"
1078 Glib::set_application_name(_("Synfig Studio"));
1080 Splash splash_screen;
1081 splash_screen.show();
1083 shutdown_in_progress=false;
1084 SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1085 SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1087 // Initialize the Synfig library
1088 try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1091 get_ui_interface()->error(_("Failed to initialize synfig!"));
1095 // add the preferences to the settings
1096 synfigapp::Main::settings().add_domain(&_preferences,"pref");
1100 studio_init_cb.task(_("Init UI Manager..."));
1101 App::ui_manager_=studio::UIManager::create();
1104 studio_init_cb.task(_("Init Dock Manager..."));
1105 dock_manager=new studio::DockManager();
1107 studio_init_cb.task(_("Init State Manager..."));
1108 state_manager=new StateManager();
1110 studio_init_cb.task(_("Init Toolbox..."));
1111 toolbox=new studio::Toolbox();
1113 studio_init_cb.task(_("Init About Dialog..."));
1114 about=new studio::About();
1116 studio_init_cb.task(_("Init Tool Options..."));
1117 dialog_tool_options=new studio::Dialog_ToolOptions();
1118 dock_manager->register_dockable(*dialog_tool_options);
1120 studio_init_cb.task(_("Init History..."));
1121 dock_history=new studio::Dock_History();
1122 dock_manager->register_dockable(*dock_history);
1124 studio_init_cb.task(_("Init Canvases..."));
1125 dock_canvases=new studio::Dock_Canvases();
1126 dock_manager->register_dockable(*dock_canvases);
1128 studio_init_cb.task(_("Init Keyframes..."));
1129 dock_keyframes=new studio::Dock_Keyframes();
1130 dock_manager->register_dockable(*dock_keyframes);
1132 studio_init_cb.task(_("Init Layers..."));
1133 dock_layers=new studio::Dock_Layers();
1134 dock_manager->register_dockable(*dock_layers);
1136 studio_init_cb.task(_("Init Params..."));
1137 dock_params=new studio::Dock_Params();
1138 dock_manager->register_dockable(*dock_params);
1140 studio_init_cb.task(_("Init MetaData..."));
1141 dock_meta_data=new studio::Dock_MetaData();
1142 dock_manager->register_dockable(*dock_meta_data);
1144 studio_init_cb.task(_("Init Children..."));
1145 dock_children=new studio::Dock_Children();
1146 dock_manager->register_dockable(*dock_children);
1148 studio_init_cb.task(_("Init Info..."));
1149 dock_info = new studio::Dock_Info();
1150 dock_manager->register_dockable(*dock_info);
1152 studio_init_cb.task(_("Init Navigator..."));
1153 dock_navigator = new studio::Dock_Navigator();
1154 dock_manager->register_dockable(*dock_navigator);
1156 studio_init_cb.task(_("Init Timetrack..."));
1157 dock_timetrack = new studio::Dock_Timetrack();
1158 dock_manager->register_dockable(*dock_timetrack);
1160 studio_init_cb.task(_("Init Curve Editor..."));
1161 dock_curves = new studio::Dock_Curves();
1162 dock_manager->register_dockable(*dock_curves);
1164 studio_init_cb.task(_("Init Layer Groups..."));
1165 dock_layer_groups = new studio::Dock_LayerGroups();
1166 dock_manager->register_dockable(*dock_layer_groups);
1169 studio_init_cb.task(_("Init Color Dialog..."));
1170 dialog_color=new studio::Dialog_Color();
1172 studio_init_cb.task(_("Init Gradient Dialog..."));
1173 dialog_gradient=new studio::Dialog_Gradient();
1175 studio_init_cb.task(_("Init DeviceTracker..."));
1176 device_tracker=new studio::DeviceTracker();
1178 studio_init_cb.task(_("Init Tools..."));
1181 state_manager->add_state(&state_normal);
1182 state_manager->add_state(&state_smooth_move);
1183 state_manager->add_state(&state_scale);
1184 state_manager->add_state(&state_rotate);
1185 studio_init_cb.task(_("Init ModMirror...")); module_list_.push_back(new ModMirror()); module_list_.back()->start();
1188 state_manager->add_state(&state_bline);
1189 state_manager->add_state(&state_circle);
1190 state_manager->add_state(&state_rectangle);
1191 state_manager->add_state(&state_gradient);
1192 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1195 if(!getenv("SYNFIG_DISABLE_DRAW" )) state_manager->add_state(&state_draw); // Enabled for now. Let's see whether they're good enough yet.
1196 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1197 state_manager->add_state(&state_fill);
1198 state_manager->add_state(&state_eyedrop);
1199 state_manager->add_state(&state_zoom);
1201 state_manager->add_state(&state_text);
1203 // Disabled by default - it doesn't work properly?
1204 if(getenv("SYNFIG_ENABLE_WIDTH" )) state_manager->add_state(&state_width);
1206 studio_init_cb.task(_("Init ModPalette..."));
1207 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1209 studio_init_cb.task(_("Init Setup Dialog..."));
1210 dialog_setup=new studio::Dialog_Setup();
1212 studio_init_cb.task(_("Init Input Dialog..."));
1213 dialog_input=new Gtk::InputDialog();
1214 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1215 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1217 studio_init_cb.task(_("Init auto recovery..."));
1218 auto_recover=new AutoRecover();
1220 studio_init_cb.amount_complete(9250,10000);
1221 studio_init_cb.task(_("Loading Settings..."));
1223 studio_init_cb.task(_("Checking auto-recover..."));
1225 studio_init_cb.amount_complete(9900,10000);
1227 bool opened_any = false;
1228 if(auto_recover->recovery_needed())
1230 splash_screen.hide();
1231 if (get_ui_interface()->yes_no(_("Auto Recovery"),
1232 _("Synfig Studio seems to have crashed\n"
1233 "before you could save all your files.\n"
1234 "Would you like to re-open those files\n"
1235 "and recover your unsaved changes?")) ==
1236 synfigapp::UIInterface::RESPONSE_YES)
1238 int number_recovered;
1239 if(!auto_recover->recover(number_recovered))
1240 if (number_recovered)
1241 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1243 get_ui_interface()->error(_("Unable to recover from previous crash"));
1245 get_ui_interface()->error(
1246 _("Synfig Studio has attempted to recover\n"
1247 "from a previous crash. The files that it has\n"
1248 "recovered are NOT YET SAVED. It would be a good\n"
1249 "idea to review them and save them now."));
1251 if (number_recovered)
1254 splash_screen.show();
1257 // Look for any files given on the command line,
1258 // and load them if found.
1259 for(;*argc>=1;(*argc)--)
1260 if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1262 studio_init_cb.task(_("Loading files..."));
1263 splash_screen.hide();
1264 open((*argv)[*argc]);
1266 splash_screen.show();
1269 // if no file was specified to be opened, create a new document to help new users get started more easily
1270 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1273 studio_init_cb.task(_("Done."));
1274 studio_init_cb.amount_complete(10000,10000);
1280 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1284 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1288 StateManager* App::get_state_manager() { return state_manager; }
1292 shutdown_in_progress=true;
1296 synfigapp::Main::settings().remove_domain("pref");
1298 selected_instance=0;
1300 // Unload all of the modules
1301 for(;!module_list_.empty();module_list_.pop_back())
1304 delete state_manager;
1308 delete auto_recover;
1314 // studio::App::iteration(false);
1318 // studio::App::iteration(false);
1320 // studio::App::iteration(false);
1322 delete dialog_setup;
1324 delete dialog_gradient;
1326 delete dialog_color;
1328 delete dialog_input;
1330 delete dock_manager;
1332 instance_list.clear();
1334 // studio::App::iteration(false);
1338 App::get_user_app_directory()
1340 //! \todo do we need locale_from_utf8() on non-Windows boxes too? (bug #1837445)
1342 return Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR));
1344 return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1349 App::get_config_file(const synfig::String& file)
1351 return Glib::build_filename(get_user_app_directory(),file);
1354 #define SCALE_FACTOR (1280)
1355 //! set the \a instance's canvas(es) position and size to be those specified in the first entry of recent_files_window_size
1357 App::set_recent_file_window_size(etl::handle<Instance> instance)
1359 int screen_w(Gdk::screen_width());
1360 int screen_h(Gdk::screen_height());
1362 const std::string &canvas_window_size = *recent_files_window_size.begin();
1364 if(canvas_window_size.empty())
1367 synfig::String::size_type current=0;
1368 bool seen_root(false), shown_non_root(false);
1370 while(current != synfig::String::npos)
1372 // find end of first field (canvas) or return
1373 synfig::String::size_type separator = canvas_window_size.find_first_of(' ', current);
1374 if(separator == synfig::String::npos) break;
1377 synfig::Canvas::Handle canvas;
1379 canvas = instance->get_canvas()->find_canvas(String(canvas_window_size, current, separator-current));
1381 catch(Exception::IDNotFound) {
1382 // can't find the canvas; skip to the next canvas or return
1383 separator = canvas_window_size.find_first_of('\t', current);
1384 if(separator == synfig::String::npos) return;
1385 current = separator+1;
1389 if (canvas->is_root())
1392 shown_non_root = true;
1394 // check that we have the tab character the ends this canvas' data or return
1395 current = separator+1;
1396 separator = canvas_window_size.find_first_of('\t', current);
1397 if(separator == synfig::String::npos) return;
1400 if(!strscanf(String(canvas_window_size, current, separator-current),"%d %d %d %d",&x, &y, &w, &h))
1402 current = separator+1;
1406 if (x > SCALE_FACTOR) x = SCALE_FACTOR - 150; if (x < 0) x = 0;
1407 if (y > SCALE_FACTOR) y = SCALE_FACTOR - 150; if (y < 0) y = 0;
1408 x=x*screen_w/SCALE_FACTOR;
1409 y=y*screen_h/SCALE_FACTOR;
1410 if(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"))
1411 x += atoi(getenv("SYNFIG_WINDOW_POSITION_X_OFFSET"));
1412 if(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"))
1413 y += atoi(getenv("SYNFIG_WINDOW_POSITION_Y_OFFSET"));
1415 if (w > SCALE_FACTOR) w = 150; if (w < 0) w = 0;
1416 if (h > SCALE_FACTOR) h = 150; if (h < 0) h = 0;
1418 CanvasView::Handle canvasview = instance->find_canvas_view(canvas);
1419 canvasview->move(x,y);
1420 canvasview->resize(w*screen_w/SCALE_FACTOR,h*screen_h/SCALE_FACTOR);
1421 canvasview->present();
1423 current = separator+1;
1426 if (shown_non_root && !seen_root)
1427 instance->find_canvas_view(instance->get_canvas())->hide();
1431 App::add_recent_file(const etl::handle<Instance> instance)
1433 int screen_w(Gdk::screen_width());
1434 int screen_h(Gdk::screen_height());
1436 std::string canvas_window_size;
1438 const Instance::CanvasViewList& cview_list = instance->canvas_view_list();
1439 Instance::CanvasViewList::const_iterator iter;
1441 for(iter=cview_list.begin();iter!=cview_list.end();iter++)
1443 if( !((*iter)->is_visible()) )
1446 etl::handle<synfig::Canvas> canvas = (*iter)->get_canvas();
1447 int x_pos, y_pos, x_size, y_size;
1448 (*iter)->get_position(x_pos,y_pos);
1449 (*iter)->get_size(x_size,y_size);
1451 canvas_window_size += strprintf("%s %d %d %d %d\t",
1452 canvas->get_relative_id(canvas->get_root()).c_str(),
1453 x_pos*SCALE_FACTOR/screen_w, y_pos*SCALE_FACTOR/screen_h,
1454 x_size*SCALE_FACTOR/screen_w, y_size*SCALE_FACTOR/screen_h);
1457 add_recent_file(absolute_path(instance->get_file_name()), canvas_window_size);
1462 App::add_recent_file(const std::string &file_name, const std::string &window_size)
1464 std::string filename(file_name);
1466 assert(!filename.empty());
1468 if(filename.empty())
1471 // Toss out any "hidden" files
1472 if(basename(filename)[0]=='.')
1475 // If we aren't an absolute path, turn ourselves into one
1476 if(!is_absolute_path(filename))
1477 filename=absolute_path(filename);
1479 std::string old_window_size;
1481 list<string>::iterator iter;
1482 list<string>::iterator iter_wsize;
1483 // Check to see if the file is already on the list.
1484 // If it is, then remove it from the list
1485 for(iter=recent_files.begin(), iter_wsize=recent_files_window_size.begin();iter!=recent_files.end();iter++, iter_wsize++)
1488 recent_files.erase(iter);
1489 old_window_size = *iter_wsize;
1490 recent_files_window_size.erase(iter_wsize);
1495 // Push the filename to the front of the list
1496 recent_files.push_front(filename);
1497 if(window_size.empty())
1498 recent_files_window_size.push_front(old_window_size);
1500 recent_files_window_size.push_front(window_size);
1502 // Clean out the files at the end of the list.
1503 while(recent_files.size()>(unsigned)get_max_recent_files())
1505 recent_files.pop_back();
1506 recent_files_window_size.pop_back();
1509 signal_recent_files_changed_();
1514 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1517 App::get_time_format()
1519 return _App_time_format;
1523 App::set_time_format(synfig::Time::Format x)
1530 App::save_settings()
1535 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1536 setlocale(LC_NUMERIC, "C");
1538 std::string filename=get_config_file("accelrc");
1539 Gtk::AccelMap::save(filename);
1542 std::string filename=get_config_file("recentfiles");
1544 std::ofstream file(filename.c_str());
1548 synfig::warning("Unable to save %s",filename.c_str());
1552 list<string>::reverse_iterator iter;
1554 for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1558 std::string filename=get_config_file("recentfiles")+std::string("_window_size");
1560 std::ofstream file(filename.c_str());
1564 synfig::warning("Unable to save %s",filename.c_str());
1568 list<string>::reverse_iterator iter;
1570 for(iter=recent_files_window_size.rbegin();iter!=recent_files_window_size.rend();iter++)
1574 std::string filename=get_config_file("settings");
1575 synfigapp::Main::settings().save_to_file(filename);
1576 setlocale(LC_NUMERIC,old_locale);
1580 synfig::warning("Caught exception when attempting to save settings.");
1585 App::load_settings()
1590 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1591 setlocale(LC_NUMERIC, "C");
1593 std::string filename=get_config_file("accelrc");
1594 Gtk::AccelMap::load(filename);
1597 bool window_size_broken = false;
1599 std::string filename=get_config_file("recentfiles");
1600 std::string filename_window_size=filename+std::string("_window_size");
1602 std::ifstream file(filename.c_str());
1603 std::ifstream file_window_size(filename_window_size.c_str());
1605 if(!file_window_size)
1606 window_size_broken = true;
1610 std::string recent_file;
1611 std::string recent_file_window_size;
1612 getline(file,recent_file);
1613 if(!window_size_broken)
1614 getline(file_window_size,recent_file_window_size);
1615 if(!recent_file.empty())
1617 if(!window_size_broken && !file_window_size)
1618 window_size_broken = true;
1619 if(!window_size_broken)
1620 add_recent_file(recent_file,recent_file_window_size);
1622 add_recent_file(recent_file);
1625 if(!window_size_broken && file_window_size)
1626 window_size_broken = true;
1628 if(window_size_broken)
1630 recent_files_window_size.clear();
1631 recent_files_window_size.resize(recent_files.size());
1634 std::string filename=get_config_file("settings");
1635 if(!synfigapp::Main::settings().load_from_file(filename))
1637 //std::string filename=Glib::locale_from_utf8(Glib::build_filename(Glib::get_home_dir(),".synfigrc"));
1638 //if(!synfigapp::Main::settings().load_from_file(filename))
1640 gamma.set_gamma(1.0/2.2);
1641 reset_initial_window_configuration();
1644 setlocale(LC_NUMERIC,old_locale);
1648 synfig::warning("Caught exception when attempting to load settings.");
1653 App::reset_initial_window_configuration()
1655 synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1656 synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1657 synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1658 synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1659 synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1660 synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1661 synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1662 synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1663 synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1664 synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1665 synfigapp::Main::settings().set_value("pref.distance_system","pt");
1666 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1667 #ifdef SINGLE_THREADED
1668 synfigapp::Main::settings().set_value("pref.single_threaded","0");
1670 synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
1671 synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1675 App::shutdown_request(GdkEventAny*)
1679 //return !shutdown_in_progress;
1685 if(shutdown_in_progress)return;
1688 get_ui_interface()->task(_("Quit Request"));
1691 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1695 std::list<etl::handle<Instance> >::iterator iter;
1696 for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1698 if(!(*iter)->safe_close())
1702 if((*iter)->synfigapp::Instance::get_action_count())
1704 handle<synfigapp::UIInterface> uim;
1705 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1707 string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1708 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1710 case synfigapp::UIInterface::RESPONSE_NO:
1712 case synfigapp::UIInterface::RESPONSE_YES:
1715 case synfigapp::UIInterface::RESPONSE_CANCEL:
1724 if((*iter)->synfigapp::Instance::is_modified())
1726 handle<synfigapp::UIInterface> uim;
1727 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1729 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() );
1730 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1732 case synfigapp::UIInterface::RESPONSE_NO:
1734 case synfigapp::UIInterface::RESPONSE_YES:
1735 (*iter)->dialog_cvs_commit();
1737 case synfigapp::UIInterface::RESPONSE_CANCEL:
1746 // This next line causes things to crash for some reason
1750 shutdown_in_progress=true;
1752 instance_list.clear();
1754 while(studio::App::events_pending())studio::App::iteration(false);
1757 auto_recover->normal_shutdown();
1759 get_ui_interface()->task(_("Quit Request sent"));
1765 dialog_setup->refresh();
1766 dialog_setup->show();
1769 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1770 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1773 //#define USE_WIN32_FILE_DIALOGS 1
1776 #ifdef USE_WIN32_FILE_DIALOGS
1777 static OPENFILENAME ofn={};
1781 #include <gdk/gdkwin32.h>
1785 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
1787 info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1789 #ifdef USE_WIN32_FILE_DIALOGS
1790 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1792 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1793 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1794 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1796 ofn.lStructSize=sizeof(OPENFILENAME);
1797 ofn.hwndOwner = hWnd;
1798 ofn.hInstance = hInstance;
1799 ofn.lpstrFilter = szFilter;
1800 // ofn.lpstrCustomFilter=NULL;
1801 // ofn.nMaxCustFilter=0;
1802 // ofn.nFilterIndex=0;
1803 // ofn.lpstrFile=NULL;
1804 ofn.nMaxFile=MAX_PATH;
1805 // ofn.lpstrFileTitle=NULL;
1806 // ofn.lpstrInitialDir=NULL;
1807 // ofn.lpstrTitle=NULL;
1808 ofn.Flags=OFN_HIDEREADONLY;
1809 // ofn.nFileOffset=0;
1810 // ofn.nFileExtension=0;
1811 ofn.lpstrDefExt=TEXT("sif");
1812 // ofn.lCustData = 0l;
1814 // ofn.lpTemplateName=NULL;
1816 CHAR szFilename[MAX_PATH];
1818 strcpy(szFilename,filename.c_str());
1819 strcpy(szTitle,title.c_str());
1821 ofn.lpstrFile=szFilename;
1822 ofn.lpstrFileTitle=szTitle;
1824 if(GetOpenFileName(&ofn))
1826 filename=szFilename;
1832 synfig::String prev_path;
1834 if(!_preferences.get_value(preference, prev_path))
1837 prev_path = absolute_path(prev_path);
1839 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
1841 dialog->set_current_folder(prev_path);
1842 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1843 dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1845 if (filename.empty())
1846 dialog->set_filename(prev_path);
1847 else if (is_absolute_path(filename))
1848 dialog->set_filename(filename);
1850 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1852 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
1853 filename = dialog->get_filename();
1854 info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
1855 _preferences.set_value(preference, dirname(filename));
1869 GtkWidget *fileselection;
1870 fileselection = gtk_file_selection_new(title.c_str());
1873 if(basename(filename)==filename)
1875 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1878 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1880 gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1882 ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1883 cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1885 gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1886 gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1888 gtk_widget_show(fileselection);
1896 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1897 _preferences.set_value(preference,dirname(filename));
1901 gtk_widget_destroy(fileselection);
1904 gtk_widget_destroy(fileselection);
1911 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
1913 info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
1915 #if USE_WIN32_FILE_DIALOGS
1916 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1918 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1919 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1920 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1922 ofn.lStructSize=sizeof(OPENFILENAME);
1923 ofn.hwndOwner = hWnd;
1924 ofn.hInstance = hInstance;
1925 ofn.lpstrFilter = szFilter;
1926 // ofn.lpstrCustomFilter=NULL;
1927 // ofn.nMaxCustFilter=0;
1928 // ofn.nFilterIndex=0;
1929 // ofn.lpstrFile=NULL;
1930 ofn.nMaxFile=MAX_PATH;
1931 // ofn.lpstrFileTitle=NULL;
1932 // ofn.lpstrInitialDir=NULL;
1933 // ofn.lpstrTitle=NULL;
1934 ofn.Flags=OFN_OVERWRITEPROMPT;
1935 // ofn.nFileOffset=0;
1936 // ofn.nFileExtension=0;
1937 ofn.lpstrDefExt=TEXT("sif");
1938 // ofn.lCustData = 0l;
1940 // ofn.lpTemplateName=NULL;
1942 CHAR szFilename[MAX_PATH];
1944 strcpy(szFilename,filename.c_str());
1945 strcpy(szTitle,title.c_str());
1947 ofn.lpstrFile=szFilename;
1948 ofn.lpstrFileTitle=szTitle;
1950 if(GetSaveFileName(&ofn))
1952 filename=szFilename;
1953 _preferences.set_value(preference,dirname(filename));
1958 synfig::String prev_path;
1960 if(!_preferences.get_value(preference, prev_path))
1963 prev_path = absolute_path(prev_path);
1965 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
1967 dialog->set_current_folder(prev_path);
1968 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1969 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1971 Widget_Enum *file_type_enum = 0;
1972 if (preference == ANIMATION_DIR_PREFERENCE)
1974 file_type_enum = manage(new Widget_Enum());
1975 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
1976 .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
1977 .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
1978 .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
1979 .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
1980 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
1982 Gtk::HBox *hbox = manage(new Gtk::HBox);
1983 hbox->pack_start(*manage(new Gtk::Label(_("File Format Version: "))),Gtk::PACK_SHRINK,0);
1984 hbox->pack_start(*file_type_enum,Gtk::PACK_EXPAND_WIDGET,0);
1987 dialog->set_extra_widget(*hbox);
1990 if (filename.empty())
1991 dialog->set_filename(prev_path);
1994 std::string full_path;
1995 if (is_absolute_path(filename))
1996 full_path = filename;
1998 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2000 // select the file if it exists
2001 dialog->set_filename(full_path);
2003 // if the file doesn't exist, put its name into the filename box
2005 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2006 dialog->set_current_name(basename(filename));
2009 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2010 if (preference == ANIMATION_DIR_PREFERENCE)
2011 set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
2012 filename = dialog->get_filename();
2013 info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
2014 _preferences.set_value(preference, dirname(filename));
2025 App::dialog_error_blocking(const std::string &title, const std::string &message)
2027 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2028 dialog.set_title(title);
2034 App::dialog_warning_blocking(const std::string &title, const std::string &message)
2036 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
2037 dialog.set_title(title);
2043 App::dialog_yes_no(const std::string &title, const std::string &message)
2048 true // use_separator
2050 Gtk::Label label(message);
2053 dialog.get_vbox()->pack_start(label);
2054 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2055 dialog.add_button(Gtk::StockID("gtk-no"),0);
2057 return dialog.run();
2061 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
2066 true // use_separator
2068 Gtk::Label label(message);
2071 dialog.get_vbox()->pack_start(label);
2072 dialog.add_button(Gtk::StockID("gtk-yes"),1);
2073 dialog.add_button(Gtk::StockID("gtk-no"),0);
2074 dialog.add_button(Gtk::StockID("gtk-cancel"),2);
2076 return dialog.run();
2080 App::dialog_not_implemented()
2082 Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2083 dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
2088 try_open_url(const std::string &url)
2091 return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
2093 std::vector<std::string> command_line;
2094 std::vector<std::string> browsers;
2095 browsers.reserve(23);
2097 // Browser wrapper scripts
2098 #ifdef USE_OPEN_FOR_URLS
2099 browsers.push_back("open"); // Apple MacOS X wrapper, on Linux it opens a virtual console
2101 browsers.push_back("xdg-open"); // XDG wrapper
2102 browsers.push_back("sensible-browser"); // Debian wrapper
2103 browsers.push_back("gnome-open"); // GNOME wrapper
2104 browsers.push_back("kfmclient"); // KDE wrapper
2105 browsers.push_back("exo-open"); // XFCE wrapper
2107 // Alternatives system
2108 browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
2109 browsers.push_back("x-www-browser"); // Debian GUI alternative
2111 // Individual browsers
2112 browsers.push_back("firefox");
2113 browsers.push_back("epiphany-browser");
2114 browsers.push_back("epiphany");
2115 browsers.push_back("konqueror");
2116 browsers.push_back("iceweasel");
2117 browsers.push_back("mozilla");
2118 browsers.push_back("netscape");
2119 browsers.push_back("icecat");
2120 browsers.push_back("galeon");
2121 browsers.push_back("midori");
2122 browsers.push_back("safari");
2123 browsers.push_back("opera");
2124 browsers.push_back("amaya");
2125 browsers.push_back("netsurf");
2126 browsers.push_back("dillo");
2128 // Try the user-specified browser first
2129 command_line.push_back(App::browser_command);
2130 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2131 command_line.push_back(url);
2133 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
2134 catch( Glib::SpawnError& exception ){
2136 while ( !browsers.empty() )
2138 // Skip the browser if we already tried it
2139 if( browsers[0] == App::browser_command )
2142 // Construct the command line
2143 command_line.clear();
2144 command_line.push_back(browsers[0]);
2145 if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
2146 command_line.push_back(url);
2148 // Remove the browser from the list
2149 browsers.erase(browsers.begin());
2151 // Try to spawn the browser
2152 try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
2153 // Failed, move on to the next one
2154 catch(Glib::SpawnError& exception){ continue; }
2155 return true; // No exception means we succeeded!
2166 if (!try_open_url("http://synfig.org/Documentation"))
2168 Gtk::MessageDialog dialog(_("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
2169 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://www.synfig.org/Documentation"));
2170 dialog.set_title(_("Help"));
2176 App::open_url(const std::string &url)
2178 if(!try_open_url(url))
2180 Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
2181 dialog.set_secondary_text(url);
2182 dialog.set_title(_("No browser found"));
2188 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
2193 true); // use_separator
2195 Gtk::Label label(message);
2197 dialog.get_vbox()->pack_start(label);
2200 entry.set_text(text);
2202 entry.set_activates_default(true);
2204 dialog.get_vbox()->pack_start(entry);
2206 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2207 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2208 dialog.set_default_response(Gtk::RESPONSE_OK);
2210 entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2213 if(dialog.run()!=Gtk::RESPONSE_OK)
2216 text=entry.get_text();
2222 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
2227 true); // use_separator
2229 Gtk::Label label(message);
2231 dialog.get_vbox()->pack_start(label);
2233 Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
2234 text_buffer->set_text(text);
2235 Gtk::TextView text_view(text_buffer);
2238 dialog.get_vbox()->pack_start(text_view);
2240 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
2241 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
2242 dialog.set_default_response(Gtk::RESPONSE_OK);
2244 //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
2247 if(dialog.run()!=Gtk::RESPONSE_OK)
2250 text=text_buffer->get_text();
2256 App::open(std::string filename)
2258 return open_as(filename,filename);
2261 // this is called from autorecover.cpp:
2262 // App::open_as(get_shadow_file_name(filename),filename)
2263 // other than that, 'filename' and 'as' are the same
2265 App::open_as(std::string filename,std::string as)
2268 char long_name[1024];
2269 if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2270 // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
2271 // don't use it if that's the case
2272 if (long_name[0] != '\0')
2278 OneMoment one_moment;
2280 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2281 if(canvas && get_instance(canvas))
2283 get_instance(canvas)->find_canvas_view(canvas)->present();
2284 info("%s is already open", filename.c_str());
2285 // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2290 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2292 if (as.find(DEFAULT_FILENAME_PREFIX) != 0)
2293 add_recent_file(as);
2295 handle<Instance> instance(Instance::create(canvas));
2298 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2300 set_recent_file_window_size(instance);
2304 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)")))
2305 instance->dialog_cvs_update();
2310 dialog_error_blocking(_("Error"), x);
2315 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2326 handle<synfig::Canvas> canvas=synfig::Canvas::create();
2328 String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2329 canvas->set_name(file_name);
2330 file_name += ".sifz";
2332 canvas->rend_desc().set_frame_rate(24.0);
2333 canvas->rend_desc().set_time_start(0.0);
2334 canvas->rend_desc().set_time_end(5.0);
2335 canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2336 canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2337 canvas->rend_desc().set_tl(Vector(-4,2.25));
2338 canvas->rend_desc().set_br(Vector(4,-2.25));
2339 canvas->rend_desc().set_w(480);
2340 canvas->rend_desc().set_h(270);
2341 canvas->rend_desc().set_antialias(1);
2342 canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2343 canvas->set_file_name(file_name);
2345 handle<Instance> instance = Instance::create(canvas);
2347 if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2348 instance->find_canvas_view(canvas)->canvas_properties.present();
2352 App::dialog_open(string filename)
2354 if (filename.empty())
2357 while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
2359 // If the filename still has wildcards, then we should
2360 // continue looking for the file we want
2361 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2367 get_ui_interface()->error(_("Unable to open file"));
2372 App::set_selected_instance(etl::loose_handle<Instance> instance)
2374 /* if(get_selected_instance()==instance)
2376 selected_instance=instance;
2377 signal_instance_selected()(instance);
2383 selected_instance=instance;
2384 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2388 instance->focus(instance->get_canvas());
2391 set_selected_canvas_view(0);
2393 signal_instance_selected()(instance);
2397 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2399 selected_canvas_view=canvas_view;
2400 signal_canvas_view_focus()(selected_canvas_view);
2403 selected_instance=canvas_view->get_instance();
2404 signal_instance_selected()(canvas_view->get_instance());
2407 if(get_selected_canvas_view()==canvas_view)
2409 signal_canvas_view_focus()(selected_canvas_view);
2410 signal_instance_selected()(canvas_view->get_instance());
2413 selected_canvas_view=canvas_view;
2414 if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2415 set_selected_instance(canvas_view->get_instance());
2416 signal_canvas_view_focus()(selected_canvas_view);
2420 etl::loose_handle<Instance>
2421 App::get_instance(etl::handle<synfig::Canvas> canvas)
2423 if(!canvas) return 0;
2424 canvas=canvas->get_root();
2426 std::list<etl::handle<Instance> >::iterator iter;
2427 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2429 if((*iter)->get_canvas()==canvas)
2445 if(selected_instance)
2446 selected_instance->undo();
2452 if(selected_instance)
2453 selected_instance->redo();
2457 studio::App::get_base_path()
2459 return app_base_path_;