partially revert some of the recent work on tempo to reflect new understanding of...
[ardour.git] / gtk2_ardour / midi_list_editor.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include <cmath>
20
21 #include "evoral/midi_util.h"
22 #include "evoral/Note.hpp"
23
24 #include "ardour/beats_frames_converter.h"
25 #include "ardour/midi_model.h"
26 #include "ardour/midi_region.h"
27 #include "ardour/midi_source.h"
28 #include "ardour/session.h"
29 #include "ardour/tempo.h"
30
31 #include "midi_list_editor.h"
32
33 #include "i18n.h"
34
35 using namespace std;
36 using namespace Gtk;
37 using namespace Glib;
38 using namespace ARDOUR;
39
40 MidiListEditor::MidiListEditor (Session* s, boost::shared_ptr<MidiRegion> r)
41         : ArdourWindow (r->name())
42         , region (r)
43 {
44         /* We do not handle nested sources/regions. Caller should have tackled this */
45
46         if (r->max_source_level() > 0) {
47                 throw failed_constructor();
48         }
49
50         set_session (s);
51
52         model = ListStore::create (columns);
53         view.set_model (model);
54
55         view.signal_key_press_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_press));
56         view.signal_key_release_event().connect (sigc::mem_fun (*this, &MidiListEditor::key_release));
57
58         view.append_column (_("Start"), columns.start);
59         view.append_column (_("Channel"), columns.channel);
60         view.append_column (_("Num"), columns.note);
61         view.append_column (_("Name"), columns.note_name);
62         view.append_column (_("Vel"), columns.velocity);
63         view.append_column (_("Length"), columns.length);
64         view.append_column (_("End"), columns.end);
65         view.set_headers_visible (true);
66         view.set_name (X_("MidiListView"));
67         view.set_rules_hint (true);
68
69         for (int i = 0; i < 6; ++i) {
70                 CellRendererText* renderer = dynamic_cast<CellRendererText*>(view.get_column_cell_renderer (i));
71                 renderer->property_editable() = true;
72
73                 renderer->signal_editing_started().connect (sigc::bind (sigc::mem_fun (*this, &MidiListEditor::editing_started), i));
74                 renderer->signal_editing_canceled().connect (sigc::mem_fun (*this, &MidiListEditor::editing_canceled));
75                 renderer->signal_edited().connect (sigc::mem_fun (*this, &MidiListEditor::edited));
76         }
77
78         scroller.add (view);
79         scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
80
81         redisplay_model ();
82
83         view.show ();
84         scroller.show ();
85
86         add (scroller);
87         set_size_request (400, 400);
88 }
89
90 MidiListEditor::~MidiListEditor ()
91 {
92 }
93
94 bool
95 MidiListEditor::key_press (GdkEventKey* ev)
96 {
97         bool editing = !_current_edit.empty();
98         bool ret = false;
99
100         if (editing) {
101                 switch (ev->keyval) {
102                 case GDK_Tab:
103                         break;
104                 case GDK_Right:
105                         break;
106                 case GDK_Left:
107                         break;
108                 case GDK_Up:
109                         break;
110                 case GDK_Down:
111                         break;
112                 case GDK_Escape:
113                         break;
114                 }
115         }
116
117         return ret;
118 }
119
120 bool
121 MidiListEditor::key_release (GdkEventKey* ev)
122 {
123         bool ret = false;
124
125         switch (ev->keyval) {
126         case GDK_Delete:
127                 delete_selected_note ();
128                 ret = true;
129                 break;
130         default:
131                 break;
132         }
133
134         return ret;
135 }
136
137 void
138 MidiListEditor::delete_selected_note ()
139 {
140         Glib::RefPtr<TreeSelection> selection = view.get_selection();
141         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
142
143         if (rows.empty()) {
144                 return;
145         }
146
147         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
148         TreeIter iter;
149
150         /* selection mode is single, so rows.begin() is it */
151
152         if ((iter = model->get_iter (*i))) {
153                 boost::shared_ptr<NoteType> note = (*iter)[columns._note];
154                 cerr << "Would have deleted " << *note << endl;
155         }
156
157 }
158
159 void
160 MidiListEditor::editing_started (CellEditable*, const string& path, int colno)
161 {
162         _current_edit = path;
163         cerr << "Now editing " << _current_edit << " Column " << colno << endl;
164 }
165
166 void
167 MidiListEditor::editing_canceled ()
168 {
169         _current_edit = "";
170 }
171
172 void
173 MidiListEditor::edited (const std::string& path, const std::string& /* text */)
174 {
175         TreeModel::iterator iter = model->get_iter (path);
176
177         cerr << "Edit at " << path << endl;
178
179         if (!iter) {
180                 return;
181         }
182
183         boost::shared_ptr<NoteType> note = (*iter)[columns._note];
184
185         cerr << "Edited " << *note << endl;
186
187         redisplay_model ();
188
189         /* keep selected row(s), move cursor there, to allow us to continue editing */
190 }
191
192 void
193 MidiListEditor::redisplay_model ()
194 {
195         view.set_model (Glib::RefPtr<Gtk::ListStore>(0));
196         model->clear ();
197
198         if (_session) {
199
200                 BeatsFramesConverter conv (_session->tempo_map(), region->position());
201                 MidiModel::Notes notes = region->midi_source(0)->model()->notes();
202                 TreeModel::Row row;
203                 stringstream ss;
204
205                 for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
206                         row = *(model->append());
207                         row[columns.channel] = (*i)->channel() + 1;
208                         row[columns.note_name] = Evoral::midi_note_name ((*i)->note());
209                         row[columns.note] = (*i)->note();
210                         row[columns.velocity] = (*i)->velocity();
211
212                         Timecode::BBT_Time bbt;
213                         double dur;
214
215                         _session->tempo_map().bbt_time (conv.to ((*i)->time()), bbt);
216
217                         ss.str ("");
218                         ss << bbt;
219                         row[columns.start] = ss.str();
220
221                         bbt.bars = 0;
222                         dur = (*i)->end_time() - (*i)->time();
223                         bbt.beats = floor (dur);
224                         bbt.ticks = (uint32_t) lrint (fmod (dur, 1.0) * Timecode::BBT_Time::ticks_per_beat);
225
226                         _session->tempo_map().bbt_duration_at (region->position(), bbt, 0);
227
228                         ss.str ("");
229                         ss << bbt;
230                         row[columns.length] = ss.str();
231
232                         _session->tempo_map().bbt_time (conv.to ((*i)->end_time()), bbt);
233
234                         ss.str ("");
235                         ss << bbt;
236                         row[columns.end] = ss.str();
237
238                         row[columns._note] = (*i);
239                 }
240         }
241
242         view.set_model (model);
243 }