Update/add copyrights for people who modified the code.
[synfig.git] / synfig-studio / trunk / src / gtkmm / app.cpp
index e9dcbb2..091d75a 100644 (file)
@@ -8,6 +8,7 @@
 **     Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
 **     Copyright (c) 2007, 2008 Chris Moore
 **     Copyright (c) 2008 Gerald Young
+**  Copyright (c) 2008 Carlos López
 **
 **     This package is free software; you can redistribute it and/or
 **     modify it under the terms of the GNU General Public License as
@@ -54,6 +55,7 @@
 #include <gtkmm/inputdialog.h>
 #include <gtkmm/accelmap.h>
 #include <gtkmm/uimanager.h>
+#include <gtkmm/textview.h>
 
 #include <gtk/gtk.h>
 
@@ -88,6 +90,8 @@
 #include "state_rectangle.h"
 #include "state_smoothmove.h"
 #include "state_scale.h"
+#include "state_star.h"
+#include "state_text.h"
 #include "state_width.h"
 #include "state_rotate.h"
 #include "state_zoom.h"
@@ -271,7 +275,15 @@ bool studio::App::use_colorspace_gamma=true;
 bool studio::App::single_threaded=false;
 #endif
 bool studio::App::restrict_radius_ducks=false;
-String studio::App::browser_command("firefox");
+String studio::App::custom_filename_prefix(DEFAULT_FILENAME_PREFIX);
+int studio::App::preferred_x_size=480;
+int studio::App::preferred_y_size=270;
+String studio::App::predefined_size(DEFAULT_PREDEFINED_SIZE);
+#ifdef USE_OPEN_FOR_URLS
+String studio::App::browser_command("open"); // MacOS only
+#else
+String studio::App::browser_command("xdg-open"); // Linux XDG standard
+#endif
 
 static int max_recent_files_=25;
 int studio::App::get_max_recent_files() { return max_recent_files_; }
@@ -504,7 +516,27 @@ public:
                        value=App::browser_command;
                        return true;
                }
-
+               if(key=="custom_filename_prefix")
+               {
+                       value=App::custom_filename_prefix;
+                       return true;
+               }
+               if(key=="preferred_x_size")
+               {
+                       value=strprintf("%i",App::preferred_x_size);
+                       return true;
+               }
+               if(key=="preferred_y_size")
+               {
+                       value=strprintf("%i",App::preferred_y_size);
+                       return true;
+               }
+               if(key=="predefined_size")
+               {
+                       value=strprintf("%s",App::predefined_size.c_str());
+                       return true;
+               }
+               
                return synfigapp::Settings::get_value(key,value);
        }
 
@@ -573,7 +605,28 @@ public:
                        App::browser_command=value;
                        return true;
                }
-
+               if(key=="custom_filename_prefix")
+               {
+                       App::custom_filename_prefix=value;
+                       return true;
+               }
+               if(key=="preferred_x_size")
+               {
+                       int i(atoi(value.c_str()));
+                       App::preferred_x_size=i;
+                       return true;
+               }
+               if(key=="preferred_y_size")
+               {
+                       int i(atoi(value.c_str()));
+                       App::preferred_y_size=i;
+                       return true;
+               }
+               if(key=="predefined_size")
+               {
+                       App::predefined_size=value;
+                       return true;
+               }
                return synfigapp::Settings::set_value(key,value);
        }
 
@@ -591,6 +644,10 @@ public:
                ret.push_back("auto_recover_backup_interval");
                ret.push_back("restrict_radius_ducks");
                ret.push_back("browser_command");
+               ret.push_back("custom_filename_prefix");
+               ret.push_back("preferred_x_size");
+               ret.push_back("preferred_y_size");
+               ret.push_back("predefined_size");               
                return ret;
        }
 };
@@ -663,6 +720,8 @@ init_ui_manager()
        DEFINE_ACTION("copy", Gtk::StockID("gtk-copy"));
        DEFINE_ACTION("paste", Gtk::StockID("gtk-paste"));
        DEFINE_ACTION("select-all-ducks", _("Select All Ducks"));
+       DEFINE_ACTION("unselect-all-ducks", _("Unselect All Ducks"));
+       DEFINE_ACTION("select-all-layers", _("Select All Layers"));
        DEFINE_ACTION("unselect-all-layers", _("Unselect All Layers"));
        DEFINE_ACTION("properties", _("Properties"));
 
@@ -791,8 +850,10 @@ init_ui_manager()
 "              <menuitem action='copy'/>"
 "              <menuitem action='paste'/>"
 "              <separator name='bleh06'/>"
-"              <menuitem action='select-all-ducks'/>"
+"              <menuitem action='select-all-layers'/>"
 "              <menuitem action='unselect-all-layers'/>"
+"              <menuitem action='select-all-ducks'/>"
+"              <menuitem action='unselect-all-ducks'/>"
 "              <separator name='bleh07'/>"
 "              <menuitem action='properties'/>"
 "      </menu>"
@@ -931,7 +992,9 @@ init_ui_manager()
        }
 
        ACCEL("<Actions>//select-all-ducks","<Control>a");
-       ACCEL("<Actions>//unselect-all-layers","<Control>d");
+       ACCEL("<Actions>//unselect-all-ducks","<Control>d");
+       ACCEL("<Actions>//select-all-layers","<Control><Shift>a");
+       ACCEL("<Actions>//unselect-all-layers","<Control><Shift>d");
        ACCEL("<Actions>//render","F9");
        ACCEL("<Actions>//preview","F11");
        ACCEL("<Actions>//properties","F8");
@@ -995,23 +1058,25 @@ init_ui_manager()
 
        ACCEL("<Actions>//state-normal",      "<Mod1>a");
        ACCEL("<Actions>//state-smooth_move", "<Mod1>v");
-       ACCEL("<Actions>//state-scale",       "<Mod1>d");
-       ACCEL("<Actions>//state-rotate",      "<Mod1>s");
+       ACCEL("<Actions>//state-scale",       "<Mod1>s");
+       ACCEL("<Actions>//state-rotate",      "<Mod1>t");
+       ACCEL("<Actions>//state-mirror",      "<Mod1>m");
 
-       ACCEL("<Actions>//state-bline",       "<Mod1>b");
        ACCEL("<Actions>//state-circle",      "<Mod1>c");
        ACCEL("<Actions>//state-rectangle",   "<Mod1>r");
+       ACCEL("<Actions>//state-star",        "<Mod1>q");
        ACCEL("<Actions>//state-gradient",    "<Mod1>g");
+       ACCEL("<Actions>//state-polygon",     "<Mod1>p");
 
-       ACCEL("<Actions>//state-eyedrop",     "<Mod1>e");
+       ACCEL("<Actions>//state-bline",       "<Mod1>b");
+       ACCEL("<Actions>//state-text",        "<Mod1>x");
        ACCEL("<Actions>//state-fill",        "<Mod1>f");
+       ACCEL("<Actions>//state-eyedrop",     "<Mod1>e");
        ACCEL("<Actions>//state-zoom",        "<Mod1>z");
-       ACCEL("<Actions>//state-polygon",     "<Mod1>p");
 
-       ACCEL("<Actions>//state-draw",        "<Mod1>w");
+       ACCEL("<Actions>//state-draw",        "<Mod1>d");
        ACCEL("<Actions>//state-sketch",      "<Mod1>k");
-       ACCEL("<Actions>//state-width",       "<Mod1>t");
-       ACCEL("<Actions>//state-mirror",      "<Mod1>m");
+       ACCEL("<Actions>//state-width",       "<Mod1>w");
 
        ACCEL("<Actions>//canvas-zoom-fit","<Control><Shift>z");
 
@@ -1070,7 +1135,6 @@ App::App(int *argc, char ***argv):
        Glib::set_application_name(_("Synfig Studio"));
 
        Splash splash_screen;
-       splash_screen.set_can_self_destruct(false);
        splash_screen.show();
 
        shutdown_in_progress=false;
@@ -1178,21 +1242,22 @@ App::App(int *argc, char ***argv):
                studio_init_cb.task(_("Init ModMirror...")); module_list_.push_back(new ModMirror()); module_list_.back()->start();
 
                /* row 2 */
-               state_manager->add_state(&state_bline);
                state_manager->add_state(&state_circle);
                state_manager->add_state(&state_rectangle);
+               state_manager->add_state(&state_star);
                state_manager->add_state(&state_gradient);
                if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
 
                /* row 3 */
-               if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw); // Enabled for now.  Let's see whether they're good enough yet.
-               if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
+               state_manager->add_state(&state_bline);
+               state_manager->add_state(&state_text);
                state_manager->add_state(&state_fill);
                state_manager->add_state(&state_eyedrop);
                state_manager->add_state(&state_zoom);
 
-               // Disabled by default - it doesn't work properly?
-               if(getenv("SYNFIG_ENABLE_WIDTH"    )) state_manager->add_state(&state_width);
+               if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw); // Enabled for now.  Let's see whether they're good enough yet.
+               if(!getenv("SYNFIG_DISABLE_SKETCH" )) state_manager->add_state(&state_sketch);
+               if(!getenv("SYNFIG_DISABLE_WIDTH"  )) state_manager->add_state(&state_width); // Enabled since 0.61.09
 
                studio_init_cb.task(_("Init ModPalette..."));
                module_list_.push_back(new ModPalette()); module_list_.back()->start();
@@ -1607,10 +1672,13 @@ App::load_settings()
                                {
                                        if(!window_size_broken && !file_window_size)
                                                window_size_broken = true;
-                                       if(!window_size_broken)
-                                               add_recent_file(recent_file,recent_file_window_size);
-                                       else
-                                               add_recent_file(recent_file);
+                                       if (std::ifstream(recent_file.c_str()))
+                                       {
+                                               if(!window_size_broken)
+                                                       add_recent_file(recent_file,recent_file_window_size);
+                                               else
+                                                       add_recent_file(recent_file);
+                                       }
                                }
                        }
                        if(!window_size_broken && file_window_size)
@@ -1659,6 +1727,10 @@ App::reset_initial_window_configuration()
        synfigapp::Main::settings().set_value("pref.single_threaded","0");
 #endif
        synfigapp::Main::settings().set_value("pref.restrict_radius_ducks","0");
+       synfigapp::Main::settings().set_value("pref.custom_filename_prefix",DEFAULT_FILENAME_PREFIX);
+       synfigapp::Main::settings().set_value("pref.preferred_x_size","480");
+       synfigapp::Main::settings().set_value("pref.preferred_y_size","270");
+       synfigapp::Main::settings().set_value("pref.predefined_size",DEFAULT_PREDEFINED_SIZE);
        synfigapp::Main::settings().set_value("window.toolbox.pos","4 4");
 }
 
@@ -1775,7 +1847,7 @@ static OPENFILENAME ofn={};
 bool
 App::dialog_open_file(const std::string &title, std::string &filename, std::string preference)
 {
-       info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
+       // info("App::dialog_open_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
 
 #ifdef USE_WIN32_FILE_DIALOGS
        static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
@@ -1842,7 +1914,7 @@ App::dialog_open_file(const std::string &title, std::string &filename, std::stri
 
     if(dialog->run() == GTK_RESPONSE_ACCEPT) {
         filename = dialog->get_filename();
-               info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
+               // info("Saving preference %s = '%s' in App::dialog_open_file()", preference.c_str(), dirname(filename).c_str());
                _preferences.set_value(preference, dirname(filename));
         delete dialog;
         return true;
@@ -1901,7 +1973,7 @@ App::dialog_open_file(const std::string &title, std::string &filename, std::stri
 bool
 App::dialog_save_file(const std::string &title, std::string &filename, std::string preference)
 {
-       info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
+       // info("App::dialog_save_file('%s', '%s', '%s')", title.c_str(), filename.c_str(), preference.c_str());
 
 #if USE_WIN32_FILE_DIALOGS
        static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
@@ -1964,7 +2036,8 @@ App::dialog_save_file(const std::string &title, std::string &filename, std::stri
        {
                file_type_enum = manage(new Widget_Enum());
                file_type_enum->set_param_desc(ParamDesc().set_hint("enum")
-                                                                          .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", strprintf("0.61.08 (%s)", _("current")))
+                                                                          .add_enum_value(synfig::RELEASE_VERSION_0_61_09, "0.61.09", strprintf("0.61.09 (%s)", _("current")))
+                                                                          .add_enum_value(synfig::RELEASE_VERSION_0_61_08, "0.61.08", "0.61.08")
                                                                           .add_enum_value(synfig::RELEASE_VERSION_0_61_07, "0.61.07", "0.61.07")
                                                                           .add_enum_value(synfig::RELEASE_VERSION_0_61_06, "0.61.06", strprintf("0.61.06 %s", _("and older"))));
                file_type_enum->set_value(RELEASE_VERSION_END-1); // default to the most recent version
@@ -2000,7 +2073,7 @@ App::dialog_save_file(const std::string &title, std::string &filename, std::stri
                if (preference == ANIMATION_DIR_PREFERENCE)
                        set_file_version(synfig::ReleaseVersion(file_type_enum->get_value()));
         filename = dialog->get_filename();
-               info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
+               // info("Saving preference %s = '%s' in App::dialog_save_file()", preference.c_str(), dirname(filename).c_str());
                _preferences.set_value(preference, dirname(filename));
         delete dialog;
         return true;
@@ -2079,24 +2152,75 @@ try_open_url(const std::string &url)
 {
 #ifdef WIN32
        return ShellExecute(GetDesktopWindow(), "open", url.c_str(), NULL, NULL, SW_SHOW);
-#else  // WIN32
-       gchar* argv[3] = {strdup(App::browser_command.c_str()), strdup(url.c_str()), NULL};
-
-       GError* gerror = NULL;
-       gboolean retval;
-       retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &gerror);
-       free(argv[0]);
-       free(argv[1]);
-
-       if (!retval)
-    {
-               error(_("Could not execute specified web browser: %s"), gerror->message);
-               g_error_free(gerror);
-               return false;
-    }
+#else // !WIN32
+       std::vector<std::string> command_line;
+       std::vector<std::string> browsers;
+       browsers.reserve(23);
+
+       // Browser wrapper scripts
+#ifdef USE_OPEN_FOR_URLS
+       browsers.push_back("open");              // Apple MacOS X wrapper, on Linux it opens a virtual console
+#endif
+       browsers.push_back("xdg-open");          // XDG wrapper
+       browsers.push_back("sensible-browser");  // Debian wrapper
+       browsers.push_back("gnome-open");        // GNOME wrapper
+       browsers.push_back("kfmclient");         // KDE wrapper
+       browsers.push_back("exo-open");          // XFCE wrapper
+
+       // Alternatives system
+       browsers.push_back("gnome-www-browser"); // Debian GNOME alternative
+       browsers.push_back("x-www-browser");     // Debian GUI alternative
+
+       // Individual browsers
+       browsers.push_back("firefox");
+       browsers.push_back("epiphany-browser");
+       browsers.push_back("epiphany");
+       browsers.push_back("konqueror");
+       browsers.push_back("iceweasel");
+       browsers.push_back("mozilla");
+       browsers.push_back("netscape");
+       browsers.push_back("icecat");
+       browsers.push_back("galeon");
+       browsers.push_back("midori");
+       browsers.push_back("safari");
+       browsers.push_back("opera");
+       browsers.push_back("amaya");
+       browsers.push_back("netsurf");
+       browsers.push_back("dillo");
+
+       // Try the user-specified browser first
+       command_line.push_back(App::browser_command);
+       if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
+       command_line.push_back(url);
+
+       try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); return true; }
+       catch( Glib::SpawnError& exception ){
+
+               while ( !browsers.empty() )
+               {
+                       // Skip the browser if we already tried it
+                       if( browsers[0] == App::browser_command )
+                               continue;
+
+                       // Construct the command line
+                       command_line.clear();
+                       command_line.push_back(browsers[0]);
+                       if( command_line[0] == "kfmclient" ) command_line.push_back("openURL");
+                       command_line.push_back(url);
+
+                       // Remove the browser from the list
+                       browsers.erase(browsers.begin());
+
+                       // Try to spawn the browser
+                       try { Glib::spawn_async(".", command_line, Glib::SPAWN_SEARCH_PATH); }
+                       // Failed, move on to the next one
+                       catch(Glib::SpawnError& exception){ continue; }
+                       return true; // No exception means we succeeded!                        
+               }
+       }
 
-       return true;
-#endif  // WIN32
+       return false;
+#endif // !WIN32
 }
 
 void
@@ -2114,7 +2238,13 @@ App::dialog_help()
 void
 App::open_url(const std::string &url)
 {
-       try_open_url(url);
+       if(!try_open_url(url))
+       {
+               Gtk::MessageDialog dialog(_("No browser was found. Please load this website manually:"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
+               dialog.set_secondary_text(url);
+               dialog.set_title(_("No browser found"));
+               dialog.run();
+       }
 }
 
 bool
@@ -2123,8 +2253,8 @@ App::dialog_entry(const std::string &title, const std::string &message,std::stri
        Gtk::Dialog dialog(
                title,          // Title
                true,           // Modal
-               true            // use_separator
-       );
+               true);          // use_separator
+
        Gtk::Label label(message);
        label.show();
        dialog.get_vbox()->pack_start(label);
@@ -2133,11 +2263,13 @@ App::dialog_entry(const std::string &title, const std::string &message,std::stri
        entry.set_text(text);
        entry.show();
        entry.set_activates_default(true);
+
        dialog.get_vbox()->pack_start(entry);
 
        dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
        dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
        dialog.set_default_response(Gtk::RESPONSE_OK);
+
        entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
        dialog.show();
 
@@ -2149,8 +2281,39 @@ App::dialog_entry(const std::string &title, const std::string &message,std::stri
        return true;
 }
 
+bool
+App::dialog_paragraph(const std::string &title, const std::string &message,std::string &text)
+{
+       Gtk::Dialog dialog(
+               title,          // Title
+               true,           // Modal
+               true);          // use_separator
+
+       Gtk::Label label(message);
+       label.show();
+       dialog.get_vbox()->pack_start(label);
+
+       Glib::RefPtr<Gtk::TextBuffer> text_buffer(Gtk::TextBuffer::create());
+       text_buffer->set_text(text);
+       Gtk::TextView text_view(text_buffer);
+       text_view.show();
 
+       dialog.get_vbox()->pack_start(text_view);
 
+       dialog.add_button(Gtk::StockID("gtk-ok"),Gtk::RESPONSE_OK);
+       dialog.add_button(Gtk::StockID("gtk-cancel"),Gtk::RESPONSE_CANCEL);
+       dialog.set_default_response(Gtk::RESPONSE_OK);
+
+       //text_entry.signal_activate().connect(sigc::bind(sigc::mem_fun(dialog,&Gtk::Dialog::response),Gtk::RESPONSE_OK));
+       dialog.show();
+
+       if(dialog.run()!=Gtk::RESPONSE_OK)
+               return false;
+
+       text=text_buffer->get_text();
+
+       return true;
+}
 
 bool
 App::open(std::string filename)
@@ -2189,7 +2352,8 @@ App::open_as(std::string filename,std::string as)
                        if(!canvas)
                                throw (String)strprintf(_("Unable to open file \"%s\""),filename.c_str());
 
-                       add_recent_file(as);
+                       if (as.find(custom_filename_prefix.c_str()) != 0)
+                               add_recent_file(as);
 
                        handle<Instance> instance(Instance::create(canvas));
 
@@ -2224,7 +2388,7 @@ App::new_instance()
 {
        handle<synfig::Canvas> canvas=synfig::Canvas::create();
 
-       String file_name(strprintf("%s%d", DEFAULT_FILENAME_PREFIX, Instance::get_count()+1));
+       String file_name(strprintf("%s%d", App::custom_filename_prefix.c_str(), Instance::get_count()+1));
        canvas->set_name(file_name);
        file_name += ".sifz";
 
@@ -2233,10 +2397,12 @@ App::new_instance()
        canvas->rend_desc().set_time_end(5.0);
        canvas->rend_desc().set_x_res(DPI2DPM(72.0f));
        canvas->rend_desc().set_y_res(DPI2DPM(72.0f));
-       canvas->rend_desc().set_tl(Vector(-4,2.25));
-       canvas->rend_desc().set_br(Vector(4,-2.25));
-       canvas->rend_desc().set_w(480);
-       canvas->rend_desc().set_h(270);
+       // The top left and botton right positions are expressed in units
+       // Original convention is that 1 unit = 60 pixels
+       canvas->rend_desc().set_tl(Vector(-(preferred_x_size/60.0)/2.0,(preferred_y_size/60.0)/2.0));
+       canvas->rend_desc().set_br(Vector((preferred_x_size/60.0)/2.0,-(preferred_y_size/60.0)/2.0));
+       canvas->rend_desc().set_w(preferred_x_size);
+       canvas->rend_desc().set_h(preferred_y_size);
        canvas->rend_desc().set_antialias(1);
        canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
        canvas->set_file_name(file_name);