Slightly hacky fix for AFL/PFL button misbehaviour
[ardour.git] / gtk2_ardour / midi_list_editor.cc
index b807de28f9813f901172a7b259ebf966689ca4c1..879b1a9f2fb99257432870a9b7d014605c865b7c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 2009 Paul Davis
+    Copyright (C) 2009 Paul Davis
 
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
 */
 
+#include <cmath>
+
 #include "evoral/midi_util.h"
+#include "evoral/Note.hpp"
+
+#include "ardour/beats_frames_converter.h"
+#include "ardour/midi_model.h"
 #include "ardour/midi_region.h"
+#include "ardour/midi_source.h"
 #include "ardour/session.h"
 #include "ardour/tempo.h"
 
@@ -31,14 +37,24 @@ using namespace Gtk;
 using namespace Glib;
 using namespace ARDOUR;
 
-MidiListEditor::MidiListEditor (Session& s, boost::shared_ptr<MidiRegion> r)
-       : ArdourDialog (r->name(), false, false)
-       , session (s)
+MidiListEditor::MidiListEditor (Session* s, boost::shared_ptr<MidiRegion> r)
+       : ArdourWindow (r->name())
        , region (r)
 {
+       /* We do not handle nested sources/regions. Caller should have tackled this */
+
+       if (r->max_source_level() > 0) {
+               throw failed_constructor();
+       }
+
+       set_session (s);
+
        model = ListStore::create (columns);
        view.set_model (model);
 
+       view.signal_key_press_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_press));
+       view.signal_key_release_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_release));
+
        view.append_column (_("Start"), columns.start);
        view.append_column (_("Channel"), columns.channel);
        view.append_column (_("Num"), columns.note);
@@ -53,6 +69,9 @@ MidiListEditor::MidiListEditor (Session& s, boost::shared_ptr<MidiRegion> r)
        for (int i = 0; i < 6; ++i) {
                CellRendererText* renderer = dynamic_cast<CellRendererText*>(view.get_column_cell_renderer (i));
                renderer->property_editable() = true;
+
+               renderer->signal_editing_started().connect (sigc::bind (sigc::mem_fun (*this, &MidiListEditor::editing_started), i));
+               renderer->signal_editing_canceled().connect (sigc::mem_fun (*this, &MidiListEditor::editing_canceled));
                renderer->signal_edited().connect (sigc::mem_fun (*this, &MidiListEditor::edited));
        }
 
@@ -64,7 +83,7 @@ MidiListEditor::MidiListEditor (Session& s, boost::shared_ptr<MidiRegion> r)
        view.show ();
        scroller.show ();
 
-       get_vbox()->pack_start (scroller);
+       add (scroller);
        set_size_request (400, 400);
 }
 
@@ -72,8 +91,86 @@ MidiListEditor::~MidiListEditor ()
 {
 }
 
+bool
+MidiListEditor::key_press (GdkEventKey* ev)
+{
+       bool editing = !_current_edit.empty();
+       bool ret = false;
+
+       if (editing) {
+               switch (ev->keyval) {
+               case GDK_Tab:
+                       break;
+               case GDK_Right:
+                       break;
+               case GDK_Left:
+                       break;
+               case GDK_Up:
+                       break;
+               case GDK_Down:
+                       break;
+               case GDK_Escape:
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+bool
+MidiListEditor::key_release (GdkEventKey* ev)
+{
+       bool ret = false;
+
+       switch (ev->keyval) {
+       case GDK_Delete:
+               delete_selected_note ();
+               ret = true;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+void
+MidiListEditor::delete_selected_note ()
+{
+       Glib::RefPtr<TreeSelection> selection = view.get_selection();
+       TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
+
+       if (rows.empty()) {
+               return;
+       }
+
+       TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
+       TreeIter iter;
+
+       /* selection mode is single, so rows.begin() is it */
+
+       if ((iter = model->get_iter (*i))) {
+               boost::shared_ptr<NoteType> note = (*iter)[columns._note];
+               cerr << "Would have deleted " << *note << endl;
+       }
+
+}
+
+void
+MidiListEditor::editing_started (CellEditable*, const string& path, int colno)
+{
+       _current_edit = path;
+       cerr << "Now editing " << _current_edit << " Column " << colno << endl;
+}
+
+void
+MidiListEditor::editing_canceled ()
+{
+       _current_edit = "";
+}
+
 void
-MidiListEditor::edited (const Glib::ustring& path, const Glib::ustring& /* text */)
+MidiListEditor::edited (const std::string& path, const std::string& /* text */)
 {
        TreeModel::iterator iter = model->get_iter (path);
 
@@ -88,6 +185,8 @@ MidiListEditor::edited (const Glib::ustring& path, const Glib::ustring& /* text
        cerr << "Edited " << *note << endl;
 
        redisplay_model ();
+
+       /* keep selected row(s), move cursor there, to allow us to continue editing */
 }
 
 void
@@ -96,40 +195,48 @@ MidiListEditor::redisplay_model ()
        view.set_model (Glib::RefPtr<Gtk::ListStore>(0));
        model->clear ();
 
-       MidiModel::Notes notes = region->midi_source(0)->model()->notes();
-       TreeModel::Row row;
-
-       for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
-               row = *(model->append());
-               row[columns.channel] = (*i)->channel();
-               row[columns.note_name] = _("Note");
-               row[columns.note] = (*i)->note();
-               row[columns.velocity] = (*i)->velocity();
+       if (_session) {
 
-               BBT_Time bbt;
-               BBT_Time dur;
+               BeatsFramesConverter conv (_session->tempo_map(), region->position());
+               MidiModel::Notes notes = region->midi_source(0)->model()->notes();
+               TreeModel::Row row;
                stringstream ss;
 
-               session.tempo_map().bbt_time (region->position(), bbt);
+               for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
+                       row = *(model->append());
+                       row[columns.channel] = (*i)->channel() + 1;
+                       row[columns.note_name] = Evoral::midi_note_name ((*i)->note());
+                       row[columns.note] = (*i)->note();
+                       row[columns.velocity] = (*i)->velocity();
+
+                       Timecode::BBT_Time bbt;
+                       double dur;
+
+                       _session->tempo_map().bbt_time (conv.to ((*i)->time()), bbt);
+
+                       ss.str ("");
+                       ss << bbt;
+                       row[columns.start] = ss.str();
 
-               dur.bars = 0;
-               dur.beats = floor ((*i)->time());
-               dur.ticks = 0;
+                       bbt.bars = 0;
+                       dur = (*i)->end_time() - (*i)->time();
+                       bbt.beats = floor (dur);
+                       bbt.ticks = (uint32_t) lrint (fmod (dur, 1.0) * Timecode::BBT_Time::ticks_per_beat);
 
-               session.tempo_map().bbt_duration_at (region->position(), dur, 0);
+                       _session->tempo_map().bbt_duration_at (region->position(), bbt, 0);
 
-               ss << bbt;
-               row[columns.start] = ss.str();
-               ss << dur;
-               row[columns.length] = ss.str();
+                       ss.str ("");
+                       ss << bbt;
+                       row[columns.length] = ss.str();
 
-               session.tempo_map().bbt_time (region->position(), bbt);
-               /* XXX get end point */
+                       _session->tempo_map().bbt_time (conv.to ((*i)->end_time()), bbt);
 
-               ss << bbt;
-               row[columns.end] = ss.str();
+                       ss.str ("");
+                       ss << bbt;
+                       row[columns.end] = ss.str();
 
-               row[columns._note] = (*i);
+                       row[columns._note] = (*i);
+               }
        }
 
        view.set_model (model);