r269@gandalf: fugalh | 2006-08-03 20:18:05 -0600
[ardour.git] / gtk2_ardour / sfdb_ui.cc
1 /*
2     Copyright (C) 2005 Paul Davis 
3     Written by Taybin Rutkin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19
20 */
21
22 #include <map>
23 #include <cerrno>
24
25 #include <gtkmm/box.h>
26 #include <gtkmm/stock.h>
27
28 #include <pbd/convert.h>
29
30 #include <gtkmm2ext/utils.h>
31
32 #include <ardour/audio_library.h>
33 #include <ardour/audioregion.h>
34 #include <ardour/audiofilesource.h>
35
36 #include "ardour_ui.h"
37 #include "gui_thread.h"
38 #include "prompter.h"
39 #include "sfdb_ui.h"
40 #include "utils.h"
41
42 #include "i18n.h"
43
44 using namespace ARDOUR;
45 using namespace PBD;
46 using namespace std;
47
48 SoundFileBox::SoundFileBox ()
49         :
50         _session(0),
51         current_pid(0),
52         fields(Gtk::ListStore::create(label_columns)),
53         main_box (false, 3),
54         top_box (true, 4),
55         bottom_box (true, 4),
56         play_btn(_("Play")),
57         stop_btn(_("Stop")),
58         add_field_btn(_("Add Field...")),
59         remove_field_btn(_("Remove Field"))
60 {
61         set_name (X_("SoundFileBox"));
62         border_frame.set_label (_("Soundfile Info"));
63         border_frame.add (main_box);
64
65         pack_start (border_frame);
66         set_border_width (4);
67
68         main_box.set_border_width (4);
69
70         main_box.pack_start(length, false, false);
71         main_box.pack_start(format, false, false);
72         main_box.pack_start(channels, false, false);
73         main_box.pack_start(samplerate, false, false);
74         main_box.pack_start(field_view, true, true);
75         main_box.pack_start(top_box, false, false);
76         main_box.pack_start(bottom_box, false, false);
77
78         field_view.set_model (fields);
79         field_view.set_size_request(200, 150);
80         field_view.append_column (_("Field"), label_columns.field);
81         field_view.append_column_editable (_("Value"), label_columns.data);
82
83         top_box.set_homogeneous(true);
84         top_box.pack_start(add_field_btn);
85         top_box.pack_start(remove_field_btn);
86
87         remove_field_btn.set_sensitive(false);
88
89         bottom_box.set_homogeneous(true);
90         bottom_box.pack_start(play_btn);
91         bottom_box.pack_start(stop_btn);
92
93         play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::play_btn_clicked));
94         stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_btn_clicked));
95
96         add_field_btn.signal_clicked().connect
97                         (mem_fun (*this, &SoundFileBox::add_field_clicked));
98         remove_field_btn.signal_clicked().connect
99                         (mem_fun (*this, &SoundFileBox::remove_field_clicked));
100         
101         Gtk::CellRendererText* cell(dynamic_cast<Gtk::CellRendererText*>(field_view.get_column_cell_renderer(1)));
102         cell->signal_edited().connect (mem_fun (*this, &SoundFileBox::field_edited));
103
104         field_view.get_selection()->signal_changed().connect (mem_fun (*this, &SoundFileBox::field_selected));
105         Library->fields_changed.connect (mem_fun (*this, &SoundFileBox::setup_fields));
106
107         show_all();
108         stop_btn.hide();
109 }
110
111 void
112 SoundFileBox::set_session(Session* s)
113 {
114         _session = s;
115
116         if (!_session) {
117                 play_btn.set_sensitive(false);
118         } else {
119                 _session->AuditionActive.connect(mem_fun (*this, &SoundFileBox::audition_status_changed));
120         }
121 }
122
123 bool
124 SoundFileBox::setup_labels (string filename) 
125 {
126         path = filename;
127
128         string error_msg;
129         if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
130                 return false;
131         }
132
133         length.set_alignment (0.0f, 0.0f);
134         length.set_text (string_compose("Length: %1", PBD::length2string(sf_info.length, sf_info.samplerate)));
135
136         format.set_alignment (0.0f, 0.0f);
137         format.set_text (sf_info.format_name);
138
139         channels.set_alignment (0.0f, 0.0f);
140         channels.set_text (string_compose("Channels: %1", sf_info.channels));
141
142         samplerate.set_alignment (0.0f, 0.0f);
143         samplerate.set_text (string_compose("Samplerate: %1", sf_info.samplerate));
144
145         setup_fields ();
146
147         return true;
148 }
149
150 void
151 SoundFileBox::setup_fields ()
152 {
153         ENSURE_GUI_THREAD(mem_fun (*this, &SoundFileBox::setup_fields));
154
155         fields->clear ();
156
157         vector<string> field_list;
158         Library->get_fields(field_list);
159
160         vector<string>::iterator i;
161         Gtk::TreeModel::iterator iter;
162         Gtk::TreeModel::Row row;
163         for (i = field_list.begin(); i != field_list.end(); ++i) {
164                 if (!(*i == _("channels") || *i == _("samplerate") ||
165                         *i == _("resolution") || *i == _("format"))) {
166                         iter = fields->append();
167                         row = *iter;
168
169                         string value = Library->get_field(path, *i);
170                         row[label_columns.field] = *i;
171                         row[label_columns.data]  = value;
172                 }
173         }
174 }
175
176 void
177 SoundFileBox::play_btn_clicked ()
178 {
179         if (!_session) {
180                 return;
181         }
182
183         _session->cancel_audition();
184
185         if (access(path.c_str(), R_OK)) {
186                 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
187                 return;
188         }
189
190         static std::map<string, AudioRegion*> region_cache;
191
192         if (region_cache.find (path) == region_cache.end()) {
193                 AudioRegion::SourceList srclist;
194                 AudioFileSource* afs;
195
196                 for (int n = 0; n < sf_info.channels; ++n) {
197                         try {
198                                 afs = AudioFileSource::create (path+":"+string_compose("%1", n));
199                                 srclist.push_back(afs);
200
201                         } catch (failed_constructor& err) {
202                                 error << _("Could not access soundfile: ") << path << endmsg;
203                                 return;
204                         }
205                 }
206
207                 if (srclist.empty()) {
208                         return;
209                 }
210
211                 string result;
212                 _session->region_name (result, Glib::path_get_basename(srclist[0]->name()), false);
213                 AudioRegion* a_region = new AudioRegion(srclist, 0, srclist[0]->length(), result, 0, Region::DefaultFlags, false);
214                 region_cache[path] = a_region;
215         }
216
217         play_btn.hide();
218         stop_btn.show();
219
220         _session->audition_region(*region_cache[path]);
221 }
222
223 void
224 SoundFileBox::stop_btn_clicked ()
225 {
226         if (_session) {
227                 _session->cancel_audition();
228                 play_btn.show();
229                 stop_btn.hide();
230         }
231 }
232
233 void
234 SoundFileBox::add_field_clicked ()
235 {
236     ArdourPrompter prompter (true);
237     string name;
238
239     prompter.set_prompt (_("Name for Field"));
240     prompter.add_button (Gtk::Stock::ADD, Gtk::RESPONSE_ACCEPT);
241     prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
242
243     switch (prompter.run ()) {
244                 case Gtk::RESPONSE_ACCEPT:
245                 prompter.get_result (name);
246                         if (name.length()) {
247                                 Library->add_field (name);
248                                 Library->save_changes ();
249                         }
250                 break;
251
252             default:
253                 break;
254     }
255 }
256
257 void
258 SoundFileBox::remove_field_clicked ()
259 {
260         field_view.get_selection()->selected_foreach_iter(mem_fun(*this, &SoundFileBox::delete_row));
261
262         Library->save_changes ();
263 }
264
265 void
266 SoundFileBox::field_edited (const Glib::ustring& str1, const Glib::ustring& str2)
267 {
268         cout << "field_edited" << endl;
269         Library->save_changes ();
270 }
271
272 void
273 SoundFileBox::delete_row (const Gtk::TreeModel::iterator& iter)
274 {
275         Gtk::TreeModel::Row row = *iter;
276
277         Library->remove_field(row[label_columns.field]);
278 }
279
280 void
281 SoundFileBox::audition_status_changed (bool active)
282 {
283         ENSURE_GUI_THREAD(bind (mem_fun (*this, &SoundFileBox::audition_status_changed), active));
284         
285         if (!active) {
286                 stop_btn_clicked ();
287         }
288 }
289
290 void
291 SoundFileBox::field_selected ()
292 {
293         if (field_view.get_selection()->count_selected_rows()) {
294                 remove_field_btn.set_sensitive(true);
295         } else {
296                 remove_field_btn.set_sensitive(false);
297         }
298 }
299
300 SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s)
301         : ArdourDialog (title, false),
302           chooser (Gtk::FILE_CHOOSER_ACTION_OPEN)
303 {
304         get_vbox()->pack_start(chooser);
305         chooser.set_preview_widget(preview);
306         chooser.set_select_multiple (true);
307
308         chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
309
310         set_session (s);
311 }
312
313 void
314 SoundFileBrowser::set_session (Session* s)
315 {
316         preview.set_session(s);
317 }
318
319 void
320 SoundFileBrowser::update_preview ()
321 {
322         chooser.set_preview_widget_active(preview.setup_labels(chooser.get_filename()));
323 }
324
325 SoundFileChooser::SoundFileChooser (string title, ARDOUR::Session* s)
326         :
327         SoundFileBrowser(title, s)
328 {
329         add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
330         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
331         show_all ();
332 }
333
334 static const char *import_mode_strings[] = {
335         X_("Add to Region list"),
336         X_("Add as new Track(s)"),
337         X_("Add to selected Track(s)"),
338         0
339 };
340
341 vector<string> SoundFileOmega::mode_strings;
342
343 SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s)
344         : SoundFileBrowser (title, s),
345           split_check (_("Split Channels"))
346 {
347         if (mode_strings.empty()) {
348                 mode_strings = PBD::internationalize (import_mode_strings);
349         }
350
351         ARDOUR_UI::instance()->tooltips().set_tip(split_check, 
352                         _("Create a region for each channel"));
353
354         Gtk::Button* btn = add_button (_("Embed"), ResponseEmbed);
355         ARDOUR_UI::instance()->tooltips().set_tip(*btn, 
356                         _("Link to an external file"));
357
358         btn = add_button (_("Import"), ResponseImport);
359         ARDOUR_UI::instance()->tooltips().set_tip(*btn, 
360                         _("Copy a file to the session folder"));
361
362         add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE);
363
364         Gtk::HBox *box = manage (new Gtk::HBox());
365
366         Gtkmm2ext::set_popdown_strings (mode_combo, mode_strings);
367
368         set_mode (Editing::ImportAsRegion);
369
370         box->pack_start (split_check);
371         box->pack_start (mode_combo);
372
373         mode_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::mode_changed));
374
375         chooser.set_extra_widget (*box);
376         
377         show_all ();
378 }
379
380 bool
381 SoundFileOmega::get_split ()
382 {
383         return split_check.get_active();
384 }
385
386 vector<Glib::ustring>
387 SoundFileOmega::get_paths ()
388 {
389         return chooser.get_filenames();
390 }
391
392 void
393 SoundFileOmega::set_mode (Editing::ImportMode mode)
394 {
395         mode_combo.set_active_text (mode_strings[(int)mode]);
396
397         switch (mode) {
398         case Editing::ImportAsRegion:
399                 split_check.set_sensitive (true);
400                 break;
401         case Editing::ImportAsTrack:
402                 split_check.set_sensitive (true);
403                 break;
404         case Editing::ImportToTrack:
405                 split_check.set_sensitive (false);
406                 break;
407         case Editing::ImportAsTapeTrack:
408                 split_check.set_sensitive (true);
409                 break;
410         }
411 }
412
413 Editing::ImportMode
414 SoundFileOmega::get_mode ()
415 {
416         vector<string>::iterator i;
417         uint32_t n;
418         string str = mode_combo.get_active_text ();
419
420         for (n = 0, i = mode_strings.begin (); i != mode_strings.end(); ++i, ++n) {
421                 if (str == (*i)) {
422                         break;
423                 }
424         }
425
426         if (i == mode_strings.end()) {
427                 fatal << string_compose (_("programming error: %1"), X_("unknown import mode string")) << endmsg;
428                 /*NOTREACHED*/
429         }
430
431         return (Editing::ImportMode) (n);
432 }
433
434 void
435 SoundFileOmega::mode_changed ()
436 {
437         Editing::ImportMode mode = get_mode();
438
439         switch (mode) {
440         case Editing::ImportAsRegion:
441                 split_check.set_sensitive (true);
442                 break;
443         case Editing::ImportAsTrack:
444                 split_check.set_sensitive (true);
445                 break;
446         case Editing::ImportToTrack:
447                 split_check.set_sensitive (false);
448                 break;
449         case Editing::ImportAsTapeTrack:
450                 split_check.set_sensitive (true);
451                 break;
452         }
453 }