enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / session_dialog.cc
index 996dab1cc4144f21eb93282b173fcfd3018938f1..225b02df84ba3d42eab0748bacff1f232778db26 100644 (file)
 #include "gtk2ardour-config.h"
 #endif
 
-#include <fstream>
 #include <algorithm>
 
+#include <glib.h>
+#include "pbd/gstdio_compat.h"
+
+#include <glibmm.h>
+#include <glibmm/datetime.h>
+
 #include <gtkmm/filechooser.h>
 
+#include "pbd/basename.h"
 #include "pbd/failed_constructor.h"
 #include "pbd/file_utils.h"
 #include "pbd/replace_all.h"
 #include "pbd/whitespace.h"
 #include "pbd/stacktrace.h"
+#include "pbd/stl_delete.h"
 #include "pbd/openuri.h"
 
+#include "gtkmm2ext/utils.h"
+
 #include "ardour/audioengine.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/recent_sessions.h"
@@ -45,7 +54,9 @@
 #include "session_dialog.h"
 #include "opts.h"
 #include "engine_dialog.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
+#include "tooltips.h"
+#include "ui_config.h"
 #include "utils.h"
 
 using namespace std;
@@ -54,13 +65,7 @@ using namespace Gdk;
 using namespace Glib;
 using namespace PBD;
 using namespace ARDOUR;
-
-static string poor_mans_glob (string path)
-{
-       string copy = path;
-       replace_all (copy, "~", Glib::get_home_dir());
-       return copy;
-}
+using namespace ARDOUR_UI_UTILS;
 
 SessionDialog::SessionDialog (bool require_new, const std::string& session_name, const std::string& session_path, const std::string& template_name, bool cancel_not_quit)
        : ArdourDialog (_("Session Setup"), true, true)
@@ -74,7 +79,6 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
        , _master_bus_channel_count_adj (2, 0, 100, 1, 10, 0)
        , _existing_session_chooser_used (false)
 {
-       set_keep_above (true);
        set_position (WIN_POS_CENTER);
        get_vbox()->set_spacing (6);
 
@@ -97,22 +101,22 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
        get_vbox()->pack_start (info_frame, false, false);
 
        setup_new_session_page ();
-       
+
        if (!new_only) {
                setup_initial_choice_box ();
                get_vbox()->pack_start (ic_vbox, true, true);
        } else {
                get_vbox()->pack_start (session_new_vbox, true, true);
        }
-       
+
        if (!template_name.empty()) {
                use_template_button.set_active (false);
                load_template_override = template_name;
        }
-       
+
        get_vbox()->show_all ();
 
-       /* fill data models and how/hide accordingly */
+       /* fill data models and show/hide accordingly */
 
        populate_session_templates ();
 
@@ -129,9 +133,11 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
                if (cnt > 0) {
                        recent_scroller.show();
                        recent_label.show ();
-                       
+
                        if (cnt > 4) {
                                recent_scroller.set_size_request (-1, 300);
+                       } else {
+                               recent_scroller.set_size_request (-1, 80);
                        }
                } else {
                        recent_scroller.hide();
@@ -153,6 +159,41 @@ SessionDialog::SessionDialog (bool require_new, const std::string& session_name,
        }
 }
 
+SessionDialog::SessionDialog ()
+       : ArdourDialog (_("Recent Sessions"), true, true)
+       , new_only (false)
+       , _provided_session_name ("")
+       , _provided_session_path ("")
+       // the following are unused , but have no default ctor
+       , _output_limit_count_adj (1, 0, 100, 1, 10, 0)
+       , _input_limit_count_adj (1, 0, 100, 1, 10, 0)
+       , _master_bus_channel_count_adj (2, 0, 100, 1, 10, 0)
+       , _existing_session_chooser_used (false) // caller must check should_be_new
+{
+       get_vbox()->set_spacing (6);
+
+       cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
+       open_button = add_button (Stock::OPEN, RESPONSE_ACCEPT);
+       open_button->set_sensitive (false);
+
+       setup_recent_sessions ();
+
+       get_vbox()->pack_start (recent_scroller, true, true);
+       get_vbox()->show_all ();
+
+       recent_scroller.show();
+
+       int cnt = redisplay_recent_sessions ();
+       if (cnt > 4) {
+               recent_scroller.set_size_request (-1, 300);
+       } else {
+               recent_scroller.set_size_request (-1, 80);
+       }
+
+}
+
+
+
 SessionDialog::~SessionDialog()
 {
 }
@@ -191,7 +232,7 @@ SessionDialog::session_template_name ()
                TreeModel::Row row = (*iter);
                string s = row[session_template_columns.path];
                return s;
-       } 
+       }
 
        return string();
 }
@@ -207,9 +248,13 @@ SessionDialog::session_name (bool& should_be_new)
        /* Try recent session selection */
 
        TreeIter iter = recent_session_display.get_selection()->get_selected();
-       
+
        if (iter) {
                should_be_new = false;
+               string s = (*iter)[recent_session_columns.fullpath];
+               if (Glib::file_test (s, Glib::FILE_TEST_IS_REGULAR)) {
+                       return PBD::basename_nosuffix (s);
+               }
                return (*iter)[recent_session_columns.visible_name];
        }
 
@@ -233,9 +278,9 @@ SessionDialog::session_folder ()
        }
 
        /* Try recent session selection */
-       
+
        TreeIter iter = recent_session_display.get_selection()->get_selected();
-       
+
        if (iter) {
                string s = (*iter)[recent_session_columns.fullpath];
                if (Glib::file_test (s, Glib::FILE_TEST_IS_REGULAR)) {
@@ -248,11 +293,37 @@ SessionDialog::session_folder ()
                /* existing session chosen from file chooser */
                return Glib::path_get_dirname (existing_session_chooser.get_current_folder ());
        } else {
-               std::string legal_session_folder_name = legalize_for_path (new_name_entry.get_text());
-               return Glib::build_filename (new_folder_chooser.get_current_folder(), legal_session_folder_name);
+               std::string val = new_name_entry.get_text();
+               strip_whitespace_edges (val);
+               std::string legal_session_folder_name = legalize_for_path (val);
+               return Glib::build_filename (new_folder_chooser.get_filename (), legal_session_folder_name);
        }
 }
 
+void
+SessionDialog::setup_recent_sessions ()
+{
+       recent_session_model = TreeStore::create (recent_session_columns);
+       recent_session_model->signal_sort_column_changed().connect (sigc::mem_fun (*this, &SessionDialog::recent_session_sort_changed));
+
+       recent_session_display.set_model (recent_session_model);
+       recent_session_display.append_column (_("Session Name"), recent_session_columns.visible_name);
+       recent_session_display.append_column (_("Sample Rate"), recent_session_columns.sample_rate);
+       recent_session_display.append_column (_("File Resolution"), recent_session_columns.disk_format);
+       recent_session_display.append_column (_("Last Modified"), recent_session_columns.time_formatted);
+       recent_session_display.set_headers_visible (true);
+       recent_session_display.get_selection()->set_mode (SELECTION_SINGLE);
+
+       recent_session_display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &SessionDialog::recent_session_row_selected));
+
+       recent_scroller.add (recent_session_display);
+       recent_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+       recent_scroller.set_shadow_type (Gtk::SHADOW_IN);
+
+       recent_session_display.show();
+       recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &SessionDialog::recent_row_activated));
+}
+
 void
 SessionDialog::setup_initial_choice_box ()
 {
@@ -277,16 +348,19 @@ SessionDialog::setup_initial_choice_box ()
 
        string image_path;
 
-       if (find_file_in_search_path (ardour_data_search_path(), "small-splash.png", image_path)) {
+       Searchpath rc (ARDOUR::ardour_data_search_path());
+       rc.add_subdirectory_to_paths ("resources");
+
+       if (find_file (rc, PROGRAM_NAME "-small-splash.png", image_path)) {
                Gtk::Image* image;
                if ((image = manage (new Gtk::Image (image_path))) != 0) {
                        hbox->pack_start (*image, false, false);
                }
        }
-       
+
        vbox->pack_start (ic_new_session_button, true, true, 20);
        hbox->pack_start (*vbox, true, true, 20);
-       
+
        centering_vbox->pack_start (*hbox, false, false);
 
        /* Possible update message */
@@ -305,7 +379,7 @@ SessionDialog::setup_initial_choice_box ()
                Gtk::Button *updates_button = manage (new Gtk::Button (_("Check the website for more...")));
 
                updates_button->signal_clicked().connect (mem_fun(*this, &SessionDialog::updates_button_clicked) );
-               ARDOUR_UI::instance()->tooltips().set_tip (*updates_button, _("Click to open the program website in your web browser"));
+               set_tooltip (*updates_button, _("Click to open the program website in your web browser"));
 
                info_box->pack_start (*updates_button, false, false);
 
@@ -315,55 +389,47 @@ SessionDialog::setup_initial_choice_box ()
        }
 
        /* recent session scroller */
+       setup_recent_sessions ();
 
        recent_label.set_no_show_all (true);
        recent_scroller.set_no_show_all (true);
-       
+
        recent_label.set_markup (string_compose ("<span weight=\"bold\" size=\"large\">%1</span>", _("Recent Sessions")));
-       
-       recent_session_model = TreeStore::create (recent_session_columns);
-       
-       recent_session_display.set_model (recent_session_model);
-       recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name);
-       recent_session_display.append_column (_("Sample Rate"), recent_session_columns.sample_rate);
-       recent_session_display.append_column (_("Disk Format"), recent_session_columns.disk_format);
-       recent_session_display.set_headers_visible (false);
-       recent_session_display.get_selection()->set_mode (SELECTION_SINGLE);
-       
-       recent_session_display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &SessionDialog::recent_session_row_selected));
-       
-       recent_scroller.add (recent_session_display);
-       recent_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
-       recent_scroller.set_shadow_type (Gtk::SHADOW_IN);
-       
-       recent_session_display.show();
-       recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &SessionDialog::recent_row_activated));
-       
+
        centering_vbox->pack_start (recent_label, false, false, 12);
-       centering_vbox->pack_start (recent_scroller, false, true);
+       centering_vbox->pack_start (recent_scroller, true, true);
 
        /* Browse button */
-       
+
        existing_session_chooser.set_title (_("Select session file"));
        existing_session_chooser.signal_file_set().connect (sigc::mem_fun (*this, &SessionDialog::existing_session_selected));
        existing_session_chooser.set_current_folder(poor_mans_glob (Config->get_default_session_parent_dir()));
-       
+
        FileFilter session_filter;
-       session_filter.add_pattern ("*.ardour");
+       session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
        session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
        existing_session_chooser.add_filter (session_filter);
        existing_session_chooser.set_filter (session_filter);
-       
-#ifdef GTKOSX
-       existing_session_chooser.add_shortcut_folder ("/Volumes");
-#endif
-       
+
+       Gtkmm2ext::add_volume_shortcuts (existing_session_chooser);
+
        Label* browse_label = manage (new Label);
        browse_label->set_markup (string_compose ("<span weight=\"bold\" size=\"large\">%1</span>", _("Other Sessions")));
-       
+
        centering_vbox->pack_start (*browse_label, false, false, 12);
        centering_vbox->pack_start (existing_session_chooser, false, false);
 
+       /* --disable plugins UI */
+
+       _disable_plugins.set_label (_("Safe Mode: Disable all Plugins"));
+       _disable_plugins.set_flags (Gtk::CAN_FOCUS);
+       _disable_plugins.set_relief (Gtk::RELIEF_NORMAL);
+       _disable_plugins.set_mode (true);
+       _disable_plugins.set_active (ARDOUR::Session::get_disable_all_loaded_plugins());
+       _disable_plugins.set_border_width(0);
+       _disable_plugins.signal_clicked().connect (sigc::mem_fun (*this, &SessionDialog::disable_plugins_clicked));
+       centering_vbox->pack_start (_disable_plugins, false, false);
+
        /* pack it all up */
 
        centering_hbox->pack_start (*centering_vbox, true, true);
@@ -481,8 +547,6 @@ SessionDialog::setup_new_session_page ()
        } else if (ARDOUR_UI::instance()->session_loaded) {
                // point the new session file chooser at the parent directory of the current session
                string session_parent_dir = Glib::path_get_dirname(ARDOUR_UI::instance()->the_session()->path());
-               string::size_type last_dir_sep = session_parent_dir.rfind(G_DIR_SEPARATOR);
-               session_parent_dir = session_parent_dir.substr(0, last_dir_sep);
                new_folder_chooser.set_current_folder (session_parent_dir);
                string default_session_folder = poor_mans_glob (Config->get_default_session_parent_dir());
 
@@ -499,12 +563,10 @@ SessionDialog::setup_new_session_page ()
        new_folder_chooser.show ();
        new_folder_chooser.set_title (_("Select folder for session"));
 
-#ifdef __APPLE__
-       new_folder_chooser.add_shortcut_folder ("/Volumes");
-#endif
+       Gtkmm2ext::add_volume_shortcuts (new_folder_chooser);
 
        vbox1->pack_start (*hbox2, false, false);
-               
+
        session_new_vbox.pack_start (*vbox1, false, false);
 
        /* --- */
@@ -527,20 +589,20 @@ SessionDialog::setup_new_session_page ()
 
        HBox* hbox4a = manage (new HBox);
        use_template_button.set_label (_("Use this template"));
-               
+
        TreeModel::Row row = *template_model->prepend ();
        row[session_template_columns.name] = (_("no template"));
        row[session_template_columns.path] = string();
-               
+
        hbox4a->set_spacing (6);
        hbox4a->pack_start (use_template_button, false, false);
        hbox4a->pack_start (template_chooser, true, true);
-               
+
        template_chooser.set_model (template_model);
-               
+
        Gtk::CellRendererText* text_renderer = Gtk::manage (new Gtk::CellRendererText);
        text_renderer->property_editable() = false;
-               
+
        template_chooser.pack_start (*text_renderer);
        template_chooser.add_attribute (text_renderer->property_text(), session_template_columns.name);
        template_chooser.set_active (0);
@@ -548,21 +610,21 @@ SessionDialog::setup_new_session_page ()
        vbox3->pack_start (*hbox4a, false, false);
 
        /* --- */
-       
+
        HBox* hbox5 = manage (new HBox);
-       
+
        hbox5->set_spacing (6);
        hbox5->pack_start (more_new_session_options_button, false, false);
-       
+
        setup_more_options_box ();
        more_new_session_options_button.add (more_options_vbox);
-       
+
        vbox3->pack_start (*hbox5, false, false);
        hbox3->pack_start (*vbox3, true, true, 8);
        vbox2->pack_start (*hbox3, false, false);
-       
+
        /* --- */
-       
+
        session_new_vbox.pack_start (*vbox2, false, false);
        session_new_vbox.show_all ();
 }
@@ -600,14 +662,14 @@ SessionDialog::redisplay_recent_sessions ()
                recent_session_display.set_model (recent_session_model);
                return 0;
        }
-       //
+
        // sort them alphabetically
        sort (rs.begin(), rs.end(), cmp);
 
        for (ARDOUR::RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
                session_directories.push_back ((*i).second);
        }
-       
+
        int session_snapshot_count = 0;
 
        for (vector<std::string>::const_iterator i = session_directories.begin(); i != session_directories.end(); ++i)
@@ -618,7 +680,7 @@ SessionDialog::redisplay_recent_sessions ()
 
                get_state_files_in_directory (*i, state_file_paths);
 
-               vector<string*>* states;
+               vector<string> states;
                vector<const gchar*> item;
                string dirname = *i;
 
@@ -636,7 +698,9 @@ SessionDialog::redisplay_recent_sessions ()
 
                /* now get available states for this session */
 
-               if ((states = Session::possible_states (dirname)) == 0) {
+               states = Session::possible_states (dirname);
+
+               if (states.empty()) {
                        /* no state file? */
                        continue;
                }
@@ -651,23 +715,41 @@ SessionDialog::redisplay_recent_sessions ()
 
                float sr;
                SampleFormat sf;
-               std::string s = Glib::build_filename (dirname, state_file_names.front() + statefile_suffix);
 
-               row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
-               row[recent_session_columns.fullpath] = dirname; /* just the dir, but this works too */
-               row[recent_session_columns.tip] = Glib::Markup::escape_text (dirname);
+               std::string state_file_basename;
+
+               if (state_file_names.size() > 1) {
+                       state_file_basename = Session::get_snapshot_from_instant (dirname);
+                       std::string s = Glib::build_filename (dirname, state_file_basename + statefile_suffix);
+                       if (!Glib::file_test (s, Glib::FILE_TEST_IS_REGULAR)) {
+                               state_file_basename = "";
+                       }
+               }
+
+               if (state_file_basename.empty()) {
+                       state_file_basename = state_file_names.front();
+               }
+
+               std::string s = Glib::build_filename (dirname, state_file_basename + statefile_suffix);
+
+               GStatBuf gsb;
+               g_stat (s.c_str(), &gsb);
+
+               row[recent_session_columns.fullpath] = s;
+               row[recent_session_columns.tip] = Gtkmm2ext::markup_escape_text (dirname);
+               row[recent_session_columns.time_modified] = gsb.st_mtime;
 
                if (Session::get_info_from_path (s, sr, sf) == 0) {
                        row[recent_session_columns.sample_rate] = rate_as_string (sr);
                        switch (sf) {
                        case FormatFloat:
-                               row[recent_session_columns.disk_format] = _("32 bit float");
+                               row[recent_session_columns.disk_format] = _("32-bit float");
                                break;
                        case FormatInt24:
-                               row[recent_session_columns.disk_format] = _("24 bit");
+                               row[recent_session_columns.disk_format] = _("24-bit");
                                break;
                        case FormatInt16:
-                               row[recent_session_columns.disk_format] = _("16 bit");
+                               row[recent_session_columns.disk_format] = _("16-bit");
                                break;
                        }
                } else {
@@ -678,46 +760,100 @@ SessionDialog::redisplay_recent_sessions ()
                ++session_snapshot_count;
 
                if (state_file_names.size() > 1) {
+                       // multiple session files in the session directory - show the directory name.
+                       // if there's not a session file with the same name as the session directory,
+                       // opening the parent item will fail, but expanding it will show the session
+                       // files that actually exist, and the right one can then be opened.
+                       row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
+                       int64_t most_recent = 0;
 
                        // add the children
-
                        for (std::vector<std::string>::iterator i2 = state_file_names.begin(); i2 != state_file_names.end(); ++i2) {
 
+                               s = Glib::build_filename (dirname, *i2 + statefile_suffix);
                                Gtk::TreeModel::Row child_row = *(recent_session_model->append (row.children()));
-                               
+
                                child_row[recent_session_columns.visible_name] = *i2;
-                               child_row[recent_session_columns.fullpath] = Glib::build_filename (dirname, *i2 + statefile_suffix);
-                               child_row[recent_session_columns.tip] = Glib::Markup::escape_text (dirname);
-                               
+                               child_row[recent_session_columns.fullpath] = s;
+                               child_row[recent_session_columns.tip] = Gtkmm2ext::markup_escape_text (dirname);
+                               g_stat (s.c_str(), &gsb);
+                               child_row[recent_session_columns.time_modified] = gsb.st_mtime;
+
+                               Glib::DateTime gdt(Glib::DateTime::create_now_local (gsb.st_mtime));
+                               child_row[recent_session_columns.time_formatted] = gdt.format ("%F %H:%M");
+
+                               if (gsb.st_mtime > most_recent) {
+                                       most_recent = gsb.st_mtime;
+                               }
+
                                if (Session::get_info_from_path (s, sr, sf) == 0) {
                                        child_row[recent_session_columns.sample_rate] = rate_as_string (sr);
                                        switch (sf) {
                                        case FormatFloat:
-                                               child_row[recent_session_columns.disk_format] = _("32 bit float");
+                                               child_row[recent_session_columns.disk_format] = _("32-bit float");
                                                break;
                                        case FormatInt24:
-                                               child_row[recent_session_columns.disk_format] = _("24 bit");
+                                               child_row[recent_session_columns.disk_format] = _("24-bit");
                                                break;
                                        case FormatInt16:
-                                               child_row[recent_session_columns.disk_format] = _("16 bit");
+                                               child_row[recent_session_columns.disk_format] = _("16-bit");
                                                break;
                                        }
                                } else {
                                        child_row[recent_session_columns.sample_rate] = "??";
                                        child_row[recent_session_columns.disk_format] = "--";
                                }
-                               
 
                                ++session_snapshot_count;
                        }
+
+                       assert (most_recent >= row[recent_session_columns.time_modified]);
+                       row[recent_session_columns.time_modified] = most_recent;
+
+               } else {
+                       // only a single session file in the directory - show its actual name.
+                       row[recent_session_columns.visible_name] = state_file_basename;
                }
+
+               Glib::DateTime gdt(Glib::DateTime::create_now_local (row[recent_session_columns.time_modified]));
+               row[recent_session_columns.time_formatted] = gdt.format ("%F %H:%M");
        }
 
-       recent_session_display.set_tooltip_column(1); // recent_session_columns.tip 
+       recent_session_display.set_tooltip_column(1); // recent_session_columns.tip
        recent_session_display.set_model (recent_session_model);
+
+       // custom sort
+       Gtk::TreeView::Column* pColumn;
+       if ((pColumn = recent_session_display.get_column (0))) { // name
+               pColumn->set_sort_column (recent_session_columns.visible_name);
+       }
+       if ((pColumn = recent_session_display.get_column (3))) { // date
+               pColumn->set_sort_column (recent_session_columns.time_modified); // unixtime
+       }
+
+       int32_t sort = UIConfiguration::instance().get_recent_session_sort();
+       if (abs(sort) != 1 + recent_session_columns.visible_name.index () &&
+           abs(sort) != 1 + recent_session_columns.time_modified.index ()) {
+               sort = 1 + recent_session_columns.visible_name.index();
+       }
+       recent_session_model->set_sort_column (abs (sort) -1, sort < 0 ? Gtk::SORT_DESCENDING : Gtk::SORT_ASCENDING);
+
        return session_snapshot_count;
 }
 
+void
+SessionDialog::recent_session_sort_changed ()
+{
+       int column;
+       SortType order;
+       if (recent_session_model->get_sort_column_id (column, order)) {
+               int32_t sort = (column + 1) * (order == Gtk::SORT_DESCENDING ? -1 : 1);
+               if (sort != UIConfiguration::instance().get_recent_session_sort()) {
+                       UIConfiguration::instance().set_recent_session_sort(sort);
+               }
+       }
+}
+
 void
 SessionDialog::recent_session_row_selected ()
 {
@@ -1030,10 +1166,17 @@ SessionDialog::recent_row_activated (const Gtk::TreePath&, Gtk::TreeViewColumn*)
        response (RESPONSE_ACCEPT);
 }
 
+void
+SessionDialog::disable_plugins_clicked ()
+{
+       ARDOUR::Session::set_disable_all_loaded_plugins (_disable_plugins.get_active());
+}
+
 void
 SessionDialog::existing_session_selected ()
 {
        _existing_session_chooser_used = true;
+       recent_session_display.get_selection()->unselect_all();
        /* mark this sensitive in case we come back here after a failed open
         * attempt and the user has hacked up the fix. sigh.
         */