1 /* === S Y N F I G ========================================================= */
2 /*! \file app.cpp
3 ** \brief writeme
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
10 ** Copyright (c) 2008 Gerald Young
11 ** Copyright (c) 2008, 2010-2013 Carlos López
12 ** Copyright (c) 2009, 2011 Nikita Kitaev
13 ** Copyright (c) 2012-2015 Konstantin Dmitriev
14 ** Copyright (c) 2013-2016 Jerome Blanchi
15 **
16 ** This package is free software; you can redistribute it and/or
17 ** modify it under the terms of the GNU General Public License as
18 ** published by the Free Software Foundation; either version 2 of
19 ** the License, or (at your option) any later version.
20 **
21 ** This package is distributed in the hope that it will be useful,
22 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 ** General Public License for more details.
25 ** \endlegal
26 */
27 /* ========================================================================= */
28
29 /* === H E A D E R S ======================================================= */
30
31 #ifdef USING_PCH
32 # include "pch.h"
33 #else
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
37
38 #include <fstream>
39 #include <iostream>
40 #include <locale>
41 #include <cstring>
42
43 #ifdef __OpenBSD__
44 #include <errno.h>
45 #elif defined(HAVE_SYS_ERRNO_H)
46 #include <sys/errno.h>
47 #endif
48 #include <gtkmm/accelmap.h>
49 #include <gtkmm/cssprovider.h>
50 #include <gtkmm/dialog.h>
51 #include <gtkmm/filechooserdialog.h>
52 #include <gtkmm/filefilter.h>
53 #include <gtkmm/iconsource.h>
54 #include <gtkmm/label.h>
55 #include <gtkmm/messagedialog.h>
56 #include <gtkmm/stock.h>
57 #include <gtkmm/stockitem.h>
58 #include <gtkmm/textview.h>
59 #include <gtkmm/uimanager.h>
60
61 #include <glibmm/main.h>
62 #include <glibmm/miscutils.h>
63 #include <glibmm/spawn.h>
64 #include <glibmm/thread.h>
65 #include <glibmm/timer.h>
66
67 #include <gtk/gtk.h>
68 #include <gdk/gdk.h>
69
70 #include <gdkmm/general.h>
71
72 #ifdef _WIN32
73 #define WINVER 0x0500
74 #include <windows.h>
75 #endif
76
77 #include <synfig/general.h>
78
79 #include <synfig/canvasfilenaming.h>
80 #include <synfig/filesystemnative.h>
81 #include <synfig/filesystemgroup.h>
82 #include <synfig/filesystemtemporary.h>
83 #include <synfig/importer.h>
84 #include <synfig/loadcanvas.h>
85 #include <synfig/savecanvas.h>
86
87 #include "app.h"
88 #include "splash.h"
89 #include "instance.h"
90 #include "canvasview.h"
91 #include "dialogs/about.h"
92 #include "dialogs/dialog_color.h"
93 #include "dialogs/dialog_gradient.h"
94 #include "dialogs/dialog_input.h"
95 #include "dialogs/dialog_setup.h"
96 #include "onemoment.h"
97 #include "devicetracker.h"
98 #include "widgets/widget_enum.h"
99
100 #include "statemanager.h"
101
102 #include "states/state_bline.h"
103 #include "states/state_brush.h"
104 #include "states/state_circle.h"
105 #include "states/state_draw.h"
106 #include "states/state_eyedrop.h"
107 #include "states/state_fill.h"
108 #include "states/state_gradient.h"
109 #include "states/state_lasso.h"
110 #include "states/state_mirror.h"
111 #include "states/state_normal.h"
112 #include "states/state_polygon.h"
113 #include "states/state_rectangle.h"
114 #include "states/state_rotate.h"
115 #include "states/state_scale.h"
116 #include "states/state_sketch.h"
117 #include "states/state_smoothmove.h"
118 #include "states/state_star.h"
119 #include "states/state_text.h"
120 #include "states/state_width.h"
121 #include "states/state_zoom.h"
122
123 #include "autorecover.h"
124
125 #include <synfigapp/settings.h>
126
127 #include "docks/dockmanager.h"
128 #include "docks/dialog_tooloptions.h"
129 #include "docks/dock_canvases.h"
130 #include "docks/dock_children.h"
131 #include "docks/dock_curves.h"
132 #include "docks/dock_history.h"
133 #include "docks/dock_info.h"
134 #include "docks/dock_keyframes.h"
135 #include "docks/dock_layers.h"
136 #include "docks/dock_layergroups.h"
137 #include "docks/dock_params.h"
138 #include "docks/dock_metadata.h"
139 #include "docks/dock_navigator.h"
140 #include "docks/dock_timetrack.h"
141 #include "docks/dock_toolbox.h"
142
143 #include "modules/module.h"
144 #include "modules/mod_palette/mod_palette.h"
145
146 #include "ipc.h"
147
148 #ifdef WITH_FMOD
149 #include <fmod.h>
150 #endif
151
152 #include <gui/localization.h>
153
154 #endif
155
156 /* === U S I N G =========================================================== */
157
158 using namespace std;
159 using namespace etl;
160 using namespace synfig;
161 using namespace studio;
162
163 /* === M A C R O S ========================================================= */
164
165 #ifndef DPM2DPI
166 #define DPM2DPI(x) (float(x)/39.3700787402f)
167 #define DPI2DPM(x) (float(x)*39.3700787402f)
168 #endif
169
170 #ifdef _WIN32
171 # ifdef IMAGE_DIR
172 # undef IMAGE_DIR
173 # define IMAGE_DIR "share\\pixmaps"
174 # endif
175 #endif
176
177 #ifndef IMAGE_DIR
178 # define IMAGE_DIR "/usr/local/share/pixmaps"
179 #endif
180
181 #ifndef IMAGE_EXT
182 # define IMAGE_EXT "tif"
183 #endif
184
185 #ifdef _WIN32
186 # ifdef PLUGIN_DIR
187 # undef PLUGIN_DIR
188 # define PLUGIN_DIR "share\\synfig\\plugins"
189 # endif
190 #endif
191
192 #ifndef PLUGIN_DIR
193 # define PLUGIN_DIR "/usr/local/share/synfig/plugins"
194 #endif
195
196 #include <synfigapp/main.h>
197
198 /* === S I G N A L S ======================================================= */
199
200 static sigc::signal<void> signal_present_all_;
201 sigc::signal<void>&
signal_present_all()202 App::signal_present_all() { return signal_present_all_; }
203
204 static sigc::signal<void> signal_recent_files_changed_;
205 sigc::signal<void>&
signal_recent_files_changed()206 App::signal_recent_files_changed() { return signal_recent_files_changed_; }
207
208 static sigc::signal<void,etl::loose_handle<CanvasView> > signal_canvas_view_focus_;
209 sigc::signal<void,etl::loose_handle<CanvasView> >&
signal_canvas_view_focus()210 App::signal_canvas_view_focus() { return signal_canvas_view_focus_; }
211
212 static sigc::signal<void,etl::handle<Instance> > signal_instance_selected_;
213 sigc::signal<void,etl::handle<Instance> >&
signal_instance_selected()214 App::signal_instance_selected() { return signal_instance_selected_; }
215
216 static sigc::signal<void,etl::handle<Instance> > signal_instance_created_;
217 sigc::signal<void,etl::handle<Instance> >&
signal_instance_created()218 App::signal_instance_created() { return signal_instance_created_; }
219
220 static sigc::signal<void,etl::handle<Instance> > signal_instance_deleted_;
221 sigc::signal<void,etl::handle<Instance> >&
signal_instance_deleted()222 App::signal_instance_deleted() { return signal_instance_deleted_; }
223
224 /* === G L O B A L S ======================================================= */
225
226 static std::list<std::string> recent_files;
get_recent_files()227 const std::list<std::string>& App::get_recent_files() { return recent_files; }
228
229 int App::Busy::count;
230 bool App::shutdown_in_progress;
231
232 synfig::Gamma App::gamma;
233
234 Glib::RefPtr<studio::UIManager> App::ui_manager_;
235
236 int App::jack_locks_=0;
237
238 synfig::Distance::System App::distance_system;
239
240 studio::Dialog_Setup* App::dialog_setup;
241
242 etl::handle< studio::ModPalette > mod_palette_;
243 //studio::Dialog_Palette* App::dialog_palette;
244
245 std::list<etl::handle<Instance> > App::instance_list;
246
247 static etl::handle<synfigapp::UIInterface> ui_interface_;
get_ui_interface()248 const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_interface_; }
249
250 etl::handle<Instance> App::selected_instance;
251 etl::handle<CanvasView> App::selected_canvas_view;
252
253 studio::About *studio::App::about=NULL;
254
255 studio::MainWindow *studio::App::main_window=NULL;
256
257 studio::Dock_Toolbox *studio::App::dock_toolbox=NULL;
258
259 studio::AutoRecover *studio::App::auto_recover=NULL;
260
261 studio::IPC *ipc=NULL;
262
263 studio::DockManager* studio::App::dock_manager=0;
264
265 studio::DeviceTracker* studio::App::device_tracker=0;
266
267 studio::Dialog_Gradient* studio::App::dialog_gradient;
268
269 studio::Dialog_Color* studio::App::dialog_color;
270
271 studio::Dialog_Input* studio::App::dialog_input;
272
273 studio::Dialog_ToolOptions* studio::App::dialog_tool_options;
274
275 studio::Dock_History* dock_history;
276 studio::Dock_Canvases* dock_canvases;
277 studio::Dock_Keyframes* dock_keyframes;
278 studio::Dock_Layers* dock_layers;
279 studio::Dock_Params* dock_params;
280 studio::Dock_MetaData* dock_meta_data;
281 studio::Dock_Children* dock_children;
282 studio::Dock_Info* dock_info;
283 studio::Dock_LayerGroups* dock_layer_groups;
284 studio::Dock_Navigator* dock_navigator;
285 studio::Dock_Timetrack* dock_timetrack;
286 studio::Dock_Curves* dock_curves;
287
288 std::list< etl::handle< studio::Module > > module_list_;
289
290 bool studio::App::use_colorspace_gamma=true;
291 #ifdef SINGLE_THREADED
292 //#ifdef WIN32
293 bool studio::App::single_threaded=true;
294 //#else
295 //bool studio::App::single_threaded=false;
296 //#endif // WIN32
297 #endif // SINGLE THREADED
298 bool studio::App::restrict_radius_ducks=true;
299 bool studio::App::resize_imported_images=false;
300 bool studio::App::enable_experimental_features=false;
301 bool studio::App::use_dark_theme=false;
302 bool studio::App::show_file_toolbar=true;
303 String studio::App::custom_filename_prefix(DEFAULT_FILENAME_PREFIX);
304 int studio::App::preferred_x_size=480;
305 int studio::App::preferred_y_size=270;
306 String studio::App::predefined_size(DEFAULT_PREDEFINED_SIZE);
307 String studio::App::predefined_fps(DEFAULT_PREDEFINED_FPS);
308 float studio::App::preferred_fps=24.0;
309 synfigapp::PluginManager studio::App::plugin_manager;
310 std::set< String > studio::App::brushes_path;
311 String studio::App::sequence_separator(".");
312 String studio::App::navigator_renderer;
313 String studio::App::workarea_renderer;
314
315 bool studio::App::enable_mainwin_menubar = true;
316 String studio::App::ui_language ("os_LANG");
317 long studio::App::ui_handle_tooltip_flag(Duck::STRUCT_DEFAULT);
318
319 static int max_recent_files_=25;
get_max_recent_files()320 int studio::App::get_max_recent_files() { return max_recent_files_; }
set_max_recent_files(int x)321 void studio::App::set_max_recent_files(int x) { max_recent_files_=x; }
322
323 static synfig::String app_base_path_;
324
325 namespace studio {
326
327 bool
really_delete_widget(Gtk::Widget * widget)328 really_delete_widget(Gtk::Widget *widget)
329 {
330 // synfig::info("really delete %p", (void*)widget);
331 delete widget;
332 return false;
333 }
334
335 // nasty workaround - when we've finished with a popup menu, we want to delete it
336 // attaching to the signal_hide() signal gets us here before the action on the menu has run,
337 // so schedule the real delete to happen in 50ms, giving the action a chance to run
338 void
delete_widget(Gtk::Widget * widget)339 delete_widget(Gtk::Widget *widget)
340 {
341 // synfig::info("delete %p", (void*)widget);
342 Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
343 }
344
345 }; // END of namespace studio
346 studio::StateManager* state_manager;
347
348
349
350
351 class GlobalUIInterface : public synfigapp::UIInterface
352 {
353 public:
354
confirmation(const std::string & message,const std::string & details,const std::string & cancel,const std::string & confirm,Response dflt)355 virtual Response confirmation(
356 const std::string &message,
357 const std::string &details,
358 const std::string &cancel,
359 const std::string &confirm,
360 Response dflt
361 )
362 {
363 Gtk::MessageDialog dialog(
364 message,
365 false,
366 Gtk::MESSAGE_WARNING,
367 Gtk::BUTTONS_NONE,
368 true
369 );
370
371 if (! details.empty())
372 dialog.set_secondary_text(details);
373
374 dialog.add_button(cancel, RESPONSE_CANCEL);
375 dialog.add_button(confirm, RESPONSE_OK);
376 dialog.set_default_response(dflt);
377
378 dialog.show_all();
379 return (Response) dialog.run();
380 }
381
382
yes_no_cancel(const std::string & message,const std::string & details,const std::string & button1,const std::string & button2,const std::string & button3,Response dflt=RESPONSE_YES)383 virtual Response yes_no_cancel(
384 const std::string &message,
385 const std::string &details,
386 const std::string &button1,
387 const std::string &button2,
388 const std::string &button3,
389 Response dflt=RESPONSE_YES
390 )
391 {
392 Gtk::MessageDialog dialog(
393 message,
394 false,
395 Gtk::MESSAGE_QUESTION,
396 Gtk::BUTTONS_NONE,
397 true
398 );
399
400 dialog.set_secondary_text(details);
401 dialog.add_button(button1, RESPONSE_NO);
402 dialog.add_button(button2, RESPONSE_CANCEL);
403 dialog.add_button(button3, RESPONSE_YES);
404
405 dialog.set_default_response(dflt);
406 dialog.show();
407 return (Response)dialog.run();
408 }
409
410
411 virtual bool
task(const std::string & task)412 task(const std::string &task)
413 {
414 std::cerr<<task.c_str()<<std::endl;
415 while(studio::App::events_pending())studio::App::iteration(false);
416 return true;
417 }
418
419 virtual bool
error(const std::string & err)420 error(const std::string &err)
421 {
422 Gtk::MessageDialog dialog(err, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
423 dialog.set_transient_for(*App::main_window);
424 dialog.show();
425 dialog.run();
426 return true;
427 }
428
429 virtual bool
warning(const std::string & err)430 warning(const std::string &err)
431 {
432 std::cerr<<"warning: "<<err.c_str()<<std::endl;
433 while(studio::App::events_pending())studio::App::iteration(false);
434 return true;
435 }
436
437 virtual bool
amount_complete(int,int)438 amount_complete(int /*current*/, int /*total*/)
439 {
440 while(studio::App::events_pending())studio::App::iteration(false);
441 return true;
442 }
443 };
444
445 /* === P R O C E D U R E S ================================================= */
446
447 /*
448 void
449 studio::UIManager::insert_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group, int pos)
450 {
451 action_group_list.push_back(action_group);
452 Gtk::UIManager::insert_action_group(action_group, pos);
453 }
454
455 void
456 studio::UIManager::remove_action_group (const Glib::RefPtr<Gtk::ActionGroup>& action_group)
457 {
458 std::list<Glib::RefPtr<Gtk::ActionGroup> >::iterator iter;
459 for(iter=action_group_list.begin();iter!=action_group_list.end();++iter)
460 if(*iter==action_group)
461 {
462 action_group_list.erase(iter);
463 Gtk::UIManager::remove_action_group(action_group);
464 return;
465 }
466 synfig::error("Unable to find action group");
467 }
468
469 void
470 studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib::RefPtr<Gtk::ActionGroup> group)
471 {
472 ui_manager->insert_action_group(group,0);
473 return;
474 std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
475 std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
476
477 for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
478 {
479 if(*iter && (*iter)->get_name()!="menus")
480 {
481 synfig::info("Removing action group "+(*iter)->get_name());
482 ui_manager->remove_action_group(*iter);
483 }
484 }
485 ui_manager->insert_action_group(group,0);
486
487 for(;!prev_groups.empty();prev_groups.pop_front())
488 {
489 if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
490 ui_manager->insert_action_group(prev_groups.front(),1);
491 }
492 }
493 */
494 class Preferences : public synfigapp::Settings
495 {
496 public:
get_value(const synfig::String & key,synfig::String & value) const497 virtual bool get_value(const synfig::String& key, synfig::String& value)const
498 {
499 try
500 {
501 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
502 if(key=="gamma")
503 {
504 value=strprintf("%f %f %f %f",
505 App::gamma.get_gamma_r(),
506 App::gamma.get_gamma_g(),
507 App::gamma.get_gamma_b(),
508 App::gamma.get_black_level()
509 );
510 return true;
511 }
512 if(key=="time_format")
513 {
514 value=strprintf("%i",App::get_time_format());
515 return true;
516 }
517 if(key=="file_history.size")
518 {
519 value=strprintf("%i",App::get_max_recent_files());
520 return true;
521 }
522 if(key=="use_colorspace_gamma")
523 {
524 value=strprintf("%i",(int)App::use_colorspace_gamma);
525 return true;
526 }
527 if(key=="distance_system")
528 {
529 value=strprintf("%s",Distance::system_name(App::distance_system).c_str());
530 return true;
531 }
532 #ifdef SINGLE_THREADED
533 if(key=="use_single_threaded")
534 {
535 value=strprintf("%i",(int)App::single_threaded);
536 return true;
537 }
538 #endif
539 if(key=="auto_recover_backup")
540 {
541 value=strprintf("%i",App::auto_recover->get_enabled());
542 return true;
543 }
544 if(key=="auto_recover_backup_interval")
545 {
546 value=strprintf("%i",App::auto_recover->get_timeout_ms());
547 return true;
548 }
549 if(key=="restrict_radius_ducks")
550 {
551 value=strprintf("%i",(int)App::restrict_radius_ducks);
552 return true;
553 }
554 if(key=="resize_imported_images")
555 {
556 value=strprintf("%i",(int)App::resize_imported_images);
557 return true;
558 }
559 if(key=="enable_experimental_features")
560 {
561 value=strprintf("%i",(int)App::enable_experimental_features);
562 return true;
563 }
564 if(key=="use_dark_theme")
565 {
566 value=strprintf("%i",(int)App::use_dark_theme);
567 return true;
568 }
569 if(key=="show_file_toolbar")
570 {
571 value=strprintf("%i",(int)App::show_file_toolbar);
572 return true;
573 }
574 //! "Keep brushes_path" preferences entry for backward compatibilty (15/12 - v1.0.3)
575 //! Now brush path(s) are hold by input preferences : brush.path_count & brush.path_%d
576 if(key=="brushes_path")
577 {
578 value="";
579 if(!App::brushes_path.empty())
580 value=*(App::brushes_path.begin());
581 return true;
582 }
583 if(key=="custom_filename_prefix")
584 {
585 value=App::custom_filename_prefix;
586 return true;
587 }
588 if(key=="preferred_x_size")
589 {
590 value=strprintf("%i",App::preferred_x_size);
591 return true;
592 }
593 if(key=="preferred_y_size")
594 {
595 value=strprintf("%i",App::preferred_y_size);
596 return true;
597 }
598 if(key=="predefined_size")
599 {
600 value=strprintf("%s",App::predefined_size.c_str());
601 return true;
602 }
603 if(key=="preferred_fps")
604 {
605 value=strprintf("%f",App::preferred_fps);
606 return true;
607 }
608 if(key=="predefined_fps")
609 {
610 value=strprintf("%s",App::predefined_fps.c_str());
611 return true;
612 }
613 if(key=="sequence_separator")
614 {
615 value=App::sequence_separator;
616 return true;
617 }
618 if(key=="navigator_renderer")
619 {
620 value=App::navigator_renderer;
621 return true;
622 }
623 if(key=="workarea_renderer")
624 {
625 value=App::workarea_renderer;
626 return true;
627 }
628 if(key=="enable_mainwin_menubar")
629 {
630 value=strprintf("%i", (int)App::enable_mainwin_menubar);
631 return true;
632 }
633 if(key=="ui_handle_tooltip_flag")
634 {
635 value=strprintf("%il", (long)App::ui_handle_tooltip_flag);
636 return true;
637 }
638 }
639 catch(...)
640 {
641 synfig::warning("Preferences: Caught exception when attempting to get value.");
642 }
643 return synfigapp::Settings::get_value(key,value);
644 }
645
set_value(const synfig::String & key,const synfig::String & value)646 virtual bool set_value(const synfig::String& key,const synfig::String& value)
647 {
648 try
649 {
650 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
651 if(key=="gamma")
652 {
653 float r,g,b,blk;
654
655 strscanf(value,"%f %f %f %f",
656 &r,
657 &g,
658 &b,
659 &blk
660 );
661
662 App::gamma.set_all(r,g,b,blk);
663
664 return true;
665 }
666 if(key=="time_format")
667 {
668 int i(atoi(value.c_str()));
669 App::set_time_format(static_cast<synfig::Time::Format>(i));
670 return true;
671 }
672 if(key=="auto_recover_backup")
673 {
674 int i(atoi(value.c_str()));
675 App::auto_recover->set_enabled(i);
676 return true;
677 }
678 if(key=="auto_recover_backup_interval")
679 {
680 int i(atoi(value.c_str()));
681 App::auto_recover->set_timeout_ms(i);
682 return true;
683 }
684 if(key=="file_history.size")
685 {
686 int i(atoi(value.c_str()));
687 App::set_max_recent_files(i);
688 return true;
689 }
690 if(key=="use_colorspace_gamma")
691 {
692 int i(atoi(value.c_str()));
693 App::use_colorspace_gamma=i;
694 return true;
695 }
696 if(key=="distance_system")
697 {
698 App::distance_system=Distance::ident_system(value);;
699 return true;
700 }
701 #ifdef SINGLE_THREADED
702 if(key=="use_single_threaded")
703 {
704 int i(atoi(value.c_str()));
705 App::single_threaded=i;
706 return true;
707 }
708 #endif
709 if(key=="restrict_radius_ducks")
710 {
711 int i(atoi(value.c_str()));
712 App::restrict_radius_ducks=i;
713 return true;
714 }
715 if(key=="resize_imported_images")
716 {
717 int i(atoi(value.c_str()));
718 App::resize_imported_images=i;
719 return true;
720 }
721 if(key=="enable_experimental_features")
722 {
723 int i(atoi(value.c_str()));
724 App::enable_experimental_features=i;
725 return true;
726 }
727 if(key=="use_dark_theme")
728 {
729 int i(atoi(value.c_str()));
730 App::use_dark_theme=i;
731 return true;
732 }
733 if(key=="show_file_toolbar")
734 {
735 int i(atoi(value.c_str()));
736 App::show_file_toolbar=i;
737 return true;
738 }
739 //! "Keep brushes_path" preferences entry for backward compatibilty (15/12 - v1.0.3)
740 //! Now brush path(s) are hold by input preferences : brush.path_count & brush.path_%d
741 if(key=="brushes_path")
742 {
743 App::brushes_path.insert(value);
744 return true;
745 }
746 if(key=="custom_filename_prefix")
747 {
748 App::custom_filename_prefix=value;
749 return true;
750 }
751 if(key=="preferred_x_size")
752 {
753 int i(atoi(value.c_str()));
754 App::preferred_x_size=i;
755 return true;
756 }
757 if(key=="preferred_y_size")
758 {
759 int i(atoi(value.c_str()));
760 App::preferred_y_size=i;
761 return true;
762 }
763 if(key=="predefined_size")
764 {
765 App::predefined_size=value;
766 return true;
767 }
768 if(key=="preferred_fps")
769 {
770 float i(atof(value.c_str()));
771 App::preferred_fps=i;
772 return true;
773 }
774 if(key=="predefined_fps")
775 {
776 App::predefined_fps=value;
777 return true;
778 }
779 if(key=="sequence_separator")
780 {
781 App::sequence_separator=value;
782 return true;
783 }
784 if(key=="navigator_renderer")
785 {
786 App::navigator_renderer=value;
787 return true;
788 }
789 if(key=="workarea_renderer")
790 {
791 App::workarea_renderer=value;
792 return true;
793 }
794 if(key=="enable_mainwin_menubar")
795 {
796 int i(atoi(value.c_str()));
797 App::enable_mainwin_menubar = i;
798 return true;
799 }
800 if(key=="ui_handle_tooltip_flag")
801 {
802 long l(atol(value.c_str()));
803 App::ui_handle_tooltip_flag = l;
804 return true;
805 }
806 }
807 catch(...)
808 {
809 synfig::warning("Preferences: Caught exception when attempting to set value.");
810 }
811 return synfigapp::Settings::set_value(key,value);
812 }
813
get_key_list() const814 virtual KeyList get_key_list()const
815 {
816 KeyList ret(synfigapp::Settings::get_key_list());
817 ret.push_back("gamma");
818 ret.push_back("time_format");
819 ret.push_back("distance_system");
820 ret.push_back("file_history.size");
821 ret.push_back("use_colorspace_gamma");
822 #ifdef SINGLE_THREADED
823 ret.push_back("use_single_threaded");
824 #endif
825 ret.push_back("auto_recover_backup");
826 ret.push_back("auto_recover_backup_interval");
827 ret.push_back("restrict_radius_ducks");
828 ret.push_back("resize_imported_images");
829 ret.push_back("enable_experimental_features");
830 ret.push_back("use_dark_theme");
831 ret.push_back("show_file_toolbar");
832 ret.push_back("brushes_path");
833 ret.push_back("custom_filename_prefix");
834 ret.push_back("ui_language");
835 ret.push_back("preferred_x_size");
836 ret.push_back("preferred_y_size");
837 ret.push_back("predefined_size");
838 ret.push_back("preferred_fps");
839 ret.push_back("predefined_fps");
840 ret.push_back("sequence_separator");
841 ret.push_back("navigator_renderer");
842 ret.push_back("workarea_renderer");
843 ret.push_back("enable_mainwin_menubar");
844 ret.push_back("ui_handle_tooltip_flag");
845
846 return ret;
847 }
848 };
849
850 static ::Preferences _preferences;
851
852 void
init_ui_manager()853 init_ui_manager()
854 {
855 Glib::RefPtr<Gtk::ActionGroup> menus_action_group = Gtk::ActionGroup::create("menus");
856
857 Glib::RefPtr<Gtk::ActionGroup> actions_action_group = Gtk::ActionGroup::create("actions");
858
859 menus_action_group->add( Gtk::Action::create("menu-file", _("_File")));
860 menus_action_group->add( Gtk::Action::create("menu-open-recent", _("Open Recent")));
861
862 menus_action_group->add( Gtk::Action::create("menu-edit", _("_Edit")));
863
864 menus_action_group->add( Gtk::Action::create("menu-view", _("_View")));
865 menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Handles")));
866 menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")));
867 menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")));
868
869 menus_action_group->add( Gtk::Action::create("menu-canvas", _("_Canvas")));
870
871 menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")));
872 menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")));
873 menus_action_group->add( Gtk::Action::create("menu-toolbox", _("Toolbox")));
874 menus_action_group->add( Gtk::Action::create("menu-plugins", _("Plug-Ins")));
875
876 menus_action_group->add( Gtk::Action::create("menu-window", _("_Window")));
877 menus_action_group->add( Gtk::Action::create("menu-arrange", _("_Arrange")));
878 menus_action_group->add( Gtk::Action::create("menu-workspace", _("Work_space")));
879
880 menus_action_group->add( Gtk::Action::create("menu-help", _("_Help")) );
881
882 menus_action_group->add(Gtk::Action::create("menu-keyframe","Keyframe"));
883
884 // Add the synfigapp actions (layer panel toolbar items, etc...)
885 synfigapp::Action::Book::iterator iter;
886 for(iter=synfigapp::Action::book().begin();iter!=synfigapp::Action::book().end();++iter)
887 {
888 actions_action_group->add(Gtk::Action::create(
889 "action-"+iter->second.name,
890 get_action_stock_id(iter->second),
891 iter->second.local_name,iter->second.local_name
892 ));
893 }
894
895 // predefined actions to initial menu items, so that there is all menu items listing
896 // even there is no any canvas instance existed, for example, when app just opened.
897 // the menu items (action names) should be named consistently with those in canvasview.cpp and others.
898 #define DEFINE_ACTION(x,stock) { Glib::RefPtr<Gtk::Action> action( Gtk::Action::create(x, stock) ); actions_action_group->add(action); }
899
900 // actions in File menu
901 DEFINE_ACTION("new", Gtk::StockID("synfig-new"));
902 DEFINE_ACTION("open", Gtk::StockID("synfig-open"));
903 DEFINE_ACTION("save", Gtk::StockID("synfig-save"));
904 DEFINE_ACTION("save-as", Gtk::StockID("synfig-save_as"));
905 DEFINE_ACTION("save-all", Gtk::StockID("synfig-save_all"));
906 DEFINE_ACTION("revert", Gtk::Stock::REVERT_TO_SAVED);
907 DEFINE_ACTION("import", _("Import..."));
908 DEFINE_ACTION("render", _("Render..."));
909 DEFINE_ACTION("preview", _("Preview..."));
910 DEFINE_ACTION("close-document", _("Close Document"));
911 DEFINE_ACTION("quit", Gtk::Stock::QUIT);
912
913 // actions in Edit menu
914 DEFINE_ACTION("undo", Gtk::StockID("synfig-undo"));
915 DEFINE_ACTION("redo", Gtk::StockID("synfig-redo"));
916 DEFINE_ACTION("copy", Gtk::Stock::COPY);
917 DEFINE_ACTION("cut", Gtk::Stock::CUT);
918 DEFINE_ACTION("paste", Gtk::Stock::PASTE);
919 DEFINE_ACTION("select-all-ducks", _("Select All Handles"));
920 DEFINE_ACTION("unselect-all-ducks", _("Unselect All Handles"));
921 DEFINE_ACTION("select-all-layers", _("Select All Layers"));
922 DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
923 DEFINE_ACTION("input-devices", _("Input Devices..."));
924 DEFINE_ACTION("setup", _("Preferences..."));
925 DEFINE_ACTION("restore-default-settings", _("Restore Defaults"));
926
927 // actions in View menu
928 DEFINE_ACTION("toggle-mainwin-menubar", _("Menubar"));
929 DEFINE_ACTION("toggle-mainwin-toolbar", _("Toolbar"));
930
931 DEFINE_ACTION("mask-none-ducks", _("Toggle None/Last visible Handles"));
932 DEFINE_ACTION("mask-position-ducks", _("Show Position Handles"));
933 DEFINE_ACTION("mask-vertex-ducks", _("Show Vertex Handles"));
934 DEFINE_ACTION("mask-tangent-ducks", _("Show Tangent Handles"));
935 DEFINE_ACTION("mask-radius-ducks", _("Show Radius Handles"));
936 DEFINE_ACTION("mask-width-ducks", _("Show Width Handles"));
937 DEFINE_ACTION("mask-widthpoint-position-ducks", _("Show WidthPoints Position Handles"));
938 DEFINE_ACTION("mask-angle-ducks", _("Show Angle Handles"));
939 DEFINE_ACTION("mask-bone-setup-ducks", _("Show Bone Setup Handles"));
940 DEFINE_ACTION("mask-bone-recursive-ducks", _("Show Recursive Scale Bone Handles"));
941 DEFINE_ACTION("mask-bone-ducks", _("Next Bone Handles"));
942 DEFINE_ACTION("quality-00", _("Use Parametric Renderer"));
943 DEFINE_ACTION("quality-01", _("Use Quality Level 1"));
944 DEFINE_ACTION("quality-02", _("Use Quality Level 2"));
945 DEFINE_ACTION("quality-03", _("Use Quality Level 3"));
946 DEFINE_ACTION("quality-04", _("Use Quality Level 4"));
947 DEFINE_ACTION("quality-05", _("Use Quality Level 5"));
948 DEFINE_ACTION("quality-06", _("Use Quality Level 6"));
949 DEFINE_ACTION("quality-07", _("Use Quality Level 7"));
950 DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
951 DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
952 DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
953
954 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
955 DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
956
957 DEFINE_ACTION("play", _("Play"));
958 // the stop is not a normal stop but a pause. So use "Pause" in UI, including TEXT and
959 // icon. the internal code is still using stop.
960 DEFINE_ACTION("stop", _("Pause"));
961
962 DEFINE_ACTION("toggle-grid-show", _("Toggle Grid Show"));
963 DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
964 DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
965 DEFINE_ACTION("toggle-guide-snap", _("Toggle Guide Snap"));
966 DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
967 DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
968 DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
969 DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
970 DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
971 DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
972 DEFINE_ACTION("canvas-zoom-fit", Gtk::StockID("gtk-zoom-fit"));
973 DEFINE_ACTION("canvas-zoom-100", Gtk::StockID("gtk-zoom-100"));
974 DEFINE_ACTION("time-zoom-in", Gtk::StockID("gtk-zoom-in"));
975 DEFINE_ACTION("time-zoom-out", Gtk::StockID("gtk-zoom-out"));
976 DEFINE_ACTION("jump-next-keyframe", _("Seek to Next Keyframe"));
977 DEFINE_ACTION("jump-prev-keyframe", _("Seek to previous Keyframe"));
978 DEFINE_ACTION("seek-next-frame", _("Seek to Next Frame"));
979 DEFINE_ACTION("seek-prev-frame", _("Seek to Previous Frame"));
980 DEFINE_ACTION("seek-next-second", _("Seek Forward"));
981 DEFINE_ACTION("seek-prev-second", _("Seek Backward"));
982 DEFINE_ACTION("seek-begin", _("Seek to Begin"));
983 DEFINE_ACTION("seek-end", _("Seek to End"));
984
985 // actions in Canvas menu
986 DEFINE_ACTION("properties", _("Properties..."));
987 DEFINE_ACTION("options", _("Options..."));
988
989 // actions in Layer menu
990 DEFINE_ACTION("amount-inc", _("Increase Layer Amount"))
991 DEFINE_ACTION("amount-dec", _("Decrease Layer Amount"))
992
993 // actions in Window menu
994 DEFINE_ACTION("workspace-compositing", _("Compositing"));
995 DEFINE_ACTION("workspace-default", _("Default"));
996 DEFINE_ACTION("workspace-animating", _("Animating"));
997 DEFINE_ACTION("dialog-flipbook", _("Preview Dialog"));
998 DEFINE_ACTION("panel-toolbox","Toolbox");
999 DEFINE_ACTION("panel-tool_options",_("Tool Options"));
1000 DEFINE_ACTION("panel-history", "History");
1001 DEFINE_ACTION("panel-canvases",_("Canvas Browser"));
1002 DEFINE_ACTION("panel-keyframes",_("Keyframes"));
1003 DEFINE_ACTION("panel-layers",_("Layers"));
1004 DEFINE_ACTION("panel-params",_("Parameters"));
1005 DEFINE_ACTION("panel-meta_data",_("Canvas MetaData"));
1006 DEFINE_ACTION("panel-children",_("Library"));
1007 DEFINE_ACTION("panel-info",_("Info"));
1008 DEFINE_ACTION("panel-navigator",_("Navigator"));
1009 DEFINE_ACTION("panel-timetrack",_("Timetrack"));
1010 DEFINE_ACTION("panel-curves",_("Graphs"));
1011 DEFINE_ACTION("panel-groups",_("Sets"));
1012 DEFINE_ACTION("panel-pal_edit",_("Palette Editor"));
1013
1014 // actions in Help menu
1015 DEFINE_ACTION("help", Gtk::Stock::HELP);
1016 DEFINE_ACTION("help-tutorials", Gtk::Stock::HELP);
1017 DEFINE_ACTION("help-reference", Gtk::Stock::HELP);
1018 DEFINE_ACTION("help-faq", Gtk::Stock::HELP);
1019 DEFINE_ACTION("help-support", Gtk::Stock::HELP);
1020 DEFINE_ACTION("help-about", Gtk::StockID("synfig-about"));
1021
1022 // actions: Keyframe
1023 DEFINE_ACTION("keyframe-properties","Properties");
1024
1025
1026 //Layout the actions in the main menu (caret menu, right click on canvas menu) and toolbar:
1027 Glib::ustring ui_info_menu =
1028 " <menu action='menu-file'>"
1029 " <menuitem action='new' />"
1030 " <menuitem action='open' />"
1031 " <menu action='menu-open-recent' />"
1032 " <separator name='sep-file1'/>"
1033 " <menuitem action='save' />"
1034 " <menuitem action='save-as' />"
1035 " <menuitem action='save-all' />"
1036 " <menuitem action='revert' />"
1037 " <separator name='sep-file2'/>"
1038 " <menuitem action='import' />"
1039 " <separator name='sep-file4'/>"
1040 " <menuitem action='preview' />"
1041 " <menuitem action='render' />"
1042 " <separator name='sep-file5'/>"
1043 " <menuitem action='close-document' />"
1044 " <separator name='sep-file6'/>"
1045 " <menuitem action='quit' />"
1046 " </menu>"
1047 " <menu action='menu-edit'>"
1048 " <menuitem action='undo'/>"
1049 " <menuitem action='redo'/>"
1050 " <separator name='sep-edit1'/>"
1051 " <menuitem action='cut'/>"
1052 " <menuitem action='copy'/>"
1053 " <menuitem action='paste'/>"
1054 " <separator name='sep-edit2'/>"
1055 " <menuitem action='select-all-layers'/>"
1056 " <menuitem action='unselect-all-layers'/>"
1057 " <menuitem action='select-all-ducks'/>"
1058 " <menuitem action='unselect-all-ducks'/>"
1059 " <separator name='sep-edit3'/>"
1060 " <menuitem action='input-devices' />"
1061 " <menuitem action='setup' />"
1062 " </menu>"
1063 " <menu action='menu-view'>"
1064 " <menuitem action='toggle-mainwin-menubar' />"
1065 " <menuitem action='toggle-mainwin-toolbar' />"
1066 " <separator />"
1067 " <menu action='menu-duck-mask'>"
1068 " <menuitem action='mask-none-ducks' />"
1069 " <menuitem action='mask-position-ducks' />"
1070 " <menuitem action='mask-vertex-ducks' />"
1071 " <menuitem action='mask-tangent-ducks' />"
1072 " <menuitem action='mask-radius-ducks' />"
1073 " <menuitem action='mask-width-ducks' />"
1074 " <menuitem action='mask-widthpoint-position-ducks' />"
1075 " <menuitem action='mask-angle-ducks' />"
1076 " <menuitem action='mask-bone-setup-ducks' />"
1077 " <menuitem action='mask-bone-recursive-ducks' />"
1078 " <menuitem action='mask-bone-ducks' />"
1079 " </menu>"
1080 " <menu action='menu-preview-quality'>"
1081 " <menuitem action='quality-00' />"
1082 " <menuitem action='quality-01' />"
1083 " <menuitem action='quality-02' />"
1084 " <menuitem action='quality-03' />"
1085 " <menuitem action='quality-04' />"
1086 " <menuitem action='quality-05' />"
1087 " <menuitem action='quality-06' />"
1088 " <menuitem action='quality-07' />"
1089 " <menuitem action='quality-08' />"
1090 " <menuitem action='quality-09' />"
1091 " <menuitem action='quality-10' />"
1092 " </menu>"
1093 " <menu action='menu-lowres-pixel'>"
1094 " <menuitem action='decrease-low-res-pixel-size'/>"
1095 " <menuitem action='increase-low-res-pixel-size'/>"
1096 " <separator name='pixel-size-separator'/>"
1097 ;
1098
1099 for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
1100 ui_info_menu += strprintf(" <menuitem action='lowres-pixel-%d' />", *iter);
1101
1102 ui_info_menu +=
1103 " </menu>"
1104 " <separator name='sep-view1'/>"
1105 " <menuitem action='play'/>"
1106 " <menuitem action='stop'/>"
1107 " <separator name='sep-view2'/>"
1108 " <menuitem action='toggle-grid-show'/>"
1109 " <menuitem action='toggle-grid-snap'/>"
1110 " <menuitem action='toggle-guide-show'/>"
1111 " <menuitem action='toggle-guide-snap'/>"
1112 " <menuitem action='toggle-low-res'/>"
1113 " <menuitem action='toggle-onion-skin'/>"
1114 " <separator name='sep-view3'/>"
1115 " <menuitem action='canvas-zoom-in'/>"
1116 " <menuitem action='canvas-zoom-out'/>"
1117 " <menuitem action='canvas-zoom-fit'/>"
1118 " <menuitem action='canvas-zoom-100'/>"
1119 " <separator name='sep-view4'/>"
1120 " <menuitem action='time-zoom-in'/>"
1121 " <menuitem action='time-zoom-out'/>"
1122 " <separator name='sep-view5'/>"
1123 " <menuitem action='jump-prev-keyframe'/>"
1124 " <menuitem action='jump-next-keyframe'/>"
1125 " <menuitem action='seek-prev-frame'/>"
1126 " <menuitem action='seek-next-frame'/>"
1127 " <menuitem action='seek-prev-second'/>"
1128 " <menuitem action='seek-next-second'/>"
1129 " <menuitem action='seek-begin'/>"
1130 " <menuitem action='seek-end'/>"
1131 " </menu>"
1132 " <menu action='menu-canvas'>"
1133 " <menuitem action='properties'/>"
1134 " <menuitem action='options'/>"
1135 " </menu>"
1136 " <menu action='menu-toolbox'>"
1137 " </menu>"
1138 " <menu action='menu-layer'>"
1139 " <menu action='menu-layer-new'></menu>"
1140 " <menuitem action='amount-inc'/>"
1141 " <menuitem action='amount-dec'/>"
1142 " </menu>"
1143 " <menu action='menu-plugins'>"
1144 ;
1145
1146 list<synfigapp::PluginManager::plugin> plugin_list = studio::App::plugin_manager.get_list();
1147 for(list<synfigapp::PluginManager::plugin>::const_iterator p=plugin_list.begin();p!=plugin_list.end();++p) {
1148
1149 // TODO: (Plugins) Arrange menu items into groups
1150
1151 synfigapp::PluginManager::plugin plugin = *p;
1152
1153 DEFINE_ACTION(plugin.id, plugin.name);
1154 ui_info_menu += strprintf(" <menuitem action='%s'/>", plugin.id.c_str());
1155 }
1156
1157 ui_info_menu +=
1158 " </menu>"
1159 " <menu action='menu-window'>"
1160 " <menu action='menu-arrange'> </menu>"
1161 " <menu action='menu-workspace'>"
1162 " <menuitem action='workspace-default' />"
1163 " <menuitem action='workspace-compositing' />"
1164 " <menuitem action='workspace-animating' />"
1165 " </menu>"
1166 " <separator />"
1167 " <menuitem action='dialog-flipbook'/>"
1168 " <menuitem action='panel-toolbox' />"
1169 " <menuitem action='panel-tool_options' />"
1170 " <menuitem action='panel-history' />"
1171 " <menuitem action='panel-canvases' />"
1172 " <menuitem action='panel-keyframes' />"
1173 " <menuitem action='panel-layers' />"
1174 " <menuitem action='panel-params' />"
1175 " <menuitem action='panel-meta_data' />"
1176 " <menuitem action='panel-children' />"
1177 " <menuitem action='panel-info' />"
1178 " <menuitem action='panel-navigator' />"
1179 " <menuitem action='panel-timetrack' />"
1180 " <menuitem action='panel-curves' />"
1181 " <menuitem action='panel-groups' />"
1182 " <menuitem action='panel-pal_edit' />"
1183 " <separator />"
1184 // opened documents will be listed here below the above separator.
1185 " </menu>"
1186 " <menu action='menu-help'>"
1187 " <menuitem action='help'/>"
1188 " <separator name='sep-help1'/>"
1189 " <menuitem action='help-tutorials'/>"
1190 " <menuitem action='help-reference'/>"
1191 " <menuitem action='help-faq'/>"
1192 " <separator name='sep-help2'/>"
1193 " <menuitem action='help-support'/>"
1194 " <separator name='sep-help3'/>"
1195 " <menuitem action='help-about'/>"
1196 " </menu>";
1197
1198 Glib::ustring ui_info_main_tool =
1199 " <toolitem action='new'/>"
1200 " <toolitem action='open'/>"
1201 " <toolitem action='save'/>"
1202 " <toolitem action='save-as'/>"
1203 " <toolitem action='save-all'/>"
1204 " <separator />"
1205 " <toolitem action='undo'/>"
1206 " <toolitem action='redo'/>"
1207 " <separator />"
1208 " <toolitem action='render'/>"
1209 " <toolitem action='preview'/>";
1210
1211 Glib::ustring ui_info =
1212 "<ui>"
1213 " <popup name='menu-toolbox' action='menu-toolbox'>"
1214 " <menu action='menu-file'>"
1215 " </menu>"
1216 " </popup>"
1217 " <popup name='menu-main' action='menu-main'>" + ui_info_menu + "</popup>"
1218 " <menubar name='menubar-main' action='menubar-main'>" + ui_info_menu + "</menubar>"
1219 " <toolbar name='toolbar-main'>" + ui_info_main_tool + "</toolbar>"
1220 "</ui>";
1221
1222 #undef DEFINE_ACTION
1223
1224 try
1225 {
1226 actions_action_group->set_sensitive(false);
1227 App::ui_manager()->set_add_tearoffs(false);
1228 App::ui_manager()->insert_action_group(menus_action_group,1);
1229 App::ui_manager()->insert_action_group(actions_action_group,1);
1230 App::ui_manager()->add_ui_from_string(ui_info);
1231
1232 //App::ui_manager()->get_accel_group()->unlock();
1233 }
1234 catch(const Glib::Error& ex)
1235 {
1236 synfig::error("building menus and toolbars failed: " + ex.what());
1237 }
1238
1239 // Add default keyboard accelerators
1240 #define ACCEL(accel,path) \
1241 { \
1242 Gtk::AccelKey accel_key(accel,path); \
1243 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1244 }
1245
1246 #define ACCEL2(accel) \
1247 { \
1248 Gtk::AccelKey accel_key(accel); \
1249 Gtk::AccelMap::add_entry(accel_key.get_path(), accel_key.get_key(), accel_key.get_mod()); \
1250 }
1251
1252 // the toolbox
1253 ACCEL("<Mod1>a", "<Actions>/action_group_state_manager/state-normal" );
1254 ACCEL("<Mod1>v", "<Actions>/action_group_state_manager/state-smooth_move" );
1255 ACCEL("<Mod1>s", "<Actions>/action_group_state_manager/state-scale" );
1256 ACCEL("<Mod1>t", "<Actions>/action_group_state_manager/state-rotate" );
1257 ACCEL("<Mod1>m", "<Actions>/action_group_state_manager/state-mirror" );
1258 ACCEL("<Mod1>c", "<Actions>/action_group_state_manager/state-circle" );
1259 ACCEL("<Mod1>r", "<Actions>/action_group_state_manager/state-rectangle" );
1260 ACCEL("<Mod1>q", "<Actions>/action_group_state_manager/state-star" );
1261 ACCEL("<Mod1>g", "<Actions>/action_group_state_manager/state-gradient" );
1262 ACCEL("<Mod1>p", "<Actions>/action_group_state_manager/state-polygon" );
1263 ACCEL("<Mod1>b", "<Actions>/action_group_state_manager/state-bline" );
1264 ACCEL("<Mod1>x", "<Actions>/action_group_state_manager/state-text" );
1265 ACCEL("<Mod1>f", "<Actions>/action_group_state_manager/state-fill" );
1266 ACCEL("<Mod1>e", "<Actions>/action_group_state_manager/state-eyedrop" );
1267 ACCEL("<Mod1>z", "<Actions>/action_group_state_manager/state-zoom" );
1268 ACCEL("<Mod1>d", "<Actions>/action_group_state_manager/state-draw" );
1269 ACCEL("<Mod1>k", "<Actions>/action_group_state_manager/state-sketch" );
1270 ACCEL("<Mod1>w", "<Actions>/action_group_state_manager/state-width" );
1271
1272 // everything else
1273 ACCEL("<Control>a", "<Actions>/canvasview/select-all-ducks" );
1274 ACCEL("<Control>d", "<Actions>/canvasview/unselect-all-ducks" );
1275 ACCEL("<Control><Shift>a", "<Actions>/canvasview/select-all-layers" );
1276 ACCEL("<Control><Shift>d", "<Actions>/canvasview/unselect-all-layers" );
1277 ACCEL("F9", "<Actions>/canvasview/render" );
1278 ACCEL("F11", "<Actions>/canvasview/preview" );
1279 ACCEL("F8", "<Actions>/canvasview/properties" );
1280 ACCEL("F12", "<Actions>/canvasview/options" );
1281 ACCEL("<control>i", "<Actions>/canvasview/import" );
1282 ACCEL2(Gtk::AccelKey(GDK_KEY_Escape,static_cast<Gdk::ModifierType>(0), "<Actions>/canvasview/stop" ));
1283 ACCEL("<Control>g", "<Actions>/canvasview/toggle-grid-show" );
1284 ACCEL("<Control>l", "<Actions>/canvasview/toggle-grid-snap" );
1285 ACCEL("<Control>n", "<Actions>/mainwindow/new" );
1286 ACCEL("<Control>o", "<Actions>/mainwindow/open" );
1287 ACCEL("<Control>s", "<Actions>/canvasview/save" );
1288 ACCEL("<Control><Shift>s", "<Actions>/canvasview/save-as" );
1289 ACCEL2(Gtk::AccelKey('`',Gdk::CONTROL_MASK, "<Actions>/canvasview/toggle-low-res" ));
1290 ACCEL("<Mod1>0", "<Actions>/canvasview/mask-none-ducks" );
1291 ACCEL("<Mod1>1", "<Actions>/canvasview/mask-position-ducks" );
1292 ACCEL("<Mod1>2", "<Actions>/canvasview/mask-vertex-ducks" );
1293 ACCEL("<Mod1>3", "<Actions>/canvasview/mask-tangent-ducks" );
1294 ACCEL("<Mod1>4", "<Actions>/canvasview/mask-radius-ducks" );
1295 ACCEL("<Mod1>5", "<Actions>/canvasview/mask-width-ducks" );
1296 ACCEL("<Mod1>6", "<Actions>/canvasview/mask-angle-ducks" );
1297 ACCEL("<Mod1>7", "<Actions>/canvasview/mask-bone-setup-ducks" );
1298 ACCEL("<Mod1>8", "<Actions>/canvasview/mask-bone-recursive-ducks" );
1299 ACCEL("<Mod1>9", "<Actions>/canvasview/mask-bone-ducks" );
1300 ACCEL("<Mod1>5", "<Actions>/canvasview/mask-widthpoint-position-ducks" );
1301 ACCEL2(Gtk::AccelKey(GDK_KEY_Page_Up,Gdk::SHIFT_MASK, "<Actions>/action_group_layer_action_manager/action-LayerRaise" ));
1302 ACCEL2(Gtk::AccelKey(GDK_KEY_Page_Down,Gdk::SHIFT_MASK, "<Actions>/action_group_layer_action_manager/action-LayerLower" ));
1303 ACCEL("<Control>1", "<Actions>/canvasview/quality-01" );
1304 ACCEL("<Control>2", "<Actions>/canvasview/quality-02" );
1305 ACCEL("<Control>3", "<Actions>/canvasview/quality-03" );
1306 ACCEL("<Control>4", "<Actions>/canvasview/quality-04" );
1307 ACCEL("<Control>5", "<Actions>/canvasview/quality-05" );
1308 ACCEL("<Control>6", "<Actions>/canvasview/quality-06" );
1309 ACCEL("<Control>7", "<Actions>/canvasview/quality-07" );
1310 ACCEL("<Control>8", "<Actions>/canvasview/quality-08" );
1311 ACCEL("<Control>9", "<Actions>/canvasview/quality-09" );
1312 ACCEL("<Control>0", "<Actions>/canvasview/quality-10" );
1313 ACCEL("<Control>z", "<Actions>/action_group_dock_history/undo" );
1314 ACCEL("<Control>r", "<Actions>/action_group_dock_history/redo" );
1315 ACCEL2(Gtk::AccelKey(GDK_KEY_Delete,Gdk::CONTROL_MASK, "<Actions>/action_group_layer_action_manager/action-LayerRemove" ));
1316 ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK, "<Actions>/canvasview/decrease-low-res-pixel-size" ));
1317 ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK, "<Actions>/canvasview/increase-low-res-pixel-size" ));
1318 ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK, "<Actions>/action_group_layer_action_manager/amount-dec" ));
1319 ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK, "<Actions>/action_group_layer_action_manager/amount-inc" ));
1320 ACCEL2(Gtk::AccelKey(']',Gdk::CONTROL_MASK, "<Actions>/canvasview/jump-next-keyframe" ));
1321 ACCEL2(Gtk::AccelKey('[',Gdk::CONTROL_MASK, "<Actions>/canvasview/jump-prev-keyframe" ));
1322 ACCEL2(Gtk::AccelKey('=',Gdk::CONTROL_MASK, "<Actions>/canvasview/canvas-zoom-in" ));
1323 ACCEL2(Gtk::AccelKey('-',Gdk::CONTROL_MASK, "<Actions>/canvasview/canvas-zoom-out" ));
1324 ACCEL2(Gtk::AccelKey('+',Gdk::CONTROL_MASK, "<Actions>/canvasview/time-zoom-in" ));
1325 ACCEL2(Gtk::AccelKey('_',Gdk::CONTROL_MASK, "<Actions>/canvasview/time-zoom-out" ));
1326 ACCEL2(Gtk::AccelKey('.',Gdk::CONTROL_MASK, "<Actions>/canvasview/seek-next-frame" ));
1327 ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK, "<Actions>/canvasview/seek-prev-frame" ));
1328 ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK, "<Actions>/canvasview/seek-next-second" ));
1329 ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK, "<Actions>/canvasview/seek-prev-second" ));
1330 ACCEL("<Mod1>o", "<Actions>/canvasview/toggle-onion-skin" );
1331 ACCEL("<Control><Shift>z", "<Actions>/canvasview/canvas-zoom-fit" );
1332 ACCEL("<Control>p", "<Actions>/canvasview/play" );
1333 ACCEL("Home", "<Actions>/canvasview/seek-begin" );
1334 ACCEL("End", "<Actions>/canvasview/seek-end" );
1335
1336
1337 #undef ACCEL
1338 #undef ACCEL2
1339 }
1340
1341 #ifdef _WIN32
1342 #define mkdir(x,y) mkdir(x)
1343 #endif
1344
1345 /* === M E T H O D S ======================================================= */
1346
App(const synfig::String & basepath,int * argc,char *** argv)1347 App::App(const synfig::String& basepath, int *argc, char ***argv):
1348 Gtk::Main(argc,argv),
1349 IconController(basepath)
1350 {
1351
1352 app_base_path_=etl::dirname(basepath);
1353
1354 // Set ui language
1355 load_language_settings();
1356 if (ui_language != "os_LANG")
1357 {
1358 Glib::setenv ("LANGUAGE", App::ui_language.c_str(), 1);
1359 }
1360
1361 std::string path_to_icons;
1362 #ifdef _WIN32
1363 path_to_icons=basepath+ETL_DIRECTORY_SEPARATOR+".."+ETL_DIRECTORY_SEPARATOR+IMAGE_DIR;
1364 #else
1365 path_to_icons=IMAGE_DIR;
1366 #endif
1367 char* synfig_root=getenv("SYNFIG_ROOT");
1368 if(synfig_root) {
1369 path_to_icons=synfig_root;
1370 path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1371 path_to_icons+="share";
1372 path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1373 path_to_icons+="pixmaps";
1374 path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1375 path_to_icons+="synfigstudio";
1376 }
1377 path_to_icons+=ETL_DIRECTORY_SEPARATOR;
1378 init_icons(path_to_icons);
1379
1380 ui_interface_=new GlobalUIInterface();
1381
1382 // don't call thread_init() if threads are already initialized
1383 // on some machines bonobo_init() initialized threads before we get here
1384 if (!g_thread_supported())
1385 Glib::thread_init();
1386
1387 distance_system=Distance::SYSTEM_PIXELS;
1388
1389 if(mkdir(synfigapp::Main::get_user_app_directory().c_str(),ACCESSPERMS)<0)
1390 {
1391 if(errno!=EEXIST)
1392 synfig::error("UNABLE TO CREATE \"%s\"",synfigapp::Main::get_user_app_directory().c_str());
1393 }
1394 else
1395 {
1396 synfig::info("Created directory \"%s\"",synfigapp::Main::get_user_app_directory().c_str());
1397 }
1398
1399
1400 ipc=new IPC();
1401
1402 if(!SYNFIG_CHECK_VERSION())
1403 {
1404 cerr<<"FATAL: Synfig Version Mismatch"<<endl;
1405 dialog_message_1b(
1406 "ERROR",
1407 _("Synfig version mismatched!"),
1408 _("This copy of Synfig Studio was compiled against a "
1409 "different version of libsynfig than what is currently "
1410 "installed. Synfig Studio will now abort. Try downloading "
1411 "the latest version from the Synfig website at "
1412 "http://www.synfig.org/cms/en/download/"),
1413 _("Close"));
1414
1415 throw 40;
1416 }
1417 Glib::set_application_name(_("Synfig Studio"));
1418
1419 Splash splash_screen;
1420 splash_screen.show();
1421
1422 shutdown_in_progress=false;
1423 SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
1424 SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
1425
1426 // Initialize the Synfig library
1427 try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(basepath,&synfig_init_cb)); }
1428 catch(std::runtime_error &x)
1429 {
1430 get_ui_interface()->error(strprintf("%s\n\n%s", _("Failed to initialize synfig!"), x.what()));
1431 throw;
1432 }
1433 catch(...)
1434 {
1435 get_ui_interface()->error(_("Failed to initialize synfig!"));
1436 throw;
1437 }
1438
1439
1440 // add the preferences to the settings
1441 synfigapp::Main::settings().add_domain(&_preferences,"pref");
1442
1443 try
1444 {
1445 // Try to load settings early to get access to some important
1446 // values, like "enable_experimental_features".
1447 studio_init_cb.task(_("Loading Basic Settings..."));
1448
1449 load_settings("pref.use_dark_theme");
1450 App::apply_gtk_settings();
1451
1452 load_settings("pref.show_file_toolbar");
1453
1454 // Set experimental features
1455 load_settings("pref.enable_experimental_features");
1456
1457 // Set main window menu and toolbar
1458 load_settings("pref.enable_mainwin_menubar");
1459
1460 studio_init_cb.task(_("Loading Plugins..."));
1461
1462 std::string pluginsprefix;
1463
1464 // system plugins path
1465 #ifdef _WIN32
1466 pluginsprefix=App::get_base_path()+ETL_DIRECTORY_SEPARATOR+PLUGIN_DIR;
1467 #else
1468 pluginsprefix=PLUGIN_DIR;
1469 #endif
1470 char* synfig_root=getenv("SYNFIG_ROOT");
1471 if(synfig_root) {
1472 pluginsprefix=std::string(synfig_root)
1473 +ETL_DIRECTORY_SEPARATOR+"share"
1474 +ETL_DIRECTORY_SEPARATOR+"synfig"
1475 +ETL_DIRECTORY_SEPARATOR+"plugins";
1476 }
1477 plugin_manager.load_dir(pluginsprefix);
1478
1479 // user plugins path
1480 pluginsprefix=Glib::build_filename(synfigapp::Main::get_user_app_directory(),"plugins");
1481 plugin_manager.load_dir(pluginsprefix);
1482
1483 studio_init_cb.task(_("Init UI Manager..."));
1484 App::ui_manager_=studio::UIManager::create();
1485 init_ui_manager();
1486
1487 studio_init_cb.task(_("Init Dock Manager..."));
1488 dock_manager=new studio::DockManager();
1489
1490 studio_init_cb.task(_("Init State Manager..."));
1491 state_manager=new StateManager();
1492
1493 studio_init_cb.task(_("Init Main Window..."));
1494 main_window=new studio::MainWindow();
1495 main_window->add_accel_group(App::ui_manager_->get_accel_group());
1496
1497 studio_init_cb.task(_("Init Toolbox..."));
1498 dock_toolbox=new studio::Dock_Toolbox();
1499 dock_manager->register_dockable(*dock_toolbox);
1500
1501 studio_init_cb.task(_("Init About Dialog..."));
1502 about=new studio::About();
1503
1504 studio_init_cb.task(_("Init Tool Options..."));
1505 dialog_tool_options=new studio::Dialog_ToolOptions();
1506 dock_manager->register_dockable(*dialog_tool_options);
1507
1508 studio_init_cb.task(_("Init History..."));
1509 dock_history=new studio::Dock_History();
1510 dock_manager->register_dockable(*dock_history);
1511
1512 studio_init_cb.task(_("Init Canvases..."));
1513 dock_canvases=new studio::Dock_Canvases();
1514 dock_manager->register_dockable(*dock_canvases);
1515
1516 studio_init_cb.task(_("Init Keyframes..."));
1517 dock_keyframes=new studio::Dock_Keyframes();
1518 dock_manager->register_dockable(*dock_keyframes);
1519
1520 //! Must be done before Dock_Timetrack and Dock_Curves :
1521 //! both are connected to a studio::LayerTree::param_tree_view_'s signal, and
1522 //! studio::LayerTree is created from Dock_Layers::init_canvas_view_vfunc
1523 studio_init_cb.task(_("Init Layers..."));
1524 dock_layers=new studio::Dock_Layers();
1525 dock_manager->register_dockable(*dock_layers);
1526
1527 studio_init_cb.task(_("Init Parameters..."));
1528 dock_params=new studio::Dock_Params();
1529 dock_manager->register_dockable(*dock_params);
1530
1531 studio_init_cb.task(_("Init MetaData..."));
1532 dock_meta_data=new studio::Dock_MetaData();
1533 dock_manager->register_dockable(*dock_meta_data);
1534
1535 studio_init_cb.task(_("Init Library..."));
1536 dock_children=new studio::Dock_Children();
1537 dock_manager->register_dockable(*dock_children);
1538
1539 studio_init_cb.task(_("Init Info..."));
1540 dock_info = new studio::Dock_Info();
1541 dock_manager->register_dockable(*dock_info);
1542
1543 studio_init_cb.task(_("Init Navigator..."));
1544 dock_navigator = new studio::Dock_Navigator();
1545 dock_manager->register_dockable(*dock_navigator);
1546
1547 studio_init_cb.task(_("Init Timetrack..."));
1548 dock_timetrack = new studio::Dock_Timetrack();
1549 dock_manager->register_dockable(*dock_timetrack);
1550
1551 studio_init_cb.task(_("Init Curve Editor..."));
1552 dock_curves = new studio::Dock_Curves();
1553 dock_manager->register_dockable(*dock_curves);
1554
1555 studio_init_cb.task(_("Init Layer Sets..."));
1556 dock_layer_groups = new studio::Dock_LayerGroups();
1557 dock_manager->register_dockable(*dock_layer_groups);
1558
1559
1560 studio_init_cb.task(_("Init Color Dialog..."));
1561 dialog_color=new studio::Dialog_Color();
1562
1563 studio_init_cb.task(_("Init Gradient Dialog..."));
1564 dialog_gradient=new studio::Dialog_Gradient();
1565
1566 studio_init_cb.task(_("Init DeviceTracker..."));
1567 device_tracker=new studio::DeviceTracker();
1568
1569 //Init Tools...was here
1570
1571 studio_init_cb.task(_("Init ModPalette..."));
1572 module_list_.push_back(new ModPalette()); module_list_.back()->start();
1573
1574 studio_init_cb.task(_("Init Setup Dialog..."));
1575 dialog_setup=new studio::Dialog_Setup(*App::main_window);
1576
1577 studio_init_cb.task(_("Init Input Dialog..."));
1578 dialog_input=new studio::Dialog_Input(*App::main_window);
1579 dialog_input->signal_apply().connect( sigc::mem_fun( *device_tracker, &DeviceTracker::save_preferences) );
1580
1581 studio_init_cb.task(_("Init auto recovery..."));
1582 auto_recover=new AutoRecover();
1583
1584 studio_init_cb.amount_complete(9250,10000);
1585 studio_init_cb.task(_("Loading Settings..."));
1586 load_accel_map();
1587 if (!load_settings())
1588 {
1589 gamma.set_gamma(1.0/2.2);
1590 set_workspace_default();
1591 }
1592 load_file_window_size();
1593
1594 // Init Tools must be done after load_accel_map() : accelerators keys
1595 // are displayed in toolbox labels
1596 studio_init_cb.task(_("Init Tools..."));
1597 /* editing tools */
1598 state_manager->add_state(&state_normal);
1599 state_manager->add_state(&state_smooth_move);
1600 state_manager->add_state(&state_scale);
1601 state_manager->add_state(&state_rotate);
1602 state_manager->add_state(&state_mirror);
1603
1604 /* geometry */
1605 state_manager->add_state(&state_circle);
1606 state_manager->add_state(&state_rectangle);
1607 state_manager->add_state(&state_star);
1608 if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
1609 state_manager->add_state(&state_gradient);
1610
1611 /* bline tools */
1612 state_manager->add_state(&state_bline);
1613 if(!getenv("SYNFIG_DISABLE_DRAW" )) state_manager->add_state(&state_draw ); // Enabled for now. Let's see whether they're good enough yet.
1614 state_manager->add_state(&state_lasso); // Enabled for now. Let's see whether they're good enough yet.
1615 if(!getenv("SYNFIG_DISABLE_WIDTH" )) state_manager->add_state(&state_width); // Enabled since 0.61.09
1616 state_manager->add_state(&state_fill);
1617 state_manager->add_state(&state_eyedrop);
1618
1619 /* other */
1620 state_manager->add_state(&state_text);
1621 if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
1622 if(!getenv("SYNFIG_DISABLE_BRUSH" )) state_manager->add_state(&state_brush);
1623 state_manager->add_state(&state_zoom);
1624
1625
1626 device_tracker->load_preferences();
1627 // If the default bline width is modified before focus a canvas
1628 // window, the Distance widget doesn't understand the given value
1629 // and produces this message:
1630 // Distance::ident_system(): Unknown distance system ".00pt"
1631 // setting the default bline width to 1 unit.
1632 // This line fixes that.
1633 synfigapp::Main::set_bline_width(synfigapp::Main::get_selected_input_device()->get_bline_width());
1634
1635 studio_init_cb.task(_("Checking auto-recover..."));
1636
1637 studio_init_cb.amount_complete(9900,10000);
1638
1639 bool opened_any = false;
1640 if (!getenv("SYNFIG_DISABLE_AUTO_RECOVERY") && auto_recover->recovery_needed())
1641 {
1642 splash_screen.hide();
1643 if (get_ui_interface()->confirmation(
1644 _("Auto recovery file(s) found. Do you want to recover unsaved changes?"),
1645 _("Synfig Studio seems to have crashed before you could save all your files."),
1646 _("Ignore"),
1647 _("Recover")
1648 ) == synfigapp::UIInterface::RESPONSE_OK)
1649 {
1650 int number_recovered;
1651 if(!auto_recover->recover(number_recovered))
1652 if (number_recovered)
1653 get_ui_interface()->error(_("Unable to fully recover from previous crash"));
1654 else
1655 get_ui_interface()->error(_("Unable to recover from previous crash"));
1656 else
1657 dialog_message_1b(
1658 "WARNING",
1659 _("It would be a good idea to review and save recovered files now."),
1660 _("Synfig Studio has attempted to recover from a previous crash. "
1661 "The files just recovered are NOT YET SAVED."),
1662 _("Thanks"));
1663
1664 if (number_recovered)
1665 opened_any = true;
1666 }
1667 else
1668 {
1669 auto_recover->clear_backups();
1670 }
1671 splash_screen.show();
1672 }
1673
1674 // Look for any files given on the command line,
1675 // and load them if found.
1676 for(;*argc>=1;(*argc)--)
1677 if((*argv)[*argc] && (*argv)[*argc][0]!='-')
1678 {
1679 studio_init_cb.task(_("Loading files..."));
1680 splash_screen.hide();
1681 open((*argv)[*argc]);
1682 opened_any = true;
1683 splash_screen.show();
1684 }
1685
1686 // if no file was specified to be opened, create a new document to help new users get started more easily
1687 if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
1688 new_instance();
1689
1690 studio_init_cb.task(_("Done."));
1691 studio_init_cb.amount_complete(10000,10000);
1692
1693 // To avoid problems with some window managers and gtk >= 2.18
1694 // we should show dock dialogs after the settings load.
1695 // If dock dialogs are shown before the settings are loaded,
1696 // the windows manager can act over it.
1697 // See discussions here:
1698 // * http://synfig.org/forums/viewtopic.php?f=1&t=1131&st=0&sk=t&sd=a&start=30
1699 // * http://synfig.org/forums/viewtopic.php?f=15&t=1062
1700 dock_manager->show_all_dock_dialogs();
1701
1702 main_window->present();
1703 dock_toolbox->present();
1704
1705 splash_screen.hide();
1706
1707 String message;
1708 String details;
1709 /*
1710 if (App::enable_experimental_features) {
1711 message = _("Following experimental features are enabled: ");
1712 message += ("Skeleton Layer");
1713 detials = _("The experimental features are NOT intended for production use. "
1714 "It is quite posiible their functionality will change in the "
1715 "future versions, which can break compatibility for your "
1716 "files. Use for testing purposes only. You can disable "
1717 "experimental features on the \"Misc\" tab of Setup dialog.");
1718 }
1719 */
1720 #ifdef _WIN32
1721 if (message!=""){
1722 message = _("There is a bug, which can cause computer to hang/freeze when "
1723 "resizing the canvas window.");
1724 details = _("If you got affected by this issue, consider pressing ALT+TAB "
1725 "to unfreeze your system and get it back to the working "
1726 "state. Please accept our apologies for inconvenience, we "
1727 "hope to get this issue resolved in the future versions.");
1728 }
1729 #endif
1730 if (message!="")
1731 dialog_message_1b("WARNING",
1732 message,
1733 details,
1734 _("Got it"));
1735 }
1736 catch(String &x)
1737 {
1738 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
1739 }
1740 catch(...)
1741 {
1742 get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
1743 }
1744 }
1745
get_state_manager()1746 StateManager* App::get_state_manager() { return state_manager; }
1747
~App()1748 App::~App()
1749 {
1750 shutdown_in_progress=true;
1751
1752 save_settings();
1753
1754 synfigapp::Main::settings().remove_domain("pref");
1755
1756 selected_instance=0;
1757
1758 // Unload all of the modules
1759 for(;!module_list_.empty();module_list_.pop_back())
1760 ;
1761
1762 delete state_manager;
1763
1764 delete ipc;
1765
1766 delete auto_recover;
1767
1768 delete about;
1769
1770 main_window->hide();
1771
1772 delete main_window;
1773
1774 delete dialog_setup;
1775
1776 delete dialog_gradient;
1777
1778 delete dialog_color;
1779
1780 delete dialog_input;
1781
1782 delete dock_manager;
1783
1784 instance_list.clear();
1785 }
1786
1787 synfig::String
get_config_file(const synfig::String & file)1788 App::get_config_file(const synfig::String& file)
1789 {
1790 return Glib::build_filename(synfigapp::Main::get_user_app_directory(),file);
1791 }
1792
1793 void
add_recent_file(const etl::handle<Instance> instance)1794 App::add_recent_file(const etl::handle<Instance> instance)
1795 {
1796 add_recent_file(absolute_path(instance->get_file_name()));
1797 }
1798
1799 void
add_recent_file(const std::string & file_name)1800 App::add_recent_file(const std::string &file_name)
1801 {
1802 std::string filename(file_name);
1803
1804 assert(!filename.empty());
1805
1806 if(filename.empty())
1807 return;
1808
1809 // Toss out any "hidden" files
1810 if(basename(filename)[0]=='.')
1811 return;
1812
1813 // If we aren't an absolute path, turn ourselves into one
1814 if(!is_absolute_path(filename))
1815 filename=absolute_path(filename);
1816
1817 list<string>::iterator iter;
1818 // Check to see if the file is already on the list.
1819 // If it is, then remove it from the list
1820 for(iter=recent_files.begin();iter!=recent_files.end();iter++)
1821 if(*iter==filename)
1822 {
1823 recent_files.erase(iter);
1824 break;
1825 }
1826
1827
1828 // Push the filename to the front of the list
1829 recent_files.push_front(filename);
1830
1831 // Clean out the files at the end of the list.
1832 while(recent_files.size()>(unsigned)get_max_recent_files())
1833 {
1834 recent_files.pop_back();
1835 }
1836
1837 signal_recent_files_changed_();
1838
1839 return;
1840 }
1841
1842 static Time::Format _App_time_format(Time::FORMAT_FRAMES);
1843
jack_is_locked()1844 bool App::jack_is_locked()
1845 {
1846 return jack_locks_ > 0;
1847 }
1848
jack_lock()1849 void App::jack_lock()
1850 {
1851 ++jack_locks_;
1852 if (jack_locks_ == 1)
1853 {
1854 // lock jack in instances
1855 for(std::list< etl::handle<Instance> >::const_iterator i = instance_list.begin(); i != instance_list.end(); ++i)
1856 {
1857 const Instance::CanvasViewList &views = (*i)->canvas_view_list();
1858 for(Instance::CanvasViewList::const_iterator j = views.begin(); j != views.end(); ++j)
1859 (*j)->jack_lock();
1860 }
1861 }
1862 }
1863
jack_unlock()1864 void App::jack_unlock()
1865 {
1866 --jack_locks_;
1867 assert(jack_locks_ >= 0);
1868 if (jack_locks_ == 0)
1869 {
1870 // unlock jack in instances
1871 for(std::list< etl::handle<Instance> >::const_iterator i = instance_list.begin(); i != instance_list.end(); ++i)
1872 {
1873 const Instance::CanvasViewList &views = (*i)->canvas_view_list();
1874 for(Instance::CanvasViewList::const_iterator j = views.begin(); j != views.end(); ++j)
1875 (*j)->jack_unlock();
1876 }
1877 }
1878 }
1879
1880
1881 Time::Format
get_time_format()1882 App::get_time_format()
1883 {
1884 return _App_time_format;
1885 }
1886
1887 void
set_time_format(synfig::Time::Format x)1888 App::set_time_format(synfig::Time::Format x)
1889 {
1890 _App_time_format=x;
1891 }
1892
1893
1894 void
save_settings()1895 App::save_settings()
1896 {
1897 try
1898 {
1899 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1900 {
1901 std::string filename=get_config_file("accelrc");
1902 Gtk::AccelMap::save(filename);
1903 }
1904 {
1905 std::string filename=get_config_file("language");
1906
1907 std::ofstream file(filename.c_str());
1908
1909 if(!file)
1910 {
1911 synfig::warning("Unable to save %s",filename.c_str());
1912 } else {
1913 file<<App::ui_language.c_str()<<endl;
1914 }
1915 }
1916 do{
1917 std::string filename=get_config_file("recentfiles");
1918
1919 std::ofstream file(filename.c_str());
1920
1921 if(!file)
1922 {
1923 synfig::warning("Unable to save %s",filename.c_str());
1924 break;
1925 }
1926
1927 list<string>::reverse_iterator iter;
1928
1929 for(iter=recent_files.rbegin();iter!=recent_files.rend();iter++)
1930 file<<(*iter).c_str()<<endl;
1931 }while(0);
1932 std::string filename=get_config_file("settings-1.0");
1933 synfigapp::Main::settings().save_to_file(filename);
1934
1935 }
1936 catch(...)
1937 {
1938 synfig::warning("Caught exception when attempting to save settings.");
1939 }
1940 }
1941
1942 bool
load_settings(const synfig::String & key_filter)1943 App::load_settings(const synfig::String& key_filter)
1944 {
1945 bool ret=false;
1946 try
1947 {
1948 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1949 std::string filename=get_config_file("settings-1.0");
1950 ret=synfigapp::Main::settings().load_from_file(filename, key_filter);
1951 }
1952 catch(...)
1953 {
1954 synfig::warning("Caught exception when attempting to load settings.");
1955 }
1956 return ret;
1957 }
1958
1959 void
load_accel_map()1960 App::load_accel_map()
1961 {
1962 try
1963 {
1964 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1965 {
1966 std::string filename=get_config_file("accelrc");
1967 Gtk::AccelMap::load(filename);
1968 }
1969 }
1970 catch(...)
1971 {
1972 synfig::warning("Caught exception when attempting to load accel map settings.");
1973 }
1974 }
1975
1976 void
load_file_window_size()1977 App::load_file_window_size()
1978 {
1979 try
1980 {
1981 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
1982 {
1983 std::string filename=get_config_file("recentfiles");
1984 std::ifstream file(filename.c_str());
1985
1986 while(file)
1987 {
1988 std::string recent_file;
1989 std::string recent_file_window_size;
1990 getline(file,recent_file);
1991 if(!recent_file.empty() && FileSystemNative::instance()->is_file(recent_file))
1992 add_recent_file(recent_file);
1993 }
1994 }
1995
1996 }
1997 catch(...)
1998 {
1999 synfig::warning("Caught exception when attempting to load window settings.");
2000 }
2001 }
2002
2003 void
load_language_settings()2004 App::load_language_settings()
2005 {
2006 try
2007 {
2008 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
2009 {
2010 std::string filename=get_config_file("language");
2011 std::ifstream file(filename.c_str());
2012
2013 while(file)
2014 {
2015 std::string language;
2016 getline(file,language);
2017 if(!language.empty())
2018 App::ui_language=language;
2019 }
2020 }
2021
2022 }
2023 catch(...)
2024 {
2025 synfig::warning("Caught exception when attempting to loading language settings.");
2026 }
2027 }
2028
2029 void
set_workspace_default()2030 App::set_workspace_default()
2031 {
2032 Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
2033 Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
2034 Gdk::Rectangle rect;
2035 // A proper way to obtain the primary monitor is to use the
2036 // Gdk::Screen::get_primary_monitor () const member. But as it
2037 // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2038 // primary one.
2039 screen->get_monitor_geometry(0,rect);
2040 float dx = (float)rect.get_x();
2041 float dy = (float)rect.get_y();
2042 float sx = (float)rect.get_width();
2043 float sy = (float)rect.get_height();
2044
2045 std::string tpl =
2046 "[mainwindow|%0X|%0Y|%100x|%90y|"
2047 "[hor|%75x"
2048 "|[vert|%70y"
2049 "|[hor|%10x"
2050 "|[book|toolbox]"
2051 "|[mainnotebook]"
2052 "]"
2053 "|[hor|%25x"
2054 "|[book|params|keyframes]"
2055 "|[book|timetrack|curves|children|meta_data]"
2056 "]"
2057 "]"
2058 "|[vert|%20y"
2059 "|[book|canvases|pal_edit|navigator|info]"
2060 "|[vert|%25y"
2061 "|[book|tool_options|history]"
2062 "|[book|layers|groups]"
2063 "]"
2064 "]"
2065 "]"
2066 "]";
2067
2068 std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2069 dock_manager->load_layout_from_string(layout);
2070 dock_manager->show_all_dock_dialogs();
2071 }
2072
2073 void
set_workspace_compositing()2074 App::set_workspace_compositing()
2075 {
2076 Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
2077 Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
2078 Gdk::Rectangle rect;
2079 // A proper way to obtain the primary monitor is to use the
2080 // Gdk::Screen::get_primary_monitor () const member. But as it
2081 // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2082 // primary one.
2083 screen->get_monitor_geometry(0,rect);
2084 float dx = (float)rect.get_x();
2085 float dy = (float)rect.get_y();
2086 float sx = (float)rect.get_width();
2087 float sy = (float)rect.get_height();
2088
2089 std::string tpl =
2090 "[mainwindow|%0X|%0Y|%100x|%90y|"
2091 "[hor|%1x"
2092 "|[vert|%1y|[book|toolbox]|[book|tool_options]]"
2093 "|[hor|%60x|[mainnotebook]"
2094 "|[hor|%50x|[book|params]"
2095 "|[vert|%30y|[book|history|groups]|[book|layers|canvases]]"
2096 "]"
2097 "]"
2098 "]";
2099
2100 std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2101 dock_manager->load_layout_from_string(layout);
2102 dock_manager->show_all_dock_dialogs();
2103 }
2104
2105 void
set_workspace_animating()2106 App::set_workspace_animating()
2107 {
2108 Glib::RefPtr<Gdk::Display> display(Gdk::Display::get_default());
2109 Glib::RefPtr<const Gdk::Screen> screen(display->get_default_screen());
2110 Gdk::Rectangle rect;
2111 // A proper way to obtain the primary monitor is to use the
2112 // Gdk::Screen::get_primary_monitor () const member. But as it
2113 // was introduced in gtkmm 2.20 I assume that the monitor 0 is the
2114 // primary one.
2115 screen->get_monitor_geometry(0,rect);
2116 float dx = (float)rect.get_x();
2117 float dy = (float)rect.get_y();
2118 float sx = (float)rect.get_width();
2119 float sy = (float)rect.get_height();
2120
2121 std::string tpl =
2122 "[mainwindow|%0X|%0Y|%100x|%90y|"
2123 "[hor|%70x"
2124 "|[vert|%1y"
2125 "|[hor|%1x|[book|toolbox]|[mainnotebook]]"
2126 "|[hor|%25x|[book|params|children]|[book|timetrack|curves]]"
2127 "]"
2128 "|[vert|%30y"
2129 "|[book|keyframes|history|groups]|[book|layers|canvases]]"
2130 "]"
2131 "]"
2132 "]";
2133
2134 std::string layout = DockManager::layout_from_template(tpl, dx, dy, sx, sy);
2135 dock_manager->load_layout_from_string(layout);
2136 dock_manager->show_all_dock_dialogs();
2137 }
2138
2139
2140 void
restore_default_settings()2141 App::restore_default_settings()
2142 {
2143 // TODO autorecover default auto_recover_backup_interval
2144 synfigapp::Main::settings().set_value("pref.distance_system","pt");
2145 synfigapp::Main::settings().set_value("pref.use_colorspace_gamma","1");
2146 #ifdef SINGLE_THREADED
2147 synfigapp::Main::settings().set_value("pref.use_single_threaded","1");
2148 #endif
2149 synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","1");
2150 synfigapp::Main::settings().set_value("pref.resize_imported_images","0");
2151 synfigapp::Main::settings().set_value("pref.enable_experimental_features","0");
2152 synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
2153 synfigapp::Main::settings().set_value("pref.ui_language", "os_LANG");
2154 synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
2155 synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
2156 synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
2157 synfigapp::Main::settings().set_value("pref.preferred_fps","24.0");
2158 synfigapp::Main::settings().set_value("pref.predefined_fps",DEFAULT_PREDEFINED_FPS);
2159 synfigapp::Main::settings().set_value("sequence_separator", ".");
2160 synfigapp::Main::settings().set_value("navigator_renderer", "");
2161 synfigapp::Main::settings().set_value("workarea_renderer", "");
2162 synfigapp::Main::settings().set_value("pref.enable_mainwin_menubar", "1");
2163 ostringstream temp;
2164 temp << Duck::STRUCT_DEFAULT;
2165 synfigapp::Main::settings().set_value("pref.ui_handle_tooltip_flag", temp.str());
2166 synfigapp::Main::settings().set_value("pref.auto_recover_backup", "1");
2167 }
2168
2169 void
apply_gtk_settings()2170 App::apply_gtk_settings()
2171 {
2172 GtkSettings *gtk_settings;
2173 gtk_settings = gtk_settings_get_default ();
2174
2175 gchar *theme_name=getenv("SYNFIG_GTK_THEME");
2176 if(theme_name) {
2177 g_object_set (G_OBJECT (gtk_settings), "gtk-theme-name", theme_name, NULL);
2178 }
2179
2180 // dark theme
2181 g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", App::use_dark_theme, NULL);
2182
2183 // enable menu icons
2184 g_object_set (G_OBJECT (gtk_settings), "gtk-menu-images", TRUE, NULL);
2185
2186 // fix CSS
2187 Glib::ustring data;
2188 // Fix GtkPaned (big margin makes it hard to grab first keyframe))
2189 data += "GtkPaned { margin: 2px; }\n";
2190 // Fix #348: Synfig's Interface went Too Thick
2191 // following css works for gtk 3.14:
2192 data += ".button { padding-left: 4px; padding-right: 4px; }\n";
2193 data += ".button { padding-top: 0px; padding-bottom: 0px; }\n";
2194 data += ".button * { padding-top: 4px; padding-bottom: 4px; }\n";
2195 data += ".button > GtkBox { padding-top: 0px; padding-bottom: 0px; }\n";
2196 data += ".button > GtkBox > * { padding-top: 4px; padding-bottom: 4px; }\n";
2197 data += ".button > GtkLabel { padding-top: 0px; padding-bottom: 0px; }\n";
2198 data += "GtkComboBox > .button > GtkBox > * { padding-top: 0px; padding-bottom: 0px; }\n";
2199 data += ".entry { padding-top: 0px; padding-bottom: 0px; }\n";
2200 // following css works for gtk 3.22:
2201 #ifdef __APPLE__
2202 // This is a temporary fix as we do not have gtk 3.22 on OSX build yet --KD
2203 data += "button { padding: 0px; }\n";
2204 #else
2205 data += "button { min-height: 16px; min-width: 16px; padding: 0px; }\n";
2206 #endif
2207 data += "button > box { padding: 5px; }\n";
2208 data += "button > image { padding: 5px; }\n";
2209 #ifndef __APPLE__
2210 // This is a temporary fix as we do not have gtk 3.22 on OSX build yet --KD
2211 data += "entry, spinbutton { min-height: 16px; }\n";
2212 #endif
2213 data += "combobox > box > button > box { padding-top: 0px; padding-bottom: 0px; }\n";
2214 // Fix #810: Insetsetive context menus on OSX
2215 g_object_get (G_OBJECT (gtk_settings), "gtk-theme-name", &theme_name, NULL);
2216 if ( String(theme_name) == "Adwaita" )
2217 data += ".window-frame, .window-frame:backdrop { box-shadow: none; margin: 0; }\n";
2218 if (!data.empty()) {
2219 Glib::RefPtr<Gtk::CssProvider> css = Gtk::CssProvider::create();
2220 try {
2221 css->load_from_data(data);
2222 } catch (Gtk::CssProviderError &e) {
2223 synfig::warning("Failed to load css rules. %s", e.what().c_str());
2224 }
2225 Glib::RefPtr<Gdk::Screen> screen = Gdk::Screen::get_default();
2226 Gtk::StyleContext::add_provider_for_screen(screen,css, GTK_STYLE_PROVIDER_PRIORITY_USER);
2227 }
2228 }
2229
2230 bool
shutdown_request(GdkEventAny *)2231 App::shutdown_request(GdkEventAny*)
2232 {
2233 quit();
2234 return true;
2235 //return !shutdown_in_progress;
2236 }
2237
2238 void
quit()2239 App::quit()
2240 {
2241 if(shutdown_in_progress)return;
2242
2243 get_ui_interface()->task(_("Quit Request"));
2244 if(Busy::count)
2245 {
2246 dialog_message_1b(
2247 "ERROR",
2248 _("Tasks are currently running. Please cancel the current tasks and try again"),
2249 "details",
2250 _("Close"));
2251
2252 return;
2253 }
2254
2255 std::list<etl::handle<Instance> >::iterator iter;
2256 for(iter=instance_list.begin();!instance_list.empty();iter=instance_list.begin())
2257 {
2258 if(!(*iter)->safe_close())
2259 return;
2260
2261 /*
2262 if((*iter)->synfigapp::Instance::get_action_count())
2263 {
2264 handle<synfigapp::UIInterface> uim;
2265 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
2266 assert(uim);
2267 string str=strprintf(_("Would you like to save your changes to %s?"),(*iter)->get_file_name().c_str() );
2268 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
2269 {
2270 case synfigapp::UIInterface::RESPONSE_NO:
2271 break;
2272 case synfigapp::UIInterface::RESPONSE_YES:
2273 (*iter)->save();
2274 break;
2275 case synfigapp::UIInterface::RESPONSE_CANCEL:
2276 return;
2277 default:
2278 assert(0);
2279 return;
2280 }
2281 }
2282
2283
2284 if((*iter)->synfigapp::Instance::is_modified())
2285 {
2286 handle<synfigapp::UIInterface> uim;
2287 uim=(*iter)->find_canvas_view((*iter)->get_canvas())->get_ui_interface();
2288 assert(uim);
2289 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() );
2290 switch(uim->yes_no_cancel((*iter)->get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES))
2291 {
2292 case synfigapp::UIInterface::RESPONSE_NO:
2293 break;
2294 case synfigapp::UIInterface::RESPONSE_YES:
2295 (*iter)->dialog_cvs_commit();
2296 break;
2297 case synfigapp::UIInterface::RESPONSE_CANCEL:
2298 return;
2299 default:
2300 assert(0);
2301 return;
2302 }
2303 }
2304 */
2305
2306 // This next line causes things to crash for some reason
2307 //(*iter)->close();
2308 }
2309
2310 instance_list.clear();
2311
2312 while(studio::App::events_pending())studio::App::iteration(false);
2313
2314 Gtk::Main::quit();
2315
2316 get_ui_interface()->task(_("Quit Request sent"));
2317 }
2318
2319 void
show_setup()2320 App::show_setup()
2321 {
2322 dialog_setup->refresh();
2323 dialog_setup->show();
2324 }
2325
Signal_Open_Ok(GtkWidget *,int * val)2326 gint Signal_Open_Ok(GtkWidget */*widget*/, int *val){*val=1;return 0;}
Signal_Open_Cancel(GtkWidget *,int * val)2327 gint Signal_Open_Cancel(GtkWidget */*widget*/, int *val){*val=2;return 0;}
2328
2329 //#ifdef _WIN32
2330 //#define USE_WIN32_FILE_DIALOGS 1
2331 //#endif
2332
2333 #ifdef USE_WIN32_FILE_DIALOGS
2334 static OPENFILENAME ofn={};
2335 #endif
2336
2337 #ifdef _WIN32
2338 #include <gdk/gdkwin32.h>
2339 #endif
2340
2341 bool
dialog_open_file(const std::string & title,std::string & filename,std::string preference)2342 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
2343 {
2344 // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2345 // TODO: Win32 native dialod not ready yet
2346 #ifdef USE_WIN32_FILE_DIALOGS
2347 static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2348
2349 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2350 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2351 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2352
2353 ofn.lStructSize=sizeof(OPENFILENAME);
2354 ofn.hwndOwner = hWnd;
2355 ofn.hInstance = hInstance;
2356 ofn.lpstrFilter = szFilter;
2357 // ofn.lpstrCustomFilter=NULL;
2358 // ofn.nMaxCustFilter=0;
2359 // ofn.nFilterIndex=0;
2360 // ofn.lpstrFile=NULL;
2361 ofn.nMaxFile=MAX_PATH;
2362 // ofn.lpstrFileTitle=NULL;
2363 // ofn.lpstrInitialDir=NULL;
2364 // ofn.lpstrTitle=NULL;
2365 ofn.Flags=OFN_HIDEREADONLY;
2366 // ofn.nFileOffset=0;
2367 // ofn.nFileExtension=0;
2368 ofn.lpstrDefExt=TEXT("sif");
2369 // ofn.lCustData = 0l;
2370 ofn.lpfnHook=NULL;
2371 // ofn.lpTemplateName=NULL;
2372
2373 CHAR szFilename[MAX_PATH];
2374 CHAR szTitle[500];
2375 strcpy(szFilename,filename.c_str());
2376 strcpy(szTitle,title.c_str());
2377
2378 ofn.lpstrFile=szFilename;
2379 ofn.lpstrFileTitle=szTitle;
2380
2381 if(GetOpenFileName(&ofn))
2382 {
2383 filename=szFilename;
2384 return true;
2385 }
2386 return false;
2387
2388 #else // not USE_WIN32_FILE_DIALOGS
2389 synfig::String prev_path;
2390
2391 if(!_preferences.get_value(preference, prev_path))
2392 prev_path = Glib::get_home_dir();
2393 prev_path = absolute_path(prev_path);
2394
2395 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2396 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2397
2398 dialog->set_transient_for(*App::main_window);
2399 dialog->set_current_folder(prev_path);
2400 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2401 dialog->add_button(_("Import"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2402
2403 // 0 All supported files
2404 // 0.1 Synfig documents. sfg is not supported to import
2405 Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2406 filter_supported->set_name(_("All supported files"));
2407 filter_supported->add_mime_type("application/x-sif");
2408 filter_supported->add_pattern("*.sif");
2409 filter_supported->add_pattern("*.sifz");
2410 // 0.2 Image files
2411 filter_supported->add_mime_type("image/png");
2412 filter_supported->add_mime_type("image/jpeg");
2413 filter_supported->add_mime_type("image/jpg");
2414 filter_supported->add_mime_type("image/bmp");
2415 filter_supported->add_mime_type("image/svg+xml");
2416 filter_supported->add_pattern("*.png");
2417 filter_supported->add_pattern("*.jpeg");
2418 filter_supported->add_pattern("*.jpg");
2419 filter_supported->add_pattern("*.bmp");
2420 filter_supported->add_pattern("*.svg");
2421 filter_supported->add_pattern("*.lst");
2422 // 0.3 Audio files
2423 filter_supported->add_mime_type("audio/x-vorbis+ogg");
2424 filter_supported->add_mime_type("audio/mpeg");
2425 filter_supported->add_mime_type("audio/x-wav");
2426 filter_supported->add_pattern("*.ogg");
2427 filter_supported->add_pattern("*.mp3");
2428 filter_supported->add_pattern("*.wav");
2429 // 0.4 lipsync files
2430 filter_supported->add_pattern("*.pgo");
2431
2432 // Sub fileters
2433 // 1 Synfig documents. sfg is not supported to import
2434 Glib::RefPtr<Gtk::FileFilter> filter_synfig = Gtk::FileFilter::create();
2435 filter_synfig->set_name(_("Synfig files (*.sif, *.sifz)"));
2436 filter_synfig->add_mime_type("application/x-sif");
2437 filter_synfig->add_pattern("*.sif");
2438 filter_synfig->add_pattern("*.sifz");
2439
2440 // 2.1 Image files
2441 Glib::RefPtr<Gtk::FileFilter> filter_image = Gtk::FileFilter::create();
2442 filter_image->set_name(_("Images (*.png, *.jpeg, *.bmp, *.svg)"));
2443 filter_image->add_mime_type("image/png");
2444 filter_image->add_mime_type("image/jpeg");
2445 filter_image->add_mime_type("image/jpg");
2446 filter_image->add_mime_type("image/bmp");
2447 filter_image->add_mime_type("image/svg+xml");
2448 filter_image->add_pattern("*.png");
2449 filter_image->add_pattern("*.jpeg");
2450 filter_image->add_pattern("*.jpg");
2451 filter_image->add_pattern("*.bmp");
2452 filter_image->add_pattern("*.svg");
2453
2454 // 2.2 Image sequence/list files
2455 Glib::RefPtr<Gtk::FileFilter> filter_image_list = Gtk::FileFilter::create();
2456 filter_image_list->set_name(_("Image sequence files (*.lst)"));
2457 filter_image_list->add_pattern("*.lst");
2458
2459 // 3 Audio files
2460 Glib::RefPtr<Gtk::FileFilter> filter_audio = Gtk::FileFilter::create();
2461 filter_audio->set_name(_("Audio (*.ogg, *.mp3, *.wav)"));
2462 filter_audio->add_mime_type("audio/x-vorbis+ogg");
2463 filter_audio->add_mime_type("audio/mpeg");
2464 filter_audio->add_mime_type("audio/x-wav");
2465 filter_audio->add_pattern("*.ogg");
2466 filter_audio->add_pattern("*.mp3");
2467 filter_audio->add_pattern("*.wav");
2468
2469 // 4 Lipsync files
2470 Glib::RefPtr<Gtk::FileFilter> filter_lipsync = Gtk::FileFilter::create();
2471 filter_lipsync->set_name(_("Lipsync (*.pgo)"));
2472 filter_lipsync->add_pattern("*.pgo");
2473
2474 // 5 Any files
2475 Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2476 filter_any->set_name(_("Any files"));
2477 filter_any->add_pattern("*");
2478
2479 dialog->add_filter(filter_supported);
2480 dialog->add_filter(filter_synfig);
2481 dialog->add_filter(filter_image);
2482 dialog->add_filter(filter_image_list);
2483 dialog->add_filter(filter_audio);
2484 dialog->add_filter(filter_lipsync);
2485 dialog->add_filter(filter_any);
2486
2487 if (filename.empty())
2488 dialog->set_filename(prev_path);
2489 else if (is_absolute_path(filename))
2490 dialog->set_filename(filename);
2491 else
2492 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2493
2494 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2495 filename = dialog->get_filename();
2496 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2497 _preferences.set_value(preference, dirname(filename));
2498 delete dialog;
2499 return true;
2500 }
2501
2502 delete dialog;
2503 return false;
2504 #endif // not USE_WIN32_FILE_DIALOGS
2505 }
2506
2507
2508 bool
dialog_open_file_spal(const std::string & title,std::string & filename,std::string preference)2509 App::dialog_open_file_spal(const std::string &title, std::string &filename, std::string preference)
2510 {
2511 synfig::String prev_path;
2512
2513 if(!_preferences.get_value(preference, prev_path))
2514 prev_path = Glib::get_home_dir();
2515 prev_path = absolute_path(prev_path);
2516
2517 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2518 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2519
2520 dialog->set_transient_for(*App::main_window);
2521 dialog->set_current_folder(prev_path);
2522 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2523 dialog->add_button(_("Load"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2524
2525 Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2526 filter_supported->set_name(_("Palette files (*.spal, *.gpl)"));
2527 filter_supported->add_pattern("*.spal");
2528 filter_supported->add_pattern("*.gpl");
2529 dialog->add_filter(filter_supported);
2530
2531 // show only Synfig color palette file (*.spal)
2532 Glib::RefPtr<Gtk::FileFilter> filter_spal = Gtk::FileFilter::create();
2533 filter_spal->set_name(_("Synfig palette files (*.spal)"));
2534 filter_spal->add_pattern("*.spal");
2535 dialog->add_filter(filter_spal);
2536
2537 // ...and add GIMP color palette file too (*.gpl)
2538 Glib::RefPtr<Gtk::FileFilter> filter_gpl = Gtk::FileFilter::create();
2539 filter_gpl->set_name(_("GIMP palette files (*.gpl)"));
2540 filter_gpl->add_pattern("*.gpl");
2541 dialog->add_filter(filter_gpl);
2542
2543 if (filename.empty())
2544 dialog->set_filename(prev_path);
2545 else if (is_absolute_path(filename))
2546 dialog->set_filename(filename);
2547 else
2548 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2549
2550 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2551 filename = dialog->get_filename();
2552 _preferences.set_value(preference, dirname(filename));
2553 delete dialog;
2554 return true;
2555 }
2556
2557 delete dialog;
2558 return false;
2559 }
2560
2561 bool
dialog_open_file_sketch(const std::string & title,std::string & filename,std::string preference)2562 App::dialog_open_file_sketch(const std::string &title, std::string &filename, std::string preference)
2563 {
2564 synfig::String prev_path;
2565
2566 if(!_preferences.get_value(preference, prev_path))
2567 prev_path = Glib::get_home_dir();
2568 prev_path = absolute_path(prev_path);
2569
2570 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2571 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2572
2573 dialog->set_transient_for(*App::main_window);
2574 dialog->set_current_folder(prev_path);
2575 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2576 dialog->add_button(_("Load"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2577
2578 // show only Synfig sketch file (*.sketch)
2579 Glib::RefPtr<Gtk::FileFilter> filter_sketch = Gtk::FileFilter::create();
2580 filter_sketch->set_name(_("Synfig sketch files (*.sketch)"));
2581 filter_sketch->add_pattern("*.sketch");
2582 dialog->add_filter(filter_sketch);
2583
2584 if (filename.empty())
2585 dialog->set_filename(prev_path);
2586 else if (is_absolute_path(filename))
2587 dialog->set_filename(filename);
2588 else
2589 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2590
2591 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2592 filename = dialog->get_filename();
2593 _preferences.set_value(preference, dirname(filename));
2594 delete dialog;
2595 return true;
2596 }
2597
2598 delete dialog;
2599 return false;
2600 }
2601
2602
2603 bool
dialog_open_file_image(const std::string & title,std::string & filename,std::string preference)2604 App::dialog_open_file_image(const std::string &title, std::string &filename, std::string preference)
2605 {
2606 synfig::String prev_path;
2607
2608 if(!_preferences.get_value(preference, prev_path))
2609 prev_path = Glib::get_home_dir();
2610
2611 prev_path = absolute_path(prev_path);
2612
2613 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2614 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2615
2616 dialog->set_transient_for(*App::main_window);
2617 dialog->set_current_folder(prev_path);
2618 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2619 dialog->add_button(_("Load"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2620
2621 // show only images
2622 Glib::RefPtr<Gtk::FileFilter> filter_image = Gtk::FileFilter::create();
2623 filter_image->set_name(_("Images and sequence files (*.png, *.jpg, *.jpeg, *.bmp, *.svg, *.lst)"));
2624 filter_image->add_mime_type("image/png");
2625 filter_image->add_mime_type("image/jpeg");
2626 filter_image->add_mime_type("image/jpg");
2627 filter_image->add_mime_type("image/bmp");
2628 filter_image->add_mime_type("image/svg+xml");
2629 filter_image->add_pattern("*.png");
2630 filter_image->add_pattern("*.jpeg");
2631 filter_image->add_pattern("*.jpg");
2632 filter_image->add_pattern("*.bmp");
2633 filter_image->add_pattern("*.svg");
2634 filter_image->add_pattern("*.lst");
2635 dialog->add_filter(filter_image);
2636
2637 // Any files
2638 Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2639 filter_any->set_name(_("Any files"));
2640 filter_any->add_pattern("*");
2641 dialog->add_filter(filter_any);
2642
2643 if (filename.empty())
2644 dialog->set_filename(prev_path);
2645 else if (is_absolute_path(filename))
2646 dialog->set_filename(filename);
2647 else
2648 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2649
2650 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2651 filename = dialog->get_filename();
2652 _preferences.set_value(preference, dirname(filename));
2653 delete dialog;
2654 return true;
2655 }
2656
2657 delete dialog;
2658 return false;
2659 }
2660
2661
2662 bool
dialog_open_file_audio(const std::string & title,std::string & filename,std::string preference)2663 App::dialog_open_file_audio(const std::string &title, std::string &filename, std::string preference)
2664 {
2665 synfig::String prev_path;
2666
2667 if(!_preferences.get_value(preference, prev_path))
2668 prev_path = Glib::get_home_dir();
2669
2670 prev_path = absolute_path(prev_path);
2671
2672 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2673 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2674
2675 dialog->set_transient_for(*App::main_window);
2676 dialog->set_current_folder(prev_path);
2677 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2678 dialog->add_button(_("Load"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2679
2680 // Audio files
2681 Glib::RefPtr<Gtk::FileFilter> filter_audio = Gtk::FileFilter::create();
2682 filter_audio->set_name(_("Audio (*.ogg, *.mp3, *.wav)"));
2683 filter_audio->add_mime_type("audio/x-vorbis+ogg");
2684 filter_audio->add_mime_type("audio/mpeg");
2685 filter_audio->add_mime_type("audio/x-wav");
2686 filter_audio->add_pattern("*.ogg");
2687 filter_audio->add_pattern("*.mp3");
2688 filter_audio->add_pattern("*.wav");
2689 dialog->add_filter(filter_audio);
2690
2691 // Any files
2692 Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2693 filter_any->set_name(_("Any files"));
2694 filter_any->add_pattern("*");
2695 dialog->add_filter(filter_any);
2696
2697 if (filename.empty())
2698 dialog->set_filename(prev_path);
2699 else if (is_absolute_path(filename))
2700 dialog->set_filename(filename);
2701 else
2702 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2703
2704 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
2705 filename = dialog->get_filename();
2706 _preferences.set_value(preference, dirname(filename));
2707 delete dialog;
2708 return true;
2709 }
2710
2711 delete dialog;
2712 return false;
2713 }
2714
2715 void
on_open_dialog_with_history_selection_changed(Gtk::FileChooserDialog * dialog,Gtk::Button * history_button)2716 on_open_dialog_with_history_selection_changed(Gtk::FileChooserDialog *dialog, Gtk::Button* history_button)
2717 {
2718 // activate the history button when something is selected
2719 history_button->set_sensitive(!dialog->get_filename().empty());
2720 }
2721
2722 bool
dialog_open_file_with_history_button(const std::string & title,std::string & filename,bool & show_history,std::string preference)2723 App::dialog_open_file_with_history_button(const std::string &title, std::string &filename, bool &show_history, std::string preference)
2724 {
2725 // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2726
2727 // TODO: Win32 native dialog not ready yet
2728 //#ifdef USE_WIN32_FILE_DIALOGS
2729 #if 0
2730 static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2731
2732 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2733 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2734 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2735
2736 ofn.lStructSize=sizeof(OPENFILENAME);
2737 ofn.hwndOwner = hWnd;
2738 ofn.hInstance = hInstance;
2739 ofn.lpstrFilter = szFilter;
2740 // ofn.lpstrCustomFilter=NULL;
2741 // ofn.nMaxCustFilter=0;
2742 // ofn.nFilterIndex=0;
2743 // ofn.lpstrFile=NULL;
2744 ofn.nMaxFile=MAX_PATH;
2745 // ofn.lpstrFileTitle=NULL;
2746 // ofn.lpstrInitialDir=NULL;
2747 // ofn.lpstrTitle=NULL;
2748 ofn.Flags=OFN_HIDEREADONLY;
2749 // ofn.nFileOffset=0;
2750 // ofn.nFileExtension=0;
2751 ofn.lpstrDefExt=TEXT("sif");
2752 // ofn.lCustData = 0l;
2753 ofn.lpfnHook=NULL;
2754 // ofn.lpTemplateName=NULL;
2755
2756 CHAR szFilename[MAX_PATH];
2757 CHAR szTitle[500];
2758 strcpy(szFilename,filename.c_str());
2759 strcpy(szTitle,title.c_str());
2760
2761 ofn.lpstrFile=szFilename;
2762 ofn.lpstrFileTitle=szTitle;
2763
2764 if(GetOpenFileName(&ofn))
2765 {
2766 filename=szFilename;
2767 return true;
2768 }
2769 return false;
2770
2771 #else // not USE_WIN32_FILE_DIALOGS
2772 synfig::String prev_path;
2773
2774 if(!_preferences.get_value(preference, prev_path))
2775 prev_path = Glib::get_home_dir();
2776
2777 prev_path = absolute_path(prev_path);
2778
2779 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2780 title, Gtk::FILE_CHOOSER_ACTION_OPEN);
2781
2782 dialog->set_transient_for(*App::main_window);
2783 dialog->set_current_folder(prev_path);
2784 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2785 dialog->add_button(_("Open"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2786 Gtk::Button* history_button = dialog->add_button(_("Open history"), RESPONSE_ACCEPT_WITH_HISTORY);
2787 // TODO: the Open history button should be file type sensitive one.
2788 dialog->set_response_sensitive(RESPONSE_ACCEPT_WITH_HISTORY, true);
2789
2790 // File filters
2791 // Synfig Documents
2792 Glib::RefPtr<Gtk::FileFilter> filter_supported = Gtk::FileFilter::create();
2793 filter_supported->set_name(_("Synfig files (*.sif, *.sifz, *.sfg)"));
2794 filter_supported->add_mime_type("application/x-sif");
2795 filter_supported->add_pattern("*.sif");
2796 filter_supported->add_pattern("*.sifz");
2797 filter_supported->add_pattern("*.sfg");
2798 // Any files
2799 Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
2800 filter_any->set_name(_("Any files"));
2801 filter_any->add_pattern("*");
2802
2803 dialog->add_filter(filter_supported);
2804 dialog->add_filter(filter_any);
2805
2806 if (filename.empty())
2807 dialog->set_filename(prev_path);
2808 else if (is_absolute_path(filename))
2809 dialog->set_filename(filename);
2810 else
2811 dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
2812
2813 // this ptr is't available to a static member fnc, connect to global function.
2814 sigc::connection connection_sc = dialog->signal_selection_changed().connect(sigc::bind(sigc::ptr_fun(on_open_dialog_with_history_selection_changed), dialog, history_button));
2815
2816 int response = dialog->run();
2817 if (response == Gtk::RESPONSE_ACCEPT || response == RESPONSE_ACCEPT_WITH_HISTORY) {
2818 filename = dialog->get_filename();
2819 show_history = response == RESPONSE_ACCEPT_WITH_HISTORY;
2820 // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
2821 _preferences.set_value(preference, dirname(filename));
2822 delete dialog;
2823 return true;
2824 }
2825
2826 connection_sc.disconnect();
2827 delete dialog;
2828 return false;
2829 #endif // not USE_WIN32_FILE_DIALOGS
2830 }
2831
2832 bool
dialog_open_folder(const std::string & title,std::string & foldername,std::string preference,Gtk::Window & transientwind)2833 App::dialog_open_folder(const std::string &title, std::string &foldername, std::string preference, Gtk::Window& transientwind)
2834 {
2835 synfig::String prev_path;
2836 synfigapp::Settings settings;
2837 if(settings.get_value(preference, prev_path))
2838 prev_path = ".";
2839
2840 prev_path = absolute_path(prev_path);
2841
2842 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window,
2843 title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
2844
2845 dialog->set_transient_for(transientwind);
2846 dialog->set_current_folder(prev_path);
2847 dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
2848 dialog->add_button(_("Open"), Gtk::RESPONSE_ACCEPT)->set_image_from_icon_name("gtk-open", Gtk::ICON_SIZE_BUTTON);
2849
2850 if(dialog->run() == GTK_RESPONSE_ACCEPT)
2851 {
2852 foldername = dialog->get_filename();
2853 delete dialog;
2854 return true;
2855 }
2856 delete dialog;
2857 return false;
2858 }
2859
2860
2861 bool
dialog_save_file(const std::string & title,std::string & filename,std::string preference)2862 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
2863 {
2864 // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
2865
2866 #if USE_WIN32_FILE_DIALOGS
2867 static TCHAR szFilter[] = TEXT (_("All Files (*.*)\0*.*\0\0")) ;
2868
2869 GdkWindow *gdkWinPtr=toolbox->get_window()->gobj();
2870 HINSTANCE hInstance=static_cast<HINSTANCE>(GetModuleHandle(NULL));
2871 HWND hWnd=static_cast<HWND>(GDK_WINDOW_HWND(gdkWinPtr));
2872
2873 ofn.lStructSize=sizeof(OPENFILENAME);
2874 ofn.hwndOwner = hWnd;
2875 ofn.hInstance = hInstance;
2876 ofn.lpstrFilter = szFilter;
2877 // ofn.lpstrCustomFilter=NULL;
2878 // ofn.nMaxCustFilter=0;
2879 // ofn.nFilterIndex=0;
2880 // ofn.lpstrFile=NULL;
2881 ofn.nMaxFile=MAX_PATH;
2882 // ofn.lpstrFileTitle=NULL;
2883 // ofn.lpstrInitialDir=NULL;
2884 // ofn.lpstrTitle=NULL;
2885 ofn.Flags=OFN_OVERWRITEPROMPT;
2886 // ofn.nFileOffset=0;
2887 // ofn.nFileExtension=0;
2888 ofn.lpstrDefExt=TEXT("sif");
2889 // ofn.lCustData = 0l;
2890 ofn.lpfnHook=NULL;
2891 // ofn.lpTemplateName=NULL;
2892
2893 CHAR szFilename[MAX_PATH];
2894 CHAR szTitle[500];
2895 strcpy(szFilename,filename.c_str());
2896 strcpy(szTitle,title.c_str());
2897
2898 ofn.lpstrFile=szFilename;
2899 ofn.lpstrFileTitle=szTitle;
2900
2901 if(GetSaveFileName(&ofn))
2902 {
2903 filename=szFilename;
2904 _preferences.set_value(preference,dirname(filename));
2905 return true;
2906 }
2907 return false;
2908 #else
2909 synfig::String prev_path;
2910
2911 if(!_preferences.get_value(preference, prev_path))
2912 prev_path = Glib::get_home_dir();
2913
2914 prev_path = absolute_path(prev_path);
2915
2916 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
2917
2918 // file type filters
2919 Glib::RefPtr<Gtk::FileFilter> filter_sif = Gtk::FileFilter::create();
2920 filter_sif->set_name(_("Uncompressed Synfig file (*.sif)"));
2921
2922 // sif share same mime type "application/x-sif" with sifz, so it will mixed .sif and .sifz files. Use only
2923 // pattern ("*.sif") for sif file format should be oK.
2924 //filter_sif->add_mime_type("application/x-sif");
2925 filter_sif->add_pattern("*.sif");
2926
2927 Glib::RefPtr<Gtk::FileFilter> filter_sifz = Gtk::FileFilter::create();
2928 filter_sifz->set_name(_("Compressed Synfig file (*.sifz)"));
2929 filter_sifz->add_pattern("*.sifz");
2930
2931 Glib::RefPtr<Gtk::FileFilter> filter_sfg = Gtk::FileFilter::create();
2932 filter_sfg->set_name(_("Container format file (*.sfg)"));
2933 filter_sfg->add_pattern("*.sfg");
2934
2935 dialog->set_current_folder(prev_path);
2936 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2937 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2938
2939 dialog->add_filter(filter_sifz);
2940 dialog->add_filter(filter_sif);
2941 dialog->add_filter(filter_sfg);
2942
2943 Widget_Enum *file_type_enum = 0;
2944 if (preference == ANIMATION_DIR_PREFERENCE)
2945 {
2946 file_type_enum = manage(new Widget_Enum());
2947 file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
2948 .add_enum_value(synfig::RELEASE_VERSION_CURRENT, "Current", _("Current"))
2949 .add_enum_value(synfig::RELEASE_VERSION_1_2_0, "1.2.1", "1.2.1")
2950 .add_enum_value(synfig::RELEASE_VERSION_1_2_0, "1.2.0", "1.2.0")
2951 .add_enum_value(synfig::RELEASE_VERSION_1_0_2, "1.0.2", "1.0.2")
2952 .add_enum_value(synfig::RELEASE_VERSION_1_0, "1.0", "1.0")
2953 .add_enum_value(synfig::RELEASE_VERSION_0_64_3, "0.64.3", "0.64.3")
2954 .add_enum_value(synfig::RELEASE_VERSION_0_64_2, "0.64.2", "0.64.2")
2955 .add_enum_value(synfig::RELEASE_VERSION_0_64_1, "0.64.1", "0.64.1")
2956 .add_enum_value(synfig::RELEASE_VERSION_0_64_0, "0.64.0", "0.64.0")
2957 .add_enum_value(synfig::RELEASE_VERSION_0_63_04, "0.63.05", "0.63.05")
2958 .add_enum_value(synfig::RELEASE_VERSION_0_63_04, "0.63.04", "0.63.04")
2959 .add_enum_value(synfig::RELEASE_VERSION_0_63_03, "0.63.03", "0.63.03")
2960 .add_enum_value(synfig::RELEASE_VERSION_0_63_02, "0.63.02", "0.63.02")
2961 .add_enum_value(synfig::RELEASE_VERSION_0_63_01, "0.63.01", "0.63.01")
2962 .add_enum_value(synfig::RELEASE_VERSION_0_63_00, "0.63.00", "0.63.00")
2963 .add_enum_value(synfig::RELEASE_VERSION_0_62_02, "0.62.02", "0.62.02")
2964 .add_enum_value(synfig::RELEASE_VERSION_0_62_01, "0.62.01", "0.62.01")
2965 .add_enum_value(synfig::RELEASE_VERSION_0_62_00, "0.62.00", "0.61.00")
2966 .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", "0.61.09")
2967 .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
2968 .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
2969 .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
2970 file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
2971
2972 Gtk::Grid *grid = manage(new Gtk::Grid);
2973 grid->attach(*manage(new Gtk::Label(_("File Format Version: "))),0,0,1,1);
2974 grid->attach(*file_type_enum,1,0,1,1);
2975 grid->show_all();
2976
2977 dialog->set_extra_widget(*grid);
2978 }
2979
2980 if (filename.empty()) {
2981 dialog->set_filename(prev_path);
2982
2983 }else{
2984 std::string full_path;
2985 if (is_absolute_path(filename))
2986 full_path = filename;
2987 else
2988 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
2989
2990 // select the file if it exists
2991 dialog->set_filename(full_path);
2992
2993 // if the file doesn't exist, put its name into the filename box
2994 struct stat s;
2995 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
2996 dialog->set_current_name(basename(filename));
2997
2998 }
2999 // set file filter according to previous file format
3000 if (filename_extension(filename) == ".sif" ) dialog->set_filter(filter_sif);
3001 if (filename_extension(filename)== ".sifz" ) dialog->set_filter(filter_sifz);
3002 if (filename_extension(filename) == ".sfg" ) dialog->set_filter(filter_sfg);
3003
3004 // set focus to the file name entry(box) of dialog instead to avoid the name
3005 // we are going to save changes while changing file filter each time.
3006 dialog->set_current_name(basename(filename));
3007
3008 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
3009
3010 if (preference == ANIMATION_DIR_PREFERENCE)
3011 set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
3012
3013 // add file extension according to file filter selected by user if he doesn't type file extension in
3014 // file name entry. Right now it still detetes file extension from file name entry, if extension is one
3015 // of .sif, sifz and sfg, it will be used otherwise, saved file format will depend on selected file filter.
3016 // It should be improved by changing file extension according to selted file type filter, such as:
3017 // dialog->property_filter().signal_changed().connect(sigc::mem_fun(*this, &App::on_save_dialog_filter_changed));
3018 filename = dialog->get_filename();
3019
3020 if (filename_extension(filename) != ".sif" &&
3021 filename_extension(filename) != ".sifz" &&
3022 filename_extension(filename) != ".sfg")
3023 {
3024 if (dialog->get_filter() == filter_sif)
3025 filename = dialog->get_filename() + ".sif";
3026 else if (dialog->get_filter() == filter_sifz)
3027 filename = dialog->get_filename() + ".sifz";
3028 else if (dialog->get_filter() == filter_sfg)
3029 filename = dialog->get_filename() + ".sfg";
3030 }
3031
3032 // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
3033 _preferences.set_value(preference, dirname(filename));
3034 delete dialog;
3035 return true;
3036 }
3037
3038 delete dialog;
3039 return false;
3040 #endif
3041 }
3042
3043
3044 bool
dialog_save_file_spal(const std::string & title,std::string & filename,std::string preference)3045 App::dialog_save_file_spal(const std::string &title, std::string &filename, std::string preference)
3046 {
3047 synfig::String prev_path;
3048 if(!_preferences.get_value(preference, prev_path))
3049 prev_path=Glib::get_home_dir();
3050 prev_path = absolute_path(prev_path);
3051
3052 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
3053
3054 // file type filters
3055 Glib::RefPtr<Gtk::FileFilter> filter_spal = Gtk::FileFilter::create();
3056 filter_spal->set_name(_("Synfig palette files (*.spal)"));
3057 filter_spal->add_pattern("*.spal");
3058
3059 dialog->set_current_folder(prev_path);
3060 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3061 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
3062
3063 dialog->add_filter(filter_spal);
3064
3065 if (filename.empty()) {
3066 dialog->set_filename(prev_path);
3067
3068 }else{
3069 std::string full_path;
3070 if (is_absolute_path(filename))
3071 full_path = filename;
3072 else
3073 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
3074
3075 // select the file if it exists
3076 dialog->set_filename(full_path);
3077
3078 // if the file doesn't exist, put its name into the filename box
3079 struct stat s;
3080 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
3081 dialog->set_current_name(basename(filename));
3082
3083 }
3084
3085 dialog->set_filter(filter_spal);
3086
3087 // set focus to the file name entry(box) of dialog instead to avoid the name
3088 // we are going to save changes while changing file filter each time.
3089 dialog->set_current_name(basename(filename));
3090
3091 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
3092
3093 // add file extension according to file filter selected by user
3094 filename = dialog->get_filename();
3095 if (filename_extension(filename) != ".spal")
3096 filename = dialog->get_filename() + ".spal";
3097
3098 delete dialog;
3099 return true;
3100 }
3101
3102 delete dialog;
3103 return false;
3104 }
3105
3106 bool
dialog_save_file_sketch(const std::string & title,std::string & filename,std::string preference)3107 App::dialog_save_file_sketch(const std::string &title, std::string &filename, std::string preference)
3108 {
3109 synfig::String prev_path;
3110 if(!_preferences.get_value(preference, prev_path))
3111 prev_path=Glib::get_home_dir();
3112 prev_path = absolute_path(prev_path);
3113
3114 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
3115
3116 // file type filters
3117 Glib::RefPtr<Gtk::FileFilter> filter_sketch = Gtk::FileFilter::create();
3118 filter_sketch->set_name(_("Synfig sketch files (*.sketch)"));
3119 filter_sketch->add_pattern("*.sketch");
3120
3121 dialog->set_current_folder(prev_path);
3122 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3123 dialog->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
3124
3125 dialog->add_filter(filter_sketch);
3126
3127 if (filename.empty()) {
3128 dialog->set_filename(prev_path);
3129
3130 }else{
3131 std::string full_path;
3132 if (is_absolute_path(filename))
3133 full_path = filename;
3134 else
3135 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
3136
3137 // select the file if it exists
3138 dialog->set_filename(full_path);
3139
3140 // if the file doesn't exist, put its name into the filename box
3141 struct stat s;
3142 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
3143 dialog->set_current_name(basename(filename));
3144
3145 }
3146
3147 dialog->set_filter(filter_sketch);
3148
3149 // set focus to the file name entry(box) of dialog instead to avoid the name
3150 // we are going to save changes while changing file filter each time.
3151 dialog->set_current_name(basename(filename));
3152
3153 if(dialog->run() == GTK_RESPONSE_ACCEPT) {
3154
3155 // add file extension according to file filter selected by user
3156 filename = dialog->get_filename();
3157 if (filename_extension(filename) != ".sketch")
3158 filename = dialog->get_filename() + ".sketch";
3159
3160 delete dialog;
3161 return true;
3162 }
3163
3164 delete dialog;
3165 return false;
3166 }
3167
3168
3169 bool
dialog_save_file_render(const std::string & title,std::string & filename,std::string preference)3170 App::dialog_save_file_render(const std::string &title, std::string &filename, std::string preference)
3171 {
3172 synfig::String prev_path;
3173 if(!_preferences.get_value(preference, prev_path))
3174 prev_path=Glib::get_home_dir();
3175 prev_path = absolute_path(prev_path);
3176
3177 Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(*App::main_window, title, Gtk::FILE_CHOOSER_ACTION_SAVE);
3178
3179 dialog->set_current_folder(prev_path);
3180 dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3181 dialog->add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
3182
3183 if (filename.empty()) {
3184 dialog->set_filename(prev_path);
3185
3186 }else{
3187 std::string full_path;
3188 if (is_absolute_path(filename))
3189 full_path = filename;
3190 else
3191 full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
3192
3193 // select the file if it exists
3194 dialog->set_filename(full_path);
3195
3196 // if the file doesn't exist, put its name into the filename box
3197 struct stat s;
3198 if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
3199 dialog->set_current_name(basename(filename));
3200
3201 }
3202
3203 if(dialog->run() == GTK_RESPONSE_ACCEPT)
3204 {
3205 filename = dialog->get_filename();
3206
3207 delete dialog;
3208 return true;
3209 }
3210
3211 delete dialog;
3212 return false;
3213 }
3214
3215
3216 bool
dialog_select_list_item(const std::string & title,const std::string & message,const std::list<std::string> & list,int & item_index)3217 App::dialog_select_list_item(const std::string &title, const std::string &message, const std::list<std::string> &list, int &item_index)
3218 {
3219 Gtk::Dialog dialog(title, *App::main_window, true);
3220
3221 Gtk::Label* label = manage (new Gtk::Label(message, 0, 0));
3222 label->set_line_wrap();
3223
3224 class ModelColumns : public Gtk::TreeModel::ColumnRecord
3225 {
3226 public:
3227 Gtk::TreeModelColumn<int> column_index;
3228 Gtk::TreeModelColumn<Glib::ustring> column_main;
3229 ModelColumns() { add(column_index); add(column_main); }
3230 } model_columns;
3231
3232 Glib::RefPtr<Gtk::ListStore> list_store = Gtk::ListStore::create(model_columns);
3233
3234 int k = 0;
3235 for(std::list<std::string>::const_iterator i = list.begin(); i != list.end(); i++) {
3236 Gtk::ListStore::iterator j = list_store->append();
3237 j->set_value(model_columns.column_index, k++);
3238 j->set_value(model_columns.column_main, Glib::ustring(*i));
3239 }
3240
3241 Gtk::TreeView * tree = manage (new Gtk::TreeView(list_store));
3242 Gtk::TreeViewColumn column_index("", model_columns.column_index);
3243 Gtk::TreeViewColumn column_main("", model_columns.column_main);
3244 column_index.set_visible(false);
3245 tree->append_column(column_index);
3246 tree->append_column(column_main);
3247 tree->set_hexpand(TRUE);
3248 tree->set_halign(Gtk::ALIGN_FILL);
3249 tree->set_vexpand(TRUE);
3250 tree->set_valign(Gtk::ALIGN_FILL);
3251
3252 Gtk::TreeModel::Row selected_row = list_store->children()[item_index];
3253 if (selected_row)
3254 tree->get_selection()->select(selected_row);
3255
3256 Gtk::Grid* grid = manage(new Gtk::Grid());
3257 grid->attach(*label, 0, 0, 1, 1);
3258 grid->attach(*tree, 0, 1, 1, 2);
3259
3260 dialog.get_content_area()->pack_start(*grid);
3261 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
3262 dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
3263 dialog.set_default_size(300, 450);
3264 dialog.show_all();
3265
3266 if (dialog.run() == Gtk::RESPONSE_ACCEPT) {
3267 item_index = tree->get_selection()->get_selected()->get_value(model_columns.column_index);
3268 return true;
3269 }
3270
3271 return false;
3272 }
3273
3274
3275 void
dialog_not_implemented()3276 App::dialog_not_implemented()
3277 {
3278 Gtk::MessageDialog dialog(*App::main_window, _("Feature not available"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
3279 dialog.set_secondary_text(_("Sorry, this feature has not yet been implemented."));
3280 dialog.run();
3281 }
3282
3283
3284 // message dialog with 1 button.
3285 void
dialog_message_1b(const std::string & type,const std::string & message,const std::string & details,const std::string & button1,const std::string & long_details)3286 App::dialog_message_1b(
3287 const std::string &type,
3288 //INFO: Gtk::MESSAGE_INFO - Informational message.
3289 //WARNING: Gtk::MESSAGE_WARNING - Non-fatal warning message.
3290 //QUESTION: Gtk::MESSAGE_QUESTION - Question requiring a choice.
3291 //ERROR: Gtk::MESSAGE_ERROR - Fatal error message.
3292 //OTHER: Gtk::MESSAGE_OTHER - None of the above, doesn’t get an icon.
3293 const std::string &message,
3294 const std::string &details,
3295 const std::string &button1,
3296 const std::string &long_details)
3297 {
3298 Gtk::MessageType _type = Gtk::MESSAGE_OTHER; // default
3299 if (type == "INFO")
3300 _type = Gtk::MESSAGE_INFO;
3301 if (type == "WARNING")
3302 _type = Gtk::MESSAGE_WARNING;
3303 if (type == "QUESTION")
3304 _type = Gtk::MESSAGE_QUESTION;
3305 if (type == "ERROR")
3306 _type = Gtk::MESSAGE_ERROR;
3307 if (type == "OTHER")
3308 _type = Gtk::MESSAGE_OTHER;
3309
3310 Gtk::MessageDialog dialog(*App::main_window, message, false, _type, Gtk::BUTTONS_NONE, true);
3311
3312 if (details != "details")
3313 dialog.set_secondary_text(details);
3314
3315 Gtk::Label label;
3316 Gtk::ScrolledWindow sw;
3317 if (long_details != "long_details")
3318 {
3319 label.set_text(long_details);
3320 label.show();
3321 sw.add(label);
3322 sw.set_size_request(400,300);
3323 sw.show();
3324 dialog.get_content_area()->pack_end(sw);
3325 dialog.set_resizable(true);
3326 }
3327
3328 dialog.add_button(button1, 0);
3329
3330 dialog.run();
3331 }
3332
3333
3334 // message dialog with 2 buttons.
3335 bool
dialog_message_2b(const std::string & message,const std::string & details,const Gtk::MessageType & type,const std::string & button1,const std::string & button2)3336 App::dialog_message_2b(const std::string &message,
3337 const std::string &details,
3338 const Gtk::MessageType &type,
3339 //MESSAGE_INFO - Informational message.
3340 //MESSAGE_WARNING - Non-fatal warning message.
3341 //MESSAGE_QUESTION - Question requiring a choice.
3342 //MESSAGE_ERROR - Fatal error message.
3343 //MESSAGE_OTHER - None of the above, doesn’t get an icon.
3344 const std::string &button1,
3345 const std::string &button2)
3346 {
3347 Gtk::MessageDialog dialog(*App::main_window, message, false, type, Gtk::BUTTONS_NONE, true);
3348 dialog.set_secondary_text(details);
3349 dialog.add_button(button1, 0);
3350 dialog.add_button(button2, 1);
3351
3352 return dialog.run();
3353 }
3354
3355
3356 // message dialog with 3 buttons.
3357 int
dialog_message_3b(const std::string & message,const std::string & detials,const Gtk::MessageType & type,const std::string & button1,const std::string & button2,const std::string & button3)3358 App::dialog_message_3b(const std::string &message,
3359 const std::string &detials,
3360 const Gtk::MessageType &type,
3361 //MESSAGE_INFO - Informational message.
3362 //MESSAGE_WARNING - Non-fatal warning message.
3363 //MESSAGE_QUESTION - Question requiring a choice.
3364 //MESSAGE_ERROR - Fatal error message.
3365 //MESSAGE_OTHER - None of the above, doesn’t get an icon.
3366 const std::string &button1,
3367 const std::string &button2,
3368 const std::string &button3)
3369 {
3370 Gtk::MessageDialog dialog(*App::main_window, message, false, type, Gtk::BUTTONS_NONE, true);
3371 dialog.set_secondary_text(detials);
3372 dialog.add_button(button1, 0);
3373 dialog.add_button(button2, 1);
3374 dialog.add_button(button3, 2);
3375
3376 return dialog.run();
3377 }
3378
3379
3380 static bool
try_open_uri(const std::string & uri)3381 try_open_uri(const std::string &uri)
3382 {
3383 return gtk_show_uri(NULL, uri.c_str(), GDK_CURRENT_TIME, NULL);
3384 }
3385
3386 void
dialog_help()3387 App::dialog_help()
3388 {
3389 if (!try_open_uri("http://synfig.org/wiki/Category:Manual"))
3390 {
3391 Gtk::MessageDialog dialog(*App::main_window, _("Documentation"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE, true);
3392 dialog.set_secondary_text(_("Documentation for Synfig Studio is available on the website:\n\nhttp://synfig.org/wiki/Category:Manual"));
3393 dialog.set_title(_("Help"));
3394 dialog.run();
3395 }
3396 }
3397
open_uri(const std::string & uri)3398 void App::open_uri(const std::string &uri)
3399 {
3400 synfig::info("Opening URI: " + uri);
3401 if(!try_open_uri(uri))
3402 {
3403 Gtk::MessageDialog dialog(*App::main_window, _("No compatible application was found. Please load open file manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
3404 dialog.set_secondary_text(uri);
3405 dialog.set_title(_("Error"));
3406 dialog.run();
3407 }
3408 }
3409
3410 bool
dialog_entry(const std::string & action,const std::string & content,std::string & text,const std::string & button1,const std::string & button2)3411 App::dialog_entry(const std::string &action, const std::string &content, std::string &text, const std::string &button1, const std::string &button2)
3412 {
3413 Gtk::MessageDialog dialog(
3414 *App::main_window,
3415 action,
3416 false,
3417 Gtk::MESSAGE_INFO,
3418 Gtk::BUTTONS_NONE,
3419 true
3420 );
3421
3422 // TODO Group All HARDCODED user interface information somewhere "global"
3423 // TODO All UI info from .rc
3424 #define DIALOG_ENTRY_MARGIN 18
3425 Gtk::Label* label = manage (new Gtk::Label(content));
3426 label->set_margin_start(DIALOG_ENTRY_MARGIN);
3427
3428 Gtk::Entry* entry = manage(new Gtk::Entry());
3429 entry->set_text(text);
3430 entry->set_margin_end(DIALOG_ENTRY_MARGIN);
3431 entry->set_activates_default(true);
3432 entry->set_hexpand(TRUE);
3433 entry->set_halign(Gtk::ALIGN_FILL);
3434 #undef DIALOG_ENTRY_MARGIN
3435
3436 Gtk::Grid* grid = manage (new Gtk::Grid());
3437 grid->add(*label);
3438 grid->add(*entry);
3439
3440 grid->show_all();
3441
3442 dialog.get_content_area()->pack_start(*grid);
3443 dialog.add_button(button1, Gtk::RESPONSE_CANCEL);
3444 dialog.add_button(button2, Gtk::RESPONSE_OK);
3445
3446 dialog.set_default_response(Gtk::RESPONSE_OK);
3447 entry->signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
3448 dialog.show();
3449
3450 if(dialog.run()!=Gtk::RESPONSE_OK)
3451 return false;
3452
3453 text = entry->get_text();
3454
3455 return true;
3456 }
3457
3458
3459 bool
dialog_paragraph(const std::string & title,const std::string & message,std::string & text)3460 App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
3461 {
3462 Gtk::Dialog dialog(
3463 title, // Title
3464 *App::main_window, // Parent
3465 true // Modal
3466 );
3467
3468 Gtk::Label* label = manage(new Gtk::Label(message));
3469 label->show();
3470 dialog.get_content_area()->pack_start(*label);
3471
3472 Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
3473 text_buffer->set_text(text);
3474 Gtk::TextView text_view(text_buffer);
3475 text_view.show();
3476
3477 dialog.get_content_area()->pack_start(text_view);
3478
3479 dialog.add_button(_("OK"), Gtk::RESPONSE_OK)->set_image_from_icon_name("gtk-ok", Gtk::ICON_SIZE_BUTTON);
3480 dialog.add_button(_("Cancel"), Gtk::RESPONSE_CANCEL)->set_image_from_icon_name("gtk-cancel", Gtk::ICON_SIZE_BUTTON);
3481 dialog.set_default_response(Gtk::RESPONSE_OK);
3482
3483 //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
3484 dialog.show();
3485
3486 if(dialog.run()!=Gtk::RESPONSE_OK)
3487 return false;
3488
3489 text=text_buffer->get_text();
3490
3491 return true;
3492 }
3493
3494 std::string
get_temporary_directory()3495 App::get_temporary_directory()
3496 {
3497 return synfigapp::Main::get_user_app_directory() + ETL_DIRECTORY_SEPARATOR + "tmp";
3498 }
3499
3500 synfig::FileSystemTemporary::Handle
wrap_into_temporary_filesystem(synfig::FileSystem::Handle canvas_file_system,std::string filename,std::string as,synfig::FileContainerZip::file_size_t truncate_storage_size)3501 App::wrap_into_temporary_filesystem(
3502 synfig::FileSystem::Handle canvas_file_system,
3503 std::string filename,
3504 std::string as,
3505 synfig::FileContainerZip::file_size_t truncate_storage_size )
3506 {
3507 FileSystemTemporary::Handle temporary_file_system = new FileSystemTemporary("instance", get_temporary_directory(), canvas_file_system);
3508 temporary_file_system->set_meta("filename", filename);
3509 temporary_file_system->set_meta("as", as);
3510 temporary_file_system->set_meta("truncate", etl::strprintf("%d", truncate_storage_size));
3511 return temporary_file_system;
3512 }
3513
3514 bool
open(std::string filename)3515 App::open(std::string filename)
3516 {
3517 return open_as(filename,filename);
3518 }
3519
3520 bool
open_as(std::string filename,std::string as,synfig::FileContainerZip::file_size_t truncate_storage_size)3521 App::open_as(std::string filename,std::string as,synfig::FileContainerZip::file_size_t truncate_storage_size)
3522 {
3523 #ifdef _WIN32
3524 size_t buf_size = PATH_MAX - 1;
3525 char* long_name = (char*)malloc(buf_size);
3526 long_name[0] = '\0';
3527 if(GetLongPathName(as.c_str(),long_name,sizeof(long_name)));
3528 // when called from autorecover.cpp, filename doesn't exist, and so long_name is empty
3529 // don't use it if that's the case
3530 if (long_name[0] != '\0')
3531 as=String(long_name);
3532 free(long_name);
3533 #endif
3534
3535 try
3536 {
3537 OneMoment one_moment;
3538 String errors, warnings;
3539
3540 // try open container
3541 FileSystem::Handle container = CanvasFileNaming::make_filesystem_container(filename, truncate_storage_size);
3542 if (!container)
3543 throw (String)strprintf(_("Unable to open container \"%s\"\n\n"),filename.c_str());
3544
3545 // make canvas file system
3546 FileSystem::Handle canvas_file_system = CanvasFileNaming::make_filesystem(container);
3547
3548 // wrap into temporary file system
3549 canvas_file_system = wrap_into_temporary_filesystem(canvas_file_system, filename, as, truncate_storage_size);
3550
3551 // file to open inside canvas file-system
3552 String canvas_filename = CanvasFileNaming::project_file(filename);
3553
3554 etl::handle<synfig::Canvas> canvas = open_canvas_as(canvas_file_system ->get_identifier(canvas_filename), as, errors, warnings);
3555 if(canvas && get_instance(canvas))
3556 {
3557 get_instance(canvas)->find_canvas_view(canvas)->present();
3558 info("%s is already open", canvas_filename.c_str());
3559 // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
3560 }
3561 else
3562 {
3563 if(!canvas)
3564 throw (String)strprintf(_("Unable to load \"%s\":\n\n"),filename.c_str()) + errors;
3565
3566 if (warnings != "")
3567 dialog_message_1b(
3568 "WARNING",
3569 _("Warning"),
3570 "details",
3571 _("Close"),
3572 warnings);
3573
3574 if (as.find(custom_filename_prefix.c_str()) != 0)
3575 add_recent_file(as);
3576
3577 handle<Instance> instance(Instance::create(canvas, container));
3578
3579 if(!instance)
3580 throw (String)strprintf(_("Unable to create instance for \"%s\""),filename.c_str());
3581
3582 one_moment.hide();
3583
3584 if(instance->is_updated() && App::dialog_message_2b(
3585 _("Newer version of this file available on the CVS repository!"),
3586 _("repository. Would you like to update now? (It would probably be a good idea)"),
3587 Gtk::MESSAGE_QUESTION,
3588 _("Cancel"),
3589 _("Update Anyway"))
3590 )
3591 instance->dialog_cvs_update();
3592 }
3593 }
3594 catch(String &x)
3595 {
3596 dialog_message_1b(
3597 "ERROR",
3598 x,
3599 "details",
3600 _("Close"));
3601
3602 return false;
3603 }
3604 catch(runtime_error &x)
3605 {
3606 dialog_message_1b(
3607 "ERROR",
3608 x.what(),
3609 "details",
3610 _("Close"));
3611
3612 return false;
3613 }
3614 catch(...)
3615 {
3616 dialog_message_1b(
3617 "ERROR",
3618 _("Uncaught error on file open (BUG)"),
3619 "details",
3620 _("Close"));
3621
3622 return false;
3623 }
3624
3625 return true;
3626 }
3627
3628 // this is called from autorecover.cpp
3629 bool
open_from_temporary_filesystem(std::string temporary_filename)3630 App::open_from_temporary_filesystem(std::string temporary_filename)
3631 {
3632 try
3633 {
3634 OneMoment one_moment;
3635 String errors, warnings;
3636
3637 // try open temporary container
3638 FileSystemTemporary::Handle file_system_temporary(new FileSystemTemporary(""));
3639 if (!file_system_temporary->open_temporary(temporary_filename))
3640 throw (String)strprintf(_("Unable to open temporary container \"%s\"\n\n"), temporary_filename.c_str());
3641
3642 // get original filename
3643 String filename = file_system_temporary->get_meta("filename");
3644 String as = file_system_temporary->get_meta("as");
3645 String truncate = file_system_temporary->get_meta("truncate");
3646 if (filename.empty() || as.empty() || truncate.empty())
3647 throw (String)strprintf(_("Original filename was not set in temporary container \"%s\"\n\n"), temporary_filename.c_str());
3648 #ifdef __APPLE__
3649 FileContainerZip::file_size_t truncate_storage_size = atoll(truncate.c_str());
3650 #else
3651 FileContainerZip::file_size_t truncate_storage_size = stoll(truncate);
3652 #endif
3653
3654 // make canvas file-system
3655 FileSystem::Handle canvas_container = CanvasFileNaming::make_filesystem_container(filename, truncate_storage_size);
3656 FileSystem::Handle canvas_file_system = CanvasFileNaming::make_filesystem(canvas_container);
3657
3658 // wrap into temporary
3659 file_system_temporary->set_sub_file_system(canvas_file_system);
3660 canvas_file_system = file_system_temporary;
3661
3662 // file to open inside canvas file system
3663 String canvas_filename = CanvasFileNaming::project_file(canvas_file_system);
3664
3665 etl::handle<synfig::Canvas> canvas(open_canvas_as(canvas_file_system->get_identifier(canvas_filename), as, errors, warnings));
3666 if(canvas && get_instance(canvas))
3667 {
3668 get_instance(canvas)->find_canvas_view(canvas)->present();
3669 info("%s is already open", as.c_str());
3670 // throw (String)strprintf(_("\"%s\" appears to already be open!"),filename.c_str());
3671 }
3672 else
3673 {
3674 if(!canvas)
3675 throw (String)strprintf(_("Unable to load \"%s\":\n\n"), temporary_filename.c_str()) + errors;
3676
3677 if (warnings != "")
3678 dialog_message_1b(
3679 "WARNING",
3680 strprintf("%s:\n\n%s", _("Warning"), warnings.c_str()),
3681 "details",
3682 _("Close"));
3683
3684 if (as.find(custom_filename_prefix.c_str()) != 0)
3685 add_recent_file(as);
3686
3687 handle<Instance> instance(Instance::create(canvas, canvas_container));
3688
3689 if(!instance)
3690 throw (String)strprintf(_("Unable to create instance for \"%s\""), temporary_filename.c_str());
3691
3692 one_moment.hide();
3693
3694 if(instance->is_updated() && App::dialog_message_2b(
3695 _("Newer version of this file avaliable on the CVS repository!"),
3696 _("Would you like to update now (It would probably be a good idea)"),
3697 Gtk::MESSAGE_QUESTION,
3698 _("Cancel"),
3699 _("Update Anyway"))
3700 )
3701 instance->dialog_cvs_update();
3702
3703 // This file isn't saved! mark it as such
3704 instance->inc_action_count();
3705 }
3706 }
3707 catch(String &x)
3708 {
3709 dialog_message_1b(
3710 "ERROR",
3711 x,
3712 "details",
3713 _("Close"));
3714
3715 return false;
3716 }
3717 catch(runtime_error &x)
3718 {
3719 dialog_message_1b(
3720 "ERROR",
3721 x.what(),
3722 "details",
3723 _("Close"));
3724
3725 return false;
3726 }
3727 catch(...)
3728 {
3729 dialog_message_1b(
3730 "ERROR",
3731 _("Uncaught error on file open (BUG)"),
3732 "details",
3733 _("Close"));
3734
3735 return false;
3736 }
3737
3738 return true;
3739 }
3740
3741
3742 void
new_instance()3743 App::new_instance()
3744 {
3745 handle<synfig::Canvas> canvas=synfig::Canvas::create();
3746
3747 String filename(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
3748 canvas->set_name(filename);
3749
3750 canvas->rend_desc().set_frame_rate(preferred_fps);
3751 canvas->rend_desc().set_time_start(0.0);
3752 canvas->rend_desc().set_time_end(5.0);
3753 canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
3754 canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
3755 // The top left and botton right positions are expressed in units
3756 // Original convention is that 1 unit = 60 pixels
3757 canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
3758 canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
3759 canvas->rend_desc().set_w(preferred_x_size);
3760 canvas->rend_desc().set_h(preferred_y_size);
3761 canvas->rend_desc().set_antialias(1);
3762 canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
3763 canvas->set_file_name(filename);
3764 canvas->keyframe_list().add(synfig::Keyframe());
3765
3766 FileSystem::Handle container = new FileSystemEmpty();
3767 FileSystem::Handle file_system = CanvasFileNaming::make_filesystem(container);
3768 file_system = wrap_into_temporary_filesystem(file_system, filename, filename);
3769
3770 // file name inside canvas file-system
3771 String canvas_filename = CanvasFileNaming::project_file(filename);
3772 canvas->set_identifier(file_system->get_identifier(canvas_filename));
3773
3774 handle<Instance> instance = Instance::create(canvas, container);
3775
3776 if (getenv("SYNFIG_AUTO_ADD_SKELETON_LAYER"))
3777 instance->find_canvas_view(canvas)->add_layer("skeleton");
3778
3779 if (getenv("SYNFIG_AUTO_ADD_MOTIONBLUR_LAYER"))
3780 instance->find_canvas_view(canvas)->add_layer("MotionBlur");
3781
3782 if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
3783 instance->find_canvas_view(canvas)->canvas_properties.present();
3784 }
3785
3786 void
dialog_open(string filename)3787 App::dialog_open(string filename)
3788 {
3789 if (filename.empty() && selected_instance)
3790 filename = selected_instance->get_file_name();
3791 if (filename.empty())
3792 filename="*.sif";
3793
3794 bool show_history = false;
3795 while(dialog_open_file_with_history_button(_("Please select a file"), filename, show_history, ANIMATION_DIR_PREFERENCE))
3796 {
3797 // If the filename still has wildcards, then we should
3798 // continue looking for the file we want
3799 if(find(filename.begin(),filename.end(),'*')!=filename.end())
3800 continue;
3801
3802 FileContainerZip::file_size_t truncate_storage_size = 0;
3803
3804 // TODO: ".sfg" literal
3805 if (show_history && filename_extension(filename) == ".sfg")
3806 {
3807 // read history
3808 std::list<FileContainerZip::HistoryRecord> history
3809 = FileContainerZip::read_history(filename);
3810
3811 // build list of history entries for dialog (descending)
3812 std::list<std::string> list;
3813 int index = 0;
3814 for(std::list<FileContainerZip::HistoryRecord>::const_iterator i = history.begin(); i != history.end(); i++)
3815 list.push_front(strprintf("%s%d", _("History entry #"), ++index));
3816
3817 // show dialog
3818 index=0;
3819 if (!dialog_select_list_item(_("Please select a file"), _("Select one of previous versions of file"), list, index))
3820 continue;
3821
3822 // find selected entry in list (descending)
3823 for(std::list<FileContainerZip::HistoryRecord>::const_reverse_iterator i = history.rbegin(); i != history.rend(); i++)
3824 if (0 == index--)
3825 truncate_storage_size = i->storage_size;
3826 }
3827
3828 if(open_as(filename,filename,truncate_storage_size))
3829 break;
3830
3831 get_ui_interface()->error(_("Unable to open file"));
3832 }
3833 }
3834
3835 void
set_selected_instance(etl::loose_handle<Instance> instance)3836 App::set_selected_instance(etl::loose_handle<Instance> instance)
3837 {
3838 /* if(get_selected_instance()==instance)
3839 {
3840 selected_instance=instance;
3841 signal_instance_selected()(instance);
3842 return;
3843 }
3844 else
3845 {
3846 */
3847 selected_instance=instance;
3848 if(get_selected_canvas_view() && get_selected_canvas_view()->get_instance()!=instance)
3849 {
3850 if(instance)
3851 {
3852 instance->focus(instance->get_canvas());
3853 }
3854 else
3855 set_selected_canvas_view(0);
3856 }
3857 signal_instance_selected()(instance);
3858 }
3859
3860 void
set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)3861 App::set_selected_canvas_view(etl::loose_handle<CanvasView> canvas_view)
3862 {
3863 if(selected_canvas_view != canvas_view)
3864 {
3865 etl::loose_handle<CanvasView> prev = selected_canvas_view;
3866 selected_canvas_view = NULL;
3867 if (prev) prev->deactivate();
3868 selected_canvas_view = canvas_view;
3869 signal_canvas_view_focus()(selected_canvas_view);
3870 if (selected_canvas_view) selected_canvas_view->activate();
3871 }
3872
3873 if(canvas_view)
3874 {
3875 selected_instance=canvas_view->get_instance();
3876 signal_instance_selected()(selected_instance);
3877 }
3878
3879 /*
3880 if(get_selected_canvas_view()==canvas_view)
3881 {
3882 signal_canvas_view_focus()(selected_canvas_view);
3883 signal_instance_selected()(canvas_view->get_instance());
3884 return;
3885 }
3886 selected_canvas_view=canvas_view;
3887 if(canvas_view && canvas_view->get_instance() != get_selected_instance())
3888 set_selected_instance(canvas_view->get_instance());
3889 signal_canvas_view_focus()(selected_canvas_view);
3890 */
3891 }
3892
3893 etl::loose_handle<Instance>
get_instance(etl::handle<synfig::Canvas> canvas)3894 App::get_instance(etl::handle<synfig::Canvas> canvas)
3895 {
3896 if(!canvas) return 0;
3897 canvas=canvas->get_root();
3898
3899 std::list<etl::handle<Instance> >::iterator iter;
3900 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
3901 {
3902 if((*iter)->get_canvas()==canvas)
3903 return *iter;
3904 }
3905 return 0;
3906 }
3907
3908 void
dialog_about()3909 App::dialog_about()
3910 {
3911 if(about)
3912 about->show();
3913 }
3914
3915 void
undo()3916 studio::App::undo()
3917 {
3918 if(selected_instance)
3919 selected_instance->undo();
3920 }
3921
3922 void
redo()3923 studio::App::redo()
3924 {
3925 if(selected_instance)
3926 selected_instance->redo();
3927 }
3928
3929 synfig::String
get_base_path()3930 studio::App::get_base_path()
3931 {
3932 return FileSystem::fix_slashes(app_base_path_);
3933 }
3934
3935 void
setup_changed()3936 studio::App::setup_changed()
3937 {
3938 std::list<etl::handle<Instance> >::iterator iter;
3939 for(iter=instance_list.begin();iter!=instance_list.end();++iter)
3940 {
3941 std::list< etl::handle<synfigapp::CanvasInterface> >::iterator citer;
3942 std::list< etl::handle<synfigapp::CanvasInterface> >& cilist((*iter)->canvas_interface_list());
3943 for(citer=cilist.begin();citer!=cilist.end();++citer)
3944 {
3945 (*citer)->signal_rend_desc_changed()();
3946 }
3947 }
3948 }
3949
3950 void
process_all_events()3951 studio::App::process_all_events()
3952 {
3953 Glib::usleep(1);
3954 while(studio::App::events_pending()) {
3955 while(studio::App::events_pending())
3956 studio::App::iteration(false);
3957 Glib::usleep(1);
3958 }
3959 }
3960