Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 17625 Details for
Bug 28587
xmms-1.2.8-r1 fails to compile
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
updated dtd patch - fixes problems with compilation and cjk
xmms-1.2.8-dtd.patch (text/plain), 53.08 KB, created by
Michal Januszewski (RETIRED)
on 2003-09-13 00:39:16 UTC
(
hide
)
Description:
updated dtd patch - fixes problems with compilation and cjk
Filename:
MIME Type:
Creator:
Michal Januszewski (RETIRED)
Created:
2003-09-13 00:39:16 UTC
Size:
53.08 KB
patch
obsolete
>diff -Naur xmms-1.2.8-orig/AUTHORS xmms-1.2.8/AUTHORS >--- xmms-1.2.8-orig/AUTHORS 2003-01-07 15:50:08.000000000 +0100 >+++ xmms-1.2.8/AUTHORS 2003-09-13 09:23:46.000000000 +0200 >@@ -42,6 +42,7 @@ > Chris Wilson > Dave Yearke > Stephan K. Zitz >+ Johan Walles (Dynamic Taste Detection) > > Default skin: Leonard "Blayde" Tan > Robin Sylvestre (Equalizer and Playlist) >diff -Naur xmms-1.2.8-orig/README xmms-1.2.8/README >--- xmms-1.2.8-orig/README 2003-09-04 23:00:18.000000000 +0200 >+++ xmms-1.2.8/README 2003-09-13 09:23:46.000000000 +0200 >@@ -57,6 +57,7 @@ > 5. Features > 5.1 Supported File formats > 5.2 Supported Features >+ 5.2.1 Dynamic Taste Detection > 6. Obtaining XMMS > 7. Misc > 7.1 Shoutcast support >@@ -1121,6 +1122,34 @@ > Compiles and works on other UNIX's > Proxy authentication support > >+5.2.1 Dynamic Taste Detection >+----------------------------- >+Basically, Dynamic Taste Detection collects statistics on how you >+listen to songs, and then adapts the shuffle play and randomize >+playlist functions to better follow your taste. It takes care of >+itself entirely, so that's all you really need to know about it ;-). >+ >+For those of you who are curious, here is how it works. Two kinds of >+statistics are collected: >+ >+- What songs you don't like >+- What songs you like to hear next to each other >+ >+The first one is easy. When you skip a song, it looses a point. >+Songs with low scores are placed at the end of the playlist. >+ >+What songs you want to hear next to each other are a bit more >+intricate. Let's say you have two songs, A and B. A (or any song >+above it in the playlist) is playing. You move song B to the position >+right after A. A finishes playing, and B starts. This is interpreted >+as "you want to hear A and B next to each other". >+ >+In this example, A and B will then *tend* to (i.e. not always) end up >+next to each other in the playlist (though not necessarily in that >+order). >+ >+To explicitly change your opinion of a song, or disconnect one song >+from another, you can do so from the right-click menu in the playlist. > > 6. Obtaining XMMS > ------------------- >diff -Naur xmms-1.2.8-orig/xmms/dtd.c xmms-1.2.8/xmms/dtd.c >--- xmms-1.2.8-orig/xmms/dtd.c 1970-01-01 01:00:00.000000000 +0100 >+++ xmms-1.2.8/xmms/dtd.c 2003-09-13 09:23:46.000000000 +0200 >@@ -0,0 +1,624 @@ >+/* XMMS - Cross-platform multimedia player >+ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. >+ */ >+ >+/* Hint: DTD = Dynamic Taste Detection */ >+ >+#include <assert.h> >+ >+#include "xmms.h" >+ >+static GList *dtd_recommendations_list; >+ >+static DtdNextSong *dtd_find_next_song(DtdStartSong *start_song, >+ const gchar *filename) >+{ >+ GList *iterator; >+ >+ for (iterator = start_song->next_song; >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ if (strcmp(((DtdNextSong *)(iterator->data))->filename, >+ filename) == 0) >+ { >+ return (DtdNextSong *)(iterator->data); >+ } >+ } >+ >+ return NULL; >+} >+ >+static DtdStartSong *dtd_find_start_song(const gchar *filename) >+{ >+ GList *iterator; >+ >+ if (filename == NULL) >+ { >+ return NULL; >+ } >+ >+ for (iterator = dtd_recommendations_list; >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ if (strcmp(((DtdStartSong *)(iterator->data))->filename, >+ filename) == 0) >+ { >+ return (DtdStartSong *)(iterator->data); >+ } >+ } >+ >+ return NULL; >+} >+ >+/* >+ * Memorize that after playing the file named start, the user might >+ * want to hear the file named follow. >+ */ >+static void __dtd_recommend_next(int weight, const gchar *start, const gchar *follow) >+{ >+ DtdStartSong *start_song; >+ DtdNextSong *next_song; >+ >+ /* Check if the start song already has recommendations. */ >+ if ((start_song = dtd_find_start_song(start)) == >+ NULL) >+ { >+ /* Nope, create a new recommendation start node */ >+ start_song = g_new(DtdStartSong, 1); >+ >+ start_song->filename = g_strdup(start); >+ start_song->next_song = NULL; >+ start_song->score = 0; >+ >+ /* Add the recommendation start node to the >+ recommendation start node list. */ >+ dtd_recommendations_list = >+ g_list_append(dtd_recommendations_list, >+ start_song); >+ } >+ >+ /* Check if the next song is already recommended for this >+ start song */ >+ if ((next_song = dtd_find_next_song(start_song, >+ follow)) == NULL) >+ { >+ /* It's not. Create a recommendation next node */ >+ next_song = g_new(DtdNextSong, 1); >+ >+ next_song->filename = g_strdup(follow); >+ >+ next_song->weight = 0; >+ >+ /* Add the new recommendation to the start node */ >+ start_song->next_song = >+ g_list_append(start_song->next_song, >+ next_song); >+ } >+ >+ next_song->weight += weight; >+} >+ >+/* >+ * Memorize that after playing the file named start, the user might >+ * want to hear the file named follow. >+ */ >+void dtd_recommend_next(const gchar *start, const gchar *follow) >+{ >+ /* Recommend symmetrically */ >+ __dtd_recommend_next(1, start, follow); >+ __dtd_recommend_next(1, follow, start); >+} >+ >+void __dtd_dissociate(const gchar *file1, const gchar *file2) >+{ >+ DtdStartSong *startSong; >+ DtdNextSong *nextSong = NULL; >+ GList *nextSongIterator; >+ >+ // Find the start pointer for file1 >+ startSong = dtd_find_start_song(file1); >+ if (startSong == NULL) >+ { >+ // No such recommendation exists >+ return; >+ } >+ >+ // Find its next pointer for file2 >+ for (nextSongIterator = startSong->next_song; >+ nextSongIterator != NULL; >+ nextSongIterator = g_list_next(nextSongIterator)) >+ { >+ nextSong = (DtdNextSong *)(nextSongIterator->data); >+ if (strcmp(file2, nextSong->filename) == 0) >+ { >+ break; >+ } >+ } >+ if (nextSong == NULL) >+ { >+ // No such recommendation exists >+ return; >+ } >+ >+ // Remove the next pointer >+ startSong->next_song = >+ g_list_remove_link(startSong->next_song, nextSongIterator); >+ g_free(nextSong->filename); >+ g_free(nextSong); >+ g_list_free_1(nextSongIterator); >+} >+ >+void dtd_dissociate(const gchar *file1, const gchar *file2) >+{ >+ // The slave's chain is heavy in both ends, so let's set 'em >+ // both free :-) >+ __dtd_dissociate(file1, file2); >+ __dtd_dissociate(file2, file1); >+} >+ >+/* >+ * Modify a song's score. >+ */ >+void dtd_set_score(const gchar *filename, gint score) >+{ >+ DtdStartSong *start_song = dtd_find_start_song(filename); >+ >+ if (start_song == NULL) >+ { >+ start_song = g_new(DtdStartSong, 1); >+ >+ start_song->filename = g_strdup(filename); >+ start_song->next_song = NULL; >+ start_song->score = 0; >+ >+ /* Add the new recommendation start node to the >+ recommendation start node list. */ >+ dtd_recommendations_list = >+ g_list_append(dtd_recommendations_list, >+ start_song); >+ } >+ >+ start_song->score = score; >+ /* Scores > 0 means "always put this song at the start of the >+ list". Probably we don't want that. >+ */ >+ if (start_song->score > 0) >+ { >+ start_song->score = 0; >+ } >+} >+ >+void dtd_change_score(const gchar *filename, gint delta) >+{ >+ DtdStartSong *start_song = dtd_find_start_song(filename); >+ >+ if (start_song == NULL) >+ { >+ start_song = g_new(DtdStartSong, 1); >+ >+ start_song->filename = g_strdup(filename); >+ start_song->next_song = NULL; >+ start_song->score = 0; >+ >+ /* Add the new recommendation start node to the >+ recommendation start node list. */ >+ dtd_recommendations_list = >+ g_list_append(dtd_recommendations_list, >+ start_song); >+ } >+ >+ /* Scores > 0 means "always put this song at the start of the >+ list". Probably we don't want that. >+ */ >+ start_song->score += delta; >+ if (start_song->score > 0) >+ { >+ start_song->score = 0; >+ } >+} >+ >+/* >+ * Retrieve a song's score. >+ */ >+gint dtd_get_score(const gchar *filename) >+{ >+ DtdStartSong *start_song = dtd_find_start_song(filename); >+ >+ if (start_song == NULL) >+ { >+ return 0; >+ } >+ else >+ { >+ return start_song->score; >+ } >+} >+ >+const GList *dtd_get_recommendations(const gchar *start) >+{ >+ DtdStartSong *dtdStartSong = dtd_find_start_song(start); >+ >+ return dtdStartSong ? dtdStartSong->next_song : NULL; >+} >+ >+static void __dtd_set_score(gchar *filename, gint score) >+{ >+ DtdStartSong *start_song = dtd_find_start_song(filename); >+ >+ if (start_song == NULL) >+ { >+ start_song = g_new(DtdStartSong, 1); >+ >+ start_song->filename = g_strdup(filename); >+ start_song->next_song = NULL; >+ start_song->score = 0; >+ >+ /* Add the new recommendation start node to the >+ recommendation start node list. */ >+ dtd_recommendations_list = >+ g_list_append(dtd_recommendations_list, >+ start_song); >+ } >+ >+ start_song->score = score; >+} >+ >+static guint dtd_sum_recommendations(DtdStartSong *start_song) >+{ >+ GList *iterator; >+ guint sum = 0; >+ >+ assert(start_song != NULL); >+ >+ for (iterator = start_song->next_song; >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ sum += ((DtdNextSong *)(iterator->data))->weight; >+ } >+ >+ assert(sum != 0); >+ >+ return sum; >+} >+ >+static const gchar *dtd_get_nth_recommendation(DtdStartSong *start_song, >+ guint n) >+{ >+ GList *iterator; >+ guint sum = 0; >+ >+ assert(start_song != NULL); >+ >+ if (n == 0) >+ return NULL; >+ >+ for (iterator = start_song->next_song; >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ sum += ((DtdNextSong *)(iterator->data))->weight; >+ >+ if (sum >= n) >+ return ((DtdNextSong *)(iterator->data))->filename; >+ } >+ >+ /* If we get here then the n passed to this function was too >+ large. It can be no larger than the number calculated by >+ dtd_sum_recommendations(). */ >+ assert(FALSE); >+ >+ return NULL; >+} >+ >+/* >+ Try to recommend a song to play after first_filename. NULL means >+ that no recommendation is given (for whatever reason). >+ >+ FIXME: This method should accept a list of songs *not* to choose >+ between. If the user has asked for a recommendation and received an >+ answer that is not in the current playlist, they must be able to try >+ again. >+*/ >+const gchar *dtd_get_recommendation(const gchar *first_filename) >+{ >+ DtdStartSong *start_song; >+ guint recommendation_total; >+ >+ if (first_filename == NULL) >+ { >+ return NULL; >+ } >+ >+ if ((start_song = dtd_find_start_song(first_filename)) == >+ NULL) >+ { >+ /* There are no recommendations for first_filename */ >+ return NULL; >+ } >+ >+ if (start_song->next_song == NULL) >+ { >+ return NULL; >+ } >+ >+ recommendation_total = dtd_sum_recommendations(start_song); >+ >+ return dtd_get_nth_recommendation(start_song, >+ rand() % (recommendation_total + 1)); >+} >+ >+static void dtd_clear(void) >+{ >+ GList *start_iterator; >+ GList *next_iterator; >+ >+ if (dtd_recommendations_list == NULL) >+ { >+ return; >+ } >+ >+ /* Free all memory used by the recommendations */ >+ for (start_iterator = dtd_recommendations_list; >+ start_iterator != NULL; >+ start_iterator = g_list_next(start_iterator)) >+ { >+ /* Free all file names in the next list */ >+ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song; >+ next_iterator != NULL; >+ next_iterator = g_list_next(next_iterator)) >+ { >+ free(((DtdNextSong *)(next_iterator->data))->filename); >+ ((DtdNextSong *)(next_iterator->data))->filename = NULL; >+ >+ free(next_iterator->data); >+ next_iterator->data = NULL; >+ } >+ /* Free the next list */ >+ g_list_free(((DtdStartSong *)(start_iterator->data))->next_song); >+ ((DtdStartSong *)(start_iterator->data))->next_song = NULL; >+ >+ /* Free the file name */ >+ free(((DtdStartSong *)(start_iterator->data))->filename); >+ ((DtdStartSong *)(start_iterator->data))->filename = NULL; >+ >+ /* Free the struct */ >+ free(start_iterator->data); >+ start_iterator->data = NULL; >+ } >+ g_list_free(dtd_recommendations_list); >+ dtd_recommendations_list = NULL; >+} >+ >+void dtd_init(void) >+{ >+ gchar *dtd_data_file_name; >+ FILE *dtd_data_file; >+ >+ gchar start[950], follow[950]; >+ gchar line[999]; >+ int weight; >+ >+ enum { NEXT, SCORES } section = NEXT; >+ >+ gboolean failure = FALSE; >+ >+ /* We have no recommendations to begin with */ >+ dtd_recommendations_list = NULL; >+ >+ /* Make up a filename for the dtd data file */ >+ dtd_data_file_name = g_strconcat(g_get_home_dir(), >+ "/.xmms/", >+ DTD_DATA_FILE_NAME, >+ NULL); >+ >+ /* Open the dtd data file for input */ >+ dtd_data_file = fopen(dtd_data_file_name, >+ "r"); >+ if (dtd_data_file == NULL) >+ { >+ return; >+ } >+ >+ /* Parse the dtd data file */ >+ start[0] = '\0'; >+ while (!feof(dtd_data_file)) >+ { >+ if (fgets(line, 990, dtd_data_file) != NULL) >+ { >+ if (sscanf(line, "%d %940[^\n]\n", &weight, follow) == 2) >+ { >+ /* Found a line with both number and name */ >+ >+ switch (section) >+ { >+ case NEXT: >+ if (start[0] != '\0') >+ { >+ if (weight < 0) >+ { >+ failure= TRUE; >+ } >+ else if (weight > 0) >+ { >+ __dtd_recommend_next(weight, start, follow); >+ } >+ } >+ else >+ { >+ /* Parse error! */ >+ failure = TRUE; >+ } >+ break; >+ >+ case SCORES: >+ if (weight != 0) >+ { >+ __dtd_set_score(follow, weight); >+ } >+ break; >+ } >+ >+ if (failure) break; >+ } >+ else if (sscanf(line, "%940[^\n]\n", start) == 1) >+ { >+ if (strcmp(start, "scores") == 0) >+ { >+ /* The scores section starts here */ >+ section = SCORES; >+ start[0] = '\0'; >+ } >+ else >+ { >+ /* New start song read OK */ >+ section = NEXT; >+ } >+ } >+ else >+ { >+ /* Parse error! */ >+ failure = TRUE; >+ break; >+ } >+ } >+ } >+ >+ if (failure) >+ { >+ /* Free all space occupied by the recommendations list >+ in favour of remembering a broken list. */ >+ dtd_clear(); >+ } >+ >+ fclose(dtd_data_file); >+ dtd_data_file = NULL; >+} >+ >+void dtd_persist(void) >+{ >+ gchar *dtd_data_file_name; >+ FILE *dtd_data_file; >+ >+ GList *start_iterator; >+ GList *next_iterator; >+ >+ gboolean failure = FALSE; >+ >+ /* Are there any recommendations to store */ >+ if (dtd_recommendations_list == NULL) >+ { >+ /* Nope. */ >+ return; >+ } >+ >+ /* Make up a filename for the dtd data file */ >+ dtd_data_file_name = g_strconcat(g_get_home_dir(), >+ "/.xmms/", >+ DTD_DATA_FILE_NAME, >+ NULL); >+ >+ /* Open the dtd data file for output */ >+ dtd_data_file = fopen(dtd_data_file_name, >+ "w"); >+ if (dtd_data_file == NULL) >+ { >+ /* FIXME: We fail silently. Should we output a >+ * warning somehow? */ >+ return; >+ } >+ >+ /* Loop through all recommendation starts, storing their >+ recommended followers */ >+ for (start_iterator = dtd_recommendations_list; >+ start_iterator != NULL; >+ start_iterator = g_list_next(start_iterator)) >+ { >+ if (((DtdStartSong *)(start_iterator->data))->next_song == NULL) >+ { >+ /* Don't store songs with only a score (yet). */ >+ continue; >+ } >+ >+ if (failure || >+ (fprintf(dtd_data_file, >+ "%s\n", >+ ((DtdStartSong *)(start_iterator->data))->filename) == 0)) >+ { >+ failure = TRUE; >+ break; >+ } >+ >+ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song; >+ next_iterator != NULL; >+ next_iterator = g_list_next(next_iterator)) >+ { >+ if (fprintf(dtd_data_file, "%d %s\n", >+ ((DtdNextSong *)(next_iterator->data))->weight, >+ ((DtdNextSong *)(next_iterator->data))->filename) == 0) >+ { >+ failure = TRUE; >+ break; >+ } >+ } >+ } >+ >+ if (!failure) >+ { >+ /* Output the "here starts the scoring section" marker. */ >+ if (fprintf(dtd_data_file, "scores\n") == 0) >+ { >+ failure = TRUE; >+ } >+ >+ /* Output all (non-zero) scores */ >+ for (start_iterator = dtd_recommendations_list; >+ start_iterator != NULL; >+ start_iterator = g_list_next(start_iterator)) >+ { >+ if (((DtdStartSong *)(start_iterator->data))->score == 0) >+ { >+ /* Don't store songs with zero score. */ >+ continue; >+ } >+ >+ if (failure || >+ (fprintf(dtd_data_file, >+ "%d %s\n", >+ ((DtdStartSong *)(start_iterator->data))->score, >+ ((DtdStartSong *)(start_iterator->data))->filename) == 0)) >+ { >+ failure = TRUE; >+ break; >+ } >+ } >+ } >+ >+ fclose(dtd_data_file); >+ dtd_data_file = NULL; >+ >+ if (failure) >+ { >+ /* If writing of the data file failed, attempt to >+ remove it rather than leaving a broken data file >+ behind. */ >+ >+ unlink(dtd_data_file_name); >+ } >+} >diff -Naur xmms-1.2.8-orig/xmms/dtd.h xmms-1.2.8/xmms/dtd.h >--- xmms-1.2.8-orig/xmms/dtd.h 1970-01-01 01:00:00.000000000 +0100 >+++ xmms-1.2.8/xmms/dtd.h 2003-09-13 09:23:46.000000000 +0200 >@@ -0,0 +1,73 @@ >+/* XMMS - Cross-platform multimedia player >+ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. >+ */ >+ >+/* Hint: DTD = Dynamic Taste Detection */ >+ >+/* FIXME: Should lists prove to be too slow we could use balanced >+ trees for the DtdStartSong collection. The DtdNextSong is scanned >+ through however, so tree-ifying will do no good. */ >+ >+#ifndef DTD_H >+#define DTD_H >+ >+#define DTD_DATA_FILE_NAME "dtd-stats" >+ >+typedef struct >+{ >+ gchar *filename; >+ guint weight; >+} >+DtdNextSong; >+ >+typedef struct >+{ >+ gchar *filename; >+ gint score; >+ GList *next_song; >+} >+DtdStartSong; >+ >+void dtd_init(void); >+void dtd_persist(void); >+ >+// Tell DTD that after start, the user tends to want to hear follow >+void dtd_recommend_next(const gchar *start, >+ const gchar *follow); >+ >+// Tell DTD to break the association between file1 and file2 (if one >+// exists) >+void dtd_dissociate(const gchar *file1, >+ const gchar *file2); >+ >+ >+// Let DTD recommend what to play after start. NULL = No >+// recommendation = anything goes. >+const gchar *dtd_get_recommendation(const gchar *start); >+ >+// Change the absolute score for a file >+void dtd_change_score(const gchar *filename, gint delta); >+void dtd_set_score(const gchar *filename, gint score); >+ >+// Get the score for a file. Unknown file = score 0. >+gint dtd_get_score(const gchar *filename); >+ >+// Get the list of recommendations for a given file. NULL = no >+// recommendations for that file. >+const GList *dtd_get_recommendations(const gchar *start); >+ >+#endif >diff -Naur xmms-1.2.8-orig/xmms/main.c xmms-1.2.8/xmms/main.c >--- xmms-1.2.8-orig/xmms/main.c 2003-09-02 15:01:40.000000000 +0200 >+++ xmms-1.2.8/xmms/main.c 2003-09-13 09:23:46.000000000 +0200 >@@ -475,6 +475,8 @@ > } > } > xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format); >+ >+ xmms_cfg_read_boolean(cfgfile, "xmms", "dynamic_taste_detection", &cfg.dynamic_taste_detection); > > xmms_cfg_free(cfgfile); > } >@@ -670,6 +672,8 @@ > g_free(str); > } > xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format); >+ >+ xmms_cfg_write_boolean(cfgfile, "xmms", "dynamic_taste_detection", cfg.dynamic_taste_detection); > > xmms_cfg_write_file(cfgfile, filename); > xmms_cfg_free(cfgfile); >@@ -898,6 +902,7 @@ > playlist_clear(); > cleanup_plugins(); > sm_cleanup(); >+ dtd_persist(); > gtk_exit(0); > } > >@@ -3120,6 +3125,7 @@ > GDK_THREADS_LEAVE(); > } > >+ cfg.dynamic_taste_detection = TRUE; > > GDK_THREADS_ENTER(); > check_ctrlsocket(); >@@ -3583,6 +3589,10 @@ > mainwin_timeout_tag = gtk_timeout_add(10, idle_func, NULL); > playlist_start_get_info_thread(); > >+ >+ /* Initialize dynamic taste detection */ >+ dtd_init(); >+ > enable_x11r5_session_management(argc, argv); > sm_init(argc, argv, options.previous_session_id); > gtk_main(); >diff -Naur xmms-1.2.8-orig/xmms/main.h xmms-1.2.8/xmms/main.h >--- xmms-1.2.8-orig/xmms/main.h 2002-10-06 18:35:27.000000000 +0200 >+++ xmms-1.2.8/xmms/main.h 2003-09-13 09:25:43.000000000 +0200 >@@ -45,6 +45,7 @@ > gboolean use_backslash_as_dir_delimiter; > gboolean random_skin_on_play, use_fontsets; > gboolean mainwin_use_xfont; >+ gboolean dynamic_taste_detection; > gfloat equalizer_preamp, equalizer_bands[10]; > gchar *skin, *outputplugin, *filesel_path, *playlist_path; > gchar *playlist_font, *mainwin_font; >diff -Naur xmms-1.2.8-orig/xmms/Makefile.am xmms-1.2.8/xmms/Makefile.am >--- xmms-1.2.8-orig/xmms/Makefile.am 2003-08-11 15:47:41.000000000 +0200 >+++ xmms-1.2.8/xmms/Makefile.am 2003-09-13 09:23:46.000000000 +0200 >@@ -34,6 +34,7 @@ > menurow.c menurow.h \ > hslider.c hslider.h \ > monostereo.c monostereo.h \ >+dtd.c dtd.h \ > vis.c vis.h \ > svis.c svis.h \ > number.c number.h \ >diff -Naur xmms-1.2.8-orig/xmms/Makefile.in xmms-1.2.8/xmms/Makefile.in >--- xmms-1.2.8-orig/xmms/Makefile.in 2003-09-04 23:01:20.000000000 +0200 >+++ xmms-1.2.8/xmms/Makefile.in 2003-09-13 09:24:50.000000000 +0200 >@@ -184,7 +184,8 @@ > -I$(top_builddir)/intl -I$(top_srcdir) > > >-xmms_SOURCES = bmp.c bmp.h \ >+xmms_SOURCES = dtd.c dtd.h \ >+bmp.c bmp.h \ > skin.c skin.h \ > util.c util.h \ > output.c output.h \ >@@ -245,7 +246,7 @@ > CPPFLAGS = @CPPFLAGS@ > LDFLAGS = @LDFLAGS@ > LIBS = @LIBS@ >-xmms_OBJECTS = bmp.$(OBJEXT) skin.$(OBJEXT) util.$(OBJEXT) \ >+xmms_OBJECTS = dtd.$(OBJEXT) bmp.$(OBJEXT) skin.$(OBJEXT) util.$(OBJEXT) \ > output.$(OBJEXT) fft.$(OBJEXT) input.$(OBJEXT) effect.$(OBJEXT) \ > general.$(OBJEXT) visualization.$(OBJEXT) fullscreen.$(OBJEXT) \ > pluginenum.$(OBJEXT) playlist.$(OBJEXT) controlsocket.$(OBJEXT) \ >diff -Naur xmms-1.2.8-orig/xmms/playlist.c xmms-1.2.8/xmms/playlist.c >--- xmms-1.2.8-orig/xmms/playlist.c 2003-09-02 15:01:40.000000000 +0200 >+++ xmms-1.2.8/xmms/playlist.c 2003-09-13 09:23:46.000000000 +0200 >@@ -22,6 +22,7 @@ > #include "libxmms/util.h" > #include <sys/stat.h> > #include <unistd.h> >+#include <assert.h> > > GList *playlist = NULL; > GList *shuffle_list = NULL; >@@ -652,6 +653,17 @@ > > plist_pos_list = find_playlist_position_list(); > >+ if ((plist_pos_list != NULL) && >+ (cfg.repeat || (g_list_next(plist_pos_list) != NULL))) >+ { >+ /* >+ The user is skipping a song. We interpret this as >+ meaning "I don't like this song". >+ */ >+ dtd_change_score(((PlaylistEntry *)(plist_pos_list->data))->filename, >+ -1); >+ } >+ > if (!cfg.repeat && !g_list_next(plist_pos_list)) > { > PL_UNLOCK(); >@@ -724,7 +736,18 @@ > > plist_pos_list = find_playlist_position_list(); > if (g_list_previous(plist_pos_list)) >- playlist_position = plist_pos_list->prev->data; >+ { >+ playlist_position = >+ g_list_previous(plist_pos_list)->data; >+ >+ /* >+ The user has gone back to a song. We interpret that >+ as meaning "I like this song so much that I want to >+ hear it again". >+ */ >+ dtd_change_score(playlist_position->filename, >+ 1); >+ } > else if (cfg.repeat) > { > GList *node; >@@ -739,7 +762,7 @@ > } > PL_UNLOCK(); > playlist_check_pos_current(); >- >+ > if (restart_playing) > playlist_play(); > else >@@ -855,6 +878,7 @@ > > playlist_position = node->data; > PL_UNLOCK(); >+ ((PlaylistEntry *)playlist_position)->moved = FALSE; > playlist_check_pos_current(); > > if (restart_playing) >@@ -874,12 +898,19 @@ > > void playlist_eof_reached(void) > { >+ /* >+ * This function is called whenever a song ends and the next >+ * one should start playing. >+ */ >+ > GList *plist_pos_list; >+ GList *previous_plist_pos; > > input_stop(); > > PL_LOCK(); > plist_pos_list = find_playlist_position_list(); >+ previous_plist_pos = plist_pos_list->data; > > if (cfg.no_playlist_advance) > { >@@ -911,6 +942,25 @@ > else > playlist_position = plist_pos_list->next->data; > PL_UNLOCK(); >+ >+ if (!cfg.shuffle && (((PlaylistEntry *)playlist_position)->moved)) >+ { >+ /* >+ * Song A has stopped playing. Song B will start >+ * playing now. Song B had moved. Thus, we conclude >+ * that the user wants to hear Song B after song A. >+ * Therefore, Song B should be added to Song A's >+ * next-song-preferences. >+ * >+ * Song A is previous_plist_pos. Song B is >+ * playlist_position. */ >+ >+ dtd_recommend_next(((PlaylistEntry *)previous_plist_pos)->filename, >+ ((PlaylistEntry *)playlist_position)->filename); >+ >+ ((PlaylistEntry *)playlist_position)->moved = FALSE; >+ } >+ > playlist_check_pos_current(); > playlist_play(); > mainwin_set_info_text(); >@@ -1130,6 +1180,7 @@ > ext = strrchr(filename, '.'); > if (ext && !strcasecmp(ext, ".pls")) > { >+ /* It's a playlist, let's parse it. */ > int noe, i; > char key[10]; > >@@ -1263,6 +1314,11 @@ > char *ret; > PlaylistEntry *entry; > GList *node; >+ >+ if (pos < 0) >+ { >+ return NULL; >+ } > > PL_LOCK(); > if (!playlist) >@@ -1270,6 +1326,11 @@ > PL_UNLOCK(); > return NULL; > } >+ if (pos >= __get_playlist_length()) >+ { >+ PL_UNLOCK(); >+ return NULL; >+ } > node = g_list_nth(playlist, pos); > if (!node) > { >@@ -1327,6 +1388,84 @@ > return title; > } > >+int playlist_get_score(gint pos) >+{ >+ PlaylistEntry *entry; >+ GList *node; >+ gint score; >+ >+ PL_LOCK(); >+ if (!playlist) >+ { >+ PL_UNLOCK(); >+ return 0; >+ } >+ node = g_list_nth(playlist, pos); >+ if (!node) >+ { >+ PL_UNLOCK(); >+ return 0; >+ } >+ entry = node->data; >+ score = dtd_get_score(entry->filename); >+ PL_UNLOCK(); >+ >+ return score; >+} >+ >+void playlist_set_score(gint pos, gint score) >+{ >+ PlaylistEntry *entry; >+ GList *node; >+ >+ PL_LOCK(); >+ if (!playlist) >+ { >+ PL_UNLOCK(); >+ return; >+ } >+ node = g_list_nth(playlist, pos); >+ if (!node) >+ { >+ PL_UNLOCK(); >+ return; >+ } >+ entry = node->data; >+ dtd_set_score(entry->filename, score); >+ PL_UNLOCK(); >+} >+ >+const gchar* playlist_filename2songtitle(const gchar *filename) >+{ >+ GList *iterator; >+ >+ if (filename == NULL) >+ { >+ return NULL; >+ } >+ >+ // Scan loaded songs for filename >+ PL_LOCK(); >+ for (iterator = get_playlist(); >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ PlaylistEntry *currentEntry = >+ (PlaylistEntry *)(iterator->data); >+ >+ if (strcmp(currentEntry->filename, filename) == 0) >+ { >+ // FIXME: Race condition >+ PL_UNLOCK(); >+ return currentEntry->title; >+ } >+ } >+ PL_UNLOCK(); >+ >+ // Not found / don't know >+ return NULL; >+} >+ > gint playlist_get_songtime(gint pos) > { > int retval = -1; >@@ -1567,6 +1706,26 @@ > PL_UNLOCK(); > } > >+static guint playlist_filename_to_index(PlaylistEntry *playlist_entries[], >+ guint n_entries, >+ const gchar *filename) >+{ >+ guint i; >+ >+ for (i = 0; i < n_entries; i++) >+ { >+ if (strcmp(playlist_entries[i]->filename, >+ filename) == 0) >+ { >+ return i; >+ } >+ } >+ >+ return -1; >+} >+ >+/* This function is used by the qsort() call in >+ smart_playlist_shuffle_list() (below). */ > void playlist_sort_selected_by_date(void) > { > PL_LOCK(); >@@ -1581,7 +1740,20 @@ > PL_UNLOCK(); > } > >-static GList *playlist_shuffle_list(GList *list) >+static int playlist_entry_score_comparator(const void *a, >+ const void *b) >+{ >+ >+ int score_a = dtd_get_score((*((PlaylistEntry **)a))->filename); >+ int score_b = dtd_get_score((*((PlaylistEntry **)b))->filename); >+ >+ /* We want high scores before low, so if score_a > score_b we >+ want to return something negative. */ >+ >+ return score_b - score_a; >+} >+ >+static GList *smart_playlist_shuffle_list(GList *list) > { > /* Caller should hold playlist mutex */ > /* >@@ -1590,43 +1762,213 @@ > * fuction is run. > */ > int len = g_list_length(list); >- int i, j; >- GList *node, **ptrs; >+ gint i; >+ gint next_score_section = -1; >+ GList *iterator; >+ PlaylistEntry **ptrs; > >- if (!len) >+ if (len == 0) > return NULL; > >- ptrs = g_new(GList *, len); >+ ptrs = g_new(PlaylistEntry *, len); > >- for (node = list, i = 0; i < len; node = g_list_next(node), i++) >- ptrs[i] = node; >+ /* Convert the list into an array of pointers */ >+ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++) >+ { >+ ptrs[i] = (PlaylistEntry *)(iterator->data); >+ >+ // Shuffling songs voids information about user listening preferences >+ ptrs[i]->moved = FALSE; >+ } >+ g_list_free(list); >+ list = NULL; >+ >+ /* Sort the array by score */ >+ qsort(ptrs, len, sizeof(PlaylistEntry *), playlist_entry_score_comparator); >+ >+ /* Shuffle the pointer array */ >+ for (i = 0; i < len; i++) >+ { >+ PlaylistEntry *swap_ptr; >+ gint j; >+ >+ const gchar *previous_filename; >+ const gchar *new_filename; >+ >+ if (i >= next_score_section) >+ { >+ /* We are in a new score section; find out >+ where the next one starts */ >+ gint current_score = >+ dtd_get_score(ptrs[i]->filename); >+ >+ for (next_score_section = i; >+ next_score_section < len; >+ next_score_section++) >+ { >+ if (dtd_get_score(ptrs[next_score_section]->filename) != >+ current_score) >+ { >+ break; >+ } >+ } >+ } >+ >+ if (i == 0) >+ { >+ previous_filename = NULL; >+ } >+ else >+ { >+ previous_filename = ptrs[i - 1]->filename; >+ } >+ >+ /* Find out from which index we should get the next song */ >+ do { >+ if ((new_filename = dtd_get_recommendation(previous_filename)) != >+ NULL) >+ { >+ j = playlist_filename_to_index(&(ptrs[i]), >+ len - i, >+ new_filename); >+ if (j != -1) >+ j += i; >+ } >+ else >+ { >+ /* Only find new songs in our current >+ score section */ >+ j = (rand() % (next_score_section - i)) + i; >+ >+ assert(j < next_score_section); >+ } >+ } while (j == -1); >+ >+ assert(j < len); >+ assert(j >= i); >+ >+ if (j < next_score_section) >+ { >+ /* Swap pointer #i and pointer #j */ >+ swap_ptr = ptrs[i]; >+ ptrs[i] = ptrs[j]; >+ ptrs[j] = swap_ptr; >+ } >+ else >+ { >+ /* >+ We need to preserve the score section >+ ordering, so we do it the slow way if i and >+ j are in different scoring sections. >+ */ >+ gint k; >+ swap_ptr = ptrs[i]; >+ ptrs[i] = ptrs[j]; >+ >+ for (k = j; k >= (i + 2); k--) >+ { >+ ptrs[k] = ptrs[k - 1]; >+ } >+ >+ ptrs[i + 1] = swap_ptr; >+ } >+ } >+ >+ /* Create a new list from the pointer array */ >+ for (i = 0; i < len; i++) >+ { >+ list = g_list_append(list, ptrs[i]); >+ } > >- j = random() % len; >- list = ptrs[j]; >- ptrs[j]->next = NULL; >- ptrs[j] = ptrs[0]; >+ g_free(ptrs); > >- for (i = 1; i < len; i++) >- { >- j = random() % (len - i); >- list->prev = ptrs[i + j]; >- ptrs[i + j]->next = list; >- list = ptrs[i + j]; >- ptrs[i + j] = ptrs[i]; >- } >- list->prev = NULL; >+ return list; >+} >+ >+static GList *old_playlist_shuffle_list(GList *list) >+{ >+ /* Caller should hold playlist mutex */ >+ /* >+ * Note that this doesn't make a copy of the original list. >+ * The pointer to the original list is not valid after this >+ * fuction is run. >+ */ >+ int len = g_list_length(list); >+ gint i; >+ GList *iterator; >+ PlaylistEntry **ptrs; >+ >+ if (len == 0) >+ return NULL; >+ >+ ptrs = g_new(PlaylistEntry *, len); >+ >+ /* Convert the list into an array of pointers */ >+ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++) >+ { >+ ptrs[i] = (PlaylistEntry *)(iterator->data); >+ >+ // Shuffling songs voids information about user listening preferences >+ ptrs[i]->moved = FALSE; >+ } >+ g_list_free(list); >+ list = NULL; >+ >+ /* Shuffle the pointer array */ >+ for (i = 0; i < len; i++) >+ { >+ PlaylistEntry *swap_ptr; >+ gint j; >+ >+ /* Pick a random song among the ones that are left */ >+ j = (rand() % (len - i)) + i; >+ >+ /* Swap pointer #i and pointer #j */ >+ swap_ptr = ptrs[i]; >+ ptrs[i] = ptrs[j]; >+ ptrs[j] = swap_ptr; >+ } >+ >+ /* Create a new list from the pointer array */ >+ for (i = 0; i < len; i++) >+ { >+ list = g_list_append(list, ptrs[i]); >+ } > > g_free(ptrs); > > return list; > } > >+static GList *playlist_shuffle_list(GList *list) >+{ >+ if (cfg.dynamic_taste_detection) >+ { >+ return smart_playlist_shuffle_list(list); >+ } >+ else >+ { >+ return old_playlist_shuffle_list(list); >+ } >+} >+ > void playlist_random(void) > { >+ GList *for_each; >+ > PL_LOCK(); > > playlist = playlist_shuffle_list(playlist); > >+ /* Remove all moved-marks from the playlist */ >+ >+ for (for_each = playlist; >+ for_each != NULL; >+ for_each = g_list_next(for_each)) >+ { >+ ((PlaylistEntry *) for_each->data)->moved = FALSE; >+ } >+ > PL_UNLOCK(); > } > >@@ -1661,7 +2003,52 @@ > PL_UNLOCK(); > return num; > } >- >+ >+gint playlist_get_single_selection(void) >+{ >+ // Returns the playlist position of the current selection. If >+ // 0 or > 1 songs are selected, this method returns -1. >+ GList *iterator; >+ gint currentPos; >+ >+ gint selectedPos = -1; >+ >+ for (iterator = get_playlist(), currentPos = 0; >+ iterator != NULL; >+ iterator = g_list_next(iterator), currentPos++) >+ { >+ if (((PlaylistEntry *)(iterator->data))->selected) >+ { >+ if (selectedPos == -1) >+ { >+ // First selected song found >+ selectedPos = currentPos; >+ } >+ else >+ { >+ // > 1 selected song found >+ return -1; >+ } >+ } >+ } >+ >+ return selectedPos; >+} >+ >+gboolean playlist_is_selected(gint pos) >+{ >+ GList *node; >+ gboolean isSelected = 0; >+ >+ PL_LOCK(); >+ if ((node = g_list_nth(get_playlist(), pos)) != NULL) >+ { >+ isSelected = ((PlaylistEntry *)(node->data))->selected; >+ } >+ PL_UNLOCK(); >+ >+ return isSelected; >+} > > static void playlist_generate_shuffle_list(void) > { >diff -Naur xmms-1.2.8-orig/xmms/playlist.h xmms-1.2.8/xmms/playlist.h >--- xmms-1.2.8-orig/xmms/playlist.h 2003-06-09 15:22:10.000000000 +0200 >+++ xmms-1.2.8/xmms/playlist.h 2003-09-13 09:23:46.000000000 +0200 >@@ -25,7 +25,8 @@ > gchar *filename; > gchar *title; > gint length; >- gboolean selected; >+ gboolean selected; >+ gboolean moved; > } > PlaylistEntry; > >@@ -80,10 +81,15 @@ > void playlist_delete_filenames(GList *filenames); > gchar* playlist_get_filename(gint pos); > gchar* playlist_get_songtitle(gint pos); >+gint playlist_get_score(gint pos); >+void playlist_set_score(gint pos, gint score); >+const gchar* playlist_filename2songtitle(const gchar *filename); > gint playlist_get_songtime(gint pos); > GList * playlist_get_selected(void); > GList * playlist_get_selected_list(void); > int playlist_get_num_selected(void); >+gboolean playlist_is_selected(gint pos); >+gint playlist_get_single_selection(void); > void playlist_get_total_time(gulong *total_time, gulong *selection_time, gboolean *total_more, gboolean *selection_more); > void playlist_select_all(gboolean set); > void playlist_select_range(int min, int max, gboolean sel); >diff -Naur xmms-1.2.8-orig/xmms/playlist_list.c xmms-1.2.8/xmms/playlist_list.c >--- xmms-1.2.8-orig/xmms/playlist_list.c 2003-06-09 15:22:10.000000000 +0200 >+++ xmms-1.2.8/xmms/playlist_list.c 2003-09-13 09:23:46.000000000 +0200 >@@ -24,6 +24,8 @@ > #endif > #include <X11/Xatom.h> > >+static gint playlist_move_delta = 0; >+static PlaylistEntry *playlist_last_moved_song = NULL; > static GdkFont *playlist_list_font = NULL; > > static int playlist_list_auto_drag_down_func(gpointer data) >@@ -61,24 +63,35 @@ > GList *list; > > PL_LOCK(); >+ >+ /* If the first song is selected... */ > if ((list = get_playlist()) == NULL) > { > PL_UNLOCK(); > return; > } >+ > if (((PlaylistEntry *) list->data)->selected) > { >- /* We are at the top */ >+ /* ... don't move. */ > PL_UNLOCK(); > return; > } >- while (list) >+ >+ while (list) > { >- if (((PlaylistEntry *) list->data)->selected) >+ if (((PlaylistEntry *) list->data)->selected) >+ { >+ playlist_last_moved_song = >+ (PlaylistEntry *) list->data; > glist_moveup(list); >+ } > list = g_list_next(list); > } > PL_UNLOCK(); >+ >+ playlist_move_delta--; >+ > if (pl->pl_prev_selected != -1) > pl->pl_prev_selected--; > if (pl->pl_prev_min != -1) >@@ -92,24 +105,36 @@ > GList *list; > > PL_LOCK(); >+ >+ /* If the last song is selected... */ > if ((list = g_list_last(get_playlist())) == NULL) > { > PL_UNLOCK(); > return; > } >+ > if (((PlaylistEntry *) list->data)->selected) > { >- /* We are at the bottom */ >+ /* ... don't move. */ > PL_UNLOCK(); > return; > } >+ > while (list) > { > if (((PlaylistEntry *) list->data)->selected) >+ { >+ playlist_last_moved_song = >+ (PlaylistEntry *) list->data; > glist_movedown(list); >+ } > list = g_list_previous(list); > } >+ > PL_UNLOCK(); >+ >+ playlist_move_delta++; >+ > if (pl->pl_prev_selected != -1) > pl->pl_prev_selected++; > if (pl->pl_prev_min != -1) >@@ -177,6 +202,7 @@ > playlist_play(); > } > pl->pl_dragging = TRUE; >+ playlist_move_delta = 0; > playlistwin_update_list(); > } > } >@@ -244,11 +270,36 @@ > } > } > >+int playlist_n_selected_songs(void) >+{ >+ int n_selected = 0; >+ GList *list; >+ >+ for (list = get_playlist(); >+ list != NULL; >+ list = g_list_next(list)) >+ { >+ if (((PlaylistEntry *) list->data)->selected) >+ n_selected++; >+ } >+ >+ return n_selected; >+} >+ > void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl) > { > pl->pl_dragging = FALSE; > pl->pl_auto_drag_down = FALSE; > pl->pl_auto_drag_up = FALSE; >+ >+ if ((playlist_move_delta < 0) && (playlist_n_selected_songs() == 1)) >+ { >+ /* Exactly one song has been moved upwards, mark it as moved. */ >+ playlist_last_moved_song->moved = TRUE; >+ } >+ >+ playlist_move_delta = 0; >+ playlist_last_moved_song = NULL; > } > > #ifdef HAVE_WCHAR_H >diff -Naur xmms-1.2.8-orig/xmms/playlistwin.c xmms-1.2.8/xmms/playlistwin.c >--- xmms-1.2.8-orig/xmms/playlistwin.c 2003-04-04 19:42:51.000000000 +0200 >+++ xmms-1.2.8/xmms/playlistwin.c 2003-09-13 09:23:46.000000000 +0200 >@@ -17,6 +17,7 @@ > * along with this program; if not, write to the Free Software > * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. > */ >+#include <assert.h> > #include "xmms.h" > #include "libxmms/dirbrowser.h" > #include "libxmms/util.h" >@@ -24,7 +25,7 @@ > GtkWidget *playlistwin; > static GtkWidget *playlistwin_url_window = NULL; > static GtkItemFactory *playlistwin_sort_menu, *playlistwin_sub_menu; >-static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu; >+static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu, *playlistwin_dtd_menu; > > static GdkPixmap *playlistwin_bg; > static GdkBitmap *playlistwin_mask; >@@ -67,13 +68,15 @@ > SEL_INV, SEL_ZERO, SEL_ALL, > MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS, > PLIST_NEW, PLIST_SAVE, PLIST_LOAD, >- SEL_LOOKUP, >+ SEL_LOOKUP, DTD_GOODSONG, DTD_BADSONG, >+ DTD_DISSOCIATE > }; > > void playlistwin_sort_menu_callback(gpointer cb_data, guint action, GtkWidget * w); > void playlistwin_sub_menu_callback(gpointer cb_data, guint action, GtkWidget * w); > void playlistwin_save_type_cb(gpointer cb_data, guint action, GtkWidget * w); > void playlistwin_set_hints(void); >+void playlistwin_dissociate_callback(GtkMenuItem *menuitem, gpointer ignored); > > enum > { >@@ -150,7 +153,11 @@ > GtkItemFactoryEntry playlistwin_popup_menu_entries[] = > { > {N_("/View File Info"), NULL, playlistwin_popup_menu_callback, MISC_FILEINFO, "<Item>"}, >+ > {N_("/-"), NULL, NULL, 0, "<Separator>"}, >+ >+ {N_("/Dynamic Taste Detection"), NULL, NULL, 0, "<Item>"}, >+ > {N_("/Add"), NULL, NULL, 0, "<Branch>"}, > {N_("/Add/File"), NULL, playlistwin_popup_menu_callback, ADD_FILE, "<Item>"}, > {N_("/Add/Directory"), NULL, playlistwin_popup_menu_callback, ADD_DIR, "<Item>"}, >@@ -181,6 +188,21 @@ > sizeof(playlistwin_popup_menu_entries) / > sizeof(playlistwin_popup_menu_entries[0]); > >+GtkItemFactoryEntry playlistwin_dtd_menu_entries[] = >+{ >+ {N_("/I Like this Song"), >+ NULL, playlistwin_popup_menu_callback, DTD_GOODSONG, >+ "<RadioItem>"}, >+ {N_("/I Don't Like this Song"), >+ NULL, playlistwin_popup_menu_callback, DTD_BADSONG, >+ /* <RadioItem> */ "/I Like this Song"}, >+ {N_("/Dissociate From"), NULL, NULL, 0, "<Item>"} >+}; >+ >+static const int playlistwin_dtd_menu_entries_num = >+ sizeof(playlistwin_dtd_menu_entries) / >+ sizeof(playlistwin_dtd_menu_entries[0]); >+ > void playlistwin_draw_frame(void); > > static void playlistwin_update_info(void) >@@ -1029,6 +1051,35 @@ > case PLIST_LOAD: > playlistwin_show_load_filesel(); > break; >+ >+ /* DTD button */ >+ case DTD_GOODSONG: { >+ gint score; >+ gint selected_playlist_pos = playlist_get_single_selection(); >+ >+ assert(selected_playlist_pos > -1); >+ score = playlist_get_score(selected_playlist_pos); >+ if (score < 0) >+ { >+ playlist_set_score(selected_playlist_pos, 0); >+ } >+ >+ break; >+ } >+ >+ case DTD_BADSONG: { >+ gint score; >+ gint selected_playlist_pos = playlist_get_single_selection(); >+ >+ assert(selected_playlist_pos > -1); >+ score = playlist_get_score(selected_playlist_pos); >+ if (score >= 0) >+ { >+ playlist_set_score(selected_playlist_pos, -1); >+ } >+ >+ break; >+ } > } > } > >@@ -1052,6 +1103,133 @@ > inside_widget(x, y, playlistwin_sscroll_down)); > } > >+gchar *playlistwin_fileName2songName(const gchar *fileName) >+{ >+ const gchar *afterLastSlash, *beforeLastDot, *c; >+ gchar *returnMe; >+ gint i; >+ const gchar *loadedTitle = >+ playlist_filename2songtitle(fileName); >+ >+ if (loadedTitle != NULL) >+ { >+ return g_strdup(loadedTitle); >+ } >+ >+ // Ditch the last slash and everything before it, as well as >+ // the last dot and everything after it before returning it. >+ afterLastSlash = strrchr(fileName, '/'); >+ if (afterLastSlash != NULL) >+ { >+ afterLastSlash++; >+ } >+ else >+ { >+ afterLastSlash = fileName; >+ } >+ >+ beforeLastDot = strrchr(fileName, '.'); >+ if ((beforeLastDot != NULL) && (beforeLastDot > afterLastSlash)) >+ { >+ beforeLastDot--; >+ } >+ else >+ { >+ beforeLastDot = fileName + strlen(fileName) - 1; >+ } >+ >+ if ((afterLastSlash >= beforeLastDot) || >+ (beforeLastDot <= fileName) || >+ ((afterLastSlash - fileName) >= strlen(fileName))) >+ { >+ return g_strdup(fileName); >+ } >+ >+ // Copy from after last slash to before last dot >+ returnMe = g_new(gchar, (beforeLastDot - afterLastSlash) + 1); >+ i = 0; >+ for (c = afterLastSlash; c <= beforeLastDot; c++) >+ { >+ returnMe[i++] = *c; >+ } >+ returnMe[i] = '\0'; >+ >+ return returnMe; >+} >+ >+void playlistwin_remove_dissociation_menu(GtkMenuItem *attachPoint) >+{ >+ // Nuke any previous dissociations menu >+ >+ // FIXME: I have absolutely no clue to how GTK+ refcounting >+ // works. This implementation may or may not leak memory. >+ // Candidates for memory leaks are the user_data fields of the >+ // labels (they should be freed). >+ // /Johan Walles - jan 05 / 2002 >+ gtk_menu_item_remove_submenu(attachPoint); >+} >+ >+void playlistwin_setup_dissociation_menu(GtkMenuItem *attachPoint, >+ gint selected_playlist_pos) >+{ >+ const GList *recommendations = NULL; >+ const gchar *filename = playlist_get_filename(selected_playlist_pos); >+ >+ // Ditch the previous dissociations menu (if any) >+ playlistwin_remove_dissociation_menu(attachPoint); >+ >+ recommendations = >+ dtd_get_recommendations(filename); >+ >+ if (recommendations != NULL) >+ { >+ // Create a new menu >+ GtkWidget *dissociateMenu = gtk_menu_new(); >+ const GList *iterator; >+ >+ // Associate it with the selected song >+ gtk_object_set_user_data(GTK_OBJECT(dissociateMenu), g_strdup(filename)); >+ >+ // For all recommendations... >+ for (iterator = recommendations; >+ iterator != NULL; >+ iterator = g_list_next(iterator)) >+ { >+ const gchar *filename = ((DtdNextSong *)(iterator->data))->filename; >+ gchar *songName = playlistwin_fileName2songName(filename); >+ GtkWidget *newMenuItem; >+ >+ // ... create a new menu entry... >+ newMenuItem = gtk_menu_item_new_with_label(songName); >+ g_free(songName); >+ >+ // ... with its user_data set to point >+ // to the associated file name... >+ gtk_object_set_user_data(GTK_OBJECT(newMenuItem), g_strdup(filename)); >+ >+ // ... tell it what to do on receiving a click... >+ gtk_signal_connect(GTK_OBJECT(newMenuItem), >+ "activate", >+ GTK_SIGNAL_FUNC(playlistwin_dissociate_callback), >+ NULL); >+ >+ // ... and add it to the new menu. >+ gtk_menu_append(GTK_MENU(dissociateMenu), newMenuItem); >+ } >+ >+ // Enable our new dissociations menu >+ gtk_widget_show_all(dissociateMenu); >+ gtk_menu_item_set_submenu(attachPoint, dissociateMenu); >+ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 1); >+ } >+ else >+ { >+ // ... or disable the dissociation menu heading >+ // if there is nothing to dissociate from. >+ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 0); >+ } >+} >+ > #define REGION_L(x1,x2,y1,y2) \ > (event->x >= (x1) && event->x < (x2) && \ > event->y >= cfg.playlist_height - (y1) && \ >@@ -1226,18 +1404,101 @@ > else if (event->button == 3 && > inside_widget(event->x, event->y, playlistwin_list)) > { >- int pos, sensitive; >+ // FIXME: This block is much too long, put it in its >+ // own function >+ >+ gint clicked_playlist_pos, selected_playlist_pos; >+ gboolean sensitive; >+ > GtkWidget *w; >- pos = playlist_list_get_playlist_position(playlistwin_list, >- event->x, event->y); >- sensitive = pos != -1; >+ >+ clicked_playlist_pos = >+ playlist_list_get_playlist_position(playlistwin_list, >+ event->x, event->y); >+ >+ // Unless the clicked song is part of a (multiple) >+ // selection... >+ if (!playlist_is_selected(clicked_playlist_pos)) >+ { >+ // ... select just the current song before >+ // displaying the menu >+ playlist_select_all(0); >+ playlist_select_range(clicked_playlist_pos, clicked_playlist_pos, 1); >+ playlistwin_update_list(); >+ } >+ >+ selected_playlist_pos = playlist_get_single_selection(); >+ sensitive = (selected_playlist_pos != -1); >+ >+ // Disable "View File Info" if not exactly one song is selected > w = gtk_item_factory_get_widget(playlistwin_popup_menu, > "/View File Info"); > gtk_widget_set_sensitive(w, sensitive); >+ >+ // Disable the DTD menu hierarchy if not exactly one >+ // song is selected >+ // FIXME: or if it's disabled in the prefs >+ w = gtk_item_factory_get_widget(playlistwin_popup_menu, >+ "/Dynamic Taste Detection"); >+ gtk_widget_set_sensitive(w, sensitive); >+ >+ if (sensitive) >+ { >+ GtkWidget *playlistwin_dtd_menu_widget = >+ gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>"); >+ >+ gtk_widget_show_all(playlistwin_dtd_menu_widget); >+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), playlistwin_dtd_menu_widget); >+ } >+ else >+ { >+ gtk_menu_item_remove_submenu(GTK_MENU_ITEM(w)); >+ gtk_widget_hide_all(gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>")); >+ } >+ >+ if (selected_playlist_pos != -1) >+ { >+ // Enable either "/I Like this Song" (if the >+ // song scores >= 0) or "Bad Song" (if the >+ // song scores < 0). >+ gint score = dtd_get_score(playlist_get_filename(selected_playlist_pos)); >+ >+ w = gtk_item_factory_get_widget(playlistwin_dtd_menu, >+ "/I Like this Song"); >+ if (score >= 0) >+ { >+ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1); >+ } >+ gtk_widget_set_sensitive(w, 1); >+ w = gtk_item_factory_get_widget(playlistwin_dtd_menu, >+ "/I Don't Like this Song"); >+ if (score < 0) >+ { >+ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1); >+ } >+ gtk_widget_set_sensitive(w, 1); >+ } >+ else >+ { >+ // We don't do ratings of multiple songs at once >+ w = gtk_item_factory_get_widget(playlistwin_dtd_menu, >+ "/I Like this Song"); >+ gtk_widget_set_sensitive(w, 0); >+ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0); >+ w = gtk_item_factory_get_widget(playlistwin_dtd_menu, >+ "/I Don't Like this Song"); >+ gtk_widget_set_sensitive(w, 0); >+ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0); >+ } > >+ // Create the dissociation menu >+ w = gtk_item_factory_get_widget(playlistwin_dtd_menu, >+ "/Dissociate From"); >+ playlistwin_setup_dissociation_menu(GTK_MENU_ITEM(w), selected_playlist_pos); >+ > playlistwin_set_sensitive_sortmenu(); > util_item_factory_popup_with_data(playlistwin_popup_menu, >- GINT_TO_POINTER(pos), NULL, >+ GINT_TO_POINTER(selected_playlist_pos), NULL, > event->x_root, > event->y_root + 5, > 3, event->time); >@@ -1944,6 +2205,11 @@ > cfg.playlist_height, > gdk_rgb_get_visual()->depth); > >+ playlistwin_dtd_menu = gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL); >+ gtk_item_factory_create_items(GTK_ITEM_FACTORY(playlistwin_dtd_menu), >+ playlistwin_dtd_menu_entries_num, >+ playlistwin_dtd_menu_entries, NULL); >+ > playlistwin_popup_menu = > gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL); > gtk_item_factory_set_translate_func(playlistwin_popup_menu, >@@ -2020,6 +2286,23 @@ > tbutton_set_toggled(mainwin_pl, FALSE); > } > >+void playlistwin_dissociate_callback(GtkMenuItem *menuitem, >+ gpointer ignored) >+{ >+ gchar *first, *second; >+ >+ // Find out "first" by checking the user data of the menuitem >+ // that was activated >+ first = (gchar *)gtk_object_get_user_data(GTK_OBJECT(menuitem)); >+ >+ // Find out "second" by checking the user data of the menu >+ // that is the parent of the menuitem that was activated >+ second = (gchar *)gtk_object_get_user_data(GTK_OBJECT(GTK_WIDGET(menuitem)->parent)); >+ >+ dtd_dissociate(first, second); >+} >+ >+ > void playlistwin_popup_menu_callback(gpointer cb_data, guint action, GtkWidget * w) > { > int pos = GPOINTER_TO_INT(gtk_item_factory_popup_data_from_widget(w)); >@@ -2032,6 +2315,17 @@ > case SEL_LOOKUP: > playlist_read_info_selection(); > break; >+ case DTD_DISSOCIATE: >+ >+ break; >+ case DTD_GOODSONG: >+ case DTD_BADSONG: >+ // Act only upon the active item >+ if (GTK_CHECK_MENU_ITEM(w)->active) >+ { >+ playlistwin_popup_handler(action); >+ } >+ break; > default: > playlistwin_popup_handler(action); > } >diff -Naur xmms-1.2.8-orig/xmms/prefswin.c xmms-1.2.8/xmms/prefswin.c >--- xmms-1.2.8-orig/xmms/prefswin.c 2003-07-16 15:17:47.000000000 +0200 >+++ xmms-1.2.8/xmms/prefswin.c 2003-09-13 09:23:46.000000000 +0200 >@@ -1039,6 +1039,8 @@ > > gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_options_vbox, gtk_label_new(_("Options"))); > >+ prefswin_option_new_with_label_to_table(&cfg.dynamic_taste_detection, _("Dynamic taste detection"), GTK_TABLE(options_table), 1, 10); >+ > /* > * Fonts page > */ >diff -Naur xmms-1.2.8-orig/xmms/xmms.h xmms-1.2.8/xmms/xmms.h >--- xmms-1.2.8-orig/xmms/xmms.h 2001-03-14 15:06:39.000000000 +0100 >+++ xmms-1.2.8/xmms/xmms.h 2003-09-13 09:23:46.000000000 +0200 >@@ -81,6 +81,7 @@ > #include "sm.h" > #include "dnd.h" > #include "urldecode.h" >+#include "dtd.h" > > #include "config.h" >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 28587
: 17625