changeset 3513:d74a68871159 trunk

Automated merge with ssh://hg.atheme.org//hg/audacious-tc
author William Pitcock <nenolod@atheme.org>
date Tue, 11 Sep 2007 09:14:15 -0500
parents 69be01c2f6b6 (current diff) 6f8005df972f (diff)
children 331bfc72ef66
files
diffstat 13 files changed, 436 insertions(+), 289 deletions(-) [+]
line wrap: on
line diff
--- a/src/audacious/dbus.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/dbus.c	Tue Sep 11 09:14:15 2007 -0500
@@ -178,16 +178,16 @@
 GValue *tuple_value_to_gvalue(Tuple *tuple, const gchar *key) {
     GValue *val;
     TupleValueType type;
-    type = tuple_get_value_type(tuple, key);
+    type = tuple_get_value_type(tuple, -1, key);
     if (type == TUPLE_STRING) {
         val = g_new0(GValue, 1);
         g_value_init(val, G_TYPE_STRING);
-        g_value_set_string(val, tuple_get_string(tuple, key));
+        g_value_set_string(val, tuple_get_string(tuple, -1, key));
         return val;
     } else if (type == TUPLE_INT) {
         val = g_new0(GValue, 1);
         g_value_init(val, G_TYPE_INT);
-        g_value_set_int(val, tuple_get_int(tuple, key));
+        g_value_set_int(val, tuple_get_int(tuple, -1, key));
         return val;
     }
     return NULL;
@@ -709,17 +709,17 @@
     if (!tuple) {
         return FALSE;
     } else {
-        TupleValueType type = tuple_get_value_type(tuple, field);
+        TupleValueType type = tuple_get_value_type(tuple, -1, field);
 
         switch(type)
         {
         case TUPLE_STRING:
             g_value_init(value, G_TYPE_STRING);
-            g_value_set_string(value, tuple_get_string(tuple, field));
+            g_value_set_string(value, tuple_get_string(tuple, -1, field));
             break;
         case TUPLE_INT:
             g_value_init(value, G_TYPE_INT);
-            g_value_set_int(value, tuple_get_int(tuple, field));
+            g_value_set_int(value, tuple_get_int(tuple, -1, field));
             break;
         default:
             return FALSE;
--- a/src/audacious/input.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/input.c	Tue Sep 11 09:14:15 2007 -0500
@@ -433,7 +433,7 @@
                 pr = g_new0(ProbeResult, 1);
                 pr->ip = ip;
                 pr->tuple = tuple;
-                tuple_associate_int(pr->tuple, "mtime", input_get_mtime(filename_proxy));
+                tuple_associate_int(pr->tuple, FIELD_MTIME, NULL, input_get_mtime(filename_proxy));
 
                 return pr;
             }
@@ -511,7 +511,7 @@
                 pr = g_new0(ProbeResult, 1);
                 pr->ip = ip;
                 pr->tuple = tuple;
-                tuple_associate_int(pr->tuple, "mtime", input_get_mtime(filename_proxy));
+                tuple_associate_int(pr->tuple, FIELD_MTIME, NULL, input_get_mtime(filename_proxy));
 
                 return pr;
             }
@@ -574,6 +574,7 @@
         playback->plugin->set_eq(on, preamp, bands);
 }
 
+
 Tuple *
 input_get_song_tuple(const gchar * filename)
 {
@@ -609,13 +610,13 @@
         if ((ext = strrchr(tmp, '.')))
             *ext = '\0';
 
-        tuple_associate_string(input, "file-name", g_path_get_basename(tmp));
+        tuple_associate_string(input, FIELD_FILE_NAME, NULL, g_path_get_basename(tmp));
 
         if (ext)
-            tuple_associate_string(input, "file-ext", ext + 1);
+            tuple_associate_string(input, FIELD_FILE_EXT, NULL, ext + 1);
 
-        tuple_associate_string(input, "file-path", g_path_get_dirname(tmp));
-        tuple_associate_int(input, "length", -1);
+        tuple_associate_string(input, FIELD_FILE_PATH, NULL, g_path_get_dirname(tmp));
+        tuple_associate_int(input, FIELD_LENGTH, NULL, -1);
 
         g_free(tmp);
     }
--- a/src/audacious/playlist.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/playlist.c	Tue Sep 11 09:14:15 2007 -0500
@@ -180,12 +180,8 @@
         entry->tuple = NULL;
     }
 
-    if (entry->filename != NULL)
-        g_free(entry->filename);
-
-    if (entry->title != NULL)
-        g_free(entry->title);
-
+    g_free(entry->filename);
+    g_free(entry->title);
     mowgli_heap_free(playlist_entry_heap, entry);
 }
 
@@ -199,7 +195,8 @@
 
     g_return_val_if_fail(entry != NULL, FALSE);
 
-    if (entry->tuple == NULL || tuple_get_int(entry->tuple, "mtime") > 0 || tuple_get_int(entry->tuple, "mtime") == -1)
+    if (entry->tuple == NULL || tuple_get_int(entry->tuple, FIELD_MTIME, NULL) > 0 ||
+        tuple_get_int(entry->tuple, FIELD_MTIME, NULL) == -1)
         modtime = playlist_get_mtime(entry->filename);
     else
         modtime = 0;  /* URI -nenolod */
@@ -217,7 +214,7 @@
 
     /* renew tuple if file mtime is newer than tuple mtime. */
     if(entry->tuple){
-        if(tuple_get_int(entry->tuple, "mtime") == modtime)
+        if(tuple_get_int(entry->tuple, FIELD_MTIME, NULL) == modtime)
             return TRUE;
         else {
             mowgli_object_unref(entry->tuple);
@@ -234,13 +231,13 @@
         return FALSE;
 
     /* attach mtime */
-    tuple_associate_int(tuple, "mtime", modtime);
+    tuple_associate_int(tuple, FIELD_MTIME, NULL, modtime);
 
     /* entry is still around */
-    formatter = tuple_get_string(tuple, "formatter");
+    formatter = tuple_get_string(tuple, FIELD_FORMATTER, NULL);
     entry->title = tuple_formatter_make_title_string(tuple, formatter ?
                                                   formatter : get_gentitle_format());
-    entry->length = tuple_get_int(tuple, "length");
+    entry->length = tuple_get_int(tuple, FIELD_LENGTH, NULL);
     entry->tuple = tuple;
 
     g_free(pr);
@@ -675,7 +672,10 @@
     g_return_if_fail(playlist != NULL);
     g_return_if_fail(filename != NULL);
 
-    entry = playlist_entry_new(filename, tuple ? tuple_get_string(tuple, "title") : NULL, tuple ? tuple_get_int(tuple, "length") : -1, dec);
+    entry = playlist_entry_new(filename,
+        tuple ? tuple_get_string(tuple, FIELD_TITLE, NULL) : NULL,
+        tuple ? tuple_get_int(tuple, FIELD_LENGTH, NULL) : -1, dec);
+    
     if(!playlist->tail)
         playlist->tail = g_list_last(playlist->entries);
 
@@ -705,14 +705,14 @@
     PLAYLIST_UNLOCK(playlist);
     
     if (tuple != NULL) {
-        const gchar *formatter = tuple_get_string(tuple, "formatter");
+        const gchar *formatter = tuple_get_string(tuple, FIELD_FORMATTER, NULL);
         entry->title = tuple_formatter_make_title_string(tuple, formatter ?
                                                       formatter : get_gentitle_format());
-        entry->length = tuple_get_int(tuple, "length");
+        entry->length = tuple_get_int(tuple, FIELD_LENGTH, NULL);
         entry->tuple = tuple;
     }
 
-    if(tuple != NULL && tuple_get_int(tuple, "mtime") == -1) { // kick the scanner thread only if mtime = -1 (uninitialized).
+    if(tuple != NULL && tuple_get_int(tuple, FIELD_MTIME, NULL) == -1) { // kick the scanner thread only if mtime = -1 (uninitialized).
         g_mutex_lock(mutex_scan);
         playlist_get_info_scan_active = TRUE;
         g_mutex_unlock(mutex_scan);
@@ -1078,9 +1078,9 @@
         playlist->position->length = length;
 
         // overwrite tuple::title, mainly for streaming. it may incur side effects. --yaz
-        if(playlist->position->tuple && tuple_get_int(playlist->position->tuple, "length") == -1){
-            tuple_disassociate(playlist->position->tuple, "title");
-            tuple_associate_string(playlist->position->tuple, "title", title);
+        if(playlist->position->tuple && tuple_get_int(playlist->position->tuple, FIELD_LENGTH, NULL) == -1){
+            tuple_disassociate(playlist->position->tuple, FIELD_TITLE, NULL);
+            tuple_associate_string(playlist->position->tuple, FIELD_TITLE, NULL, title);
         }
     }
 
@@ -1893,7 +1893,7 @@
     entry = node->data;
 
     if (entry->tuple)
-        mtime = tuple_get_int(entry->tuple, "mtime");
+        mtime = tuple_get_int(entry->tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -1941,7 +1941,7 @@
     tuple = entry->tuple;
 
     if (tuple)
-        mtime = tuple_get_int(tuple, "mtime");
+        mtime = tuple_get_int(tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -1974,7 +1974,7 @@
     entry = node->data;
 
     if (entry->tuple)
-        mtime = tuple_get_int(entry->tuple, "mtime");
+        mtime = tuple_get_int(entry->tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -2011,8 +2011,8 @@
     if (b->tuple == NULL)
         return 0;
 
-    tracknumber_a = tuple_get_int(a->tuple, "track-number");
-    tracknumber_b = tuple_get_int(b->tuple, "track-number");
+    tracknumber_a = tuple_get_int(a->tuple, FIELD_TRACK_NUMBER, NULL);
+    tracknumber_b = tuple_get_int(b->tuple, FIELD_TRACK_NUMBER, NULL);
 
     return (tracknumber_a && tracknumber_b ?
 	    tracknumber_a - tracknumber_b : 0);
@@ -2063,9 +2063,9 @@
         playlist_entry_get_info(b);
 
     if (a->tuple != NULL)
-        a_title = tuple_get_string(a->tuple, "title");
+        a_title = tuple_get_string(a->tuple, FIELD_TITLE, NULL);
     if (b->tuple != NULL)
-        b_title = tuple_get_string(b->tuple, "title");
+        b_title = tuple_get_string(b->tuple, FIELD_TITLE, NULL);
 
     if (a_title != NULL && b_title != NULL)
         return strcasecmp(a_title, b_title);
@@ -2107,13 +2107,13 @@
         playlist_entry_get_info(b);
 
     if (a->tuple != NULL) {
-        a_artist = tuple_get_string(a->tuple, "artist");
+        a_artist = tuple_get_string(a->tuple, FIELD_ARTIST, NULL);
         if (str_has_prefix_nocase(a_artist, "the "))
             a_artist += 4;
     }
 
     if (b->tuple != NULL) {
-        b_artist = tuple_get_string(b->tuple, "artist");
+        b_artist = tuple_get_string(b->tuple, FIELD_ARTIST, NULL);
         if (str_has_prefix_nocase(b_artist, "the "))
             b_artist += 4;
     }
@@ -2469,7 +2469,7 @@
     PLAYLIST_UNLOCK(playlist);
 
     if (entry->tuple)
-        mtime = tuple_get_int(entry->tuple, "mtime");
+        mtime = tuple_get_int(entry->tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -2601,7 +2601,8 @@
                 entry = node->data;
 
                 if(playlist->attribute & PLAYLIST_STATIC ||
-                   (entry->tuple && tuple_get_int(entry->tuple, "length") > -1 && tuple_get_int(entry->tuple, "mtime") != -1)) {
+                   (entry->tuple && tuple_get_int(entry->tuple, FIELD_LENGTH, NULL) > -1 &&
+                    tuple_get_int(entry->tuple, FIELD_MTIME, NULL) != -1)) {
                     update_playlistwin = TRUE;
                     continue;
                 }
@@ -2643,7 +2644,8 @@
                  entry = node->data;
 
                  if(playlist->attribute & PLAYLIST_STATIC ||
-                   (entry->tuple && tuple_get_int(entry->tuple, "length") > -1 && tuple_get_int(entry->tuple, "mtime") != -1)) {
+                   (entry->tuple && tuple_get_int(entry->tuple, FIELD_LENGTH, NULL) > -1 &&
+                    tuple_get_int(entry->tuple, FIELD_MTIME, NULL) != -1)) {
                     update_playlistwin = TRUE;
                     continue;
                  }
@@ -2972,32 +2974,35 @@
     GList *entry_list = NULL, *found_list = NULL, *sel_list = NULL;
     gboolean is_first_search = TRUE;
     gint num_of_entries_found = 0;
-
-    #if defined(USE_REGEX_ONIGURUMA)
+    const gchar *regex_pattern;
+    const gchar *track_name = tuple_get_string( tuple, FIELD_TITLE, NULL );
+    const gchar *album_name = tuple_get_string( tuple, FIELD_ALBUM, NULL );
+    const gchar *performer = tuple_get_string( tuple, FIELD_PERFORMER, NULL );
+    const gchar *file_name = tuple_get_string( tuple, FIELD_FILE_NAME, NULL );
+
+#if defined(USE_REGEX_ONIGURUMA)
     /* set encoding for Oniguruma regex to UTF-8 */
     reg_set_encoding( REG_POSIX_ENCODING_UTF8 );
     onig_set_default_syntax( ONIG_SYNTAX_POSIX_BASIC );
-    #endif
+#endif
 
     PLAYLIST_LOCK(playlist);
 
-    if ( tuple_get_string(tuple, "title") != NULL )
+    if ( (regex_pattern = tuple_get_string(tuple, FIELD_TITLE, NULL)) != NULL )
     {
         /* match by track_name */
-        const gchar *regex_pattern = tuple_get_string(tuple, "title");
         regex_t regex;
-    #if defined(USE_REGEX_PCRE)
+#if defined(USE_REGEX_PCRE)
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
-    #else
+#else
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 )
-    #endif
+#endif
         {
             GList *tfound_list = NULL;
             if ( is_first_search == TRUE ) entry_list = playlist->entries;
             else entry_list = found_list; /* use found_list */
             for ( ; entry_list ; entry_list = g_list_next(entry_list) )
             {
-                const gchar *track_name = tuple_get_string( tuple, "title" );
                 PlaylistEntry *entry = entry_list->data;
                 if ( ( entry->tuple != NULL ) && ( track_name != NULL ) &&
                    ( regexec( &regex , track_name , 0 , NULL , 0 ) == 0 ) )
@@ -3012,23 +3017,21 @@
         is_first_search = FALSE;
     }
 
-    if ( tuple_get_string(tuple, "album") != NULL )
+    if ( (regex_pattern = tuple_get_string(tuple, FIELD_ALBUM, NULL)) != NULL )
     {
         /* match by album_name */
-        const gchar *regex_pattern = tuple_get_string(tuple, "album");
         regex_t regex;
-    #if defined(USE_REGEX_PCRE)
+#if defined(USE_REGEX_PCRE)
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
-    #else
+#else
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 )
-    #endif
+#endif
         {
             GList *tfound_list = NULL;
             if ( is_first_search == TRUE ) entry_list = playlist->entries;
             else entry_list = found_list; /* use found_list */
             for ( ; entry_list ; entry_list = g_list_next(entry_list) )
             {
-                const gchar *album_name = tuple_get_string( tuple, "album" );
                 PlaylistEntry *entry = entry_list->data;
                 if ( ( entry->tuple != NULL ) && ( album_name != NULL ) &&
                    ( regexec( &regex , album_name , 0 , NULL , 0 ) == 0 ) )
@@ -3043,23 +3046,21 @@
         is_first_search = FALSE;
     }
 
-    if ( tuple_get_string(tuple, "artist") != NULL )
+    if ( (regex_pattern = tuple_get_string(tuple, FIELD_ARTIST, NULL)) != NULL )
     {
         /* match by performer */
-        const gchar *regex_pattern = tuple_get_string(tuple, "artist");
         regex_t regex;
-    #if defined(USE_REGEX_PCRE)
+#if defined(USE_REGEX_PCRE)
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
-    #else
+#else
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 )
-    #endif
+#endif
         {
             GList *tfound_list = NULL;
             if ( is_first_search == TRUE ) entry_list = playlist->entries;
             else entry_list = found_list; /* use found_list */
             for ( ; entry_list ; entry_list = g_list_next(entry_list) )
             {
-                const gchar *performer = tuple_get_string( tuple, "performer" );
                 PlaylistEntry *entry = entry_list->data;
                 if ( ( entry->tuple != NULL ) && ( performer != NULL ) &&
                    ( regexec( &regex , performer , 0 , NULL , 0 ) == 0 ) )
@@ -3074,23 +3075,21 @@
         is_first_search = FALSE;
     }
 
-    if ( tuple_get_string(tuple, "file-name") != NULL )
+    if ( (regex_pattern = tuple_get_string(tuple, FIELD_FILE_NAME, NULL)) != NULL )
     {
         /* match by file_name */
-        const gchar *regex_pattern = tuple_get_string(tuple, "file-name");
         regex_t regex;
-    #if defined(USE_REGEX_PCRE)
+#if defined(USE_REGEX_PCRE)
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE | REG_UTF8 ) == 0 )
-    #else
+#else
         if ( regcomp( &regex , regex_pattern , REG_NOSUB | REG_ICASE ) == 0 )
-    #endif
+#endif
         {
             GList *tfound_list = NULL;
             if ( is_first_search == TRUE ) entry_list = playlist->entries;
             else entry_list = found_list; /* use found_list */
             for ( ; entry_list ; entry_list = g_list_next(entry_list) )
             {
-                const gchar *file_name = tuple_get_string( tuple, "file-name" );
                 PlaylistEntry *entry = entry_list->data;
                 if ( ( entry->tuple != NULL ) && ( file_name != NULL ) &&
                    ( regexec( &regex , file_name , 0 , NULL , 0 ) == 0 ) )
@@ -3219,7 +3218,7 @@
 
 	/* invalidate mtime to reread */
 	if (entry->tuple != NULL)
-            tuple_associate_int(entry->tuple, "mtime", -1); /* -1 denotes "non-initialized". now 0 is for stream etc. yaz */
+            tuple_associate_int(entry->tuple, FIELD_MTIME, NULL, -1); /* -1 denotes "non-initialized". now 0 is for stream etc. yaz */
 
         if (!playlist_entry_get_info(entry)) {
             if (g_list_index(playlist->entries, entry) == -1)
--- a/src/audacious/plugin.h	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/plugin.h	Tue Sep 11 09:14:15 2007 -0500
@@ -53,8 +53,8 @@
 #define LOWLEVEL_PLUGIN(x) ((LowlevelPlugin *)(x))
 
 #define __AUDACIOUS_NEWVFS__
-#define __AUDACIOUS_PLUGIN_API__ 5
-#define __AUDACIOUS_INPUT_PLUGIN_API__ 5
+#define __AUDACIOUS_PLUGIN_API__ 6
+#define __AUDACIOUS_INPUT_PLUGIN_API__ 6
 
 typedef enum {
     FMT_U8,
--- a/src/audacious/tuple.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/tuple.c	Tue Sep 11 09:14:15 2007 -0500
@@ -23,10 +23,37 @@
 
 #include "tuple.h"
 
+const TupleBasicType tuple_fields[FIELD_LAST] = {
+    { "artist",         TUPLE_STRING },
+    { "title",          TUPLE_STRING },
+    { "album",          TUPLE_STRING },
+    { "comment",        TUPLE_STRING },
+    { "genre",          TUPLE_STRING },
+
+    { "track",          TUPLE_STRING },
+    { "track-number",   TUPLE_INT },
+    { "length",         TUPLE_INT },
+    { "year",           TUPLE_INT },
+    { "quality",	TUPLE_STRING },
+
+    { "codec",          TUPLE_STRING },
+    { "file-name",      TUPLE_STRING },
+    { "file-path",      TUPLE_STRING },
+    { "file-ext",       TUPLE_STRING },
+    { "song-artist",    TUPLE_STRING },
+
+    { "mtime",          TUPLE_INT },
+    { "formatter",      TUPLE_STRING },
+    { "performer",      TUPLE_STRING },
+    { "copyright",      TUPLE_STRING },
+    { "date",           TUPLE_STRING },
+};
+
 static mowgli_heap_t *tuple_heap = NULL;
 static mowgli_heap_t *tuple_value_heap = NULL;
 static mowgli_object_class_t tuple_klass;
 
+
 #define TUPLE_LOCKING
 //#define TUPLE_DEBUG
 
@@ -92,12 +119,12 @@
     /* FIXME: use mowgli_object_bless_from_class() in mowgli 0.4
        when it is released --nenolod */
     tuple = mowgli_heap_alloc(tuple_heap);
+    memset(tuple, 0, sizeof(Tuple));
     mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL);
 
     tuple->dict = mowgli_dictionary_create(g_ascii_strcasecmp);
 
     TUPLE_UNLOCK_WRITE();
-    
     return tuple;
 }
 
@@ -116,11 +143,11 @@
     realfn = g_filename_from_uri(filename, NULL, NULL);
 
     scratch = g_path_get_basename(realfn ? realfn : filename);
-    tuple_associate_string(tuple, "file-name", scratch);
+    tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, scratch);
     g_free(scratch);
 
     scratch = g_path_get_dirname(realfn ? realfn : filename);
-    tuple_associate_string(tuple, "file-path", scratch);
+    tuple_associate_string(tuple, FIELD_FILE_PATH, NULL, scratch);
     g_free(scratch);
 
     g_free(realfn); realfn = NULL;
@@ -128,161 +155,227 @@
     ext = strrchr(filename, '.');
     if (ext != NULL) {
         ++ext;
-        tuple_associate_string(tuple, "file-ext", scratch);
+        tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, scratch);
     }
 
     return tuple;
 }        
 
+
+static gint tuple_get_nfield(const gchar *field)
+{
+    gint i;
+    for (i = 0; i < FIELD_LAST; i++)
+        if (!strcmp(field, tuple_fields[i].name))
+            return i;
+    return -1;
+}
+
+
+static TupleValue *
+tuple_associate_data(Tuple *tuple, const gint cnfield, const gchar *field, TupleValueType ftype)
+{
+    const gchar *tfield = field;
+    gint nfield = cnfield;
+    TupleValue *value = NULL;
+    
+    g_return_val_if_fail(tuple != NULL, NULL);
+    g_return_val_if_fail(cnfield < FIELD_LAST, NULL);
+
+    /* Check for known fields */
+    if (nfield < 0) {
+        nfield = tuple_get_nfield(field);
+        if (nfield >= 0) {
+            fprintf(stderr, "WARNING! FIELD_* not used for '%s'!\n", field);
+        }
+    }
+
+    /* Check if field was known */
+    if (nfield >= 0) {
+        tfield = tuple_fields[nfield].name;
+        value = tuple->values[nfield];
+        
+        if (ftype != tuple_fields[nfield].type) {
+            /* FIXME! Convert values perhaps .. or not? */
+            fprintf(stderr, "Invalid type for [%s](%d->%d), %d != %d\n", tfield, cnfield, nfield, ftype, tuple_fields[nfield].type);
+            //mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
+            TUPLE_UNLOCK_WRITE();
+            return NULL;
+        }
+    } else {
+        value = mowgli_dictionary_retrieve(tuple->dict, tfield);
+    }
+    
+    if (value != NULL) {
+        /* Value exists, just delete old associated data */
+        if (value->type == TUPLE_STRING) {
+            g_free(value->value.string);
+            value->value.string = NULL;
+        }
+    } else {
+        /* Allocate a new value */
+        value = mowgli_heap_alloc(tuple_value_heap);
+        value->type = ftype;
+        if (nfield >= 0)
+            tuple->values[nfield] = value;
+        else
+            mowgli_dictionary_add(tuple->dict, tfield, value);
+    }
+
+    return value;
+}
+
 gboolean
-tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string)
+tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string)
 {
     TupleValue *value;
 
-    g_return_val_if_fail(tuple != NULL, FALSE);
-    g_return_val_if_fail(field != NULL, FALSE);
+    TUPLE_LOCK_WRITE();
+    if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_STRING)) == NULL)
+        return FALSE;
 
-    TUPLE_LOCK_WRITE();
-    if ((value = mowgli_dictionary_delete(tuple->dict, field)))
-        tuple_disassociate_now(value);
-    
-    if (string == NULL) {
-        TUPLE_UNLOCK_WRITE();
-        return TRUE;
-    }
+    if (string == NULL)
+        value->value.string = NULL;
+    else
+        value->value.string = g_strdup(string);
 
-    value = mowgli_heap_alloc(tuple_value_heap);
-    value->type = TUPLE_STRING;
-    value->value.string = g_strdup(string);
-
-    mowgli_dictionary_add(tuple->dict, field, value);
     TUPLE_UNLOCK_WRITE();
-    
     return TRUE;
 }
 
 gboolean
-tuple_associate_int(Tuple *tuple, const gchar *field, gint integer)
+tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer)
 {
     TupleValue *value;
-
-    g_return_val_if_fail(tuple != NULL, FALSE);
-    g_return_val_if_fail(field != NULL, FALSE);
-
+    
     TUPLE_LOCK_WRITE();
-    if ((value = mowgli_dictionary_delete(tuple->dict, field)))
-        tuple_disassociate_now(value);
+    if ((value = tuple_associate_data(tuple, nfield, field, TUPLE_INT)) == NULL)
+        return FALSE;
 
-    value = mowgli_heap_alloc(tuple_value_heap);
-    value->type = TUPLE_INT;
     value->value.integer = integer;
-
-    mowgli_dictionary_add(tuple->dict, field, value);
+    
     TUPLE_UNLOCK_WRITE();
-    
     return TRUE;
 }
 
 void
-tuple_disassociate_now(TupleValue *value)
-{
-    if (value->type == TUPLE_STRING)
-        g_free(value->value.string);
-
-    mowgli_heap_free(tuple_value_heap, value);
-}
-
-void
-tuple_disassociate(Tuple *tuple, const gchar *field)
+tuple_disassociate(Tuple *tuple, const gint cnfield, const gchar *field)
 {
     TupleValue *value;
+    gint nfield = cnfield;
 
     g_return_if_fail(tuple != NULL);
-    g_return_if_fail(field != NULL);
+    g_return_if_fail(nfield < FIELD_LAST);
+
+    if (nfield < 0)
+        nfield = tuple_get_nfield(field);
 
-    /* why _delete()? because _delete() returns the dictnode's data on success */
     TUPLE_LOCK_WRITE();
-    value = mowgli_dictionary_delete(tuple->dict, field);
-    
+    if (nfield < 0)
+        /* why _delete()? because _delete() returns the dictnode's data on success */
+        value = mowgli_dictionary_delete(tuple->dict, field);
+    else {
+        value = tuple->values[nfield];
+        tuple->values[nfield] = NULL;
+    }
+
     if (value == NULL) {
         TUPLE_UNLOCK_WRITE();
         return;
     }
     
-    tuple_disassociate_now(value);
+    /* Free associated data */
+    if (value->type == TUPLE_STRING) {
+        g_free(value->value.string);
+        value->value.string = NULL;
+    }
+
+    mowgli_heap_free(tuple_value_heap, value);
     TUPLE_UNLOCK_WRITE();
 }
 
 TupleValueType
-tuple_get_value_type(Tuple *tuple, const gchar *field)
+tuple_get_value_type(Tuple *tuple, const gint cnfield, const gchar *field)
 {
-    TupleValue *value;
+    TupleValueType type = TUPLE_UNKNOWN;
+    gint nfield = cnfield;
 
     g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN);
-    g_return_val_if_fail(field != NULL, TUPLE_UNKNOWN);
+    g_return_val_if_fail(nfield < FIELD_LAST, TUPLE_UNKNOWN);
 
+    if (nfield < 0)
+        nfield = tuple_get_nfield(field);
+    
     TUPLE_LOCK_READ();
-    value = mowgli_dictionary_retrieve(tuple->dict, field);
+    if (nfield < 0) {
+        TupleValue *value;
+        if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL)
+            type = value->type;
+    } else {
+        if (tuple->values[nfield])
+            type = tuple->values[nfield]->type;
+    }
+    
     TUPLE_UNLOCK_READ();
-
-    if (value == NULL)
-        return TUPLE_UNKNOWN;
-
-    return value->type;
+    return type;
 }
 
 const gchar *
-tuple_get_string(Tuple *tuple, const gchar *field)
+tuple_get_string(Tuple *tuple, const gint cnfield, const gchar *field)
 {
     TupleValue *value;
-    gchar *val;
+    gint nfield = cnfield;
 
     g_return_val_if_fail(tuple != NULL, NULL);
-    g_return_val_if_fail(field != NULL, NULL);
+    g_return_val_if_fail(nfield < FIELD_LAST, NULL);
 
+    if (nfield < 0)
+        nfield = tuple_get_nfield(field);
+    
     TUPLE_LOCK_READ();
-    value = mowgli_dictionary_retrieve(tuple->dict, field);
-    
-    if (value == NULL) {
+    if (nfield < 0)
+        value = mowgli_dictionary_retrieve(tuple->dict, field);
+    else
+        value = tuple->values[nfield];
+
+    if (value) {
+        if (value->type != TUPLE_STRING)
+            mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL);
+        
+        TUPLE_UNLOCK_READ();
+        return value->value.string;
+    } else {
         TUPLE_UNLOCK_READ();
         return NULL;
     }
-
-    if (value->type != TUPLE_STRING) {
-        TUPLE_UNLOCK_READ();
-        mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL);
-    }
-
-    val = value->value.string;
-    TUPLE_UNLOCK_READ();
-    
-    return val;
 }
 
-int
-tuple_get_int(Tuple *tuple, const gchar *field)
+gint
+tuple_get_int(Tuple *tuple, const gint cnfield, const gchar *field)
 {
     TupleValue *value;
-    gint val;
+    gint nfield = cnfield;
 
     g_return_val_if_fail(tuple != NULL, 0);
-    g_return_val_if_fail(field != NULL, 0);
+    g_return_val_if_fail(nfield < FIELD_LAST, 0);
 
+    if (nfield < 0)
+        nfield = tuple_get_nfield(field);
+    
     TUPLE_LOCK_READ();
-    value = mowgli_dictionary_retrieve(tuple->dict, field);
-    
-    if (value == NULL) {
+    if (nfield < 0)
+        value = mowgli_dictionary_retrieve(tuple->dict, field);
+    else
+        value = tuple->values[nfield];
+
+    if (value) {
+        if (value->type != TUPLE_INT)
+            mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
+        
+        TUPLE_UNLOCK_READ();
+        return value->value.integer;
+    } else {
         TUPLE_UNLOCK_READ();
         return 0;
     }
-
-    if (value->type != TUPLE_INT) {
-        TUPLE_UNLOCK_READ();
-        mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
-    }
-
-    val = value->value.integer;
-    TUPLE_UNLOCK_READ();
-
-    return val;
 }
--- a/src/audacious/tuple.h	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/tuple.h	Tue Sep 11 09:14:15 2007 -0500
@@ -24,11 +24,35 @@
 #include <glib.h>
 #include <mowgli.h>
 
-typedef struct _Tuple {
-    mowgli_object_t parent;
-    mowgli_dictionary_t *dict;
-} Tuple;
+
+enum {
+    FIELD_ARTIST = 0,
+    FIELD_TITLE,
+    FIELD_ALBUM,
+    FIELD_COMMENT,
+    FIELD_GENRE,
+
+    FIELD_TRACK,
+    FIELD_TRACK_NUMBER,
+    FIELD_LENGTH,
+    FIELD_YEAR,
+    FIELD_QUALITY,
 
+    FIELD_CODEC,
+    FIELD_FILE_NAME,
+    FIELD_FILE_PATH,
+    FIELD_FILE_EXT,
+    FIELD_SONG_ARTIST,
+
+    FIELD_MTIME,
+    FIELD_FORMATTER,
+    FIELD_PERFORMER,
+    FIELD_COPYRIGHT,
+    FIELD_DATE,
+
+    /* special field, must always be last */
+    FIELD_LAST
+};
 
 typedef enum {
     TUPLE_STRING,
@@ -37,6 +61,13 @@
 } TupleValueType;
 
 typedef struct {
+    gchar *name;
+    TupleValueType type;
+} TupleBasicType;
+
+extern const TupleBasicType tuple_fields[FIELD_LAST];
+
+typedef struct {
     TupleValueType type;
     union {
         gchar *string;
@@ -44,15 +75,22 @@
     } value;
 } TupleValue;
 
+typedef struct _Tuple {
+    mowgli_object_t parent;
+    mowgli_dictionary_t *dict;
+    TupleValue *values[FIELD_LAST];
+} Tuple;
+
+
 Tuple *tuple_new(void);
 Tuple *tuple_new_from_filename(const gchar *filename);
-gboolean tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string);
-gboolean tuple_associate_int(Tuple *tuple, const gchar *field, gint integer);
-void tuple_disassociate(Tuple *tuple, const gchar *field);
+gboolean tuple_associate_string(Tuple *tuple, const gint nfield, const gchar *field, const gchar *string);
+gboolean tuple_associate_int(Tuple *tuple, const gint nfield, const gchar *field, gint integer);
+void tuple_disassociate(Tuple *tuple, const gint nfield, const gchar *field);
 void tuple_disassociate_now(TupleValue *value);
-TupleValueType tuple_get_value_type(Tuple *tuple, const gchar *field);
-const gchar *tuple_get_string(Tuple *tuple, const gchar *field);
-int tuple_get_int(Tuple *tuple, const gchar *field);
+TupleValueType tuple_get_value_type(Tuple *tuple, const gint nfield, const gchar *field);
+const gchar *tuple_get_string(Tuple *tuple, const gint nfield, const gchar *field);
+gint tuple_get_int(Tuple *tuple, const gint nfield, const gchar *field);
 #define tuple_free(x) mowgli_object_unref(x);
 
 #endif
--- a/src/audacious/tuple_compiler.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/tuple_compiler.c	Tue Sep 11 09:14:15 2007 -0500
@@ -19,28 +19,13 @@
  */
 
 /*
- * What's this?
- * ------------
- * Nothing really. A prototype / pseudo-C for an improved Tuple formatting
- * system, where the format string is "compiled" into a tree structure,
- * which can then be traversed fast while "evaluating". This file does
- * not represent anything but some of my (ccr) ideas for the concept.
- *
- * The basic ideas are: 
- * 1) compiled structure for faster traversing
- * 2) sub-expression removal / constant elimination
- * 3) indexes and/or hashes for tuple entries for faster access
- * 4) avoid expensive memory re-allocation
- *
- * and possibly 5) caching of certain things
- *
- *
  * TODO:
  * - implement definitions (${=foo,"baz"} ${=foo,1234})
  * - implement functions
  * - implement handling of external expressions
  * - error handling issues?
  * - evaluation context: how local variables should REALLY work?
+ *   currently there is just a single context, is a "global" context needed?
  */
 
 #include "config.h"
@@ -56,14 +41,14 @@
   va_start(ap, fmt);
   vfprintf(stderr, fmt, ap);
   va_end(ap);
-  exit(5);
+//  exit(5);
 }
 
 
 static void tuple_evalctx_free_var(TupleEvalVar *var)
 {
   assert(var != NULL);
-  
+  var->fieldidx = -1;
   g_free(var->defval);
   g_free(var->name);
   g_free(var);
@@ -87,22 +72,19 @@
 }
 
 
-/* "Reset" the evaluation context, clean up locally set variables,
- * but leave globals.
+/* "Reset" the evaluation context, clean up temporary variables.
  */
 void tuple_evalctx_reset(TupleEvalContext *ctx)
 {
   gint i;
   
-  /* Free local variables */
   for (i = 0; i < ctx->nvariables; i++)
     if (ctx->variables[i]) {
-      ctx->variables[i]->dictref = NULL;
+      ctx->variables[i]->fieldref = NULL;
 
-      if (ctx->variables[i]->islocal)
+      if (ctx->variables[i]->istemp)
         tuple_evalctx_free_var(ctx->variables[i]);
     }
-  
 }
 
 
@@ -133,15 +115,24 @@
 }
 
 
-gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type)
+gint tuple_evalctx_add_var(TupleEvalContext *ctx, const gchar *name, const gboolean istemp, const gint type)
 {
-  gint i;
+  gint i, ref = -1;
   TupleEvalVar * tmp = g_new0(TupleEvalVar, 1);
 
   tmp->name = g_strdup(name);
-  tmp->islocal = islocal;
+  tmp->istemp = istemp;
   tmp->type = type;
-  
+  tmp->fieldidx = ref;
+
+  /* Find fieldidx, if any */
+  if (type == VAR_FIELD) {
+    for (i = 0; i < FIELD_LAST && ref < 0; i++)
+      if (strcmp(tuple_fields[i].name, name) == 0) ref = i;
+
+    tmp->fieldidx = ref;
+  }
+
   /* Find a free slot */
   for (i = 0; i < ctx->nvariables; i++)
   if (!ctx->variables[i]) {
@@ -379,7 +370,7 @@
                   /* Integer */
                 }
                 
-                tuple_error("definitions not yet supported!\n");
+                tuple_error("Definitions are not yet supported!\n");
                 goto ret_error;
               } else
                 goto ret_error;
@@ -421,6 +412,19 @@
           case '(': c++;
             if (!strncmp(c, "empty)?", 7)) {
               c += 7;
+              literal = FALSE;
+              if (tc_get_item(&c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
+                c++;
+                tmp = tuple_evalnode_new();
+                tmp->opcode = OP_EXISTS;
+                if ((tmp->var[0] = tc_get_variable(ctx, tmps1, VAR_FIELD)) < 0) {
+                  tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, expr);
+                  goto ret_error;
+                }
+                tmp->children = tuple_compiler_pass1(level, ctx, &c);
+                tuple_evalnode_insert(&res, tmp);
+              } else
+                goto ret_error;
             } else
               goto ext_expression;
             break;
@@ -508,7 +512,6 @@
 static TupleEvalNode *tuple_compiler_pass2(gboolean *changed, TupleEvalContext *ctx, TupleEvalNode *expr)
 {
   TupleEvalNode *curr = expr, *res = NULL;
-  *changed = FALSE;
   assert(ctx != NULL);
   assert(expr != NULL);
   
@@ -543,17 +546,25 @@
 }
 
 
-/* Evaluate tuple in given TupleEval expression in given
- * context and return resulting string.
+/* Fetch a reference to a tuple field for given variable by fieldidx or dict.
+ * Return pointer to field, NULL if not available.
  */
-static TupleValue * tf_get_dictref(TupleEvalVar *var, Tuple *tuple)
+static TupleValue * tf_get_fieldref(TupleEvalVar *var, Tuple *tuple)
 {
-  if (var->type == VAR_FIELD && var->dictref == NULL)
-    var->dictref = mowgli_dictionary_retrieve(tuple->dict, var->name);
+  if (var->type == VAR_FIELD && var->fieldref == NULL) {
+    if (var->fieldidx < 0)
+      var->fieldref = mowgli_dictionary_retrieve(tuple->dict, var->name);
+    else
+      var->fieldref = tuple->values[var->fieldidx];
+  }
 
-  return var->dictref;
+  return var->fieldref;
 }
 
+
+/* Fetch string or int value of given variable, whatever type it might be.
+ * Return VAR_* type for the variable.
+ */
 static TupleValueType tf_get_var(gchar **tmps, gint *tmpi, TupleEvalVar *var, Tuple *tuple)
 {
   TupleValueType type = TUPLE_UNKNOWN;
@@ -562,12 +573,12 @@
     case VAR_DEF: *tmps = var->defval; type = TUPLE_STRING; break;
     case VAR_CONST: *tmps = var->name; type = TUPLE_STRING; break;
     case VAR_FIELD:
-      if (tf_get_dictref(var, tuple)) {
-        if (var->dictref->type == TUPLE_STRING)
-          *tmps = var->dictref->value.string;
+      if (tf_get_fieldref(var, tuple)) {
+        if (var->fieldref->type == TUPLE_STRING)
+          *tmps = var->fieldref->value.string;
         else
-          *tmpi = var->dictref->value.integer;
-        type = var->dictref->type;
+          *tmpi = var->fieldref->value.integer;
+        type = var->fieldref->type;
       }
       break;
     default:
@@ -579,6 +590,9 @@
 }
 
 
+/* Evaluate tuple in given TupleEval expression in given
+ * context and return resulting string.
+ */
 static gboolean tuple_formatter_eval_do(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple, gchar **res, size_t *resmax, size_t *reslen)
 {
   TupleEvalNode *curr = expr;
@@ -609,14 +623,14 @@
             break;
           
           case VAR_FIELD:
-            if (tf_get_dictref(var0, tuple)) {
-              switch (var0->dictref->type) {
+            if (tf_get_fieldref(var0, tuple)) {
+              switch (var0->fieldref->type) {
                 case TUPLE_STRING:
-                  str = var0->dictref->value.string;
+                  str = var0->fieldref->value.string;
                   break;
           
                 case TUPLE_INT:
-                  snprintf(tmps, sizeof(tmps), "%d", var0->dictref->value.integer);
+                  snprintf(tmps, sizeof(tmps), "%d", var0->fieldref->value.integer);
                   str = tmps;
                   break;
                 
@@ -658,28 +672,29 @@
             case OP_LTEQ:       result = (resulti <= 0); break;
             case OP_GT:         result = (resulti >  0); break;
             case OP_GTEQ:       result = (resulti >= 0); break;
-            default: result = FALSE;
+            default:		result = FALSE;
           }
             
           if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
             return FALSE;
+        } else {
+          /* FIXME?! Warn that types are not same? */
         }
         break;
       
       case OP_IS_EMPTY:
         var0 = ctx->variables[curr->var[0]];
 
-        if (var0->dictref == NULL)
-          var0->dictref = mowgli_dictionary_retrieve(tuple->dict, var0->name);
+        if (tf_get_fieldref(var0, tuple)) {
 
-        switch (var0->dictref->type) {
+          switch (var0->fieldref->type) {
           case TUPLE_INT:
-            result = (var0->dictref->value.integer == 0);
+            result = (var0->fieldref->value.integer == 0);
             break;
         
           case TUPLE_STRING:
             result = TRUE;
-            tmps2 = var0->dictref->value.string;
+            tmps2 = var0->fieldref->value.string;
           
             while (result && *tmps2 != '\0') {
               if (*tmps2 == ' ')
@@ -691,12 +706,12 @@
 
           default:
             result = TRUE;
-        }
+          }
+        } else
+          result = TRUE;
         
-        if (result) {
-          if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
-            return FALSE;
-        }
+        if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
+          return FALSE;
         break;
       
       default:
@@ -739,10 +754,7 @@
   
   if (!expr) return NULL;
   
-  if (!tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen)) {
-    g_free(res);
-    return NULL;
-  }
+  tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen);
   
   return res;
 }
--- a/src/audacious/tuple_compiler.h	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/tuple_compiler.h	Tue Sep 11 09:14:15 2007 -0500
@@ -55,7 +55,7 @@
     VAR_CONST,
     VAR_DEF
 };
-    
+
 
 /* Caching structure for deterministic functions
  */
@@ -68,10 +68,12 @@
 
 typedef struct {
     gchar *name;
-    gboolean islocal;		/* Local? true = will be cleaned with tuple_evalctx_reset() */
-    gint type;			/* Type of this "variable", see VAR_* */
-    gchar *defval;
-    TupleValue *dictref;	/* Cached tuple value ref */
+    gboolean istemp;		/* Scope of variable - TRUE = temporary */
+    gint type;			/* Type of variable, see VAR_* */
+    gchar *defval;		/* Defined value ${=foo,bar} */
+
+    gint fieldidx;		/* if >= 0: Index # of "pre-defined" Tuple fields */
+    TupleValue *fieldref;	/* Cached tuple field ref */
 } TupleEvalVar;
 
 
@@ -95,14 +97,11 @@
 TupleEvalContext * tuple_evalctx_new(void);
 void tuple_evalctx_reset(TupleEvalContext *ctx);
 void tuple_evalctx_free(TupleEvalContext *ctx);
-gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type);
+gint tuple_evalctx_add_var(TupleEvalContext *ctx, const gchar *name, const gboolean istemp, const gint type);
 
 void tuple_evalnode_free(TupleEvalNode *expr);
 
 TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, gchar *expr);
 gchar *tuple_formatter_eval(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple);
-/*
-gchar *tuple_formatter_make_title_string(Tuple *tuple, const gchar *string);
-*/
 
 #endif
--- a/src/audacious/tuple_formatter.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/tuple_formatter.c	Tue Sep 11 09:14:15 2007 -0500
@@ -271,15 +271,15 @@
     /* ${artist} */
     if (expr == NULL && argument == NULL)
     {
-        TupleValueType type = tuple_get_value_type(tuple, expression);
+        TupleValueType type = tuple_get_value_type(tuple, -1, expression);
 
         switch(type)
         {
         case TUPLE_STRING:
-             return g_strdup(tuple_get_string(tuple, expression));
+             return g_strdup(tuple_get_string(tuple, -1, expression));
              break;
         case TUPLE_INT:
-             return g_strdup_printf("%d", tuple_get_int(tuple, expression));
+             return g_strdup_printf("%d", tuple_get_int(tuple, -1, expression));
              break;
         case TUPLE_UNKNOWN:
         default:
@@ -383,7 +383,7 @@
 static gboolean
 tuple_formatter_expression_exists(Tuple *tuple, const gchar *expression)
 {
-    return (tuple_get_value_type(tuple, expression) != TUPLE_UNKNOWN) ? TRUE : FALSE;
+    return (tuple_get_value_type(tuple, -1, expression) != TUPLE_UNKNOWN) ? TRUE : FALSE;
 }
 
 /* builtin-keyword: ${==arg1,arg2}, returns TRUE if <arg1> and <arg2> match.
@@ -406,7 +406,7 @@
         else /* bad formatted arg */
             return FALSE;
     }
-    else if (tuple_get_value_type(tuple, args[0]) == TUPLE_UNKNOWN)
+    else if (tuple_get_value_type(tuple, -1, args[0]) == TUPLE_UNKNOWN)
     {
         g_strfreev(args);
         return FALSE;
@@ -423,7 +423,7 @@
         else /* bad formatted arg */
             return FALSE;
     }
-    else if (tuple_get_value_type(tuple, args[1]) == TUPLE_UNKNOWN)
+    else if (tuple_get_value_type(tuple, -1, args[1]) == TUPLE_UNKNOWN)
     {
         g_strfreev(args);
         return FALSE;
@@ -431,18 +431,18 @@
 
     if (!arg1) /* if arg1 is not "raw text", get the tuple value */
     {
-        if (tuple_get_value_type(tuple, args[0]) == TUPLE_STRING)
-            arg1 = g_strdup(tuple_get_string(tuple, args[0]));
+        if (tuple_get_value_type(tuple, -1, args[0]) == TUPLE_STRING)
+            arg1 = g_strdup(tuple_get_string(tuple, -1, args[0]));
         else
-            arg1 = g_strdup_printf("%d", tuple_get_int(tuple, args[0]));
+            arg1 = g_strdup_printf("%d", tuple_get_int(tuple, -1, args[0]));
     }
 
     if (!arg2) /* if arg2 is not "raw text", get the tuple value */
     {
-        if (tuple_get_value_type(tuple, args[1]) == TUPLE_STRING)
-            arg2 = g_strdup(tuple_get_string(tuple, args[1]));
+        if (tuple_get_value_type(tuple, -1, args[1]) == TUPLE_STRING)
+            arg2 = g_strdup(tuple_get_string(tuple, -1, args[1]));
         else
-            arg2 = g_strdup_printf("%d", tuple_get_int(tuple, args[1]));
+            arg2 = g_strdup_printf("%d", tuple_get_int(tuple, -1, args[1]));
     }
 
     ret = g_ascii_strcasecmp(arg1, arg2);
@@ -467,15 +467,15 @@
 {
     gboolean ret = TRUE;
     const gchar *iter;
-    TupleValueType type = tuple_get_value_type(tuple, expression);
+    TupleValueType type = tuple_get_value_type(tuple, -1, expression);
 
     if (type == TUPLE_UNKNOWN)
         return TRUE;
 
-    if (type == TUPLE_INT && tuple_get_int(tuple, expression) != 0)
+    if (type == TUPLE_INT && tuple_get_int(tuple, -1, expression) != 0)
         return FALSE;
 
-    iter = tuple_get_string(tuple, expression);
+    iter = tuple_get_string(tuple, -1, expression);
 
     while (ret && *iter != '\0')
     {
@@ -529,7 +529,7 @@
 
     if(!rv || !strcmp(rv, "")) {
         g_free(rv);
-        rv = g_strdup(tuple_get_string(tuple, "file-name"));
+        rv = g_strdup(tuple_get_string(tuple, FIELD_FILE_NAME, NULL));
     }
 
     return rv;
--- a/src/audacious/ui_fileinfo.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/ui_fileinfo.c	Tue Sep 11 09:14:15 2007 -0500
@@ -180,26 +180,32 @@
 
 	gtk_widget_realize(fileinfo_win);
 
-	fileinfo_entry_set_text("entry_title", tuple_get_string(tuple, "title"));
-	fileinfo_entry_set_text("entry_artist", tuple_get_string(tuple, "artist"));
-	fileinfo_entry_set_text("entry_album", tuple_get_string(tuple, "album"));
-	fileinfo_entry_set_text("entry_comment", tuple_get_string(tuple, "comment"));
-	fileinfo_entry_set_text("entry_genre", tuple_get_string(tuple, "genre"));
+	fileinfo_entry_set_text("entry_title", tuple_get_string(tuple, FIELD_TITLE, NULL));
+	fileinfo_entry_set_text("entry_artist", tuple_get_string(tuple, FIELD_ARTIST, NULL));
+	fileinfo_entry_set_text("entry_album", tuple_get_string(tuple, FIELD_ALBUM, NULL));
+	fileinfo_entry_set_text("entry_comment", tuple_get_string(tuple, FIELD_COMMENT, NULL));
+	fileinfo_entry_set_text("entry_genre", tuple_get_string(tuple, FIELD_GENRE, NULL));
 
-	tmp = g_strdup_printf("%s/%s", tuple_get_string(tuple, "file-path"), tuple_get_string(tuple, "file-name"));
+	tmp = g_strdup_printf("%s/%s",
+		tuple_get_string(tuple, FIELD_FILE_PATH, NULL),
+		tuple_get_string(tuple, FIELD_FILE_NAME, NULL));
 	if(tmp){
 		fileinfo_entry_set_text_free("entry_location", str_to_utf8(tmp));
 		g_free(tmp);
 		tmp = NULL;
 	}
 
-	if (tuple_get_int(tuple, "year"))
-		fileinfo_entry_set_text_free("entry_year", g_strdup_printf("%d", tuple_get_int(tuple, "year")));
+	if (tuple_get_int(tuple, FIELD_YEAR, NULL))
+		fileinfo_entry_set_text_free("entry_year",
+			g_strdup_printf("%d", tuple_get_int(tuple, FIELD_YEAR, NULL)));
 
-	if (tuple_get_int(tuple, "track-number"))
-		fileinfo_entry_set_text_free("entry_track", g_strdup_printf("%d", tuple_get_int(tuple, "track-number")));
+	if (tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL))
+		fileinfo_entry_set_text_free("entry_track",
+			g_strdup_printf("%d", tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL)));
 
-	tmp = fileinfo_recursive_get_image(tuple_get_string(tuple, "file-path"), tuple_get_string(tuple, "file-name"), 0);
+	tmp = fileinfo_recursive_get_image(
+		tuple_get_string(tuple, FIELD_FILE_PATH, NULL),
+		tuple_get_string(tuple, FIELD_FILE_NAME, NULL), 0);
 	
 	if(tmp)
 	{
--- a/src/audacious/ui_fileinfopopup.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/ui_fileinfopopup.c	Tue Sep 11 09:14:15 2007 -0500
@@ -350,42 +350,42 @@
         tmp = NULL;
         g_object_set_data(G_OBJECT(filepopup_win), "file", NULL);
     }
-    if (tuple_get_string(tuple, "file-path") && tuple_get_string(tuple, "file-name"))
+    if (tuple_get_string(tuple, FIELD_FILE_PATH, NULL) && tuple_get_string(tuple, FIELD_FILE_NAME, NULL))
         g_object_set_data(G_OBJECT(filepopup_win), "file",
-                          g_build_filename(tuple_get_string(tuple, "file-path"), 
-                                           tuple_get_string(tuple, "file-name"),
+                          g_build_filename(tuple_get_string(tuple, FIELD_FILE_PATH, NULL), 
+                                           tuple_get_string(tuple, FIELD_FILE_NAME, NULL),
                                            NULL));
 
     gtk_widget_realize(filepopup_win);
 
-    if (tuple_get_string(tuple, "title"))
+    if (tuple_get_string(tuple, FIELD_TITLE, NULL))
     {
         gchar *markup =
             g_markup_printf_escaped("<span style=\"italic\">%s</span>", _("Title"));
         gtk_label_set_markup(GTK_LABEL(g_object_get_data(G_OBJECT(filepopup_win), "header_title")), markup);
         g_free(markup);
-        filepopup_entry_set_text(filepopup_win, "label_title", tuple_get_string(tuple, "title"));
+        filepopup_entry_set_text(filepopup_win, "label_title", tuple_get_string(tuple, FIELD_TITLE, NULL));
     }
     else
     {
         /* display filename if track_name is not available */
         gchar *markup =
             g_markup_printf_escaped("<span style=\"italic\">%s</span>", _("Filename"));
-        gchar *utf_filename = filename_to_utf8(tuple_get_string(tuple, "file-name"));
+        gchar *utf_filename = filename_to_utf8(tuple_get_string(tuple, FIELD_FILE_NAME, NULL));
         gtk_label_set_markup(GTK_LABEL(g_object_get_data(G_OBJECT(filepopup_win), "header_title")), markup);
         g_free(markup);
         filepopup_entry_set_text(filepopup_win, "label_title", utf_filename);
         g_free(utf_filename);
     }
 
-    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, "artist"),
+    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, FIELD_ARTIST, NULL),
                                         "label_artist", "header_artist");
-    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, "album"),
+    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, FIELD_ALBUM, NULL),
                                         "label_album", "header_album");
-    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, "genre"),
+    audacious_fileinfopupup_update_data(filepopup_win, tuple_get_string(tuple, FIELD_GENRE, NULL),
                                         "label_genre", "header_genre");
 
-    length = tuple_get_int(tuple, "length");
+    length = tuple_get_int(tuple, FIELD_LENGTH, NULL);
     length_string = (length > 0) ?
         g_strdup_printf("%d:%02d", length / 60000, (length / 1000) % 60) : NULL;
     audacious_fileinfopupup_update_data(filepopup_win, length_string,
@@ -397,19 +397,18 @@
     else
       g_object_set_data( G_OBJECT(filepopup_win), "length" , GINT_TO_POINTER(-1) );
 
-    year_string = (tuple_get_int(tuple, "year") == 0) ? NULL : g_strdup_printf("%d", tuple_get_int(tuple, "year"));
+    year_string = (tuple_get_int(tuple, FIELD_YEAR, NULL) == 0) ? NULL : g_strdup_printf("%d", tuple_get_int(tuple, FIELD_YEAR, NULL));
     audacious_fileinfopupup_update_data(filepopup_win, year_string,
                                         "label_year", "header_year");
     g_free(year_string);
 
-    track_string = (tuple_get_int(tuple, "track-number") == 0) ? NULL : g_strdup_printf("%d", tuple_get_int(tuple, "track-number"));
+    track_string = (tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL) == 0) ? NULL : g_strdup_printf("%d", tuple_get_int(tuple, FIELD_TRACK_NUMBER, NULL));
     audacious_fileinfopupup_update_data(filepopup_win, track_string,
                                         "label_tracknum", "header_tracknum");
     g_free(track_string);
     
-    if (tuple_get_string(tuple, "file-name") && tuple_get_string(tuple, "file-path"))
-    {
-        tmp = fileinfo_recursive_get_image(tuple_get_string(tuple, "file-path"), tuple_get_string(tuple, "file-name"), 0);
+    if (tuple_get_string(tuple, FIELD_FILE_NAME, NULL) && tuple_get_string(tuple, FIELD_FILE_PATH, NULL)) {
+        tmp = fileinfo_recursive_get_image(tuple_get_string(tuple, FIELD_FILE_PATH, NULL), tuple_get_string(tuple, FIELD_FILE_NAME, NULL), 0);
         if (tmp) { // picture found
             if (!last_artwork || strcmp(last_artwork, tmp)) { // new picture
                 filepopup_entry_set_image(filepopup_win, "image_artwork", tmp);
@@ -454,7 +453,7 @@
 audacious_fileinfopopup_show_from_title(GtkWidget *filepopup_win, gchar *title)
 {
     Tuple * tuple = tuple_new();
-    tuple_associate_string(tuple, "title", title);
+    tuple_associate_string(tuple, FIELD_TITLE, NULL, title);
     audacious_fileinfopopup_show_from_tuple(filepopup_win, tuple);
     mowgli_object_unref(tuple);
     return;
--- a/src/audacious/ui_playlist.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/ui_playlist.c	Tue Sep 11 09:14:15 2007 -0500
@@ -574,13 +574,13 @@
          Tuple *tuple = tuple_new();
          gchar *searchdata = NULL;
          searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_title) );
-         tuple_associate_string(tuple, "title", searchdata);
+         tuple_associate_string(tuple, FIELD_TITLE, NULL, searchdata);
          searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_album) );
-         tuple_associate_string(tuple, "album", searchdata);
+         tuple_associate_string(tuple, FIELD_ALBUM, NULL, searchdata);
          searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_performer) );
-         tuple_associate_string(tuple, "artist", searchdata);
+         tuple_associate_string(tuple, FIELD_ARTIST, NULL, searchdata);
          searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_file_name) );
-         tuple_associate_string(tuple, "file-name", searchdata);
+         tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, searchdata);
          /* check if previous selection should be cleared before searching */
          if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(searchdlg_checkbt_clearprevsel)) == TRUE )
              playlistwin_select_none();
--- a/src/audacious/ui_skinned_playlist.c	Tue Sep 11 06:54:00 2007 -0500
+++ b/src/audacious/ui_skinned_playlist.c	Tue Sep 11 09:14:15 2007 -0500
@@ -1019,7 +1019,7 @@
         GtkWidget *popup = g_object_get_data(G_OBJECT(widget), "popup");
 
         tuple = playlist_get_tuple(pl_active, pos);
-        if ((tuple == NULL) || (tuple_get_int(tuple, "length") < 1)) {
+        if ((tuple == NULL) || (tuple_get_int(tuple, FIELD_LENGTH, NULL) < 1)) {
            gchar *title = playlist_get_songtitle(pl_active, pos);
            audacious_fileinfopopup_show_from_title(popup, title);
            g_free(title);