summaryrefslogtreecommitdiff
path: root/gtk2_ardour/library_ui.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gtk2_ardour/library_ui.cc')
-rw-r--r--gtk2_ardour/library_ui.cc1541
1 files changed, 1541 insertions, 0 deletions
diff --git a/gtk2_ardour/library_ui.cc b/gtk2_ardour/library_ui.cc
new file mode 100644
index 0000000000..7e17079e49
--- /dev/null
+++ b/gtk2_ardour/library_ui.cc
@@ -0,0 +1,1541 @@
+/*
+ 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 <vector>
+#include <string>
+#include <cstdlib>
+#include <cctype>
+#include <cerrno>
+
+#include <sys/stat.h>
+#include <sndfile.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <pbd/basename.h>
+#include <pbd/forkexec.h>
+#include <pbd/ftw.h>
+#include <gtk--.h>
+#include <gtk--/fileselection.h>
+#include <gtkmmext/gtk_ui.h>
+#include <ardour/audio_library.h>
+#include <ardour/audioregion.h>
+#include <ardour/region.h>
+#include <ardour/session.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/utils.h>
+
+#include <gtkmmext/doi.h>
+
+#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 (slot (*this, &ArdourDialog::wm_close_event));
+
+ import_btn.clicked.connect (slot (*this, &SoundFileSelector::import_btn_clicked));
+
+ sfdb_tree.group_selected.connect (slot(*this, &SoundFileSelector::sfdb_group_selected));
+ sfdb_tree.member_selected.connect (bind (slot(*this, &SoundFileSelector::member_selected), true));
+ sf_browser.member_selected.connect (bind (slot(*this, &SoundFileSelector::member_selected), false));
+ sf_browser.member_deselected.connect (bind (slot(*this, &SoundFileSelector::member_deselected), false));
+ sfdb_tree.deselected.connect (slot(*this, &SoundFileSelector::sfdb_deselected));
+ sf_browser.group_selected.connect (slot(*this, &SoundFileSelector::browser_group_selected));
+ notebook.switch_page.connect (slot(*this, &SoundFileSelector::page_switched));
+}
+
+SoundFileSelector::~SoundFileSelector()
+{
+}
+
+void
+SoundFileSelector::import_btn_clicked ()
+{
+ vector<string> paths;
+
+ PublicEditor& edit = ARDOUR_UI::instance()->the_editor();
+ ARDOUR::Session* sess = edit.current_session();
+ if (sess) {
+ sess->cancel_audition();
+ }
+
+ if (sfdb) {
+ for (list<string>::iterator i = sfdb_tree.selection.begin(); i != sfdb_tree.selection.end(); ++i) {
+ paths.push_back (Library->get_member_filename (*i));
+ }
+ } else {
+ for (list<RowTaggedString>::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<Label*>(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<string>& paths, bool& split)
+{
+ if (sfdb) {
+ for (list<string>::iterator i = sfdb_tree.selection.begin(); i != sfdb_tree.selection.end(); ++i) {
+ paths.push_back (Library->get_member_filename (*i));
+ }
+ } else {
+ for (list<RowTaggedString>::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.gtkobj();
+ 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(slot (*this, &SoundFileBrowser::dir_list_selected));
+ file_list->select_row.connect(slot (*this, &SoundFileBrowser::file_list_selected));
+ file_list->unselect_row.connect(slot (*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<RowTaggedString>::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_usize (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.clicked.connect (slot (*this, &LibraryTree::add_btn_clicked));
+ folder_btn.clicked.connect (slot(*this, &LibraryTree::folder_btn_clicked));
+ remove_btn.clicked.connect (slot(*this, &LibraryTree::remove_btn_clicked));
+ find_btn.clicked.connect (slot (*this, &LibraryTree::find_btn_clicked));
+
+ files_select.hide_fileop_buttons();
+ files_select.set_filename("/");
+ files_select.get_ok_button()->clicked.connect (slot ( *this,
+ &LibraryTree::file_ok_clicked));
+ files_select.get_cancel_button()->clicked.connect (slot ( *this,
+ &LibraryTree::file_cancel_clicked));
+
+
+ Library->added_group.connect (slot (*this, &LibraryTree::added_group));
+ Library->removed_group.connect (slot (*this, &LibraryTree::removed_group));
+ Library->added_member.connect (slot (*this, &LibraryTree::added_member));
+ Library->removed_member.connect (slot (*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 (slot (*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<string> groups;
+ Library->get_groups(groups, parent);
+ list<string>::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(slot(*this,&LibraryTree::cb_group_select), item, group));
+
+ uri_mapping.insert(map<string, TreeItem*>::value_type(group, item));
+ uri_parent.insert(map<string,string>::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 (slot (*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 (slot (*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<string> members;
+ Library->get_members(members, parent);
+ list<string>::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(slot(*this,&LibraryTree::cb_member_select), item, member));
+ item->deselect.connect
+ (bind(slot(*this,&LibraryTree::cb_member_deselect), item, member));
+
+ uri_mapping.insert(map<string, TreeItem*>::value_type(member, item));
+ uri_parent.insert(map<string,string>::value_type(member, parent));
+
+ item->show();
+
+ while (gtk_events_pending()){
+ gtk_main_iteration();
+ }
+}
+
+void
+LibraryTree::removed_member (string member)
+{
+ ENSURE_GUI_THREAD(bind (slot (*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<string> groups;
+ Library->get_groups(groups, group);
+
+ list<string>::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<string, Gtk::TreeItem*>::value_type(*i, item));
+ uri_parent.insert(map<string,string>::value_type(*i, group));
+
+ item->select.connect
+ (bind(slot(*this,&LibraryTree::cb_group_select), item, *i));
+
+ subpopulate (subtree, *i);
+ subtree->show();
+ item->expand();
+ item->show();
+ }
+
+ list<string> 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<string, Gtk::TreeItem*>::value_type(*i, item));
+ uri_parent.insert(map<string,string>::value_type(*i, group));
+
+ item->select.connect
+ (bind(slot(*this,&LibraryTree::cb_member_select), item, *i));
+ item->deselect.connect
+ (bind(slot(*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<string>* old_parent;
+static vector<string>* 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->clicked.connect (slot (*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<string>;
+ old_parent_uri = new vector<string>;
+ 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 == Gtkmmext::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(slot (*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_usize(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.clicked.connect (slot (*this, &SoundFileBox::play_btn_clicked));
+ stop_btn.clicked.connect (slot (*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(slot (*this, &SoundFileBox::audition_status_changed));
+ }
+
+ add_field_btn.clicked.connect
+ (slot (*this, &SoundFileBox::add_field_clicked));
+ remove_field_btn.clicked.connect
+ (slot (*this, &SoundFileBox::remove_field_clicked));
+
+ fields.selection_made.connect (slot (*this, &SoundFileBox::field_selected));
+ fields.choice_made.connect (slot (*this, &SoundFileBox::field_chosen));
+
+ Library->fields_changed.connect (slot (*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<string> flist;
+ Library->get_fields(flist);
+ list<string>::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.focus_in_event.connect (slot (ARDOUR_UI::generic_focus_in_event));
+ path_entry.focus_out_event.connect (slot (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<string, ARDOUR::AudioRegion*> 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) {
+ Gtkmmext::UI::instance()->call_slot( slot(*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 == Gtkmmext::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(slot (*this, &SoundFileBox::setup_fields));
+
+ fields.rescan();
+}
+
+void
+SoundFileBox::field_chosen (Gtkmmext::Selector *selector, Gtkmmext::SelectionResult *res)
+{
+ if (res) {
+ remove_field_btn.set_sensitive(true);
+ selected_field = selector->clist().row(res->row)[0].get_text();
+ }
+}
+
+void
+SoundFileBox::field_selected (Gtkmmext::Selector *selector, Gtkmmext::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 == Gtkmmext::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_usize(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 (slot (*this, &ArdourDialog::wm_doi_event));
+
+ find_btn.clicked.connect (slot (*this, &SearchSounds::find_btn_clicked));
+ fields.selection_made.connect (slot
+ (*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<string> flist;
+ Library->get_fields(flist);
+ list<string>::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 (Gtkmmext::Selector *selector, Gtkmmext::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 == Gtkmmext::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<string,string> 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 (slot (*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<string,string> 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_usize (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 (slot (*this, &ArdourDialog::wm_doi_event));
+
+ import_btn.clicked.connect (slot (*this, &SearchResults::import_clicked));
+
+ results.choice_made.connect (slot (*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<string> results;
+ if (search_and) {
+ Library->search_members_and (results, search_info);
+ } else {
+ Library->search_members_or (results, search_info);
+ }
+
+ list<string>::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 (Gtkmmext::Selector *selector, Gtkmmext::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);
+ }
+}