changeset 3492:1f250eb90dfe trunk

Merge.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 03 Sep 2007 10:41:19 +0300
parents 899a7ed37a70 (diff) 18cffff8eaa0 (current diff)
children 62b78f2aa450
files src/audacious/input.c
diffstat 12 files changed, 341 insertions(+), 221 deletions(-) [+]
line wrap: on
line diff
--- a/src/audacious/dbus.c	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/dbus.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/input.c	Mon Sep 03 10:41:19 2007 +0300
@@ -445,7 +445,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;
             }
@@ -523,7 +523,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;
             }
@@ -586,6 +586,7 @@
         playback->plugin->set_eq(on, preamp, bands);
 }
 
+
 Tuple *
 input_get_song_tuple(const gchar * filename)
 {
@@ -621,13 +622,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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/playlist.c	Mon Sep 03 10:41:19 2007 +0300
@@ -199,7 +199,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 +218,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 +235,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);
@@ -640,7 +641,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);
 
@@ -668,14 +672,14 @@
 
     PLAYLIST_UNLOCK( playlist->mutex );
     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);
@@ -1041,9 +1045,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);
         }
     }
 
@@ -1845,7 +1849,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;
 
@@ -1893,7 +1897,7 @@
     tuple = entry->tuple;
 
     if (tuple)
-        mtime = tuple_get_int(tuple, "mtime");
+        mtime = tuple_get_int(tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -1926,7 +1930,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;
 
@@ -1963,8 +1967,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);
@@ -2015,9 +2019,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);
@@ -2059,13 +2063,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;
     }
@@ -2421,7 +2425,7 @@
     PLAYLIST_UNLOCK(playlist->mutex);
 
     if (entry->tuple)
-        mtime = tuple_get_int(entry->tuple, "mtime");
+        mtime = tuple_get_int(entry->tuple, FIELD_MTIME, NULL);
     else
         mtime = 0;
 
@@ -2539,7 +2543,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;
                 }
@@ -2581,7 +2586,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;
                  }
@@ -2910,32 +2916,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->mutex);
 
-    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 ) )
@@ -2950,23 +2959,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 ) )
@@ -2981,23 +2988,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 ) )
@@ -3012,23 +3017,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 ) )
@@ -3157,7 +3160,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/tuple.c	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/tuple.c	Mon Sep 03 10:41:19 2007 +0300
@@ -23,6 +23,33 @@
 
 #include "tuple.h"
 
+
+const gchar *tuple_fields[FIELD_LAST] = {
+    "artist",
+    "title",
+    "album",
+    "comment",
+    "genre",
+
+    "track",
+    "track-number",
+    "length",
+    "year",
+    "quality",
+
+    "codec",
+    "file-name",
+    "file-path",
+    "file-ext",
+    "song-artist",
+
+    "mtime",
+    "formatter",
+    "performer",
+    "copyright",
+};
+
+
 static mowgli_heap_t *tuple_heap = NULL;
 static mowgli_heap_t *tuple_value_heap = NULL;
 static mowgli_object_class_t tuple_klass;
@@ -55,7 +82,7 @@
 
     if (tuple_heap == NULL)
     {
-        tuple_heap = mowgli_heap_create(sizeof(Tuple), 256, BH_NOW);
+        tuple_heap = mowgli_heap_create(sizeof(Tuple), 32, BH_NOW);
         tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 512, BH_NOW);
         mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE);
     }
@@ -85,11 +112,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;
@@ -97,22 +124,49 @@
     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 gboolean
+tuple_associate_data(const gchar **tfield, TupleValue **value, Tuple *tuple, const gint nfield, const gchar *field)
+{
+    g_return_val_if_fail(tuple != NULL, FALSE);
+    g_return_val_if_fail(nfield < FIELD_LAST, FALSE);
+
+    if (nfield < 0)
+        *tfield = field;
+    else {
+        *tfield = tuple_fields[nfield];
+        tuple->values[nfield] = NULL;
+    }
+    
+    if ((*value = mowgli_dictionary_delete(tuple->dict, *tfield)))
+        tuple_disassociate_now(*value);
+
+    return TRUE;
+}
+
+static void
+tuple_associate_data2(Tuple *tuple, const gint nfield, const gchar *field, TupleValue *value)
+{
+    mowgli_dictionary_add(tuple->dict, field, value);
+
+    if (nfield >= 0)
+        tuple->values[nfield] = 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;
+    const gchar *tfield;
 
-    g_return_val_if_fail(tuple != NULL, FALSE);
-    g_return_val_if_fail(field != NULL, FALSE);
-
-    if ((value = mowgli_dictionary_delete(tuple->dict, field)))
-        tuple_disassociate_now(value);
+    if (!tuple_associate_data(&tfield, &value, tuple, nfield, field))
+        return FALSE;
 
     if (string == NULL)
         return TRUE;
@@ -121,28 +175,24 @@
     value->type = TUPLE_STRING;
     value->value.string = g_strdup(string);
 
-    mowgli_dictionary_add(tuple->dict, field, value);
-
+    tuple_associate_data2(tuple, nfield, tfield, value);
     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);
-
-    if ((value = mowgli_dictionary_delete(tuple->dict, field)))
-        tuple_disassociate_now(value);
+    const gchar *tfield;
+    
+    if (!tuple_associate_data(&tfield, &value, tuple, nfield, field))
+        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_associate_data2(tuple, nfield, tfield, value);
     return TRUE;
 }
 
@@ -156,44 +206,64 @@
 }
 
 void
-tuple_disassociate(Tuple *tuple, const gchar *field)
+tuple_disassociate(Tuple *tuple, const gint nfield, const gchar *field)
 {
     TupleValue *value;
+    const gchar *tfield;
 
     g_return_if_fail(tuple != NULL);
-    g_return_if_fail(field != NULL);
+    g_return_if_fail(nfield < FIELD_LAST);
+
+    if (nfield < 0)
+        tfield = field;
+    else {
+        tfield = tuple_fields[nfield];
+        tuple->values[nfield] = NULL;
+    }
 
     /* why _delete()? because _delete() returns the dictnode's data on success */
-    if ((value = mowgli_dictionary_delete(tuple->dict, field)) == NULL)
+    if ((value = mowgli_dictionary_delete(tuple->dict, tfield)) == NULL)
         return;
-    
+
     tuple_disassociate_now(value);
 }
 
 TupleValueType
-tuple_get_value_type(Tuple *tuple, const gchar *field)
+tuple_get_value_type(Tuple *tuple, const gint nfield, const gchar *field)
 {
     TupleValue *value;
 
     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) {
+        if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) != NULL)
+            return value->type;
+    } else {
+        if (tuple->values[nfield])
+            return tuple->values[nfield]->type;
+    }
 
-    if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL)
-        return TUPLE_UNKNOWN;
-
-    return value->type;
+    return TUPLE_UNKNOWN;
 }
 
 const gchar *
-tuple_get_string(Tuple *tuple, const gchar *field)
+tuple_get_string(Tuple *tuple, const gint nfield, const gchar *field)
 {
     TupleValue *value;
 
     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 ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL)
-        return NULL;
+    if (nfield < 0) {
+        if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL)
+            return NULL;
+    } else {
+        if (tuple->values[nfield])
+            value = tuple->values[nfield];
+        else
+            return NULL;
+    }
 
     if (value->type != TUPLE_STRING)
         mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL);
@@ -201,16 +271,23 @@
     return value->value.string;
 }
 
-int
-tuple_get_int(Tuple *tuple, const gchar *field)
+gint
+tuple_get_int(Tuple *tuple, const gint nfield, const gchar *field)
 {
     TupleValue *value;
 
     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 ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL)
-        return 0;
+    if (nfield < 0) {
+        if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL)
+            return 0;
+    } else {
+        if (tuple->values[nfield])
+            value = tuple->values[nfield];
+        else
+            return 0;
+    }
 
     if (value->type != TUPLE_INT)
         mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0);
--- a/src/audacious/tuple.h	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/tuple.h	Mon Sep 03 10:41:19 2007 +0300
@@ -24,11 +24,36 @@
 #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,
+
+    /* special field, must always be last */
+    FIELD_LAST
+};
+
+extern const gchar *tuple_fields[FIELD_LAST];
 
 typedef enum {
     TUPLE_STRING,
@@ -44,15 +69,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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/tuple_compiler.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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?
+ * - 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) == 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;
@@ -543,17 +534,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 +561,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 +578,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 +611,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;
                 
@@ -669,17 +671,16 @@
       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 == ' ')
@@ -692,6 +693,8 @@
           default:
             result = TRUE;
         }
+        } else
+          result = TRUE;
         
         if (result) {
           if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen))
@@ -739,10 +742,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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/tuple_compiler.h	Mon Sep 03 10:41:19 2007 +0300
@@ -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,7 +97,7 @@
 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);
 
--- a/src/audacious/tuple_formatter.c	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/tuple_formatter.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/ui_fileinfo.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/ui_fileinfopopup.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/ui_playlist.c	Mon Sep 03 10:41:19 2007 +0300
@@ -573,13 +573,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	Sun Sep 02 23:52:38 2007 -0500
+++ b/src/audacious/ui_skinned_playlist.c	Mon Sep 03 10:41:19 2007 +0300
@@ -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);