various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / midi_list_editor.cc
blobceb2e7ace67d79524b96c0a0ca88f0ef69339d20
1 /*
2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #include <cmath>
21 #include "evoral/midi_util.h"
23 #include "ardour/beats_frames_converter.h"
24 #include "ardour/midi_region.h"
25 #include "ardour/session.h"
26 #include "ardour/tempo.h"
28 #include "midi_list_editor.h"
30 #include "i18n.h"
32 using namespace std;
33 using namespace Gtk;
34 using namespace Glib;
35 using namespace ARDOUR;
37 MidiListEditor::MidiListEditor (Session* s, boost::shared_ptr<MidiRegion> r)
38 : ArdourDialog (r->name(), false, false)
39 , region (r)
41 /* We do not handle nested sources/regions. Caller should have tackled this */
43 if (r->max_source_level() > 0) {
44 throw failed_constructor();
47 set_session (s);
49 model = ListStore::create (columns);
50 view.set_model (model);
52 view.signal_key_press_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_press));
53 view.signal_key_release_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_release));
55 view.append_column (_("Start"), columns.start);
56 view.append_column (_("Channel"), columns.channel);
57 view.append_column (_("Num"), columns.note);
58 view.append_column (_("Name"), columns.note_name);
59 view.append_column (_("Vel"), columns.velocity);
60 view.append_column (_("Length"), columns.length);
61 view.append_column (_("End"), columns.end);
62 view.set_headers_visible (true);
63 view.set_name (X_("MidiListView"));
64 view.set_rules_hint (true);
66 for (int i = 0; i < 6; ++i) {
67 CellRendererText* renderer = dynamic_cast<CellRendererText*>(view.get_column_cell_renderer (i));
68 renderer->property_editable() = true;
70 renderer->signal_editing_started().connect (sigc::bind (sigc::mem_fun (*this, &MidiListEditor::editing_started), i));
71 renderer->signal_editing_canceled().connect (sigc::mem_fun (*this, &MidiListEditor::editing_canceled));
72 renderer->signal_edited().connect (sigc::mem_fun (*this, &MidiListEditor::edited));
75 scroller.add (view);
76 scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
78 redisplay_model ();
80 view.show ();
81 scroller.show ();
83 get_vbox()->pack_start (scroller);
84 set_size_request (400, 400);
87 MidiListEditor::~MidiListEditor ()
91 bool
92 MidiListEditor::key_press (GdkEventKey* ev)
94 bool editing = !_current_edit.empty();
95 bool ret = false;
97 if (editing) {
98 switch (ev->keyval) {
99 case GDK_Tab:
100 break;
101 case GDK_Right:
102 break;
103 case GDK_Left:
104 break;
105 case GDK_Up:
106 break;
107 case GDK_Down:
108 break;
109 case GDK_Escape:
110 break;
114 return ret;
117 bool
118 MidiListEditor::key_release (GdkEventKey* ev)
120 bool ret = false;
122 switch (ev->keyval) {
123 case GDK_Delete:
124 delete_selected_note ();
125 ret = true;
126 break;
127 default:
128 break;
131 return ret;
134 void
135 MidiListEditor::delete_selected_note ()
137 Glib::RefPtr<TreeSelection> selection = view.get_selection();
138 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
140 if (rows.empty()) {
141 return;
144 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
145 TreeIter iter;
147 /* selection mode is single, so rows.begin() is it */
149 if ((iter = model->get_iter (*i))) {
150 boost::shared_ptr<NoteType> note = (*iter)[columns._note];
151 cerr << "Would have deleted " << *note << endl;
156 void
157 MidiListEditor::editing_started (CellEditable*, const string& path, int colno)
159 _current_edit = path;
160 cerr << "Now editing " << _current_edit << " Column " << colno << endl;
163 void
164 MidiListEditor::editing_canceled ()
166 _current_edit = "";
169 void
170 MidiListEditor::edited (const std::string& path, const std::string& /* text */)
172 TreeModel::iterator iter = model->get_iter (path);
174 cerr << "Edit at " << path << endl;
176 if (!iter) {
177 return;
180 boost::shared_ptr<NoteType> note = (*iter)[columns._note];
182 cerr << "Edited " << *note << endl;
184 redisplay_model ();
186 /* keep selected row(s), move cursor there, to allow us to continue editing */
189 void
190 MidiListEditor::redisplay_model ()
192 view.set_model (Glib::RefPtr<Gtk::ListStore>(0));
193 model->clear ();
195 if (_session) {
197 BeatsFramesConverter conv (_session->tempo_map(), region->position());
198 MidiModel::Notes notes = region->midi_source(0)->model()->notes();
199 TreeModel::Row row;
200 stringstream ss;
202 for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
203 row = *(model->append());
204 row[columns.channel] = (*i)->channel() + 1;
205 row[columns.note_name] = Evoral::midi_note_name ((*i)->note());
206 row[columns.note] = (*i)->note();
207 row[columns.velocity] = (*i)->velocity();
209 Timecode::BBT_Time bbt;
210 double dur;
212 _session->tempo_map().bbt_time (conv.to ((*i)->time()), bbt);
214 ss.str ("");
215 ss << bbt;
216 row[columns.start] = ss.str();
218 bbt.bars = 0;
219 dur = (*i)->end_time() - (*i)->time();
220 bbt.beats = floor (dur);
221 bbt.ticks = (uint32_t) lrint (fmod (dur, 1.0) * Timecode::BBT_Time::ticks_per_beat);
223 _session->tempo_map().bbt_duration_at (region->position(), bbt, 0);
225 ss.str ("");
226 ss << bbt;
227 row[columns.length] = ss.str();
229 _session->tempo_map().bbt_time (conv.to ((*i)->end_time()), bbt);
231 ss.str ("");
232 ss << bbt;
233 row[columns.end] = ss.str();
235 row[columns._note] = (*i);
239 view.set_model (model);