/* Copyright (C) 2000-2003 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ardour_ui.h" #include "public_editor.h" #include "library_ui.h" #include "prompter.h" #include "gui_thread.h" #include "i18n.h" using namespace std; using namespace ARDOUR; using namespace Gtk; using namespace sigc; SoundFileSelector::SoundFileSelector () : ArdourDialog ("sound file selector"), vbox(false, 4), sfdb_label(_("Soundfile Library")), fs_label(_("Filesystem")), import_box(false, 4), import_btn (X_("foo")), // force a label split_channels (_("Split Channels"), 0.0), info_box (0) { set_title(_("ardour: soundfile selector")); set_name("SoundFileSelector"); set_default_size (500, 400); set_keyboard_input (true); add (main_box); main_box.set_border_width (6); main_box.pack_start(vbox, true, true); vbox.pack_start(notebook, true, true); vbox.pack_start(import_box, false, false); notebook.set_name("SoundFileSelectorNotebook"); notebook.append_page(sf_browser, fs_label); notebook.append_page(sfdb_tree, sfdb_label); import_box.set_homogeneous (true); import_box.pack_start(import_btn); import_box.pack_start (split_channels); split_channels.set_active(false); split_channels.set_sensitive (false); delete_event.connect (mem_fun(*this, &ArdourDialog::wm_close_event)); import_btn.signal_clicked().connect (mem_fun(*this, &SoundFileSelector::import_btn_clicked)); sfdb_tree.group_selected.connect (mem_fun(*this, &SoundFileSelector::sfdb_group_selected)); sfdb_tree.member_selected.connect (bind (mem_fun(*this, &SoundFileSelector::member_selected), true)); sf_browser.member_selected.connect (bind (mem_fun(*this, &SoundFileSelector::member_selected), false)); sf_browser.member_deselected.connect (bind (mem_fun(*this, &SoundFileSelector::member_deselected), false)); sfdb_tree.deselected.connect (mem_fun(*this, &SoundFileSelector::sfdb_deselected)); sf_browser.group_selected.connect (mem_fun(*this, &SoundFileSelector::browser_group_selected)); notebook.switch_page.connect (mem_fun(*this, &SoundFileSelector::page_switched)); } SoundFileSelector::~SoundFileSelector() { } void SoundFileSelector::import_btn_clicked () { vector paths; PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (sess) { sess->cancel_audition(); } if (sfdb) { for (list::iterator i = sfdb_tree.selection.begin(); i != sfdb_tree.selection.end(); ++i) { paths.push_back (Library->get_member_filename (*i)); } } else { for (list::iterator i = sf_browser.selection.begin(); i != sf_browser.selection.end(); ++i) { paths.push_back ((*i).str); } } Action (paths, split_channels.get_active()); /* EMIT_SIGNAL */ if (sfdb) { sfdb_tree.clear_selection (); } else { sf_browser.clear_selection (); } if (hide_after_action) { hide (); Action.clear(); } hide_after_action = false; } void SoundFileSelector::run (string action, bool multi, bool hide_after) { static_cast(import_btn.get_child())->set_text (action); import_btn.set_sensitive(false); if (multi) { split_channels.show (); } else { split_channels.hide (); } multiable = multi; hide_after_action = hide_after; set_position (Gtk::WIN_POS_MOUSE); ArdourDialog::run (); } void SoundFileSelector::hide_import_stuff() { import_box.hide_all(); } void SoundFileSelector::page_switched(Gtk::Notebook_Helpers::Page* page, guint page_num) { if (page_num == 1) { sfdb = true; if (!sfdb_tree.selection.empty()) { member_selected (sfdb_tree.selection.back(), true); } } else { sfdb = false; if (!sf_browser.selection.empty()) { member_selected (sf_browser.selection.back().str, false); } } } void SoundFileSelector::sfdb_deselected() { import_btn.set_sensitive(false); } void SoundFileSelector::browser_group_selected() { sfdb_group_selected(); } void SoundFileSelector::sfdb_group_selected() { import_btn.set_sensitive(false); split_channels.set_sensitive(false); if (info_box) { delete info_box; info_box = 0; } } void SoundFileSelector::member_selected(string member, bool sfdb) { if (info_box) { delete info_box; info_box = 0; } if (member.empty()) { return; } try { info_box = new SoundFileBox(member, sfdb); } catch (failed_constructor& err) { /* nothing to do */ return; } main_box.pack_start (*info_box, false, false); import_btn.set_sensitive (true); if (multiable) { split_channels.set_sensitive(true); } } void SoundFileSelector::member_deselected (bool sfdb) { bool keep_action_available; string last; if (info_box) { delete info_box; info_box = 0; } if (sfdb) { if ((keep_action_available = !sfdb_tree.selection.empty())) { last = sfdb_tree.selection.back(); } } else { if ((keep_action_available = !sf_browser.selection.empty())) { last = sf_browser.selection.back().str; } } if (keep_action_available) { if (info_box) { delete info_box; info_box = 0; } try { info_box = new SoundFileBox(last, sfdb); } catch (failed_constructor& err) { /* nothing to do */ return; } import_btn.set_sensitive (true); if (multiable) { split_channels.set_sensitive(true); } main_box.pack_start(*info_box, false, false); } } void SoundFileSelector::get_result (vector& paths, bool& split) { if (sfdb) { for (list::iterator i = sfdb_tree.selection.begin(); i != sfdb_tree.selection.end(); ++i) { paths.push_back (Library->get_member_filename (*i)); } } else { for (list::iterator i = sf_browser.selection.begin(); i != sf_browser.selection.end(); ++i) { paths.push_back ((*i).str); } } split = split_channels.get_active(); } SoundFileBrowser::SoundFileBrowser() : Gtk::VBox(false, 3) { fs_selector.hide_fileop_buttons(); fs_selector.set_filename("/"); // This is ugly ugly ugly. But gtk1 (and gtk2) don't give us any // choice. GtkFileSelection* fs_gtk = fs_selector.gobj(); file_list= Gtk::wrap((GtkCList*)(fs_gtk->file_list)); Gtk::VBox* vbox = manage(new Gtk::VBox); Gtk::HBox* tmphbox = manage(new Gtk::HBox); Gtk::OptionMenu* option_menu = Gtk::wrap((GtkOptionMenu*)(fs_gtk->history_pulldown)); option_menu->reparent(*tmphbox); vbox->pack_start(*tmphbox, false, false); /* XXX This interface isn't supported in gtkmm. Redo it with a BoxList& vbox->set_child_packing(*option_menu, false, false); */ Gtk::HBox* hbox = manage(new Gtk::HBox); Gtk::ScrolledWindow* dir_scroll = manage(new Gtk::ScrolledWindow); Gtk::ScrolledWindow* file_scroll = manage(new Gtk::ScrolledWindow); dir_scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); file_scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); Gtk::CList* dir_list = Gtk::wrap((GtkCList*)(fs_gtk->dir_list)); dir_list->reparent(*dir_scroll); file_list->reparent(*file_scroll); file_list->set_selection_mode (GTK_SELECTION_MULTIPLE); hbox->pack_start(*dir_scroll); hbox->pack_start(*file_scroll); vbox->pack_start(*hbox, true, true); Gtk::VBox* tmpvbox = manage(new Gtk::VBox); Gtk::Label* selection_text = Gtk::wrap((GtkLabel*)(fs_gtk->selection_text)); selection_text->reparent(*tmpvbox); Gtk::Entry* selection_entry= Gtk::wrap((GtkEntry*)(fs_gtk->selection_entry)); selection_entry->reparent(*tmpvbox); vbox->pack_start(*tmpvbox, false, false); pack_start(*vbox, true, true); dir_list->select_row.connect(mem_fun(*this, &SoundFileBrowser::dir_list_selected)); file_list->select_row.connect(mem_fun(*this, &SoundFileBrowser::file_list_selected)); file_list->unselect_row.connect(mem_fun(*this, &SoundFileBrowser::file_list_deselected)); dir_list->set_name("SoundFileBrowserList"); file_list->set_name("SoundFileBrowserList"); } SoundFileBrowser::~SoundFileBrowser() { } void SoundFileBrowser::clear_selection () { file_list->selection().clear (); selection.clear (); } void SoundFileBrowser::dir_list_selected(gint row, gint col, GdkEvent* ev) { current_member = ""; current_group = ""; group_selected(); /* EMIT_SIGNAL */ } void SoundFileBrowser::file_list_selected(gint row, gint col, GdkEvent* ev) { current_group = ""; current_member = fs_selector.get_filename(); selection.push_back (RowTaggedString (row, current_member)); member_selected(safety_check_file(current_member)); /* EMIT_SIGNAL */ } void SoundFileBrowser::file_list_deselected(gint row, gint col, GdkEvent* ev) { current_group = ""; current_member = file_list->cell (row, 0).get_text(); for (list::iterator i = selection.begin(); i != selection.end(); ++i) { if ((*i).row == row) { selection.erase (i); break; } } member_deselected(); /* EMIT_SIGNAL */ } string SoundFileBrowser::safety_check_file(string file) { if (file.rfind(".wav") == string::npos && file.rfind(".aiff")== string::npos && file.rfind(".aif") == string::npos && file.rfind(".snd") == string::npos && file.rfind(".au") == string::npos && file.rfind(".raw") == string::npos && file.rfind(".sf") == string::npos && file.rfind(".cdr") == string::npos && file.rfind(".smp") == string::npos && file.rfind(".maud")== string::npos && file.rfind(".vwe") == string::npos && file.rfind(".paf") == string::npos && file.rfind(".voc") == string::npos) { return ""; } else { return file; } } static int32_t process_node (const char *file, const struct stat *sb, int32_t flag); static string length2string (const int, const int); LibraryTree::LibraryTree () : Gtk::VBox(false, 3), btn_box_top(true, 4), btn_box_bottom (true, 4), add_btn(_("Add to Library...")), remove_btn(_("Remove...")), find_btn(_("Find...")), folder_btn(_("Add Folder")), files_select(_("Add audio file or directory")) { set_border_width (3); pack_start(hbox, true, true); pack_start(btn_box_top, false, false); pack_start(btn_box_bottom, false, false); hbox.pack_start(scroll); scroll.set_size_request (200, 150); scroll.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll.add_with_viewport(tree); tree.set_selection_mode(GTK_SELECTION_MULTIPLE); btn_box_top.pack_start(add_btn); btn_box_top.pack_start(folder_btn); btn_box_top.pack_start(remove_btn); btn_box_bottom.pack_start(find_btn); remove_btn.set_sensitive (false); add_btn.signal_clicked().connect (mem_fun(*this, &LibraryTree::add_btn_clicked)); folder_btn.signal_clicked().connect (mem_fun(*this, &LibraryTree::folder_btn_clicked)); remove_btn.signal_clicked().connect (mem_fun(*this, &LibraryTree::remove_btn_clicked)); find_btn.signal_clicked().connect (mem_fun(*this, &LibraryTree::find_btn_clicked)); files_select.hide_fileop_buttons(); files_select.set_filename("/"); files_select.get_ok_button()-.signal_clicked().connect (mem_fun ( *this, &LibraryTree::file_ok_clicked)); files_select.get_cancel_button()-.signal_clicked().connect (mem_fun ( *this, &LibraryTree::file_cancel_clicked)); Library->added_group.connect (mem_fun(*this, &LibraryTree::added_group)); Library->removed_group.connect (mem_fun(*this, &LibraryTree::removed_group)); Library->added_member.connect (mem_fun(*this, &LibraryTree::added_member)); Library->removed_member.connect (mem_fun(*this, &LibraryTree::removed_member)); current_group = ""; current_member = ""; populate (); } LibraryTree::~LibraryTree () { } void LibraryTree::clear_selection () { using namespace Gtk::Tree_Helpers; for (SelectionList::iterator i = tree.selection().begin(); i != tree.selection().end(); ++i) { (*i)->deselect (); } selection.clear (); } void LibraryTree::added_group (string group, string parent) { using namespace Gtk; ENSURE_GUI_THREAD(bind (mem_fun(*this, &LibraryTree::added_group), group, parent)); Tree* parent_tree; if (parent.length()) { parent_tree = uri_mapping[parent]->get_subtree(); } else { parent_tree = &tree; } TreeItem *item = manage(new TreeItem(Library->get_label(group))); Tree_Helpers::ItemList items = parent_tree->tree(); Tree_Helpers::ItemList::iterator i = items.begin(); list groups; Library->get_groups(groups, parent); list::iterator j = groups.begin(); while (i != items.end() && j != groups.end()){ if ((cmp_nocase(Library->get_label(group),Library->get_label(*j)) <= 0) || !((*i)->get_subtree())){ break; } ++i; ++j; } parent_tree->tree().insert (i, *item); Tree *subtree = manage(new Tree()); item->set_subtree (*subtree); item->expand(); item->select.connect (bind(mem_fun(*this,&LibraryTree::cb_group_select), item, group)); uri_mapping.insert(map::value_type(group, item)); uri_parent.insert(map::value_type(group, parent)); subtree->show(); item->show(); while (gtk_events_pending()){ gtk_main_iteration(); } } void LibraryTree::removed_group (string group) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &LibraryTree::removed_group), group)); Gtk::TreeItem* group_item = uri_mapping[group]; Gtk::Tree* parent_tree; if (uri_parent[group].length()) { parent_tree = uri_mapping[uri_parent[group]]->get_subtree(); } else { parent_tree = &tree; } parent_tree->tree().remove(*group_item); uri_mapping.erase(uri_mapping.find(group)); uri_parent.erase(uri_parent.find(group)); while (gtk_events_pending()){ gtk_main_iteration(); } } void LibraryTree::added_member (string member, string parent) { using namespace Gtk; ENSURE_GUI_THREAD(bind (mem_fun(*this, &LibraryTree::added_member), member, parent)); Tree* parent_tree; if (parent.length()) { parent_tree = uri_mapping[parent]->get_subtree(); } else { parent_tree = &tree; } TreeItem *item = manage(new TreeItem(Library->get_label(member))); Tree_Helpers::ItemList items = parent_tree->tree(); Tree_Helpers::ItemList::iterator i = items.begin(); list members; Library->get_members(members, parent); list::iterator j = members.begin(); while (i != items.end() && j != members.end()){ if (cmp_nocase(Library->get_label(member), Library->get_label(*j)) <= 0){ break; } ++i; ++j; } parent_tree->tree().insert (i, *item); item->select.connect (bind(mem_fun(*this,&LibraryTree::cb_member_select), item, member)); item->deselect.connect (bind(mem_fun(*this,&LibraryTree::cb_member_deselect), item, member)); uri_mapping.insert(map::value_type(member, item)); uri_parent.insert(map::value_type(member, parent)); item->show(); while (gtk_events_pending()){ gtk_main_iteration(); } } void LibraryTree::removed_member (string member) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &LibraryTree::removed_member), member)); Gtk::TreeItem* member_item = uri_mapping[member]; Gtk::Tree* parent_tree; if (uri_parent[member].length()) { parent_tree = uri_mapping[uri_parent[member]]->get_subtree(); } else { parent_tree = &tree; } parent_tree->tree().remove(*member_item); uri_mapping.erase(uri_mapping.find(member)); uri_parent.erase(uri_parent.find(member)); while (gtk_events_pending()){ gtk_main_iteration(); } } void LibraryTree::populate () { subpopulate (&tree, current_group); } void LibraryTree::subpopulate (Gtk::Tree* tree, string group) { using namespace Gtk; list groups; Library->get_groups(groups, group); list::iterator i; for (i = groups.begin(); i != groups.end(); ++i) { TreeItem *item = manage(new TreeItem(Library->get_label(*i))); tree->append (*item); Tree *subtree = manage(new Tree()); item->set_subtree (*subtree); uri_mapping.insert(map::value_type(*i, item)); uri_parent.insert(map::value_type(*i, group)); item->select.connect (bind(mem_fun(*this,&LibraryTree::cb_group_select), item, *i)); subpopulate (subtree, *i); subtree->show(); item->expand(); item->show(); } list members; Library->get_members(members, group); for (i = members.begin(); i != members.end(); ++i) { TreeItem *item = manage(new TreeItem(Library->get_label(*i))); tree->append (*item); item->show(); uri_mapping.insert(map::value_type(*i, item)); uri_parent.insert(map::value_type(*i, group)); item->select.connect (bind(mem_fun(*this,&LibraryTree::cb_member_select), item, *i)); item->deselect.connect (bind(mem_fun(*this,&LibraryTree::cb_member_deselect), item, *i)); } } void LibraryTree::add_btn_clicked () { files_select.show_all(); } // Gah, too many globals static string parent_uri; static vector* old_parent; static vector* old_parent_uri; static void clone_ftw(void*); static int32_t ftw_return; static Gtk::ProgressBar* bar; void LibraryTree::file_ok_clicked () { files_select.hide_all(); string* file = new string(files_select.get_filename()); parent_uri = current_group; Gtk::Window* progress_win = new Gtk::Window(); progress_win->set_title(_("Importing")); progress_win->set_policy(false, false, true); Gtk::VBox* main_box = manage(new Gtk::VBox()); progress_win->add(*main_box); bar = manage(new Gtk::ProgressBar()); bar->set_activity_mode(true); bar->set_activity_step(15); bar->set_activity_blocks(10); main_box->pack_start(*bar); Gtk::Button* cancel_btn = manage(new Gtk::Button(_("Cancel"))); main_box->pack_start(*cancel_btn); cancel_btn-.signal_clicked().connect (mem_fun(*this, &LibraryTree::cancel_import_clicked)); progress_win->show_all(); clone_ftw((void*)file); delete progress_win; } void LibraryTree::cancel_import_clicked() { ftw_return = 1; } void clone_ftw(void* ptr) { string* file = (string*) ptr; old_parent = new vector; old_parent_uri = new vector; ftw_return = 0; if (ftw (file->c_str(), process_node, 100) < 0){ warning << compose(_("%1 not added to database"), *file) << endmsg; } delete old_parent; delete old_parent_uri; delete file; } void LibraryTree::file_cancel_clicked () { files_select.hide_all(); } void LibraryTree::folder_btn_clicked () { ArdourPrompter prompter (true); prompter.set_prompt(_("Folder name:")); prompter.done.connect(Gtk::Main::quit.slot()); prompter.show_all(); Gtk::Main::run(); if (prompter.status == Gtkmm2ext::Prompter::entered) { string name; prompter.get_result(name); if (name.length()) { Library->add_group(name, current_group); } } } void LibraryTree::cb_group_select (Gtk::TreeItem* item, string uri) { current_group = uri; current_member = ""; remove_btn.set_sensitive(true); group_selected(); /* EMIT_SIGNAL */ } void LibraryTree::cb_member_select (Gtk::TreeItem* item, string uri) { current_member = uri; current_group = ""; selection.push_back (uri); member_selected(uri); /* EMIT_SIGNAL */ remove_btn.set_sensitive(true); } void LibraryTree::cb_member_deselect (Gtk::TreeItem* item, string uri) { current_member = ""; current_group = ""; selection.remove (uri); member_deselected(); /* EMIT_SIGNAL */ } void LibraryTree::find_btn_clicked () { SearchSounds* search = new SearchSounds (); search->file_chosen.connect(mem_fun(*this, &LibraryTree::file_found)); search->show_all(); } void LibraryTree::file_found (string uri, bool multi) { file_chosen (Library->get_member_filename(uri), multi); /* EMIT_SIGNAL */ } void LibraryTree::remove_btn_clicked () { if (current_member != ""){ Library->remove_member(current_member); } else if (current_group != ""){ Library->remove_group(current_group); } else { error << _("Should not be reached") << endmsg; } current_member = ""; current_group = ""; remove_btn.set_sensitive(false); deselected(); /* EMIT_SIGNAL */ } string length2string (const int32_t frames, const int32_t sample_rate) { int secs = (int) (frames / (float) sample_rate); int hrs = secs / 3600; secs -= (hrs * 3600); int mins = secs / 60; secs -= (mins * 60); int total_secs = (hrs * 3600) + (mins * 60) + secs; int frames_remaining = frames - (total_secs * sample_rate); float fractional_secs = (float) frames_remaining / sample_rate; char duration_str[32]; sprintf (duration_str, "%02d:%02d:%05.2f", hrs, mins, (float) secs + fractional_secs); return duration_str; } int process_node (const char *file, const struct stat *sb, int32_t flag) { bar->set_value(0.0); while (gtk_events_pending()){ gtk_main_iteration(); } bar->set_value(1.0); string s_file(file); if (s_file.find("/.") != string::npos){ return ftw_return; } if (flag == FTW_D) { string::size_type size = s_file.find_last_of('/'); string label = s_file.substr(++size); while (!old_parent->empty() && (s_file.find(old_parent->back()) == string::npos)) { parent_uri = old_parent_uri->back(); old_parent_uri->pop_back(); old_parent->pop_back(); } string uri = Library->add_group(label, parent_uri); old_parent->push_back(s_file); old_parent_uri->push_back(parent_uri); parent_uri = uri; return ftw_return; } else if (flag != FTW_F) { return ftw_return; } // We can't realistically check every file - or can we ? char* suffix; if ((suffix = strrchr (file, '.')) == 0) { return ftw_return; } if (*(suffix+1) == '\0') { return ftw_return; } if (strcasecmp (suffix+1, "wav") != 0 && strcasecmp (suffix+1, "aiff") != 0 && strcasecmp (suffix+1, "aif") != 0 && strcasecmp (suffix+1, "snd") != 0 && strcasecmp (suffix+1, "au") != 0 && strcasecmp (suffix+1, "raw") != 0 && strcasecmp (suffix+1, "sf") != 0 && strcasecmp (suffix+1, "cdr") != 0 && strcasecmp (suffix+1, "smp") != 0 && strcasecmp (suffix+1, "maud") != 0 && strcasecmp (suffix+1, "vwe") != 0 && strcasecmp (suffix+1, "paf") != 0 && strcasecmp (suffix+1, "voc") != 0) { return ftw_return; } /* OK, it stands a good chance of being a sound file that we might be able to handle. */ SNDFILE *sf; SF_INFO info; if ((sf = sf_open ((char *) file, SFM_READ, &info)) < 0) { error << compose(_("file \"%1\" could not be opened"), file) << endmsg; return ftw_return; } sf_close (sf); string uri = Library->add_member(file, parent_uri); Library->set_field(uri, "channels", compose("%1", info.channels)); Library->set_field(uri, "samplerate", compose("%1", info.samplerate)); Library->set_field(uri, "resolution", compose("%1", sndfile_data_width(info.format))); Library->set_field(uri, "format", compose("%1", info.format)); return ftw_return; } static const gchar* selector_titles[] = { N_("Field"), N_("Value"), 0 }; SoundFileBox::SoundFileBox (string uri, bool meta) : uri(uri), metadata(meta), sf_info(new SF_INFO), current_pid(0), fields(_fields_refiller, this, internationalize (selector_titles), false, true), main_box (false, 3), top_box (true, 4), bottom_box (true, 4), play_btn(_("Play")), stop_btn(_("Stop")), add_field_btn(_("Add Field...")), remove_field_btn(_("Remove Field")) { set_name ("SoundFileBox"); border_frame.set_label (_("Soundfile Info")); border_frame.add (main_box); pack_start (border_frame); set_border_width (4); Gtk::HBox* path_box = manage (new HBox); path_box->set_spacing (4); path_box->pack_start (path, false, false); path_box->pack_start (path_entry, true, true); main_box.set_border_width (4); main_box.pack_start(label, false, false); main_box.pack_start(*path_box, false, false); main_box.pack_start(length, false, false); main_box.pack_start(format, false, false); main_box.pack_start(channels, false, false); main_box.pack_start(samplerate, false, false); if (metadata){ main_box.pack_start(fields, true, true); main_box.pack_start(top_box, false, false); } main_box.pack_start(bottom_box, false, false); fields.set_size_request(200, 150); top_box.set_homogeneous(true); top_box.pack_start(add_field_btn); top_box.pack_start(remove_field_btn); remove_field_btn.set_sensitive(false); bottom_box.set_homogeneous(true); bottom_box.pack_start(play_btn); bottom_box.pack_start(stop_btn); play_btn.signal_clicked().connect (mem_fun(*this, &SoundFileBox::play_btn_clicked)); stop_btn.signal_clicked().connect (mem_fun(*this, &SoundFileBox::stop_btn_clicked)); PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (!sess) { play_btn.set_sensitive(false); } else { sess->AuditionActive.connect(mem_fun(*this, &SoundFileBox::audition_status_changed)); } add_field_btn.signal_clicked().connect (mem_fun(*this, &SoundFileBox::add_field_clicked)); remove_field_btn.signal_clicked().connect (mem_fun(*this, &SoundFileBox::remove_field_clicked)); fields.selection_made.connect (mem_fun(*this, &SoundFileBox::field_selected)); fields.choice_made.connect (mem_fun(*this, &SoundFileBox::field_chosen)); Library->fields_changed.connect (mem_fun(*this, &SoundFileBox::setup_fields)); if (setup_labels (uri)) { throw failed_constructor(); } show_all(); stop_btn.hide(); } SoundFileBox::~SoundFileBox () { } void SoundFileBox::_fields_refiller (Gtk::CList &list, void* arg) { ((SoundFileBox *) arg)->fields_refiller (list); } void SoundFileBox::fields_refiller (Gtk::CList &clist) { if (metadata) { list flist; Library->get_fields(flist); list::iterator i; const gchar *rowdata[3]; guint row = 0; for (i=flist.begin(); i != flist.end(); ++i, ++row){ if (*i != "channels" && *i != "samplerate" && *i != "resolution" && *i != "format") { rowdata[0] = (*i).c_str(); rowdata[1] = Library->get_field(uri, *i).c_str(); clist.insert_row (row, rowdata); ++row; } } clist.column(0).set_auto_resize(true); clist.set_sort_column (0); clist.sort (); } } int SoundFileBox::setup_labels (string uri) { SNDFILE *sf; string file; if (metadata){ file = Library->get_member_filename(uri); } else { file = uri; } if ((sf = sf_open ((char *) file.c_str(), SFM_READ, sf_info)) < 0) { error << compose(_("file \"%1\" could not be opened"), file) << endmsg; return -1; } if (sf_info->frames == 0 && sf_info->channels == 0 && sf_info->samplerate == 0 && sf_info->format == 0 && sf_info->sections == 0) { /* .. ok, its not a sound file */ error << compose(_("file \"%1\" appears not to be an audio file"), file) << endmsg; return -1; } if (metadata) { label.set_alignment (0.0f, 0.0f); label.set_text ("Label: " + Library->get_label(uri)); } path.set_text ("Path: "); path_entry.set_text (file); path_entry.set_position (-1); path_entry.signal_focus_in_event().connect (ptr_fun (ARDOUR_UI::generic_focus_in_event)); path_entry.signal_focus_out_event().connect (ptr_fun (ARDOUR_UI::generic_focus_out_event)); length.set_alignment (0.0f, 0.0f); length.set_text (compose("Length: %1", length2string(sf_info->frames, sf_info->samplerate))); format.set_alignment (0.0f, 0.0f); format.set_text (compose("Format: %1, %2", sndfile_major_format(sf_info->format), sndfile_minor_format(sf_info->format))); channels.set_alignment (0.0f, 0.0f); channels.set_text (compose("Channels: %1", sf_info->channels)); samplerate.set_alignment (0.0f, 0.0f); samplerate.set_text (compose("Samplerate: %1", sf_info->samplerate)); return 0; } void SoundFileBox::play_btn_clicked () { PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (!sess) { return; } sess->cancel_audition(); string file; if (metadata) { file = Library->get_member_filename(uri); } else { file = uri; } if (access(file.c_str(), R_OK)) { warning << compose(_("Could not read file: %1 (%2)."), file, strerror(errno)) << endmsg; return; } static map region_cache; if (region_cache.find (file) == region_cache.end()) { AudioRegion::SourceList srclist; SndFileSource* sfs; for (int n=0; n < sf_info->channels; ++n) { try { sfs = new SndFileSource(file+":"+compose("%1", n), false); srclist.push_back(sfs); } catch (failed_constructor& err) { error << _("Could not access soundfile: ") << file << endmsg; return; } } if (srclist.empty()) { return; } string result; sess->region_name (result, PBD::basename(srclist[0]->name()), false); AudioRegion* a_region = new AudioRegion(srclist, 0, srclist[0]->length(), result, 0, Region::DefaultFlags, false); region_cache[file] = a_region; } play_btn.hide(); stop_btn.show(); sess->audition_region(*region_cache[file]); } void SoundFileBox::stop_btn_clicked () { PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (sess) { sess->cancel_audition(); play_btn.show(); stop_btn.hide(); } } void SoundFileBox::audition_status_changed (bool active) { if (!active) { Gtkmm2ext::UI::instance()->call_slot( mem_fun(*this, &SoundFileBox::stop_btn_clicked)); } } void SoundFileBox::add_field_clicked () { ArdourPrompter prompter (true); prompter.set_prompt(_("Field name:")); prompter.show_all(); prompter.done.connect(Gtk::Main::quit.slot()); Gtk::Main::run(); if (prompter.status == Gtkmm2ext::Prompter::entered) { string name; prompter.get_result(name); if (name.length()) { Library->add_field(name); } } } void SoundFileBox::remove_field_clicked () { Library->remove_field(selected_field); selected_field = ""; remove_field_btn.set_sensitive(false); } void SoundFileBox::setup_fields () { ENSURE_GUI_THREAD(mem_fun(*this, &SoundFileBox::setup_fields)); fields.rescan(); } void SoundFileBox::field_chosen (Gtkmm2ext::Selector *selector, Gtkmm2ext::SelectionResult *res) { if (res) { remove_field_btn.set_sensitive(true); selected_field = selector->clist().row(res->row)[0].get_text(); } } void SoundFileBox::field_selected (Gtkmm2ext::Selector *selector, Gtkmm2ext::SelectionResult *res) { if (!res){ return; } string field_name(selector->clist().row(res->row)[0].get_text()); ArdourPrompter prompter (true); prompter.set_prompt(_("Field value:")); prompter.set_initial_text (selector->clist().row(res->row)[1].get_text()); prompter.show_all(); prompter.done.connect(Gtk::Main::quit.slot()); Gtk::Main::run(); if (prompter.status == Gtkmm2ext::Prompter::entered) { string data; prompter.get_result(data); Library->set_field(uri, field_name, data); } fields.rescan(); } SearchSounds::SearchSounds () : ArdourDialog ("search sounds dialog"), find_btn (_("Find")), and_rbtn (_("AND")), or_rbtn (_("OR")), fields(_fields_refiller, this, internationalize(selector_titles)) { set_title (_("ardour: locate soundfiles")); set_name ("AudioSearchSound"); add(main_box); or_rbtn.set_group(and_rbtn.group()); or_rbtn.set_active(true); rbtn_box.set_homogeneous(true); rbtn_box.pack_start(and_rbtn); rbtn_box.pack_start(or_rbtn); bottom_box.set_homogeneous(true); bottom_box.pack_start(find_btn); fields.set_size_request(200, 150); main_box.pack_start(fields); main_box.pack_start(rbtn_box, false, false); main_box.pack_start(bottom_box, false, false); delete_event.connect (mem_fun(*this, &ArdourDialog::wm_doi_event)); find_btn.signal_clicked().connect (mem_fun(*this, &SearchSounds::find_btn_clicked)); fields.selection_made.connect (mem_fun (*this, &SearchSounds::field_selected)); show_all(); } SearchSounds::~SearchSounds () { } void SearchSounds::_fields_refiller (Gtk::CList &list, void* arg) { ((SearchSounds *) arg)->fields_refiller (list); } void SearchSounds::fields_refiller (Gtk::CList &clist) { list flist; Library->get_fields(flist); list::iterator i; const gchar *rowdata[3]; guint row; for (row=0,i=flist.begin(); i != flist.end(); ++i, ++row){ rowdata[0] = (*i).c_str(); rowdata[1] = ""; clist.insert_row (row, rowdata); } clist.column(0).set_auto_resize(true); clist.set_sort_column (0); clist.sort (); } void SearchSounds::field_selected (Gtkmm2ext::Selector *selector, Gtkmm2ext::SelectionResult *res) { if (!res){ return; } ArdourPrompter prompter (true); prompter.set_prompt(_("Field value:")); prompter.show_all(); prompter.done.connect(Gtk::Main::quit.slot()); Gtk::Main::run(); if (prompter.status == Gtkmm2ext::Prompter::entered) { string data; prompter.get_result(data); selector->clist().cell(res->row, 1).set_text(data); } } void SearchSounds::find_btn_clicked () { using namespace Gtk::CList_Helpers; typedef map StringMap; StringMap search_info; RowList rows = fields.clist().rows(); RowList::const_iterator i; for (i = rows.begin(); i != rows.end(); ++i) { Cell cfield((*i)[0]); Cell cdata((*i)[1]); if (cdata.get_text().length()){ search_info.insert( StringMap::value_type(cfield.get_text(), cdata.get_text())); } } SearchResults* results; if (and_rbtn.get_active()){ results = new SearchResults(search_info, true); } else { results = new SearchResults(search_info, false); } results->file_chosen.connect (mem_fun(*this, &SearchSounds::file_found)); results->show_all(); } void SearchSounds::file_found (string uri, bool multi) { PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (sess) { sess->cancel_audition(); } file_chosen (uri, multi); /* EMIT_SIGNAL */ } static const gchar* result_titles[] = { N_("Results"), N_("Uris"), 0 }; SearchResults::SearchResults (map field_values, bool and_search) : ArdourDialog ("search results dialog"), search_info(field_values), search_and (and_search), selection (""), main_box (false, 3), import_box (true, 4), import_btn (_("Import")), multichan_check (_("Create multi-channel region")), results (_results_refiller, this, internationalize (result_titles), false, true) { set_title (_("Ardour: Search Results")); set_name ("ArdourSearchResults"); add(main_box); set_border_width (3); main_box.pack_start(hbox); hbox.pack_start(results); main_box.pack_start(import_box, false, false); results.set_size_request (200, 150); import_box.set_homogeneous(true); import_box.pack_start(import_btn); import_box.pack_start(multichan_check); import_btn.set_sensitive(false); multichan_check.set_active(true); multichan_check.set_sensitive(false); delete_event.connect (mem_fun(*this, &ArdourDialog::wm_doi_event)); import_btn.signal_clicked().connect (mem_fun(*this, &SearchResults::import_clicked)); results.choice_made.connect (mem_fun(*this, &SearchResults::result_chosen)); show_all(); } SearchResults::~SearchResults () { } void SearchResults::_results_refiller (Gtk::CList &list, void* arg) { ((SearchResults *) arg)->results_refiller (list); } void SearchResults::results_refiller (Gtk::CList &clist) { list results; if (search_and) { Library->search_members_and (results, search_info); } else { Library->search_members_or (results, search_info); } list::iterator i; const gchar *rowdata[3]; guint row; for (row=0, i=results.begin(); i != results.end(); ++i, ++row){ rowdata[0] = (Library->get_label(*i)).c_str(); // hide the uri in a hidden column rowdata[1] = (*i).c_str(); clist.insert_row (row, rowdata); } clist.column(1).set_visiblity(false); clist.column(0).set_auto_resize(true); clist.set_sort_column (0); clist.sort (); } void SearchResults::import_clicked () { PublicEditor& edit = ARDOUR_UI::instance()->the_editor(); ARDOUR::Session* sess = edit.current_session(); if (sess) { sess->cancel_audition(); } file_chosen(selection, multichan_check.get_active()); /* EMIT_SIGNAL */ } void SearchResults::result_chosen (Gtkmm2ext::Selector *selector, Gtkmm2ext::SelectionResult *res) { if (res) { selection = selector->clist().row(res->row)[1].get_text(); if (info_box){ delete info_box; info_box = 0; } try { info_box = new SoundFileBox(selection, true); } catch (failed_constructor& err) { /* nothing to do */ return; } hbox.pack_start(*info_box); } }