1 /* === S Y N F I G ========================================================= */
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
37 #ifdef HAVE_SYS_ERRNO_H
38 #include <sys/errno.h>
40 #include <gtkmm/fileselection.h>
41 #include <gtkmm/dialog.h>
42 #include <gtkmm/messagedialog.h>
43 #include <gtkmm/label.h>
44 #include <gtkmm/stock.h>
45 #include <gtkmm/stockitem.h>
46 #include <gtkmm/iconsource.h>
47 #include <gtkmm/inputdialog.h>
48 #include <gtkmm/accelmap.h>
49 #include <gtkmm/uimanager.h>
53 #include <synfig/loadcanvas.h>
58 #include "canvasview.h"
59 #include "dialog_setup.h"
60 #include "dialog_gradient.h"
61 #include "dialog_color.h"
64 #include "onemoment.h"
66 #include "dockmanager.h"
68 #include "state_eyedrop.h"
69 #include "state_normal.h"
70 #include "state_draw.h"
71 #include "state_fill.h"
72 #include "state_bline.h"
73 #include "state_polygon.h"
74 #include "state_sketch.h"
75 #include "state_gradient.h"
76 #include "state_circle.h"
77 #include "state_rectangle.h"
78 #include "state_smoothmove.h"
79 #include "state_scale.h"
80 #include "state_width.h"
81 #include "state_rotate.h"
82 #include "state_zoom.h"
84 #include "devicetracker.h"
85 #include "dialog_tooloptions.h"
87 #include "autorecover.h"
89 #include <synfigapp/settings.h>
90 #include "dock_history.h"
91 #include "dock_canvases.h"
92 #include "dock_keyframes.h"
93 #include "dock_layers.h"
94 #include "dock_params.h"
95 #include "dock_metadata.h"
96 #include "dock_children.h"
97 #include "dock_info.h"
98 #include "dock_navigator.h"
99 #include "dock_layergroups.h"
100 #include "dock_timetrack.h"
101 #include "dock_curves.h"
103 #include "mod_palette/mod_palette.h"
104 #include "mod_mirror/mod_mirror.h"
106 #include <sys/stat.h>
112 #include "statemanager.h"
119 #define _WIN32_WINNT 0x0500
122 #include <gtkmm/accelmap.h>
123 #include <gtkmm/filechooser.h>
124 #include <gtkmm/filechooserdialog.h>
130 /* === U S I N G =========================================================== */
134 using namespace synfig;
135 using namespace studio;
137 /* === M A C R O S ========================================================= */
139 #ifndef SYNFIG_USER_APP_DIR
141 #define SYNFIG_USER_APP_DIR "Library/Synfig"
142 #elif defined(_WIN32)
143 #define SYNFIG_USER_APP_DIR "Synfig"
145 #define SYNFIG_USER_APP_DIR ".synfig"
150 #define DPM2DPI(x) (float(x)/39.3700787402f)
151 #define DPI2DPM(x) (float(x)*39.3700787402f)
157 # define IMAGE_DIR "share\\pixmaps"
162 # define IMAGE_DIR "/usr/local/share/pixmaps"
166 # define IMAGE_EXT "tif"
169 #include <synfigapp/main.h>
171 /* === S I G N A L S ======================================================= */
173 static sigc::signal<void> signal_present_all_;
175 App::signal_present_all() { return signal_present_all_; }
177 static sigc::signal<void> signal_recent_files_changed_;
179 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
181 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
182 sigc::signal<void,etl::loose_handle<CanvasView> >&
183 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
185 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
186 sigc::signal<void,etl::handle<Instance> >&
187 App::signal_instance_selected() { return signal_instance_selected_; }
189 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
190 sigc::signal<void,etl::handle<Instance> >&
191 App::signal_instance_created() { return signal_instance_created_; }
193 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
194 sigc::signal<void,etl::handle<Instance> >&
195 App::signal_instance_deleted() { return signal_instance_deleted_; }
197 /* === G L O B A L S ======================================================= */
199 static std::list<std::string> recent_files;
200 const std::list<std::string>& App::get_recent_files() { return recent_files; }
202 int App::Busy::count;
203 bool App::shutdown_in_progress;
205 synfig::Gamma App::gamma;
207 Glib::RefPtr<studio::UIManager> App::ui_manager_;
209 synfig::Distance::System App::distance_system;
211 studio::Dialog_Setup* App::dialog_setup;
213 etl::handle< studio::ModPalette > mod_palette_;
214 //studio::Dialog_Palette* App::dialog_palette;
216 std::list<etl::handle<Instance> > App::instance_list;
218 static etl::handle<synfigapp::UIInterface> ui_interface_;
219 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
221 etl::handle<Instance> App::selected_instance;
222 etl::handle<CanvasView> App::selected_canvas_view;
224 studio::Toolbox *studio::App::toolbox=NULL;
226 studio::AutoRecover *studio::App::auto_recover=NULL;
228 studio::IPC *ipc=NULL;
230 studio::DockManager* studio::App::dock_manager=0;
232 studio::DeviceTracker* studio::App::device_tracker=0;
234 studio::Dialog_Gradient* studio::App::dialog_gradient;
236 studio::Dialog_Color* studio::App::dialog_color;
238 Gtk::InputDialog* studio::App::dialog_input;
240 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
242 studio::Dock_History* dock_history;
243 studio::Dock_Canvases* dock_canvases;
244 studio::Dock_Keyframes* dock_keyframes;
245 studio::Dock_Layers* dock_layers;
246 studio::Dock_Params* dock_params;
247 studio::Dock_MetaData* dock_meta_data;
248 studio::Dock_Children* dock_children;
249 studio::Dock_Info* dock_info;
250 studio::Dock_LayerGroups* dock_layer_groups;
251 studio::Dock_Navigator* dock_navigator;
252 studio::Dock_Timetrack* dock_timetrack;
253 studio::Dock_Curves* dock_curves;
255 std::list< etl::handle< studio::Module > > module_list_;
257 bool studio::App::use_colorspace_gamma=true;
258 bool studio::App::single_threaded=false;
260 static int max_recent_files_=25;
261 int studio::App::get_max_recent_files() { return max_recent_files_; }
262 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
264 static synfig::String app_base_path_;
269 really_delete_widget(Gtk::Widget *widget)
271 synfig::info("really delete %p", (void*)widget);
276 // nasty workaround - when we've finished with a popup menu, we want to delete it
277 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
278 // so schedule the real delete to happen in 50ms, giving the action a chance to run
280 delete_widget(Gtk::Widget *widget)
282 synfig::info("delete %p", (void*)widget);
283 Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
286 }; // END of namespace studio
287 studio::StateManager* state_manager;
292 class GlobalUIInterface : public synfigapp::UIInterface
296 virtual Response yes_no(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
301 true // use_separator
303 Gtk::Label label(message);
306 dialog.get_vbox()->pack_start(label);
307 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
308 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
310 dialog.set_default_response(dflt);
312 return (Response)dialog.run();
314 virtual Response yes_no_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_YES)
319 true // use_separator
321 Gtk::Label label(message);
324 dialog.get_vbox()->pack_start(label);
325 dialog.add_button(Gtk::StockID("gtk-yes"),RESPONSE_YES);
326 dialog.add_button(Gtk::StockID("gtk-no"),RESPONSE_NO);
327 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
329 dialog.set_default_response(dflt);
331 return (Response)dialog.run();
333 virtual Response ok_cancel(const std::string &title, const std::string &message,Response dflt=RESPONSE_OK)
338 true // use_separator
340 Gtk::Label label(message);
343 dialog.get_vbox()->pack_start(label);
344 dialog.add_button(Gtk::StockID("gtk-ok"),RESPONSE_OK);
345 dialog.add_button(Gtk::StockID("gtk-cancel"),RESPONSE_CANCEL);
347 dialog.set_default_response(dflt);
349 return (Response)dialog.run();
353 task(const std::string &task)
355 std::cerr<<task<<std::endl;
356 while(studio::App::events_pending())studio::App::iteration(false);
361 error(const std::string &err)
363 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
370 warning(const std::string &err)
372 std::cerr<<"warning: "<<err<<std::endl;
373 while(studio::App::events_pending())studio::App::iteration(false);
378 amount_complete(int /*current*/, int /*total*/)
380 while(studio::App::events_pending())studio::App::iteration(false);
385 /* === P R O C E D U R E S ================================================= */
387 typedef unsigned char U8;
388 typedef unsigned short U16;
389 typedef unsigned long U32;
399 static inline U32 hash_U32(U32 i)
401 i=i*1664525+1013904223;
402 i=i*1664525+1013904223;
403 i=i*1664525+1013904223;
408 static const int endian_fix_table[8] = { 3, 2, 1, 0, 7, 6, 5, 4 } ;
409 #define endian_fix(x) (endian_fix_table[x])
411 #define endian_fix(x) (x)
414 int v_unwind_key(V_KeyUnwound* unwound, const char* key)
417 unwound->element.serial=0;
418 unwound->element.checksum=0;
426 case '0': data=0; break;
427 case '1': data=1; break;
428 case '2': data=2; break;
429 case '3': data=3; break;
430 case '4': data=4; break;
431 case '5': data=5; break;
432 case '6': data=6; break;
433 case '7': data=7; break;
434 case '8': data=8; break;
435 case '9': data=9; break;
436 case 'a': case 'A': data=10; break;
437 case 'b': case 'B': data=11; break;
438 case 'c': case 'C': data=12; break;
439 case 'd': case 'D': data=13; break;
440 case 'e': case 'E': data=14; break;
441 case 'f': case 'F': data=15; break;
442 default: return 0; break;
445 unwound->element.checksum|=(((U32)data&3)<<bit);
446 unwound->element.serial|=(((U32)(data>>2)&3)<<bit);
451 int v_key_check(const char* key, U32* serial, U32 appid)
453 V_KeyUnwound unwound_key;
454 U32 appid_mask_a=hash_U32(appid);
455 U32 appid_mask_b=hash_U32(appid_mask_a);
457 if(!v_unwind_key(&unwound_key, key))
459 // Invalid characters in key
464 // Undo obfuscation pass
466 U32 next=hash_U32(unwound_key.raw[endian_fix(7)]);
471 unwound_key.raw[endian_fix(i)]^=(next>>24);
475 unwound_key.element.serial^=appid_mask_a;
476 unwound_key.element.checksum^=appid_mask_b;
478 *serial=unwound_key.element.serial;
480 return unwound_key.element.checksum==hash_U32(unwound_key.element.serial);
485 # ifdef LICENSE_KEY_REQUIRED
486 int check_license(String basedir)
488 int check_license(String /*basedir*/)
491 int check_license(String /*basedir*/)
494 #ifdef LICENSE_KEY_REQUIRED
499 license_file="/usr/local/etc/.synfiglicense";
501 license_file=basedir+"\\etc\\.synfiglicense";
505 key=Glib::file_get_contents(license_file);
506 } catch (Glib::FileError) { }
508 if(!v_key_check(key.c_str(),&serial,0xdeadbeef))
510 while(!v_key_check(key.c_str(),&serial,0xdeadbeef))
514 if(!App::dialog_entry(
515 _("Synfig Studio Authentication"),
516 _("Please enter your license key below. You will not\nbe able to use this software without a valid license key."),
519 throw String("No License");
522 FILE* file=fopen(license_file.c_str(),"w");
525 fprintf(file,"%s",key.c_str());
529 synfig::error("Unable to save license key!");
531 synfig::info("License Authenticated -- Serial #%05d",serial);
540 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
542 action_group_list.push_back(action_group);
543 Gtk::UIManager::insert_action_group(action_group, pos);
547 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
549 std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
550 for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
551 if(*iter==action_group)
553 action_group_list.erase(iter);
554 Gtk::UIManager::remove_action_group(action_group);
557 synfig::error("Unable to find action group");
561 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
563 ui_manager->insert_action_group(group,0);
566 std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
567 std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
570 for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
573 if(*iter && (*iter)->get_name()!="menus")
575 synfig::info("Removing action group "+(*iter)->get_name());
576 ui_manager->remove_action_group(*iter);
580 ui_manager->insert_action_group(group,0);
583 for(;!prev_groups.empty();prev_groups.pop_front())
585 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
586 ui_manager->insert_action_group(prev_groups.front(),1);
591 class Preferences : public synfigapp::Settings
594 virtual bool get_value(const synfig::String& key, synfig::String& value)const
598 value=strprintf("%f %f %f %f",
599 App::gamma.get_gamma_r(),
600 App::gamma.get_gamma_g(),
601 App::gamma.get_gamma_b(),
602 App::gamma.get_black_level()
606 if(key=="time_format")
608 value=strprintf("%i",App::get_time_format());
611 if(key=="file_history.size")
613 value=strprintf("%i",App::get_max_recent_files());
616 if(key=="use_colorspace_gamma")
618 value=strprintf("%i",(int)App::use_colorspace_gamma);
621 if(key=="distance_system")
623 value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
626 if(key=="single_threaded")
628 value=strprintf("%i",(int)App::single_threaded);
631 if(key=="auto_recover_backup_interval")
633 value=strprintf("%i",App::auto_recover->get_timeout());
637 return synfigapp::Settings::get_value(key,value);
640 virtual bool set_value(const synfig::String& key,const synfig::String& value)
646 strscanf(value,"%f %f %f %f",
653 App::gamma.set_all(r,g,b,blk);
657 if(key=="time_format")
659 int i(atoi(value.c_str()));
660 App::set_time_format(static_cast<synfig::Time::Format>(i));
663 if(key=="auto_recover_backup_interval")
665 int i(atoi(value.c_str()));
666 App::auto_recover->set_timeout(i);
669 if(key=="file_history.size")
671 int i(atoi(value.c_str()));
672 App::set_max_recent_files(i);
675 if(key=="use_colorspace_gamma")
677 int i(atoi(value.c_str()));
678 App::use_colorspace_gamma=i;
681 if(key=="distance_system")
683 App::distance_system=Distance::ident_system(value);;
686 if(key=="single_threaded")
688 int i(atoi(value.c_str()));
689 App::single_threaded=i;
693 return synfigapp::Settings::set_value(key,value);
696 virtual KeyList get_key_list()const
698 KeyList ret(synfigapp::Settings::get_key_list());
699 ret.push_back("gamma");
700 ret.push_back("time_format");
701 ret.push_back("distance_system");
702 ret.push_back("file_history.size");
703 ret.push_back("use_colorspace_gamma");
704 ret.push_back("single_threaded");
705 ret.push_back("auto_recover_backup_interval");
710 static ::Preferences _preferences;
715 Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
717 Glib::RefPtr<Gtk::ActionGroup> toolbox_action_group = Gtk::ActionGroup::create("toolbox");
719 Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create();
721 menus_action_group->add( Gtk::Action::create("menu-file", _("_File")) );
722 menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")) );
723 menus_action_group->add( Gtk::Action::create("menu-view", _("_View")) );
724 menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")) );
725 menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
726 menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
727 menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
728 menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
729 menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
730 menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
731 menus_action_group->add( Gtk::Action::create("menu-state", _("State")) );
732 menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")) );
734 // Add the synfigapp actions...
735 synfigapp::Action::Book::iterator iter;
736 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
738 actions_action_group->add(Gtk::Action::create(
739 "action-"+iter->second.name,
740 get_action_stock_id(iter->second),
741 iter->second.local_name,iter->second.local_name
745 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); /*action->set_sensitive(false);*/ actions_action_group->add(action); }
746 #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); }
747 #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); }
749 DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
750 DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
751 DEFINE_ACTION("open", Gtk::Stock::OPEN);
752 DEFINE_ACTION("save", Gtk::Stock::SAVE);
753 DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
754 DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
755 DEFINE_ACTION("cvs-add", Gtk::StockID("synfig-cvs_add"));
756 DEFINE_ACTION("cvs-update", Gtk::StockID("synfig-cvs_update"));
757 DEFINE_ACTION("cvs-commit", Gtk::StockID("synfig-cvs_commit"));
758 DEFINE_ACTION("cvs-revert", Gtk::StockID("synfig-cvs_revert"));
759 DEFINE_ACTION("import", _("Import"));
760 DEFINE_ACTION("render", _("Render"));
761 DEFINE_ACTION("preview", _("Preview"));
762 DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
763 DEFINE_ACTION("sound", _("Sound File"));
764 DEFINE_ACTION("options", _("Options"));
765 DEFINE_ACTION("close", _("Close View"));
766 DEFINE_ACTION("close-document", _("Close Document"));
769 DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
770 DEFINE_ACTION("redo", Gtk::StockID("gtk-redo"));
771 DEFINE_ACTION("cut", Gtk::StockID("gtk-cut"));
772 DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
773 DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
774 DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
775 DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
776 DEFINE_ACTION("properties", _("Properties"));
778 DEFINE_ACTION("mask-position-ducks", _("Show Position Ducks"));
779 DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Ducks"));
780 DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Ducks"));
781 DEFINE_ACTION("mask-radius-ducks", _("Show Radius Ducks"));
782 DEFINE_ACTION("mask-width-ducks", _("Show Width Ducks"));
783 DEFINE_ACTION("mask-angle-ducks", _("Show Angle Ducks"));
784 DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
785 DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
786 DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
787 DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
788 DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
789 DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
790 DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
791 DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
792 DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
793 DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
794 DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
795 DEFINE_ACTION("play", _("Play"));
796 // DEFINE_ACTION("pause", _("Pause"));
797 DEFINE_ACTION("stop", _("Stop"));
798 DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
799 DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
800 DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
801 DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
802 DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
803 DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
804 DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
805 DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
806 DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
807 DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
808 DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
809 DEFINE_ACTION("jump-next-keyframe", _("Jump to Next Keyframe"));
810 DEFINE_ACTION("jump-prev-keyframe", _("Jump to Prev Keyframe"));
811 DEFINE_ACTION("seek-next-frame", _("Next Frame"));
812 DEFINE_ACTION("seek-prev-frame", _("Prev Frame"));
813 DEFINE_ACTION("seek-next-second", _("Seek Forward"));
814 DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
815 DEFINE_ACTION("seek-begin", _("Seek to Begin"));
816 DEFINE_ACTION("seek-end", _("Seek to End"));
818 DEFINE_ACTION("action-group_add", _("Add group"));
820 DEFINE_ACTION("canvas-new", _("New Canvas"));
822 DEFINE_ACTION("amount-inc", _("Increase Amount"));
823 DEFINE_ACTION("amount-dec", _("Decrease Amount"));
828 // Set up synfigapp actions
830 synfigapp::Action::Book::iterator iter;
832 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
834 Gtk::StockID stock_id;
836 if(!(iter->second.category&synfigapp::Action::CATEGORY_HIDDEN))
838 //Gtk::Image* image(manage(new Gtk::Image()));
839 if(iter->second.task=="raise") stock_id=Gtk::Stock::GO_UP;
840 else if(iter->second.task=="lower") stock_id=Gtk::Stock::GO_DOWN;
841 else if(iter->second.task=="move_top") stock_id=Gtk::Stock::GOTO_TOP;
842 else if(iter->second.task=="move_bottom") stock_id=Gtk::Stock::GOTO_BOTTOM;
843 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
844 else if(iter->second.task=="set_on") stock_id=Gtk::Stock::YES;
845 else if(iter->second.task=="set_off") stock_id=Gtk::Stock::NO;
846 //else if(iter->second.task=="duplicate") stock_id=Gtk::Stock::COPY;
847 else if(iter->second.task=="remove") stock_id=Gtk::Stock::DELETE;
848 else stock_id=Gtk::StockID("synfig-"+iter->second.task);
850 actions_action_group->add(Gtk::Action::create(
851 "action-"+iter->second.name,
853 iter->second.local_name,iter->second.local_name
861 Glib::ustring ui_info =
863 " <popup name='menu-toolbox' action='menu-toolbox'>"
864 " <menu action='menu-file'>"
867 " <popup name='menu-main' action='menu-main'>"
868 " <menu action='menu-file'>"
869 " <menuitem action='save' />"
870 " <menuitem action='save-as' />"
871 " <menuitem action='revert' />"
872 " <separator name='bleh01'/>"
873 " <menuitem action='cvs-add' />"
874 " <menuitem action='cvs-update' />"
875 " <menuitem action='cvs-commit' />"
876 " <menuitem action='cvs-revert' />"
877 " <separator name='bleh02'/>"
878 " <menuitem action='import' />"
879 " <separator name='bleh03'/>"
880 " <menuitem action='render' />"
881 " <menuitem action='preview' />"
882 " <menuitem action='sound' />"
883 " <separator name='bleh04'/>"
884 " <menuitem action='options' />"
885 " <menuitem action='close' />"
886 " <menuitem action='close-document' />"
888 " <menu action='menu-edit'>"
889 " <menuitem action='undo'/>"
890 " <menuitem action='redo'/>"
891 " <separator name='bleh05'/>"
892 " <menuitem action='cut'/>"
893 " <menuitem action='copy'/>"
894 " <menuitem action='paste'/>"
895 " <separator name='bleh06'/>"
896 " <menuitem action='select-all-ducks'/>"
897 " <menuitem action='unselect-all-layers'/>"
898 " <separator name='bleh07'/>"
899 " <menuitem action='properties'/>"
901 " <menu action='menu-view'>"
902 " <menu action='menu-duck-mask'>"
903 " <menuitem action='mask-position-ducks' />"
904 " <menuitem action='mask-vertex-ducks' />"
905 " <menuitem action='mask-tangent-ducks' />"
906 " <menuitem action='mask-radius-ducks' />"
907 " <menuitem action='mask-width-ducks' />"
908 " <menuitem action='mask-angle-ducks' />"
910 " <menu action='menu-preview-quality'>"
911 " <menuitem action='quality-00' />"
912 " <menuitem action='quality-01' />"
913 " <menuitem action='quality-02' />"
914 " <menuitem action='quality-03' />"
915 " <menuitem action='quality-04' />"
916 " <menuitem action='quality-05' />"
917 " <menuitem action='quality-06' />"
918 " <menuitem action='quality-07' />"
919 " <menuitem action='quality-08' />"
920 " <menuitem action='quality-09' />"
921 " <menuitem action='quality-10' />"
923 " <separator name='bleh08'/>"
924 " <menuitem action='play'/>"
925 //" <menuitem action='pause'/>"
926 " <menuitem action='stop'/>"
927 " <menuitem action='dialog-flipbook'/>"
928 " <separator name='bleh09'/>"
929 " <menuitem action='toggle-grid-show'/>"
930 " <menuitem action='toggle-grid-snap'/>"
931 " <menuitem action='toggle-guide-show'/>"
932 " <menuitem action='toggle-low-res'/>"
933 " <menuitem action='toggle-onion-skin'/>"
934 " <separator name='bleh10'/>"
935 " <menuitem action='canvas-zoom-in'/>"
936 " <menuitem action='canvas-zoom-out'/>"
937 " <menuitem action='canvas-zoom-fit'/>"
938 " <menuitem action='canvas-zoom-100'/>"
939 " <separator name='bleh11'/>"
940 " <menuitem action='time-zoom-in'/>"
941 " <menuitem action='time-zoom-out'/>"
942 " <separator name='bleh12'/>"
943 " <menuitem action='jump-next-keyframe'/>"
944 " <menuitem action='jump-prev-keyframe'/>"
945 " <menuitem action='seek-next-frame'/>"
946 " <menuitem action='seek-prev-frame'/>"
947 " <menuitem action='seek-next-second'/>"
948 " <menuitem action='seek-prev-second'/>"
949 " <menuitem action='seek-begin'/>"
950 " <menuitem action='seek-end'/>"
952 " <menu action='menu-canvas'>"
953 " <menuitem action='canvas-new'/>"
955 " <menu name='menu-state' action='menu-state'>"
957 " <menu action='menu-group'>"
958 " <menuitem action='action-group_add'/>"
960 " <menu action='menu-layer'>"
961 //" <menuitem action='cut'/>"
962 //" <menuitem action='copy'/>"
963 //" <menuitem action='paste'/>"
964 //" <separator name='bleh06'/>"
965 " <menu action='menu-layer-new'></menu>"
966 " <menuitem action='amount-inc'/>"
967 " <menuitem action='amount-dec'/>"
969 " <menu action='menu-keyframe'>"
970 " <menuitem action='keyframe-properties'/>"
977 " <menubar name='MenuBar'>"
978 " <menu action='MenuFile'>"
979 " <menuitem action='New'/>"
980 " <menuitem action='Open'/>"
982 " <menuitem action='Quit'/>"
984 " <menu action='MenuEdit'>"
985 " <menuitem action='Cut'/>"
986 " <menuitem action='Copy'/>"
987 " <menuitem action='Paste'/>"
990 " <toolbar name='ToolBar'>"
991 " <toolitem action='Open'/>"
992 " <toolitem action='Quit'/>"
998 actions_action_group->set_sensitive(false);
999 App::ui_manager()->set_add_tearoffs(true);
1000 App::ui_manager()->insert_action_group(menus_action_group,1);
1001 App::ui_manager()->insert_action_group(actions_action_group,1);
1002 App::ui_manager()->add_ui_from_string(ui_info);
1004 //App::ui_manager()->get_accel_group()->unlock();
1006 catch(const Glib::Error& ex)
1008 synfig::error("building menus and toolbars failed: " + ex.what());
1011 // Add default keyboard accelerators
1012 #define ACCEL(path,accel) \
1014 Gtk::AccelKey accel_key(accel,path); \
1015 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1018 #define ACCEL2(accel) \
1020 Gtk::AccelKey accel_key(accel); \
1021 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1024 ACCEL("<Actions>//select-all-ducks","<Control>a");
1025 ACCEL("<Actions>//unselect-all-layers","<Control>d");
1026 ACCEL("<Actions>//render","F9");
1027 ACCEL("<Actions>//preview","F11");
1028 ACCEL("<Actions>//properties","F8");
1029 ACCEL("<Actions>//options","F12");
1030 ACCEL("<Actions>//import","<control>i");
1031 ACCEL2(Gtk::AccelKey(GDK_Escape,static_cast<Gdk::ModifierType>(0),"<Actions>//stop"));
1032 ACCEL("<Actions>//toggle-grid-show","<Control>g");
1033 ACCEL("<Actions>//toggle-grid-snap","<Control>l");
1034 ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK,"<Actions>//toggle-low-res"));
1035 ACCEL("<Actions>//mask-position-ducks", "<Mod1>1");
1036 ACCEL("<Actions>//mask-vertex-ducks", "<Mod1>2");
1037 ACCEL("<Actions>//mask-tangent-ducks", "<Mod1>3");
1038 ACCEL("<Actions>//mask-radius-ducks", "<Mod1>4");
1039 ACCEL("<Actions>//mask-width-ducks", "<Mod1>5");
1040 ACCEL("<Actions>//mask-angle-ducks", "<Mod1>6");
1042 ACCEL2(Gtk::AccelKey(GDK_Page_Up,Gdk::SHIFT_MASK,"<Actions>//action-layer_raise"));
1043 ACCEL2(Gtk::AccelKey(GDK_Page_Down,Gdk::SHIFT_MASK,"<Actions>//action-layer_lower"));
1045 ACCEL("<Actions>//quality-01","<Control>1");
1046 ACCEL("<Actions>//quality-02","<Control>2");
1047 ACCEL("<Actions>//quality-03","<Control>3");
1048 ACCEL("<Actions>//quality-04","<Control>4");
1049 ACCEL("<Actions>//quality-05","<Control>5");
1050 ACCEL("<Actions>//quality-06","<Control>6");
1051 ACCEL("<Actions>//quality-07","<Control>7");
1052 ACCEL("<Actions>//quality-08","<Control>8");
1053 ACCEL("<Actions>//quality-09","<Control>9");
1054 ACCEL("<Actions>//quality-10","<Control>0");
1055 ACCEL("<Actions>//undo","<Control>z");
1056 ACCEL("<Actions>//redo","<Control>r");
1057 ACCEL("<Actions>//action-layer_remove","Delete");
1059 /* ACCEL2(Gtk::AccelKey(']',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-next-keyframe"));
1060 ACCEL2(Gtk::AccelKey('[',static_cast<Gdk::ModifierType>(0),"<Actions>//jump-prev-keyframe"));
1061 ACCEL2(Gtk::AccelKey('=',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-in"));
1062 ACCEL2(Gtk::AccelKey('-',static_cast<Gdk::ModifierType>(0),"<Actions>//canvas-zoom-out"));
1063 ACCEL("<Actions>//time-zoom-in","+");
1064 ACCEL("<Actions>//time-zoom-out","_");
1066 ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
1067 ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
1069 ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK,"<Actions>//jump-next-keyframe"));
1070 ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK,"<Actions>//jump-prev-keyframe"));
1071 ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-in"));
1072 ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK,"<Actions>//canvas-zoom-out"));
1073 ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK,"<Actions>//time-zoom-in"));
1074 ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK,"<Actions>//time-zoom-out"));
1075 ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK,"<Actions>//seek-next-frame"));
1076 ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
1077 ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
1078 ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
1079 ACCEL2(Gtk::AccelKey('o',Gdk::CONTROL_MASK,"<Actions>//toggle-onion-skin"));
1080 ACCEL("<Actions>//play", "<Control>p");
1081 ACCEL("<Actions>//seek-begin","Home");
1082 ACCEL("<Actions>//seek-end","End");
1084 ACCEL("<Actions>//state-normal", "<Mod1>a");
1085 ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
1086 ACCEL("<Actions>//state-scale", "<Mod1>d");
1087 ACCEL("<Actions>//state-rotate", "<Mod1>s");
1089 ACCEL("<Actions>//state-bline", "<Mod1>b");
1090 ACCEL("<Actions>//state-circle", "<Mod1>c");
1091 ACCEL("<Actions>//state-rectangle", "<Mod1>r");
1092 ACCEL("<Actions>//state-gradient", "<Mod1>g");
1094 ACCEL("<Actions>//state-eyedrop", "<Mod1>e");
1095 ACCEL("<Actions>//state-fill", "<Mod1>f");
1096 ACCEL("<Actions>//state-zoom", "<Mod1>z");
1097 ACCEL("<Actions>//state-polygon", "<Mod1>p");
1099 ACCEL("<Actions>//state-draw", "<Mod1>w");
1100 ACCEL("<Actions>//state-sketch", "<Mod1>k");
1101 ACCEL("<Actions>//state-width", "<Mod1>t");
1102 ACCEL("<Actions>//state-mirror", "<Mod1>m");
1104 ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
1110 #define mkdir(x,y) mkdir(x)
1113 /* === M E T H O D S ======================================================= */
1115 App::App(int *argc, char ***argv):
1116 Gtk::Main(argc,argv),
1117 IconController(etl::dirname((*argv)[0]))
1119 app_base_path_=etl::dirname(etl::dirname((*argv)[0]));
1122 serial_=check_license(app_base_path_);
1125 ui_interface_=new GlobalUIInterface();
1129 // don't call thread_init() if threads are already initialized
1130 // on some machines bonobo_init() initialized threads before we get here
1131 if (!g_thread_supported())
1132 Glib::thread_init();
1134 distance_system=Distance::SYSTEM_UNITS;
1136 if(mkdir(get_user_app_directory().c_str(),ACCESSPERMS)<0)
1139 synfig::error("UNABLE TO CREATE \"%s\"",get_user_app_directory().c_str());
1143 synfig::info("Created directory \"%s\"",get_user_app_directory().c_str());
1151 if(!SYNFIG_CHECK_VERSION())
1153 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1154 dialog_error_blocking("Synfig Studio",
1155 "This copy of Synfig Studio was compiled against a\n"
1156 "different version of libsynfig than what is currently\n"
1157 "installed. Synfig Studio will now abort. Try downloading\n"
1158 "the latest version from the Synfig website at\n"
1159 "http://www.synfig.com/ "
1164 catch(synfig::SoftwareExpired)
1166 cerr<<"FATAL: Software Expired"<<endl;
1167 dialog_error_blocking("Synfig Studio",
1168 "This copy of Synfig Studio has expired.\n"
1169 "Please erase this copy, or download and\n"
1170 "install the latest copy from the Synfig\n"
1171 "website at http://www.synfig.com/ ."
1175 Glib::set_application_name(_("Synfig Studio"));
1178 about_window.set_can_self_destruct(false);
1179 about_window.show();
1181 shutdown_in_progress=false;
1182 SuperCallback synfig_init_cb(about_window.get_callback(),0,9000,10000);
1183 SuperCallback studio_init_cb(about_window.get_callback(),9000,10000,10000);
1185 // Initialize the Synfig library
1186 try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
1189 get_ui_interface()->error(_("Failed to initialize synfig!"));
1193 // add the preferences to the settings
1194 synfigapp::Main::settings().add_domain(&_preferences,"pref");
1198 studio_init_cb.task(_("Init UI Manager..."));
1199 App::ui_manager_=studio::UIManager::create();
1202 studio_init_cb.task(_("Init Dock Manager..."));
1203 dock_manager=new studio::DockManager();
1205 studio_init_cb.task(_("Init State Manager..."));
1206 state_manager=new StateManager();
1208 studio_init_cb.task(_("Init Toolbox..."));
1209 toolbox=new studio::Toolbox();
1211 studio_init_cb.task(_("Init Tool Options..."));
1212 dialog_tool_options=new studio::Dialog_ToolOptions();
1213 dock_manager->register_dockable(*dialog_tool_options);
1215 studio_init_cb.task(_("Init History..."));
1216 dock_history=new studio::Dock_History();
1217 dock_manager->register_dockable(*dock_history);
1219 studio_init_cb.task(_("Init Canvases..."));
1220 dock_canvases=new studio::Dock_Canvases();
1221 dock_manager->register_dockable(*dock_canvases);
1223 studio_init_cb.task(_("Init Keyframes..."));
1224 dock_keyframes=new studio::Dock_Keyframes();
1225 dock_manager->register_dockable(*dock_keyframes);
1227 studio_init_cb.task(_("Init Layers..."));
1228 dock_layers=new studio::Dock_Layers();
1229 dock_manager->register_dockable(*dock_layers);
1231 studio_init_cb.task(_("Init Params..."));
1232 dock_params=new studio::Dock_Params();
1233 dock_manager->register_dockable(*dock_params);
1235 studio_init_cb.task(_("Init MetaData..."));
1236 dock_meta_data=new studio::Dock_MetaData();
1237 dock_manager->register_dockable(*dock_meta_data);
1239 studio_init_cb.task(_("Init Children..."));
1240 dock_children=new studio::Dock_Children();
1241 dock_manager->register_dockable(*dock_children);
1243 studio_init_cb.task(_("Init Info..."));
1244 dock_info = new studio::Dock_Info();
1245 dock_manager->register_dockable(*dock_info);
1247 studio_init_cb.task(_("Init Navigator..."));
1248 dock_navigator = new studio::Dock_Navigator();
1249 dock_manager->register_dockable(*dock_navigator);
1251 studio_init_cb.task(_("Init Timetrack..."));
1252 dock_timetrack = new studio::Dock_Timetrack();
1253 dock_manager->register_dockable(*dock_timetrack);
1255 studio_init_cb.task(_("Init Curve Editor..."));
1256 dock_curves = new studio::Dock_Curves();
1257 dock_manager->register_dockable(*dock_curves);
1259 studio_init_cb.task(_("Init Layer Groups..."));
1260 dock_layer_groups = new studio::Dock_LayerGroups();
1261 dock_manager->register_dockable(*dock_layer_groups);
1264 studio_init_cb.task(_("Init Color Dialog..."));
1265 dialog_color=new studio::Dialog_Color();
1267 studio_init_cb.task(_("Init Gradient Dialog..."));
1268 dialog_gradient=new studio::Dialog_Gradient();
1270 studio_init_cb.task(_("Init DeviceTracker..."));
1271 device_tracker=new studio::DeviceTracker();
1273 studio_init_cb.task(_("Init Tools..."));
1274 state_manager->add_state(&state_normal);
1275 state_manager->add_state(&state_smooth_move);
1276 state_manager->add_state(&state_scale);
1277 state_manager->add_state(&state_rotate);
1279 state_manager->add_state(&state_bline);
1282 state_manager->add_state(&state_circle);
1283 state_manager->add_state(&state_rectangle);
1285 state_manager->add_state(&state_gradient);
1286 state_manager->add_state(&state_eyedrop);
1287 state_manager->add_state(&state_fill);
1289 state_manager->add_state(&state_zoom);
1291 // Enabled - it's useful to be able to work with polygons without tangent ducks getting in the way.
1292 // I know we can switch tangent ducks off, but why not allow this kind of layer as well?
1293 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon);
1295 // Enabled for now. Let's see whether they're good enough yet.
1296 if(!getenv("SYNFIG_DISABLE_DRAW" )) state_manager->add_state(&state_draw);
1297 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1299 // Disabled by default - it doesn't work properly?
1300 if(getenv("SYNFIG_ENABLE_WIDTH" )) state_manager->add_state(&state_width);
1302 studio_init_cb.task(_("Init ModPalette..."));
1303 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1305 studio_init_cb.task(_("Init ModMirror..."));
1306 module_list_.push_back(new ModMirror()); module_list_.back()->start();
1309 studio_init_cb.task(_("Init Setup Dialog..."));
1310 dialog_setup=new studio::Dialog_Setup();
1312 studio_init_cb.task(_("Init Input Dialog..."));
1313 dialog_input=new Gtk::InputDialog();
1314 dialog_input->get_close_button()->signal_clicked().connect( sigc::mem_fun( *dialog_input, &Gtk::InputDialog::hide ) );
1315 dialog_input->get_save_button()->signal_clicked().connect( sigc::ptr_fun(studio::App::dialog_not_implemented) );
1317 studio_init_cb.task(_("Init auto recovery..."));
1318 auto_recover=new AutoRecover();
1320 studio_init_cb.amount_complete(9250,10000);
1321 studio_init_cb.task(_("Loading Settings..."));
1323 studio_init_cb.task(_("Checking auto-recover..."));
1325 studio_init_cb.amount_complete(9900,10000);
1327 if(auto_recover->recovery_needed())
1329 about_window.hide();
1331 get_ui_interface()->yes_no(
1333 _("Synfig Studio seems to have crashed\n"
1334 "before you could save all your files.\n"
1335 "Would you like to re-open those files\n"
1336 "and recover your unsaved changes?")
1337 )==synfigapp::UIInterface::RESPONSE_YES
1340 if(!auto_recover->recover())
1342 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1345 get_ui_interface()->error(
1346 _("Synfig Studio has attempted to recover\n"
1347 "from a previous crash. The files that it has\n"
1348 "recovered are NOT YET SAVED. It would be a good\n"
1349 "idea to review them and save them now.")
1352 about_window.show();
1355 // Look for any files given on the command line,
1356 // and load them if found.
1357 for(;*argc>=1;(*argc)--)
1358 if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1360 studio_init_cb.task(_("Loading files..."));
1361 about_window.hide();
1362 open((*argv)[*argc]);
1363 about_window.show();
1366 studio_init_cb.task(_("Done."));
1367 studio_init_cb.amount_complete(10000,10000);
1373 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1377 StateManager* App::get_state_manager() { return state_manager; }
1381 shutdown_in_progress=true;
1385 synfigapp::Main::settings().remove_domain("pref");
1387 selected_instance=0;
1389 // Unload all of the modules
1390 for(;!module_list_.empty();module_list_.pop_back());
1392 delete state_manager;
1396 delete auto_recover;
1400 // studio::App::iteration(false);
1404 // studio::App::iteration(false);
1406 // studio::App::iteration(false);
1408 delete dialog_setup;
1410 delete dialog_gradient;
1412 delete dialog_color;
1414 delete dialog_input;
1416 delete dock_manager;
1418 instance_list.clear();
1420 // studio::App::iteration(false);
1424 App::get_user_app_directory()
1426 return Glib::build_filename(Glib::get_home_dir(),SYNFIG_USER_APP_DIR);
1430 App::get_config_file(const synfig::String& file)
1432 return Glib::build_filename(get_user_app_directory(),file);
1436 App::add_recent_file(const std::string &file_name)
1438 std::string filename(file_name);
1440 assert(!filename.empty());
1442 if(filename.empty())
1445 // Toss out any "hidden" files
1446 if(basename(filename)[0]=='.')
1449 // If we aren't an absolute path, turn ourselves into one
1450 if(!is_absolute_path(filename))
1451 filename=absolute_path(filename);
1453 list<string>::iterator iter;
1454 // Check to see if the file is already on the list.
1455 // If it is, then remove it from the list
1456 for(iter=recent_files.begin();iter!=recent_files.end();iter++)
1459 recent_files.erase(iter);
1464 // Push the filename to the front of the list
1465 recent_files.push_front(filename);
1467 // Clean out the files at the end of the list.
1468 while(recent_files.size()>(unsigned)get_max_recent_files())
1469 recent_files.pop_back();
1471 signal_recent_files_changed_();
1476 static Time::Format _App_time_format(Time::FORMAT_NORMAL);
1479 App::get_time_format()
1481 return _App_time_format;
1485 App::set_time_format(synfig::Time::Format x)
1492 App::save_settings()
1497 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1498 setlocale(LC_NUMERIC, "C");
1500 std::string filename=get_config_file("accelrc");
1501 Gtk::AccelMap::save(filename);
1504 std::string filename=get_config_file("recentfiles");
1506 std::ofstream file(filename.c_str());
1510 synfig::warning("Unable to save %s",filename.c_str());
1514 list<string>::reverse_iterator iter;
1516 for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1520 std::string filename=get_config_file("settings");
1521 synfigapp::Main::settings().save_to_file(filename);
1522 setlocale(LC_NUMERIC,old_locale);
1526 synfig::warning("Caught exception when attempting to save settings.");
1531 App::load_settings()
1536 old_locale=strdup(setlocale(LC_NUMERIC, NULL));
1537 setlocale(LC_NUMERIC, "C");
1539 std::string filename=get_config_file("accelrc");
1540 Gtk::AccelMap::load(filename);
1543 std::string filename=get_config_file("recentfiles");
1545 std::ifstream file(filename.c_str());
1549 std::string recent_file;
1550 getline(file,recent_file);
1551 if(!recent_file.empty())
1552 add_recent_file(recent_file);
1555 std::string filename=get_config_file("settings");
1556 if(!synfigapp::Main::settings().load_from_file(filename))
1558 //std::string filename=Glib::build_filename(Glib::get_home_dir(),".synfigrc");
1559 //if(!synfigapp::Main::settings().load_from_file(filename))
1561 gamma.set_gamma(1.0/2.2);
1562 reset_initial_window_configuration();
1565 setlocale(LC_NUMERIC,old_locale);
1569 synfig::warning("Caught exception when attempting to load settings.");
1574 App::reset_initial_window_configuration()
1576 synfigapp::Main::settings().set_value("dock.dialog.1.comp_selector","1");
1577 synfigapp::Main::settings().set_value("dock.dialog.1.contents","navigator - info pal_edit pal_browse - tool_options history canvases - layers groups");
1578 synfigapp::Main::settings().set_value("dock.dialog.1.contents_size","225 167 207");
1579 synfigapp::Main::settings().set_value("dock.dialog.1.pos","1057 32");
1580 synfigapp::Main::settings().set_value("dock.dialog.1.size","208 1174");
1581 synfigapp::Main::settings().set_value("dock.dialog.2.comp_selector","0");
1582 synfigapp::Main::settings().set_value("dock.dialog.2.contents","params children keyframes | timetrack curves meta_data");
1583 synfigapp::Main::settings().set_value("dock.dialog.2.contents_size","263");
1584 synfigapp::Main::settings().set_value("dock.dialog.2.pos","0 973");
1585 synfigapp::Main::settings().set_value("dock.dialog.2.size","1045 235");
1586 synfigapp::Main::settings().set_value("pref.distance_system","pt");
1587 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
1588 synfigapp::Main::settings().set_value("pref.single_threaded","0");
1589 synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
1593 App::shutdown_request(GdkEventAny*)
1597 //return !shutdown_in_progress;
1603 if(shutdown_in_progress)return;
1606 get_ui_interface()->task(_("Quit Request"));
1609 dialog_error_blocking(_("Cannot quit!"),_("Tasks are currently running.\nPlease cancel the current tasks and try again"));
1613 std::list<etl::handle<Instance> >::iterator iter;
1614 for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
1616 if(!(*iter)->safe_close())
1620 if((*iter)->synfigapp::Instance::get_action_count())
1622 handle<synfigapp::UIInterface> uim;
1623 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1625 string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
1626 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1628 case synfigapp::UIInterface::RESPONSE_NO:
1630 case synfigapp::UIInterface::RESPONSE_YES:
1633 case synfigapp::UIInterface::RESPONSE_CANCEL:
1642 if((*iter)->synfigapp::Instance::is_modified())
1644 handle<synfigapp::UIInterface> uim;
1645 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
1647 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() );
1648 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
1650 case synfigapp::UIInterface::RESPONSE_NO:
1652 case synfigapp::UIInterface::RESPONSE_YES:
1653 (*iter)->dialog_cvs_commit();
1655 case synfigapp::UIInterface::RESPONSE_CANCEL:
1664 // This next line causes things to crash for some reason
1668 shutdown_in_progress=true;
1670 instance_list.clear();
1672 while(studio::App::events_pending())studio::App::iteration(false);
1675 auto_recover->normal_shutdown();
1677 get_ui_interface()->task(_("Quit Request sent"));
1683 dialog_setup->refresh();
1684 dialog_setup->show();
1687 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
1688 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
1691 //#define USE_WIN32_FILE_DIALOGS 1
1694 #ifdef USE_WIN32_FILE_DIALOGS
1695 static OPENFILENAME ofn={};
1699 #include <gdk/gdkwin32.h>
1703 App::dialog_open_file(const std::string &title, std::string &filename)
1705 #ifdef USE_WIN32_FILE_DIALOGS
1706 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1708 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1709 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1710 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1712 ofn.lStructSize=sizeof(OPENFILENAME);
1713 ofn.hwndOwner = hWnd;
1714 ofn.hInstance = hInstance;
1715 ofn.lpstrFilter = szFilter;
1716 // ofn.lpstrCustomFilter=NULL;
1717 // ofn.nMaxCustFilter=0;
1718 // ofn.nFilterIndex=0;
1719 // ofn.lpstrFile=NULL;
1720 ofn.nMaxFile=MAX_PATH;
1721 // ofn.lpstrFileTitle=NULL;
1722 // ofn.lpstrInitialDir=NULL;
1723 // ofn.lpstrTitle=NULL;
1724 ofn.Flags=OFN_HIDEREADONLY;
1725 // ofn.nFileOffset=0;
1726 // ofn.nFileExtension=0;
1727 ofn.lpstrDefExt=TEXT("sif");
1728 // ofn.lCustData = 0l;
1730 // ofn.lpTemplateName=NULL;
1732 CHAR szFilename[MAX_PATH];
1734 strcpy(szFilename,filename.c_str());
1735 strcpy(szTitle,title.c_str());
1737 ofn.lpstrFile=szFilename;
1738 ofn.lpstrFileTitle=szTitle;
1740 if(GetOpenFileName(&ofn))
1742 filename=szFilename;
1748 synfig::String prev_path;
1749 if(!_preferences.get_value("curr_path",prev_path))
1751 prev_path = absolute_path(prev_path);
1753 Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_OPEN);
1754 dialog->set_current_folder(prev_path);
1755 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1756 dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1757 if(!filename.empty())
1758 if (is_absolute_path(filename))
1759 dialog->set_filename(filename);
1761 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
1762 if(dialog->run()==GTK_RESPONSE_ACCEPT) {
1763 filename=dialog->get_filename();
1775 GtkWidget *fileselection;
1776 fileselection = gtk_file_selection_new(title.c_str());
1779 if(basename(filename)==filename)
1781 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),(prev_path+ETL_DIRECTORY_SEPARATOR).c_str());
1784 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection),dirname(filename).c_str());
1786 gtk_file_selection_complete(GTK_FILE_SELECTION(fileselection),basename(filename).c_str());
1788 ok=GTK_FILE_SELECTION(fileselection)->ok_button;
1789 cancel=GTK_FILE_SELECTION(fileselection)->cancel_button;
1791 gtk_signal_connect(GTK_OBJECT(ok),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Ok),&val);
1792 gtk_signal_connect(GTK_OBJECT(cancel),"clicked",GTK_SIGNAL_FUNC(Signal_Open_Cancel),&val);
1794 gtk_widget_show(fileselection);
1802 filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
1803 _preferences.set_value("curr_path",dirname(filename));
1807 gtk_widget_destroy(fileselection);
1810 gtk_widget_destroy(fileselection);
1817 App::dialog_save_file(const std::string &title, std::string &filename)
1819 #if USE_WIN32_FILE_DIALOGS
1820 static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
1822 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
1823 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
1824 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
1826 ofn.lStructSize=sizeof(OPENFILENAME);
1827 ofn.hwndOwner = hWnd;
1828 ofn.hInstance = hInstance;
1829 ofn.lpstrFilter = szFilter;
1830 // ofn.lpstrCustomFilter=NULL;
1831 // ofn.nMaxCustFilter=0;
1832 // ofn.nFilterIndex=0;
1833 // ofn.lpstrFile=NULL;
1834 ofn.nMaxFile=MAX_PATH;
1835 // ofn.lpstrFileTitle=NULL;
1836 // ofn.lpstrInitialDir=NULL;
1837 // ofn.lpstrTitle=NULL;
1838 ofn.Flags=OFN_OVERWRITEPROMPT;
1839 // ofn.nFileOffset=0;
1840 // ofn.nFileExtension=0;
1841 ofn.lpstrDefExt=TEXT("sif");
1842 // ofn.lCustData = 0l;
1844 // ofn.lpTemplateName=NULL;
1846 CHAR szFilename[MAX_PATH];
1848 strcpy(szFilename,filename.c_str());
1849 strcpy(szTitle,title.c_str());
1851 ofn.lpstrFile=szFilename;
1852 ofn.lpstrFileTitle=szTitle;
1854 if(GetSaveFileName(&ofn))
1856 filename=szFilename;
1857 _preferences.set_value("curr_path",dirname(filename));
1862 synfig::String prev_path;
1863 if(!_preferences.get_value("curr_path",prev_path))
1865 prev_path = absolute_path(prev_path);
1867 Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_SAVE);
1868 dialog->set_current_folder(prev_path);
1869 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1870 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1871 if(!filename.empty())
1873 std::string full_path;
1874 if (is_absolute_path(filename))
1875 full_path = filename;
1877 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
1878 // select the file if it exists
1879 dialog->set_filename(full_path);
1881 // if the file doesn't exist, put its name into the filename box
1883 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
1884 dialog->set_current_name(basename(filename));
1886 if(dialog->run()==GTK_RESPONSE_ACCEPT) {
1887 filename=dialog->get_filename();
1889 _preferences.set_value("curr_path",dirname(filename));
1894 // return dialog_open_file(title, filename);
1899 App::dialog_error_blocking(const std::string &title, const std::string &message)
1901 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1902 dialog.set_title(title);
1908 App::dialog_warning_blocking(const std::string &title, const std::string &message)
1910 Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true);
1911 dialog.set_title(title);
1917 App::dialog_yes_no(const std::string &title, const std::string &message)
1922 true // use_separator
1924 Gtk::Label label(message);
1927 dialog.get_vbox()->pack_start(label);
1928 dialog.add_button(Gtk::StockID("gtk-yes"),1);
1929 dialog.add_button(Gtk::StockID("gtk-no"),0);
1931 return dialog.run();
1935 App::dialog_yes_no_cancel(const std::string &title, const std::string &message)
1940 true // use_separator
1942 Gtk::Label label(message);
1945 dialog.get_vbox()->pack_start(label);
1946 dialog.add_button(Gtk::StockID("gtk-yes"),1);
1947 dialog.add_button(Gtk::StockID("gtk-no"),0);
1948 dialog.add_button(Gtk::StockID("gtk-cancel"),2);
1950 return dialog.run();
1954 App::dialog_not_implemented()
1956 Gtk::MessageDialog dialog(_("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
1957 dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
1962 App::dialog_entry(const std::string &title, const std::string &message,std::string &text)
1967 true // use_separator
1969 Gtk::Label label(message);
1971 dialog.get_vbox()->pack_start(label);
1974 entry.set_text(text);
1976 entry.set_activates_default(true);
1977 dialog.get_vbox()->pack_start(entry);
1979 dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
1980 dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
1981 dialog.set_default_response(Gtk::RESPONSE_OK);
1982 entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
1985 if(dialog.run()!=Gtk::RESPONSE_OK)
1988 text=entry.get_text();
1997 App::open(std::string filename)
1999 return open_as(filename,filename);
2002 // this is called from autorecover.cpp:
2003 // App::open_as(get_shadow_file_name(filename),filename)
2004 // other than that, 'filename' and 'as' are the same
2006 App::open_as(std::string filename,std::string as)
2009 char long_name[1024];
2010 if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
2016 OneMoment one_moment;
2018 etl::handle<synfig::Canvas> canvas(open_canvas_as(filename,as));
2019 if(canvas && get_instance(canvas))
2021 get_instance(canvas)->find_canvas_view(canvas)->present();
2022 throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
2025 throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
2027 add_recent_file(as);
2029 handle<Instance> instance(Instance::create(canvas));
2032 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
2036 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)")))
2037 instance->dialog_cvs_update();
2041 dialog_error_blocking(_("Error"), x);
2046 dialog_error_blocking(_("Error"), _("Uncaught error on file open (BUG)"));
2050 _preferences.set_value("curr_path",dirname(as));
2059 handle<synfig::Canvas> canvas=synfig::Canvas::create();
2060 canvas->set_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2062 String file_name(strprintf("%s%d.sifz", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
2064 canvas->rend_desc().set_frame_rate(24.0);
2065 canvas->rend_desc().set_time_start(0.0);
2066 canvas->rend_desc().set_time_end(00.0);
2067 canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
2068 canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
2069 canvas->rend_desc().set_tl(Vector(-4,2.25));
2070 canvas->rend_desc().set_br(Vector(4,-2.25));
2071 canvas->rend_desc().set_w(480);
2072 canvas->rend_desc().set_h(270);
2073 canvas->rend_desc().set_antialias(1);
2074 canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2075 canvas->set_file_name(file_name);
2077 handle<Instance> instance = Instance::create(canvas);
2079 if (!getenv("SYNFIG_DISABLE_NEW_CANVAS_EDIT_PROPERTIES"))
2080 instance->find_canvas_view(canvas)->canvas_properties.present();
2086 string filename="*.sif";
2088 while(dialog_open_file("Open", filename))
2090 // If the filename still has wildcards, then we should
2091 // continue looking for the file we want
2092 if(find(filename.begin(),filename.end(),'*')!=filename.end())
2098 get_ui_interface()->error(_("Unable to open file"));
2103 App::set_selected_instance(etl::loose_handle<Instance> instance)
2105 /* if(get_selected_instance()==instance)
2107 selected_instance=instance;
2108 signal_instance_selected()(instance);
2114 selected_instance=instance;
2115 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
2119 instance->focus(instance->get_canvas());
2122 set_selected_canvas_view(0);
2124 signal_instance_selected()(instance);
2128 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
2130 selected_canvas_view=canvas_view;
2131 signal_canvas_view_focus()(selected_canvas_view);
2134 selected_instance=canvas_view->get_instance();
2135 signal_instance_selected()(canvas_view->get_instance());
2138 if(get_selected_canvas_view()==canvas_view)
2140 signal_canvas_view_focus()(selected_canvas_view);
2141 signal_instance_selected()(canvas_view->get_instance());
2144 selected_canvas_view=canvas_view;
2145 if(canvas_view && canvas_view->get_instance() != get_selected_instance())
2146 set_selected_instance(canvas_view->get_instance());
2147 signal_canvas_view_focus()(selected_canvas_view);
2151 etl::loose_handle<Instance>
2152 App::get_instance(Canvas::Handle canvas)
2154 if(!canvas) return 0;
2155 canvas=canvas->get_root();
2157 std::list<etl::handle<Instance> >::iterator iter;
2158 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
2160 if((*iter)->get_canvas()==canvas)
2169 (new class About())->show();
2175 if(selected_instance)
2176 selected_instance->undo();
2182 if(selected_instance)
2183 selected_instance->redo();
2187 studio::App::get_base_path()
2189 return app_base_path_;