enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / keyeditor.cc
index f300b4751c45fe76d3d62f293493e23c51dd3449..b19b6d5fc009611b321026bac56e61bf0d1aecfe 100644 (file)
 #endif
 
 #include <map>
+#include <fstream>
+#include <sstream>
 
 #include <boost/algorithm/string.hpp>
 
+#include <glib.h>
+#include <glib/gstdio.h>
+
 #include <gtkmm/stock.h>
 #include <gtkmm/label.h>
 #include <gtkmm/accelkey.h>
@@ -34,6 +39,8 @@
 #include "gtkmm2ext/bindings.h"
 #include "gtkmm2ext/utils.h"
 
+#include "pbd/error.h"
+#include "pbd/openuri.h"
 #include "pbd/strsplit.h"
 
 #include "ardour/filesystem_paths.h"
@@ -43,7 +50,7 @@
 #include "keyboard.h"
 #include "keyeditor.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace Gtk;
@@ -53,6 +60,8 @@ using namespace PBD;
 using Gtkmm2ext::Keyboard;
 using Gtkmm2ext::Bindings;
 
+sigc::signal<void> KeyEditor::UpdateBindings;
+
 void bindings_collision_dialog (Gtk::Window& parent)
 {
        ArdourDialog dialog (parent, _("Colliding keybindings"), true);
@@ -70,6 +79,7 @@ KeyEditor::KeyEditor ()
        , unbind_box (BUTTONBOX_END)
        , filter_entry (_("Search..."), true)
        , filter_string("")
+       , print_button (_("Print"))
        , sort_column(0)
        , sort_type(Gtk::SORT_ASCENDING)
 {
@@ -84,7 +94,7 @@ KeyEditor::KeyEditor ()
        filter_entry.signal_search_string_updated ().connect (sigc::mem_fun (*this, &KeyEditor::search_string_updated));
        vpacker.pack_start (filter_entry, false, false);
 
-       Label* hint = manage (new Label (_("Select an action, then press the key(s) to (re)set its shortcut")));
+       Label* hint = manage (new Label (_("To remove a shortcut select an action then press this: ")));
        hint->show ();
        unbind_box.set_spacing (6);
        unbind_box.pack_start (*hint, false, true);
@@ -98,27 +108,58 @@ KeyEditor::KeyEditor ()
        reset_button.add (reset_label);
        reset_label.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Reset Bindings to Defaults")));
 
+       print_button.signal_clicked().connect (sigc::mem_fun (*this, &KeyEditor::print));
+
        reset_box.pack_start (reset_button, true, false);
+       reset_box.pack_start (print_button, true, false);
        reset_box.show ();
        reset_button.show ();
        reset_label.show ();
+       print_button.show ();
        reset_button.signal_clicked().connect (sigc::mem_fun (*this, &KeyEditor::reset));
        vpacker.pack_start (reset_box, false, false);
 
        add (vpacker);
 
        unbind_button.set_sensitive (false);
+       UpdateBindings.connect (sigc::mem_fun (*this, &KeyEditor::refresh));
 }
 
 void
 KeyEditor::add_tab (string const & name, Bindings& bindings)
 {
        Tab* t = new Tab (*this, name, &bindings);
-       t->populate ();
+
+       if (t->populate () == 0) {
+               /* no bindings */
+               delete t;
+               return;
+       }
+
+       tabs.push_back (t);
        t->show_all ();
        notebook.append_page (*t, name);
 }
 
+
+void
+KeyEditor::remove_tab (string const &name)
+{
+       guint npages = notebook.get_n_pages ();
+
+       for (guint n = 0; n < npages; ++n) {
+               Widget* w = notebook.get_nth_page (n);
+               Tab* tab = dynamic_cast<Tab*> (w);
+               if (tab) {
+                       if (tab->name == name) {
+                               notebook.remove_page (*w);
+                               return;
+                       }
+               }
+       }
+       cerr << "Removed " << name << endl;
+}
+
 void
 KeyEditor::unbind ()
 {
@@ -133,8 +174,12 @@ KeyEditor::page_change (GtkNotebookPage*, guint)
 }
 
 bool
-KeyEditor::Tab::on_key_press_event (GdkEventKey* ev)
+KeyEditor::Tab::key_press_event (GdkEventKey* ev)
 {
+       if (view.get_selection()->count_selected_rows() != 1) {
+               return false;
+       }
+
        if (!ev->is_modifier) {
                last_keyval = ev->keyval;
        }
@@ -148,8 +193,12 @@ KeyEditor::Tab::on_key_press_event (GdkEventKey* ev)
 }
 
 bool
-KeyEditor::Tab::on_key_release_event (GdkEventKey* ev)
+KeyEditor::Tab::key_release_event (GdkEventKey* ev)
 {
+       if (view.get_selection()->count_selected_rows() != 1) {
+               return false;
+       }
+
        if (last_keyval == 0) {
                return false;
        }
@@ -168,7 +217,7 @@ KeyEditor::Tab::Tab (KeyEditor& ke, string const & str, Bindings* b)
 {
        data_model = TreeStore::create(columns);
        populate ();
-       
+
        filter = TreeModelFilter::create(data_model);
        filter->set_visible_func (sigc::mem_fun (*this, &Tab::visible_func));
 
@@ -187,6 +236,8 @@ KeyEditor::Tab::Tab (KeyEditor& ke, string const & str, Bindings* b)
        view.set_name (X_("KeyEditorTree"));
 
        view.signal_cursor_changed().connect (sigc::mem_fun (*this, &Tab::action_selected));
+       view.signal_key_press_event().connect (sigc::mem_fun (*this, &Tab::key_press_event), false);
+       view.signal_key_release_event().connect (sigc::mem_fun (*this, &Tab::key_release_event), false);
 
        view.get_column(0)->set_sort_column (columns.name);
        view.get_column(1)->set_sort_column (columns.binding);
@@ -277,7 +328,7 @@ KeyEditor::Tab::bind (GdkEventKey* release_event, guint pressed_key)
        }
 }
 
-void
+uint32_t
 KeyEditor::Tab::populate ()
 {
        vector<string> paths;
@@ -367,6 +418,8 @@ KeyEditor::Tab::populate ()
                }
                row[columns.action] = *a;
        }
+
+       return data_model->children().size();
 }
 
 void
@@ -442,7 +495,12 @@ void
 KeyEditor::reset ()
 {
        Keyboard::the_keyboard().reset_bindings ();
+       refresh ();
+}
 
+void
+KeyEditor::refresh ()
+{
        for (Tabs::iterator t = tabs.begin(); t != tabs.end(); ++t) {
                (*t)->view.get_selection()->unselect_all ();
                (*t)->populate ();
@@ -459,6 +517,56 @@ void
 KeyEditor::search_string_updated (const std::string& filter)
 {
        filter_string = boost::to_lower_copy(filter);
-       current_tab ()->filter->refilter ();
+       KeyEditor::Tab* tab = current_tab ();
+       if (tab) {
+               tab->filter->refilter ();
+       }
 }
 
+void
+KeyEditor::print () const
+{
+       stringstream sstr;
+       Bindings::save_all_bindings_as_html (sstr);
+
+       if (sstr.str().empty()) {
+               return;
+       }
+
+
+       gchar* file_name;
+       GError *err = NULL;
+       gint fd;
+
+       if ((fd = g_file_open_tmp ("akprintXXXXXX.html", &file_name, &err)) < 0) {
+               if (err) {
+                       error << string_compose (_("Could not open temporary file to print bindings (%1)"), err->message) << endmsg;
+                       g_error_free (err);
+               }
+               return;
+       }
+
+#ifdef PLATFORM_WINDOWS
+       ::close (fd);
+#endif
+
+       err = NULL;
+
+       if (!g_file_set_contents (file_name, sstr.str().c_str(), sstr.str().size(), &err)) {
+#ifndef PLATFORM_WINDOWS
+               ::close (fd);
+#endif
+               g_unlink (file_name);
+               if (err) {
+                       error << string_compose (_("Could not save bindings to file (%1)"), err->message) << endmsg;
+                       g_error_free (err);
+               }
+               return;
+       }
+
+#ifndef PLATFORM_WINDOWS
+       ::close (fd);
+#endif
+
+       PBD::open_uri (string_compose ("file:///%1", file_name));
+}