Don't forget to distribute the other general.h too
[synfig.git] / synfig-studio / trunk / src / gtkmm / app.cpp
index cc19fbe..ba154c2 100644 (file)
@@ -33,6 +33,7 @@
 #include <fstream>
 #include <iostream>
 #include <locale>
+#include <cstring>
 
 #ifdef HAVE_SYS_ERRNO_H
 #include <sys/errno.h>
@@ -54,6 +55,7 @@
 
 #include "app.h"
 #include "about.h"
+#include "splash.h"
 #include "instance.h"
 #include "canvasview.h"
 #include "dialog_setup.h"
@@ -221,6 +223,8 @@ const etl::handle<synfigapp::UIInterface>& App::get_ui_interface() { return ui_i
 etl::handle<Instance> App::selected_instance;
 etl::handle<CanvasView> App::selected_canvas_view;
 
+studio::About *studio::App::about=NULL;
+
 studio::Toolbox *studio::App::toolbox=NULL;
 
 studio::AutoRecover *studio::App::auto_recover=NULL;
@@ -268,7 +272,7 @@ namespace studio {
 bool
 really_delete_widget(Gtk::Widget *widget)
 {
-       synfig::info("really delete %p", (void*)widget);
+       // synfig::info("really delete %p", (void*)widget);
        delete widget;
        return false;
 }
@@ -279,7 +283,7 @@ really_delete_widget(Gtk::Widget *widget)
 void
 delete_widget(Gtk::Widget *widget)
 {
-       synfig::info("delete %p", (void*)widget);
+       // synfig::info("delete %p", (void*)widget);
        Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&really_delete_widget), widget), 50);
 }
 
@@ -411,30 +415,24 @@ studio::add_action_group_to_top(Glib::RefPtr<studio::UIManager> ui_manager, Glib
 {
        ui_manager->insert_action_group(group,0);
        return;
-       DEBUGPOINT();
        std::list<Glib::RefPtr<Gtk::ActionGroup> > prev_groups(ui_manager->get_action_groups());
        std::list<Glib::RefPtr<Gtk::ActionGroup> >::reverse_iterator iter;
 
-       DEBUGPOINT();
        for(iter=prev_groups.rbegin();iter!=prev_groups.rend();++iter)
        {
-               DEBUGPOINT();
                if(*iter && (*iter)->get_name()!="menus")
                {
                        synfig::info("Removing action group "+(*iter)->get_name());
                        ui_manager->remove_action_group(*iter);
                }
        }
-       DEBUGPOINT();
        ui_manager->insert_action_group(group,0);
 
-       DEBUGPOINT();
        for(;!prev_groups.empty();prev_groups.pop_front())
        {
                if(prev_groups.front() && prev_groups.front()!=group && prev_groups.front()->get_name()!="menus")
                        ui_manager->insert_action_group(prev_groups.front(),1);
        }
-       DEBUGPOINT();
 }
 */
 class Preferences : public synfigapp::Settings
@@ -574,6 +572,7 @@ init_ui_manager()
        menus_action_group->add( Gtk::Action::create("menu-layer", _("_Layer")) );
        menus_action_group->add( Gtk::Action::create("menu-duck-mask", _("Show/Hide Ducks")) );
        menus_action_group->add( Gtk::Action::create("menu-preview-quality", _("Preview Quality")) );
+       menus_action_group->add( Gtk::Action::create("menu-lowres-pixel", _("Low-Res Pixel Size")) );
        menus_action_group->add( Gtk::Action::create("menu-layer-new", _("New Layer")) );
        menus_action_group->add( Gtk::Action::create("menu-keyframe", _("Keyframe")) );
        menus_action_group->add( Gtk::Action::create("menu-group", _("Group")) );
@@ -597,6 +596,7 @@ init_ui_manager()
 
        DEFINE_ACTION2("keyframe-properties", Gtk::StockID("gtk-properties"), _("Keyframe Properties"));
        DEFINE_ACTION("about", Gtk::StockID("synfig-about"));
+       DEFINE_ACTION("new", Gtk::Stock::NEW);
        DEFINE_ACTION("open", Gtk::Stock::OPEN);
        DEFINE_ACTION("save", Gtk::Stock::SAVE);
        DEFINE_ACTION("save-as", Gtk::Stock::SAVE_AS);
@@ -613,6 +613,7 @@ init_ui_manager()
        DEFINE_ACTION("options", _("Options"));
        DEFINE_ACTION("close", _("Close View"));
        DEFINE_ACTION("close-document", _("Close Document"));
+       DEFINE_ACTION("quit", Gtk::Stock::QUIT);
 
 
        DEFINE_ACTION("undo", Gtk::StockID("gtk-undo"));
@@ -641,6 +642,8 @@ init_ui_manager()
        DEFINE_ACTION("quality-08", _("Use Quality Level 8"));
        DEFINE_ACTION("quality-09", _("Use Quality Level 9"));
        DEFINE_ACTION("quality-10", _("Use Quality Level 10"));
+       for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
+               DEFINE_ACTION(strprintf("lowres-pixel-%d", *iter), strprintf(_("Set Low-Res pixel size to %d"), *iter));
        DEFINE_ACTION("play", _("Play"));
        // DEFINE_ACTION("pause", _("Pause"));
        DEFINE_ACTION("stop", _("Stop"));
@@ -648,6 +651,8 @@ init_ui_manager()
        DEFINE_ACTION("toggle-grid-snap", _("Toggle Grid Snap"));
        DEFINE_ACTION("toggle-guide-show", _("Toggle Guide Show"));
        DEFINE_ACTION("toggle-low-res", _("Toggle Low-Res"));
+       DEFINE_ACTION("decrease-low-res-pixel-size", _("Decrease Low-Res Pixel Size"));
+       DEFINE_ACTION("increase-low-res-pixel-size", _("Increase Low-Res Pixel Size"));
        DEFINE_ACTION("toggle-onion-skin", _("Toggle Onion Skin"));
        DEFINE_ACTION("canvas-zoom-in", Gtk::StockID("gtk-zoom-in"));
        DEFINE_ACTION("canvas-zoom-out", Gtk::StockID("gtk-zoom-out"));
@@ -715,6 +720,8 @@ init_ui_manager()
 "      </popup>"
 "      <popup name='menu-main' action='menu-main'>"
 "      <menu action='menu-file'>"
+"              <menuitem action='new' />"
+"              <menuitem action='open' />"
 "              <menuitem action='save' />"
 "              <menuitem action='save-as' />"
 "              <menuitem action='revert' />"
@@ -733,6 +740,7 @@ init_ui_manager()
 "              <menuitem action='options' />"
 "              <menuitem action='close' />"
 "              <menuitem action='close-document' />"
+"              <menuitem action='quit' />"
 "      </menu>"
 "      <menu action='menu-edit'>"
 "              <menuitem action='undo'/>"
@@ -769,6 +777,17 @@ init_ui_manager()
 "                      <menuitem action='quality-09' />"
 "                      <menuitem action='quality-10' />"
 "              </menu>"
+"              <menu action='menu-lowres-pixel'>"
+"              <menuitem action='decrease-low-res-pixel-size'/>"
+"              <menuitem action='increase-low-res-pixel-size'/>"
+"              <separator name='pixel-size-separator'/>"
+;
+
+       for(list<int>::iterator iter = CanvasView::get_pixel_sizes().begin(); iter != CanvasView::get_pixel_sizes().end(); iter++)
+               ui_info += strprintf("                  <menuitem action='lowres-pixel-%d' />", *iter);
+
+       ui_info +=
+"              </menu>"
 "              <separator name='bleh08'/>"
 "              <menuitem action='play'/>"
 //"            <menuitem action='pause'/>"
@@ -912,6 +931,9 @@ init_ui_manager()
        ACCEL("<Actions>//time-zoom-in","+");
        ACCEL("<Actions>//time-zoom-out","_");
 */
+       ACCEL2(Gtk::AccelKey('(',Gdk::CONTROL_MASK,"<Actions>//decrease-low-res-pixel-size"));
+       ACCEL2(Gtk::AccelKey(')',Gdk::CONTROL_MASK,"<Actions>//increase-low-res-pixel-size"));
+
        ACCEL2(Gtk::AccelKey('(',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-dec"));
        ACCEL2(Gtk::AccelKey(')',Gdk::MOD1_MASK|Gdk::CONTROL_MASK,"<Actions>//amount-inc"));
 
@@ -925,7 +947,7 @@ init_ui_manager()
        ACCEL2(Gtk::AccelKey(',',Gdk::CONTROL_MASK,"<Actions>//seek-prev-frame"));
        ACCEL2(Gtk::AccelKey('>',Gdk::CONTROL_MASK,"<Actions>//seek-next-second"));
        ACCEL2(Gtk::AccelKey('<',Gdk::CONTROL_MASK,"<Actions>//seek-prev-second"));
-       ACCEL2(Gtk::AccelKey('o',Gdk::CONTROL_MASK,"<Actions>//toggle-onion-skin"));
+       ACCEL2(Gtk::AccelKey('o',Gdk::MOD1_MASK,"<Actions>//toggle-onion-skin"));
        ACCEL("<Actions>//play",              "<Control>p");
        ACCEL("<Actions>//seek-begin","Home");
        ACCEL("<Actions>//seek-end","End");
@@ -1000,19 +1022,19 @@ App::App(int *argc, char ***argv):
                        "different version of libsynfig than what is currently\n"
                        "installed. Synfig Studio will now abort. Try downloading\n"
                        "the latest version from the Synfig website at\n"
-                       "http://www.synfig.com/ "
+                       "http://synfig.org/Download"
                );
                throw 40;
        }
        Glib::set_application_name(_("Synfig Studio"));
 
-       About about_window;
-       about_window.set_can_self_destruct(false);
-       about_window.show();
+       Splash splash_screen;
+       splash_screen.set_can_self_destruct(false);
+       splash_screen.show();
 
        shutdown_in_progress=false;
-       SuperCallback synfig_init_cb(about_window.get_callback(),0,9000,10000);
-       SuperCallback studio_init_cb(about_window.get_callback(),9000,10000,10000);
+       SuperCallback synfig_init_cb(splash_screen.get_callback(),0,9000,10000);
+       SuperCallback studio_init_cb(splash_screen.get_callback(),9000,10000,10000);
 
        // Initialize the Synfig library
        try { synfigapp_main=etl::smart_ptr<synfigapp::Main>(new synfigapp::Main(etl::dirname((*argv)[0]),&synfig_init_cb)); }
@@ -1040,6 +1062,9 @@ App::App(int *argc, char ***argv):
                studio_init_cb.task(_("Init Toolbox..."));
                toolbox=new studio::Toolbox();
 
+               studio_init_cb.task(_("Init About Dialog..."));
+               about=new studio::About();
+
                studio_init_cb.task(_("Init Tool Options..."));
                dialog_tool_options=new studio::Dialog_ToolOptions();
                dock_manager->register_dockable(*dialog_tool_options);
@@ -1103,30 +1128,27 @@ App::App(int *argc, char ***argv):
                device_tracker=new studio::DeviceTracker();
 
                studio_init_cb.task(_("Init Tools..."));
+
+               /* row 1 */
                state_manager->add_state(&state_normal);
                state_manager->add_state(&state_smooth_move);
                state_manager->add_state(&state_scale);
                state_manager->add_state(&state_rotate);
+               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_gradient);
-               state_manager->add_state(&state_eyedrop);
-               state_manager->add_state(&state_fill);
-
-               state_manager->add_state(&state_zoom);
+               if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon); // Enabled - for working without ducks
 
-               // Enabled - it's useful to be able to work with polygons without tangent ducks getting in the way.
-               // I know we can switch tangent ducks off, but why not allow this kind of layer as well?
-               if(!getenv("SYNFIG_DISABLE_POLYGON")) state_manager->add_state(&state_polygon);
-
-               // Enabled for now.  Let's see whether they're good enough yet.
-               if(!getenv("SYNFIG_DISABLE_DRAW"   )) state_manager->add_state(&state_draw);
+               /* 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_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);
@@ -1134,10 +1156,6 @@ App::App(int *argc, char ***argv):
                studio_init_cb.task(_("Init ModPalette..."));
                module_list_.push_back(new ModPalette()); module_list_.back()->start();
 
-               studio_init_cb.task(_("Init ModMirror..."));
-               module_list_.push_back(new ModMirror()); module_list_.back()->start();
-
-
                studio_init_cb.task(_("Init Setup Dialog..."));
                dialog_setup=new studio::Dialog_Setup();
 
@@ -1158,7 +1176,7 @@ App::App(int *argc, char ***argv):
 
                if(auto_recover->recovery_needed())
                {
-                       about_window.hide();
+                       splash_screen.hide();
                        if(
                                get_ui_interface()->yes_no(
                                        _("Auto Recovery"),
@@ -1181,25 +1199,35 @@ App::App(int *argc, char ***argv):
                                        "idea to review them and save them now.")
                                );
                        }
-                       about_window.show();
+                       splash_screen.show();
                }
 
                // Look for any files given on the command line,
                // and load them if found.
+               bool opened_any = false;
                for(;*argc>=1;(*argc)--)
                        if((*argv)[*argc] && (*argv)[*argc][0]!='-')
                        {
                                studio_init_cb.task(_("Loading files..."));
-                               about_window.hide();
+                               splash_screen.hide();
                                open((*argv)[*argc]);
-                               about_window.show();
+                               opened_any = true;
+                               splash_screen.show();
                        }
 
+               // if no file was specified to be opened, create a new document to help new users get started more easily
+               if (!opened_any && !getenv("SYNFIG_DISABLE_AUTOMATIC_DOCUMENT_CREATION"))
+                       new_instance();
+
                studio_init_cb.task(_("Done."));
                studio_init_cb.amount_complete(10000,10000);
 
                toolbox->present();
        }
+       catch(String x)
+       {
+               get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable.") + String("\n\n") + x);
+       }
        catch(...)
        {
                get_ui_interface()->error(_("Unknown exception caught when constructing App.\nThis software may be unstable."));
@@ -1219,7 +1247,8 @@ App::~App()
        selected_instance=0;
 
        // Unload all of the modules
-       for(;!module_list_.empty();module_list_.pop_back());
+       for(;!module_list_.empty();module_list_.pop_back())
+               ;
 
        delete state_manager;
 
@@ -1227,6 +1256,8 @@ App::~App()
 
        delete auto_recover;
 
+       delete about;
+       
        toolbox->hide();
 
 //     studio::App::iteration(false);
@@ -1532,8 +1563,10 @@ static OPENFILENAME ofn={};
 #endif
 
 bool
-App::dialog_open_file(const std::string &title, std::string &filename)
+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());
+
 #ifdef USE_WIN32_FILE_DIALOGS
        static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
 
@@ -1578,26 +1611,36 @@ App::dialog_open_file(const std::string &title, std::string &filename)
 
 #else
        synfig::String prev_path;
-       if(!_preferences.get_value("curr_path",prev_path))
-               prev_path=".";
+
+       if(!_preferences.get_value(preference, prev_path))
+               prev_path = ".";
+
        prev_path = absolute_path(prev_path);
 
-    Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_OPEN);
+    Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_OPEN);
+
     dialog->set_current_folder(prev_path);
     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
     dialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_ACCEPT);
-    if(!filename.empty())
-               if (is_absolute_path(filename))
-                       dialog->set_filename(filename);
-               else
-                       dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
-    if(dialog->run()==GTK_RESPONSE_ACCEPT) {
-        filename=dialog->get_filename();
+
+    if (filename.empty())
+               dialog->set_filename(prev_path);
+       else if (is_absolute_path(filename))
+               dialog->set_filename(filename);
+       else
+               dialog->set_filename(prev_path + ETL_DIRECTORY_SEPARATOR + filename);
+
+    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());
+               _preferences.set_value(preference, dirname(filename));
         delete dialog;
         return true;
     }
+
     delete dialog;
     return false;
+
     /*
 
        GtkWidget *ok;
@@ -1632,7 +1675,7 @@ App::dialog_open_file(const std::string &title, std::string &filename)
        if(val==1)
        {
                filename=gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection));
-               _preferences.set_value("curr_path",dirname(filename));
+               _preferences.set_value(preference,dirname(filename));
        }
        else
        {
@@ -1646,8 +1689,10 @@ App::dialog_open_file(const std::string &title, std::string &filename)
 }
 
 bool
-App::dialog_save_file(const std::string &title, std::string &filename)
+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());
+
 #if USE_WIN32_FILE_DIALOGS
        static TCHAR szFilter[] = TEXT ("All Files (*.*)\0*.*\0\0") ;
 
@@ -1686,27 +1731,34 @@ App::dialog_save_file(const std::string &title, std::string &filename)
        if(GetSaveFileName(&ofn))
        {
                filename=szFilename;
-               _preferences.set_value("curr_path",dirname(filename));
+               _preferences.set_value(preference,dirname(filename));
                return true;
        }
        return false;
 #else
        synfig::String prev_path;
-       if(!_preferences.get_value("curr_path",prev_path))
+
+       if(!_preferences.get_value(preference, prev_path))
                prev_path=".";
+
        prev_path = absolute_path(prev_path);
 
-    Gtk::FileChooserDialog *dialog=new Gtk::FileChooserDialog(title,Gtk::FILE_CHOOSER_ACTION_SAVE);
+    Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog(title, Gtk::FILE_CHOOSER_ACTION_SAVE);
+
     dialog->set_current_folder(prev_path);
     dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
     dialog->add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_ACCEPT);
-    if(!filename.empty())
+
+    if (filename.empty())
+               dialog->set_filename(prev_path);
+    else
        {
                std::string full_path;
                if (is_absolute_path(filename))
                        full_path = filename;
                else
                        full_path = prev_path + ETL_DIRECTORY_SEPARATOR + filename;
+
                // select the file if it exists
                dialog->set_filename(full_path);
 
@@ -1715,15 +1767,17 @@ App::dialog_save_file(const std::string &title, std::string &filename)
                if(stat(full_path.c_str(),&s) == -1 && errno == ENOENT)
                        dialog->set_current_name(basename(filename));
        }
-    if(dialog->run()==GTK_RESPONSE_ACCEPT) {
-        filename=dialog->get_filename();
+
+    if(dialog->run() == GTK_RESPONSE_ACCEPT) {
+        filename = dialog->get_filename();
+               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;
-               _preferences.set_value("curr_path",dirname(filename));
         return true;
     }
+
     delete dialog;
     return false;
-//     return dialog_open_file(title, filename);
 #endif
 }
 
@@ -1879,8 +1933,6 @@ App::open_as(std::string filename,std::string as)
                return false;
        }
 
-       _preferences.set_value("curr_path",dirname(as));
-
        return true;
 }
 
@@ -1895,7 +1947,7 @@ App::new_instance()
 
        canvas->rend_desc().set_frame_rate(24.0);
        canvas->rend_desc().set_time_start(0.0);
-       canvas->rend_desc().set_time_end(00.0);
+       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));
@@ -1908,7 +1960,7 @@ App::new_instance()
 
        handle<Instance> instance = Instance::create(canvas);
 
-       if (!getenv("SYNFIG_DISABLE_NEW_CANVAS_EDIT_PROPERTIES"))
+       if (getenv("SYNFIG_ENABLE_NEW_CANVAS_EDIT_PROPERTIES"))
                instance->find_canvas_view(canvas)->canvas_properties.present();
 }
 
@@ -1917,7 +1969,7 @@ App::dialog_open()
 {
        string filename="*.sif";
 
-       while(dialog_open_file("Open", filename))
+       while(dialog_open_file("Open", filename, ANIMATION_DIR_PREFERENCE))
        {
                // If the filename still has wildcards, then we should
                // continue looking for the file we want
@@ -1998,7 +2050,8 @@ App::get_instance(etl::handle<synfig::Canvas> canvas)
 void
 App::dialog_about()
 {
-       (new class About())->show();
+       if(about)
+               about->show();
 }
 
 void