diff options
-rw-r--r-- | media-sound/xmms/ChangeLog | 8 | ||||
-rw-r--r-- | media-sound/xmms/files/digest-xmms-1.2.8-r1 | 2 | ||||
-rw-r--r-- | media-sound/xmms/files/xmms-1.2.8-dtd.patch | 1985 | ||||
-rw-r--r-- | media-sound/xmms/files/xmms-1.2.8-jump.patch | 76 | ||||
-rw-r--r-- | media-sound/xmms/files/xmms-1.2.8-russian-charset.patch | 2174 | ||||
-rw-r--r-- | media-sound/xmms/files/xmms-1.2.8-sigterm.patch | 31 | ||||
-rw-r--r-- | media-sound/xmms/xmms-1.2.8-r1.ebuild | 139 |
7 files changed, 4414 insertions, 1 deletions
diff --git a/media-sound/xmms/ChangeLog b/media-sound/xmms/ChangeLog index b8bfe1d87efb..09ec24933832 100644 --- a/media-sound/xmms/ChangeLog +++ b/media-sound/xmms/ChangeLog @@ -1,6 +1,12 @@ # ChangeLog for media-sound/xmms # Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2 -# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.66 2003/09/12 19:27:39 seemant Exp $ +# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.67 2003/09/13 03:46:59 vapier Exp $ + +*xmms-1.2.8-r1 (12 Sep 2003) + + 12 Sep 2003; Mike Frysinger <vapier@gentoo.org> : + Added Dynamic Taste Detection #27967 and removed some of the elibtoolize + and autotools crud. *xmms-1.2.8 (12 Sep 2003) diff --git a/media-sound/xmms/files/digest-xmms-1.2.8-r1 b/media-sound/xmms/files/digest-xmms-1.2.8-r1 new file mode 100644 index 000000000000..55451e9e64ea --- /dev/null +++ b/media-sound/xmms/files/digest-xmms-1.2.8-r1 @@ -0,0 +1,2 @@ +MD5 bf1d7ea568b01d72e99d75ae9d0d1594 xmms-1.2.8.tar.gz 3884444 +MD5 a68edd5b03d650c366f09852a8414ee2 gentoo_ice.zip 68458 diff --git a/media-sound/xmms/files/xmms-1.2.8-dtd.patch b/media-sound/xmms/files/xmms-1.2.8-dtd.patch new file mode 100644 index 000000000000..94b342568b0c --- /dev/null +++ b/media-sound/xmms/files/xmms-1.2.8-dtd.patch @@ -0,0 +1,1985 @@ +diff -ruN dtd-old/AUTHORS dtd-new/AUTHORS +--- dtd-old/AUTHORS 2003-01-07 15:50:08.000000000 +0100 ++++ dtd-new/AUTHORS 2003-09-08 13:46:19.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 -ruN dtd-old/README dtd-new/README +--- dtd-old/README 2003-09-04 23:00:18.000000000 +0200 ++++ dtd-new/README 2003-09-08 13:46:19.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 -ruN dtd-old/xmms/Makefile.am dtd-new/xmms/Makefile.am +--- dtd-old/xmms/Makefile.am 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/Makefile.am 2003-09-08 13:46:19.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 -ruN dtd-old/xmms/dtd.c dtd-new/xmms/dtd.c +--- dtd-old/xmms/dtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ dtd-new/xmms/dtd.c 2003-09-08 14:37:21.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 -ruN dtd-old/xmms/dtd.h dtd-new/xmms/dtd.h +--- dtd-old/xmms/dtd.h 1970-01-01 01:00:00.000000000 +0100 ++++ dtd-new/xmms/dtd.h 2003-09-08 14:38:32.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 -ruN dtd-old/xmms/main.c dtd-new/xmms/main.c +--- dtd-old/xmms/main.c 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/main.c 2003-09-08 13:49:16.000000000 +0200 +@@ -476,6 +476,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); + } +@@ -674,6 +676,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); +@@ -903,6 +907,7 @@ + playlist_clear(); + cleanup_plugins(); + sm_cleanup(); ++ dtd_persist(); + gtk_exit(0); + } + +@@ -3130,6 +3135,7 @@ + GDK_THREADS_LEAVE(); + } + ++ cfg.dynamic_taste_detection = TRUE; + + GDK_THREADS_ENTER(); + check_ctrlsocket(); +@@ -3608,6 +3614,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 -ruN dtd-old/xmms/main.h dtd-new/xmms/main.h +--- dtd-old/xmms/main.h 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/main.h 2003-09-08 13:51:48.000000000 +0200 +@@ -64,6 +64,7 @@ + gchar *charset_id3, *charset_output, *charset_fs, *charset_pl; + gint autocharset; + gchar *shade_font; ++ gboolean dynamic_taste_detection; + } + Config; + +diff -ruN dtd-old/xmms/playlist.c dtd-new/xmms/playlist.c +--- dtd-old/xmms/playlist.c 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/playlist.c 2003-09-08 14:12:58.000000000 +0200 +@@ -23,6 +23,7 @@ + #include "charset.h" + #include <sys/stat.h> + #include <unistd.h> ++#include <assert.h> + + GList *playlist = NULL; + GList *shuffle_list = NULL; +@@ -653,6 +654,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(); +@@ -725,7 +737,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; +@@ -740,7 +763,7 @@ + } + PL_UNLOCK(); + playlist_check_pos_current(); +- ++ + if (restart_playing) + playlist_play(); + else +@@ -856,6 +879,7 @@ + + playlist_position = node->data; + PL_UNLOCK(); ++ ((PlaylistEntry *)playlist_position)->moved = FALSE; + playlist_check_pos_current(); + + if (restart_playing) +@@ -875,12 +899,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) + { +@@ -912,6 +943,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(); +@@ -1146,6 +1196,7 @@ + ext = strrchr(filename, '.'); + if (ext && !strcasecmp(ext, ".pls")) + { ++ /* It's a playlist, let's parse it. */ + int noe, i; + char key[10]; + +@@ -1283,6 +1334,11 @@ + char *ret; + PlaylistEntry *entry; + GList *node; ++ ++ if (pos < 0) ++ { ++ return NULL; ++ } + + PL_LOCK(); + if (!playlist) +@@ -1290,6 +1346,11 @@ + PL_UNLOCK(); + return NULL; + } ++ if (pos >= __get_playlist_length()) ++ { ++ PL_UNLOCK(); ++ return NULL; ++ } + node = g_list_nth(playlist, pos); + if (!node) + { +@@ -1355,6 +1416,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; +@@ -1616,6 +1755,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(); +@@ -1630,7 +1789,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 */ + /* +@@ -1639,43 +1811,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(); + } + +@@ -1710,7 +2052,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 -ruN dtd-old/xmms/playlist.h dtd-new/xmms/playlist.h +--- dtd-old/xmms/playlist.h 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/playlist.h 2003-09-08 14:14:13.000000000 +0200 +@@ -26,7 +26,8 @@ + gchar *title; + gchar *sort; + gint length; +- gboolean selected; ++ gboolean selected; ++ gboolean moved; + } + PlaylistEntry; + +@@ -81,10 +82,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 -ruN dtd-old/xmms/playlist_list.c dtd-new/xmms/playlist_list.c +--- dtd-old/xmms/playlist_list.c 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/playlist_list.c 2003-09-08 13:46:19.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 -ruN dtd-old/xmms/playlistwin.c dtd-new/xmms/playlistwin.c +--- dtd-old/xmms/playlistwin.c 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/playlistwin.c 2003-09-08 14:16:39.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; +@@ -68,13 +69,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 + { +@@ -151,7 +154,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>"}, +@@ -182,6 +189,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) +@@ -1030,6 +1052,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; ++ } + } + } + +@@ -1053,6 +1104,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) && \ +@@ -1227,18 +1405,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); +@@ -1946,6 +2207,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, +@@ -2022,6 +2288,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)); +@@ -2034,6 +2317,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 -ruN dtd-old/xmms/prefswin.c dtd-new/xmms/prefswin.c +--- dtd-old/xmms/prefswin.c 2003-09-08 13:31:40.000000000 +0200 ++++ dtd-new/xmms/prefswin.c 2003-09-08 13:46:19.000000000 +0200 +@@ -1042,6 +1042,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 -ruN dtd-old/xmms/xmms.h dtd-new/xmms/xmms.h +--- dtd-old/xmms/xmms.h 2001-03-14 15:06:39.000000000 +0100 ++++ dtd-new/xmms/xmms.h 2003-09-08 13:46:19.000000000 +0200 +@@ -81,6 +81,7 @@ + #include "sm.h" + #include "dnd.h" + #include "urldecode.h" ++#include "dtd.h" + + #include "config.h" + diff --git a/media-sound/xmms/files/xmms-1.2.8-jump.patch b/media-sound/xmms/files/xmms-1.2.8-jump.patch new file mode 100644 index 000000000000..5ebd59b3d08a --- /dev/null +++ b/media-sound/xmms/files/xmms-1.2.8-jump.patch @@ -0,0 +1,76 @@ +diff -Naur xmms-1.2.8/libxmms/xmmsctrl.c xmms-1.2.8-new/libxmms/xmmsctrl.c +--- xmms-1.2.8/libxmms/xmmsctrl.c 2003-09-04 15:48:34.000000000 +0200 ++++ xmms-1.2.8-new/libxmms/xmmsctrl.c 2003-09-06 18:18:53.000000000 +0200 +@@ -381,6 +381,11 @@ + remote_cmd(session, CMD_PLAY_PAUSE); + } + ++void xmms_remote_show_jump_box(gint session) ++{ ++ remote_cmd(session, CMD_SHOW_JUMP_BOX); ++} ++ + gboolean xmms_remote_is_playing(gint session) + { + return remote_get_gboolean(session, CMD_IS_PLAYING); +diff -Naur xmms-1.2.8/libxmms/xmmsctrl.h xmms-1.2.8-new/libxmms/xmmsctrl.h +--- xmms-1.2.8/libxmms/xmmsctrl.h 2003-06-09 15:22:10.000000000 +0200 ++++ xmms-1.2.8-new/libxmms/xmmsctrl.h 2003-09-06 18:18:53.000000000 +0200 +@@ -61,6 +61,7 @@ + gboolean xmms_remote_is_pl_win(gint session); + gboolean xmms_remote_is_eq_win(gint session); + void xmms_remote_show_prefs_box(gint session); ++void xmms_remote_show_jump_box(gint session); + void xmms_remote_toggle_aot(gint session, gboolean ontop); + void xmms_remote_eject(gint session); + void xmms_remote_playlist_prev(gint session); +diff -Naur xmms-1.2.8/xmms/controlsocket.c xmms-1.2.8-new/xmms/controlsocket.c +--- xmms-1.2.8/xmms/controlsocket.c 2003-09-04 15:48:34.000000000 +0200 ++++ xmms-1.2.8-new/xmms/controlsocket.c 2003-09-06 18:18:53.000000000 +0200 +@@ -506,6 +506,9 @@ + case CMD_SET_SKIN: + load_skin(data); + break; ++ case CMD_SHOW_JUMP_BOX: ++ show_jump_to_file(); ++ break; + case CMD_PL_WIN_TOGGLE: + tbool = *((gboolean *) data); + playlistwin_show(!!tbool); +diff -Naur xmms-1.2.8/xmms/controlsocket.h xmms-1.2.8-new/xmms/controlsocket.h +--- xmms-1.2.8/xmms/controlsocket.h 2003-06-09 15:22:10.000000000 +0200 ++++ xmms-1.2.8-new/xmms/controlsocket.h 2003-09-06 18:18:53.000000000 +0200 +@@ -46,6 +46,7 @@ + CMD_GET_EQ, CMD_GET_EQ_PREAMP, CMD_GET_EQ_BAND, + CMD_SET_EQ, CMD_SET_EQ_PREAMP, CMD_SET_EQ_BAND, + CMD_QUIT, CMD_PLAYLIST_INS_URL_STRING, CMD_PLAYLIST_INS, CMD_PLAY_PAUSE, ++ CMD_SHOW_JUMP_BOX + }; + + typedef struct +diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c +--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200 ++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:18:53.000000000 +0200 +@@ -1847,6 +1847,11 @@ + GTK_CLIST(clist)->focus_row = GPOINTER_TO_INT(GTK_CLIST(clist)->selection->data); + } + ++void show_jump_to_file(void) ++{ ++ mainwin_jump_to_file(); ++} ++ + static gboolean mainwin_configure(GtkWidget * window, GdkEventConfigure *event, gpointer data) + { + if (!GTK_WIDGET_VISIBLE(window)) +diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h +--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200 ++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 18:18:53.000000000 +0200 +@@ -117,6 +117,7 @@ + void mainwin_set_shade_menu_cb(gboolean shaded); + void mainwin_set_shade(gboolean shaded); + void mainwin_shade_toggle(void); ++void show_jump_to_file(void); + + #define PLAYER_HEIGHT ((cfg.player_shaded ? 14 : 116) * (cfg.doublesize + 1)) + #define PLAYER_WIDTH (275 * (cfg.doublesize + 1)) diff --git a/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch new file mode 100644 index 000000000000..57ffb8612f89 --- /dev/null +++ b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch @@ -0,0 +1,2174 @@ +diff -Naur xmms-1.2.8/Input/mpg123/fileinfo.c xmms-1.2.8-new/Input/mpg123/fileinfo.c +--- xmms-1.2.8/Input/mpg123/fileinfo.c 2003-07-14 15:24:28.000000000 +0200 ++++ xmms-1.2.8-new/Input/mpg123/fileinfo.c 2003-09-06 19:42:29.000000000 +0200 +@@ -26,6 +26,10 @@ + #include <libxmms/xentry.h> + #include "mpg123.h" + ++#include "libxmms/charset.h" ++#include <gdk/gdkkeysyms.h> ++#include "xmms/xmms.h" ++ + static GtkWidget *window = NULL; + static GtkWidget *filename_entry, *id3_frame; + static GtkWidget *title_entry, *artist_entry, *album_entry, *year_entry, *tracknum_entry, *comment_entry; +@@ -46,20 +50,35 @@ + { + gint stripped_len; + gchar *text; ++ gchar *ctag; ++ gint clength; + ++ ctag=xmms_charset_recode_id3(tag,length,&clength); ++ if (ctag) { ++ tag=ctag; ++ length=clength; ++ } ++ + stripped_len = mpg123_strip_spaces(tag, length); + text = g_strdup_printf("%-*.*s", stripped_len, stripped_len, tag); + gtk_entry_set_text(entry, text); + g_free(text); ++ if (ctag) g_free(ctag); + } + + static void get_entry_tag(GtkEntry * entry, gchar * tag, gint length) + { + gchar *text; ++ gchar *ctext; + + text = gtk_entry_get_text(entry); ++ ctext = xmms_charset_recode_output(text,length,NULL); ++ if (ctext) text=ctext; ++ + memset(tag, ' ', length); + memcpy(tag, text, strlen(text) > length ? length : strlen(text)); ++ ++ if (ctext) g_free(ctext); + } + + static gint find_genre_id(gchar * text) +@@ -225,6 +244,31 @@ + } + } + ++static int restore_focus; ++ ++gboolean mpg123_keypress_cb(GtkWidget * w, GdkEventKey * event, gpointer save) { ++ switch(event->keyval) { ++ case GDK_Return: ++ restore_focus=1; ++ gtk_signal_emit_by_name(GTK_OBJECT(save), "clicked", NULL); ++ break; ++ case GDK_Escape: ++ restore_focus=1; ++ break; ++ } ++ return TRUE; ++} ++ ++void gtk_widget_destroyed_focus(GtkWidget *widget, GtkWidget **widget_pointer) { ++ gtk_widget_destroyed(widget,widget_pointer); ++ if (restore_focus) { ++ gtk_widget_hide(playlistwin); ++ gtk_widget_show(playlistwin); ++// playlistwin_real_hide(); ++// playlistwin_real_show(); ++ } ++} ++ + void mpg123_file_info_box(char *filename) + { + gint i; +@@ -233,7 +277,10 @@ + gchar *tmp, *title; + const gchar *emphasis[4]; + const gchar *bool_label[2]; ++ gchar *cfilename; + ++ restore_focus=0; ++ + emphasis[0] = _("None"); + emphasis[1] = _("50/15 ms"); + emphasis[2] = ""; +@@ -247,10 +294,11 @@ + GtkWidget *mpeg_frame, *mpeg_box; + GtkWidget *label, *filename_hbox; + GtkWidget *bbox, *save, *remove_id3, *cancel; ++ GtkAccelGroup *ag; + + window = gtk_window_new(GTK_WINDOW_DIALOG); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); +- gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); ++ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed_focus), &window); + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + vbox = gtk_vbox_new(FALSE, 10); +@@ -392,6 +440,18 @@ + gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, FALSE, FALSE, 0); + ++ ag = gtk_accel_group_new(); ++ gtk_accel_group_add(ag, GDK_Escape, 0, 0, GTK_OBJECT(cancel), "clicked"); ++ gtk_accel_group_add(ag, GDK_Return, 0, 0, GTK_OBJECT(save), "clicked"); ++ gtk_window_add_accel_group(GTK_WINDOW(window), ag); ++/* gtk_signal_connect(GTK_OBJECT(title_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save); ++ gtk_signal_connect(GTK_OBJECT(artist_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save); ++ gtk_signal_connect(GTK_OBJECT(album_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save); ++ gtk_signal_connect(GTK_OBJECT(comment_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save); ++ gtk_signal_connect(GTK_OBJECT(year_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save); ++ gtk_signal_connect(GTK_OBJECT(tracknum_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);*/ ++ gtk_signal_connect(GTK_OBJECT(window),"key_press_event",GTK_SIGNAL_FUNC(mpg123_keypress_cb),save); ++ + gtk_widget_show_all(window); + } + +@@ -399,6 +459,9 @@ + g_free(current_filename); + current_filename = g_strdup(filename); + ++ cfilename=xmms_charset_recode_fsout(filename,0,NULL); ++ if (cfilename) filename=cfilename; ++ + title = g_strdup_printf(_("File Info - %s"), g_basename(filename)); + gtk_window_set_title(GTK_WINDOW(window), title); + g_free(title); +@@ -412,6 +475,8 @@ + gtk_entry_set_text(GTK_ENTRY(title_entry), title); + g_free(title); + ++ if (cfilename) g_free(cfilename); ++ + gtk_entry_set_text(GTK_ENTRY(artist_entry), ""); + gtk_entry_set_text(GTK_ENTRY(album_entry), ""); + gtk_entry_set_text(GTK_ENTRY(year_entry), ""); +diff -Naur xmms-1.2.8/Input/mpg123/mpg123.c xmms-1.2.8-new/Input/mpg123/mpg123.c +--- xmms-1.2.8/Input/mpg123/mpg123.c 2003-07-10 15:12:19.000000000 +0200 ++++ xmms-1.2.8-new/Input/mpg123/mpg123.c 2003-09-06 19:42:29.000000000 +0200 +@@ -551,6 +551,7 @@ + { + gchar *ret = NULL, *path, *temp; + TitleInput *input; ++ gint len; + + XMMS_NEW_TITLEINPUT(input); + +@@ -582,9 +583,18 @@ + /* + * Format according to filename. + */ ++ ret = xmms_charset_recode_fs(g_basename(filename),0,&len); ++ if (ret) { ++ for (;len>=0;len--) ++ if (ret[len]=='.') { ++ ret[len]=0; ++ break; ++ } ++ } else { + ret = g_strdup(g_basename(filename)); + if (extname(ret) != NULL) + *(extname(ret) - 1) = '\0'; /* removes period */ ++ } + } + + return ret; +diff -Naur xmms-1.2.8/Input/vorbis/vorbis.c xmms-1.2.8-new/Input/vorbis/vorbis.c +--- xmms-1.2.8/Input/vorbis/vorbis.c 2003-07-14 15:24:28.000000000 +0200 ++++ xmms-1.2.8-new/Input/vorbis/vorbis.c 2003-09-06 19:42:29.000000000 +0200 +@@ -47,6 +47,7 @@ + #include "libxmms/util.h" + #include "libxmms/configfile.h" + #include "libxmms/titlestring.h" ++#include "libxmms/charset.h" + #include <xmms/i18n.h> + + #include "vorbis.h" +@@ -668,6 +669,7 @@ + gchar *displaytitle = NULL, *tmp, *path, *temp; + vorbis_comment *comment; + TitleInput *input; ++ gint len; + + XMMS_NEW_TITLEINPUT(input); + path = g_strdup(fn); +@@ -705,10 +707,20 @@ + + if (!displaytitle) + { +- if (!vorbis_is_streaming) +- displaytitle = g_strdup_printf("%s", g_basename(fn)); +- else ++ if (!vorbis_is_streaming) { ++ displaytitle = xmms_charset_recode_fs(g_basename(fn),0,&len); ++ if (displaytitle) { ++ for (;len>=0;len--) ++ if (displaytitle[len]=='.') { ++ displaytitle[len]=0; ++ break; ++ } ++ } else { ++ displaytitle = g_strdup_printf("%s", g_basename(fn)); ++ } ++ } else { + displaytitle = vorbis_http_get_title(fn); ++ } + } + + return displaytitle; +diff -Naur xmms-1.2.8/libxmms/charset_auto_russian.h xmms-1.2.8-new/libxmms/charset_auto_russian.h +--- xmms-1.2.8/libxmms/charset_auto_russian.h 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/libxmms/charset_auto_russian.h 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,293 @@ ++/* Stealed from GPPL's (http://gppl.terminal.ru) autorecode-xmms1.2.7 patch, ++ changed to support limited size strings */ ++ ++typedef struct lng_stat { ++ unsigned char a; ++ unsigned char b; ++ int rate; ++} lng_stat; ++ ++//statistics calculated from "Moskva-Petushki" ++//1292 elems ++static const lng_stat win[]={ ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, ++{'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',60}, {'�','�',5}, {'�','�',7}, {'�','�',23}, {'�','�',2}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',27}, {'�','�',15}, {'�','�',10}, {'�','�',25}, {'�','�',21}, ++{'�','�',51}, {'�','�',12}, {'�','�',13}, {'�','�',31}, {'�','�',15}, {'�','�',1}, {'�','�',6}, {'�','�',17}, {'�','�',8}, {'�','�',20}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',2}, ++{'�','�',16}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',25}, {'�','�',10}, {'�','�',9}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',8}, {'�','�',2}, {'�','�',1}, {'�','�',2}, ++{'�','�',15}, {'�','�',2}, {'�','�',3}, {'�','�',5}, {'�','�',98}, {'�','�',5}, {'�','�',7}, {'�','�',5}, {'�','�',8}, {'�','�',7}, {'�','�',6}, {'�','�',107}, {'�','�',18}, {'�','�',2}, {'�','�',74}, {'�','�',6}, ++{'�','�',1}, {'�','�',3}, {'�','�',39}, {'�','�',8}, {'�','�',2}, {'�','�',6}, {'�','�',16}, {'�','�',26}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',31}, {'�','�',7}, {'�','�',2}, {'�','�',167}, {'�','�',6}, ++{'�','�',17}, {'�','�',3}, {'�','�',7}, {'�','�',3}, {'�','�',1}, {'�','�',20}, {'�','�',9}, {'�','�',19}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',5}, ++{'�','�',2}, {'�','�',19}, {'�','�',69}, {'�','�',1}, {'�','�',1}, {'�','�',5}, {'�','�',5}, {'�','�',1}, {'�','�',14}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',31}, {'�','�',3}, {'�','�',1}, ++{'�','�',2}, {'�','�',23}, {'�','�',2}, {'�','�',12}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',8}, {'�','�',78}, {'�','�',7}, {'�','�',20}, {'�','�',7}, {'�','�',1}, ++{'�','�',17}, {'�','�',3}, {'�','�',27}, {'�','�',15}, {'�','�',11}, {'�','�',21}, {'�','�',26}, {'�','�',21}, {'�','�',4}, {'�','�',21}, {'�','�',37}, {'�','�',5}, {'�','�',2}, {'�','�',3}, {'�','�',8}, {'�','�',1}, ++{'�','�',2}, {'�','�',12}, {'�','�',3}, {'�','�',100}, {'�','�',4}, {'�','�',1}, {'�','�',14}, {'�','�',4}, {'�','�',62}, {'�','�',2}, {'�','�',24}, {'�','�',10}, {'�','�',62}, {'�','�',1}, {'�','�',1}, {'�','�',2}, ++{'�','�',7}, {'�','�',11}, {'�','�',4}, {'�','�',12}, {'�','�',15}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',31}, {'�','�',3}, {'�','�',1}, {'�','�',10}, {'�','�',44}, ++{'�','�',1}, {'�','�',19}, {'�','�',93}, {'�','�',1}, {'�','�',6}, {'�','�',29}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',56}, {'�','�',111}, {'�','�',47}, {'�','�',2}, {'�','�',98}, {'�','�',100}, {'�','�',1}, ++{'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',18}, {'�','�',1}, {'�','�',4}, {'�','�',20}, {'�','�',2}, {'�','�',2}, {'�','�',7}, {'�','�',9}, {'�','�',2}, {'�','�',96}, ++{'�','�',5}, {'�','�',17}, {'�','�',16}, {'�','�',21}, {'�','�',35}, {'�','�',13}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',18}, {'�','�',148}, {'�','�',11}, {'�','�',9}, {'�','�',186}, {'�','�',43}, {'�','�',38}, ++{'�','�',4}, {'�','�',1}, {'�','�',22}, {'�','�',13}, {'�','�',5}, {'�','�',13}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',19}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',56}, ++{'�','�',16}, {'�','�',22}, {'�','�',11}, {'�','�',12}, {'�','�',6}, {'�','�',26}, {'�','�',6}, {'�','�',6}, {'�','�',3}, {'�','�',35}, {'�','�',4}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, ++{'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',79}, {'�','�',1}, {'�','�',18}, {'�','�',17}, {'�','�',34}, {'�','�',11}, {'�','�',31}, {'�','�',55}, {'�','�',2}, {'�','�',3}, {'�','�',2}, {'�','�',3}, ++{'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',9}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',8}, {'�','�',2}, ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',1}, {'�','�',16}, {'�','�',2}, {'�','�',1}, {'�','�',8}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',14}, {'�','�',7}, {'�','�',3}, ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',46}, {'�','�',60}, {'�','�',8}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',8}, {'�','�',8}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',5}, ++{'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',61}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',49}, {'�','�',5}, {'�','�',8}, {'�','�',9}, ++{'�','�',2}, {'�','�',12}, {'�','�',4}, {'�','�',10}, {'�','�',5}, {'�','�',6}, {'�','�',28}, {'�','�',24}, {'�','�',39}, {'�','�',4}, {'�','�',30}, {'�','�',7}, {'�','�',5}, {'�','�',3}, {'�','�',4}, {'�','�',3}, ++{'�','�',3}, {'�','�',56}, {'�','�',18}, {'�','�',48}, {'�','�',9}, {'�','�',26}, {'�','�',12}, {'�','�',7}, {'�','�',11}, {'�','�',53}, {'�','�',59}, {'�','�',9}, {'�','�',21}, {'�','�',32}, {'�','�',27}, {'�','�',52}, ++{'�','�',7}, {'�','�',21}, {'�','�',42}, {'�','�',10}, {'�','�',1}, {'�','�',4}, {'�','�',16}, {'�','�',2}, {'�','�',7}, {'�','�',22}, {'�','�',69}, {'�','�',257}, {'�','�',701}, {'�','�',218}, {'�','�',548}, {'�','�',328}, ++{'�','�',262}, {'�','�',626}, {'�','�',225}, {'�','�',194}, {'�','�',1285}, {'�','�',1288}, {'�','�',771}, {'�','�',911}, {'�','�',166}, {'�','�',421}, {'�','�',368}, {'�','�',802}, {'�','�',874}, {'�','�',98}, {'�','�',38}, {'�','�',228}, ++{'�','�',24}, {'�','�',300}, {'�','�',121}, {'�','�',27}, {'�','�',63}, {'�','�',238}, {'�','�',384}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',199}, {'�','�',6}, {'�','�',13}, {'�','�',6}, {'�','�',420}, {'�','�',2}, ++{'�','�',142}, {'�','�',7}, {'�','�',137}, {'�','�',7}, {'�','�',31}, {'�','�',327}, {'�','�',4}, {'�','�',172}, {'�','�',18}, {'�','�',1}, {'�','�',288}, {'�','�',11}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',32}, ++{'�','�',561}, {'�','�',10}, {'�','�',7}, {'�','�',1}, {'�','�',134}, {'�','�',11}, {'�','�',1}, {'�','�',6}, {'�','�',7}, {'�','�',5}, {'�','�',5}, {'�','�',6}, {'�','�',14}, {'�','�',2}, {'�','�',9}, {'�','�',13}, ++{'�','�',17}, {'�','�',51}, {'�','�',7}, {'�','�',18}, {'�','�',4}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',7}, {'�','�',2}, {'�','�',4}, {'�','�',989}, {'�','�',26}, {'�','�',62}, {'�','�',43}, ++{'�','�',74}, {'�','�',911}, {'�','�',26}, {'�','�',88}, {'�','�',387}, {'�','�',1}, {'�','�',105}, {'�','�',122}, {'�','�',65}, {'�','�',231}, {'�','�',1220}, {'�','�',118}, {'�','�',97}, {'�','�',851}, {'�','�',123}, {'�','�',116}, ++{'�','�',1}, {'�','�',9}, {'�','�',11}, {'�','�',47}, {'�','�',53}, {'�','�',2}, {'�','�',518}, {'�','�',33}, {'�','�',44}, {'�','�',3}, {'�','�',48}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, ++{'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',210}, {'�','�',18}, {'�','�',6}, {'�','�',3}, {'�','�',191}, ++{'�','�',83}, {'�','�',2}, {'�','�',103}, {'�','�',20}, {'�','�',205}, {'�','�',3}, {'�','�',62}, {'�','�',1340}, {'�','�',7}, {'�','�',180}, {'�','�',13}, {'�','�',4}, {'�','�',88}, {'�','�',1}, {'�','�',15}, {'�','�',1}, ++{'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',795}, ++{'�','�',7}, {'�','�',165}, {'�','�',6}, {'�','�',14}, {'�','�',839}, {'�','�',8}, {'�','�',7}, {'�','�',487}, {'�','�',1}, {'�','�',79}, {'�','�',78}, {'�','�',10}, {'�','�',289}, {'�','�',613}, {'�','�',17}, {'�','�',195}, ++{'�','�',62}, {'�','�',41}, {'�','�',417}, {'�','�',1}, {'�','�',11}, {'�','�',138}, {'�','�',6}, {'�','�',111}, {'�','�',200}, {'�','�',6}, {'�','�',68}, {'�','�',30}, {'�','�',8}, {'�','�',33}, {'�','�',7}, {'�','�',34}, ++{'�','�',13}, {'�','�',1}, {'�','�',6}, {'�','�',21}, {'�','�',21}, {'�','�',4}, {'�','�',13}, {'�','�',20}, {'�','�',21}, {'�','�',31}, {'�','�',2}, {'�','�',13}, {'�','�',10}, {'�','�',7}, {'�','�',4}, {'�','�',2}, ++{'�','�',1}, {'�','�',9}, {'�','�',2}, {'�','�',11}, {'�','�',1}, {'�','�',25}, {'�','�',44}, {'�','�',463}, {'�','�',704}, {'�','�',592}, {'�','�',613}, {'�','�',281}, {'�','�',136}, {'�','�',444}, {'�','�',177}, {'�','�',297}, ++{'�','�',398}, {'�','�',926}, {'�','�',982}, {'�','�',1512}, {'�','�',206}, {'�','�',524}, {'�','�',1231}, {'�','�',1082}, {'�','�',1607}, {'�','�',103}, {'�','�',15}, {'�','�',182}, {'�','�',66}, {'�','�',308}, {'�','�',235}, {'�','�',137}, ++{'�','�',79}, {'�','�',24}, {'�','�',87}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',187}, {'�','�',4}, {'�','�',9}, {'�','�',1}, {'�','�',145}, {'�','�',690}, {'�','�',4}, {'�','�',1}, ++{'�','�',214}, {'�','�',35}, {'�','�',1}, {'�','�',8}, {'�','�',96}, {'�','�',13}, {'�','�',14}, {'�','�',7}, {'�','�',4}, {'�','�',8}, {'�','�',69}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',2}, {'�','�',7}, ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',889}, {'�','�',39}, ++{'�','�',147}, {'�','�',67}, {'�','�',174}, {'�','�',56}, {'�','�',18}, {'�','�',11}, {'�','�',113}, {'�','�',16}, {'�','�',33}, {'�','�',64}, {'�','�',302}, {'�','�',109}, {'�','�',31}, {'�','�',53}, {'�','�',21}, {'�','�',7}, ++{'�','�',109}, {'�','�',3}, {'�','�',11}, {'�','�',4}, {'�','�',71}, {'�','�',20}, {'�','�',4}, {'�','�',43}, {'�','�',39}, {'�','�',10}, {'�','�',46}, {'�','�',5}, {'�','�',25}, {'�','�',11}, {'�','�',2}, {'�','�',1}, ++{'�','�',30}, {'�','�',24}, {'�','�',5}, {'�','�',36}, {'�','�',39}, {'�','�',25}, {'�','�',49}, {'�','�',3}, {'�','�',22}, {'�','�',25}, {'�','�',8}, {'�','�',3}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',3}, ++{'�','�',7}, {'�','�',28}, {'�','�',90}, {'�','�',242}, {'�','�',658}, {'�','�',153}, {'�','�',435}, {'�','�',280}, {'�','�',87}, {'�','�',388}, {'�','�',301}, {'�','�',216}, {'�','�',492}, {'�','�',888}, {'�','�',573}, {'�','�',958}, ++{'�','�',216}, {'�','�',335}, {'�','�',275}, {'�','�',668}, {'�','�',949}, {'�','�',108}, {'�','�',17}, {'�','�',324}, {'�','�',123}, {'�','�',363}, {'�','�',134}, {'�','�',36}, {'�','�',39}, {'�','�',22}, {'�','�',234}, {'�','�',20}, ++{'�','�',6}, {'�','�',20}, {'�','�',6}, {'�','�',16}, {'�','�',6}, {'�','�',2}, {'�','�',22}, {'�','�',18}, {'�','�',2}, {'�','�',12}, {'�','�',13}, {'�','�',15}, {'�','�',13}, {'�','�',7}, {'�','�',9}, {'�','�',15}, ++{'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',8}, {'�','�',21}, {'�','�',36}, {'�','�',112}, {'�','�',26}, {'�','�',100}, {'�','�',15}, {'�','�',21}, {'�','�',30}, {'�','�',88}, {'�','�',1}, ++{'�','�',87}, {'�','�',25}, {'�','�',47}, {'�','�',117}, {'�','�',55}, {'�','�',133}, {'�','�',42}, {'�','�',162}, {'�','�',131}, {'�','�',17}, {'�','�',6}, {'�','�',7}, {'�','�',11}, {'�','�',69}, {'�','�',20}, {'�','�',2}, ++{'�','�',15}, {'�','�',16}, {'�','�',11}, {'�','�',13}, {'�','�',7}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',20}, {'�','�',7}, {'�','�',9}, ++{'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',8}, {'�','�',1358}, {'�','�',49}, {'�','�',147}, {'�','�',15}, {'�','�',48}, {'�','�',93}, {'�','�',22}, {'�','�',55}, ++{'�','�',678}, {'�','�',28}, {'�','�',81}, {'�','�',22}, {'�','�',151}, {'�','�',1337}, {'�','�',64}, {'�','�',273}, {'�','�',131}, {'�','�',238}, {'�','�',384}, {'�','�',7}, {'�','�',10}, {'�','�',5}, {'�','�',31}, {'�','�',2}, ++{'�','�',2}, {'�','�',13}, {'�','�',25}, {'�','�',20}, {'�','�',2}, {'�','�',25}, {'�','�',5}, {'�','�',14}, {'�','�',3}, {'�','�',1}, {'�','�',19}, {'�','�',12}, {'�','�',2}, {'�','�',14}, {'�','�',31}, {'�','�',20}, ++{'�','�',20}, {'�','�',1}, {'�','�',20}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',4}, {'�','�',19}, {'�','�',758}, {'�','�',34}, {'�','�',102}, {'�','�',35}, {'�','�',55}, ++{'�','�',675}, {'�','�',33}, {'�','�',23}, {'�','�',1300}, {'�','�',1}, {'�','�',114}, {'�','�',21}, {'�','�',45}, {'�','�',111}, {'�','�',947}, {'�','�',83}, {'�','�',18}, {'�','�',320}, {'�','�',61}, {'�','�',264}, {'�','�',5}, ++{'�','�',7}, {'�','�',2}, {'�','�',66}, {'�','�',1}, {'�','�',146}, {'�','�',642}, {'�','�',21}, {'�','�',159}, {'�','�',299}, {'�','�',11}, {'�','�',2}, {'�','�',14}, {'�','�',8}, {'�','�',10}, {'�','�',1}, {'�','�',5}, ++{'�','�',12}, {'�','�',10}, {'�','�',6}, {'�','�',23}, {'�','�',12}, {'�','�',12}, {'�','�',1}, {'�','�',9}, {'�','�',16}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',10}, {'�','�',447}, ++{'�','�',69}, {'�','�',119}, {'�','�',33}, {'�','�',41}, {'�','�',838}, {'�','�',27}, {'�','�',38}, {'�','�',484}, {'�','�',2}, {'�','�',71}, {'�','�',74}, {'�','�',96}, {'�','�',497}, {'�','�',761}, {'�','�',132}, {'�','�',41}, ++{'�','�',137}, {'�','�',74}, {'�','�',411}, {'�','�',5}, {'�','�',13}, {'�','�',4}, {'�','�',46}, {'�','�',3}, {'�','�',3}, {'�','�',207}, {'�','�',33}, {'�','�',17}, {'�','�',82}, {'�','�',11}, {'�','�',5}, {'�','�',8}, ++{'�','�',3}, {'�','�',4}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',2}, {'�','�',9}, {'�','�',2}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',1}, ++{'�','�',2}, {'�','�',2}, {'�','�',1706}, {'�','�',28}, {'�','�',55}, {'�','�',48}, {'�','�',97}, {'�','�',1870}, {'�','�',5}, {'�','�',20}, {'�','�',1319}, {'�','�',84}, {'�','�',7}, {'�','�',11}, {'�','�',331}, {'�','�',1483}, ++{'�','�',54}, {'�','�',27}, {'�','�',133}, {'�','�',165}, {'�','�',480}, {'�','�',14}, {'�','�',4}, {'�','�',65}, {'�','�',55}, {'�','�',3}, {'�','�',31}, {'�','�',385}, {'�','�',196}, {'�','�',7}, {'�','�',30}, {'�','�',396}, ++{'�','�',34}, {'�','�',5}, {'�','�',44}, {'�','�',9}, {'�','�',16}, {'�','�',16}, {'�','�',4}, {'�','�',17}, {'�','�',43}, {'�','�',17}, {'�','�',3}, {'�','�',23}, {'�','�',49}, {'�','�',23}, {'�','�',40}, {'�','�',6}, ++{'�','�',14}, {'�','�',19}, {'�','�',4}, {'�','�',7}, {'�','�',4}, {'�','�',2}, {'�','�',12}, {'�','�',5}, {'�','�',27}, {'�','�',34}, {'�','�',652}, {'�','�',1590}, {'�','�',915}, {'�','�',946}, {'�','�',457}, {'�','�',437}, ++{'�','�',279}, {'�','�',291}, {'�','�',602}, {'�','�',498}, {'�','�',923}, {'�','�',1073}, {'�','�',1281}, {'�','�',241}, {'�','�',588}, {'�','�',921}, {'�','�',1264}, {'�','�',1536}, {'�','�',152}, {'�','�',35}, {'�','�',137}, {'�','�',51}, ++{'�','�',459}, {'�','�',223}, {'�','�',56}, {'�','�',89}, {'�','�',81}, {'�','�',154}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',252}, {'�','�',1}, {'�','�',1}, {'�','�',364}, {'�','�',356}, {'�','�',8}, {'�','�',189}, ++{'�','�',11}, {'�','�',1543}, {'�','�',6}, {'�','�',845}, {'�','�',6}, {'�','�',23}, {'�','�',202}, {'�','�',2}, {'�','�',33}, {'�','�',122}, {'�','�',149}, {'�','�',3}, {'�','�',4}, {'�','�',3}, {'�','�',6}, {'�','�',2}, ++{'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',10}, {'�','�',2}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',3}, {'�','�',6}, {'�','�',1264}, {'�','�',29}, {'�','�',98}, ++{'�','�',53}, {'�','�',129}, {'�','�',936}, {'�','�',41}, {'�','�',20}, {'�','�',777}, {'�','�',1}, {'�','�',54}, {'�','�',29}, {'�','�',39}, {'�','�',181}, {'�','�',1126}, {'�','�',38}, {'�','�',34}, {'�','�',76}, {'�','�',122}, ++{'�','�',355}, {'�','�',8}, {'�','�',36}, {'�','�',23}, {'�','�',9}, {'�','�',49}, {'�','�',5}, {'�','�',169}, {'�','�',98}, {'�','�',7}, {'�','�',59}, {'�','�',159}, {'�','�',6}, {'�','�',4}, {'�','�',2}, {'�','�',2}, ++{'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',306}, ++{'�','�',33}, {'�','�',259}, {'�','�',18}, {'�','�',41}, {'�','�',973}, {'�','�',8}, {'�','�',11}, {'�','�',295}, {'�','�',691}, {'�','�',519}, {'�','�',222}, {'�','�',236}, {'�','�',403}, {'�','�',274}, {'�','�',58}, {'�','�',222}, ++{'�','�',1721}, {'�','�',146}, {'�','�',18}, {'�','�',34}, {'�','�',6}, {'�','�',48}, {'�','�',20}, {'�','�',2}, {'�','�',79}, {'�','�',401}, {'�','�',10}, {'�','�',27}, {'�','�',680}, {'�','�',29}, {'�','�',5}, {'�','�',25}, ++{'�','�',3}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',16}, {'�','�',21}, {'�','�',20}, {'�','�',24}, {'�','�',10}, {'�','�',23}, {'�','�',2}, {'�','�',10}, {'�','�',13}, {'�','�',2}, {'�','�',2}, ++{'�','�',3}, {'�','�',11}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',16}, {'�','�',1225}, {'�','�',56}, {'�','�',436}, {'�','�',25}, {'�','�',67}, {'�','�',864}, {'�','�',17}, {'�','�',33}, {'�','�',758}, {'�','�',1}, ++{'�','�',180}, {'�','�',52}, {'�','�',65}, {'�','�',329}, {'�','�',2625}, {'�','�',119}, {'�','�',616}, {'�','�',337}, {'�','�',117}, {'�','�',398}, {'�','�',1}, {'�','�',24}, {'�','�',10}, {'�','�',87}, {'�','�',3}, {'�','�',4}, ++{'�','�',398}, {'�','�',1292}, {'�','�',37}, {'�','�',13}, {'�','�',103}, {'�','�',22}, {'�','�',4}, {'�','�',19}, {'�','�',4}, {'�','�',20}, {'�','�',3}, {'�','�',2}, {'�','�',25}, {'�','�',9}, {'�','�',6}, {'�','�',12}, ++{'�','�',18}, {'�','�',13}, {'�','�',18}, {'�','�',1}, {'�','�',18}, {'�','�',12}, {'�','�',2}, {'�','�',2}, {'�','�',5}, {'�','�',6}, {'�','�',3}, {'�','�',5}, {'�','�',17}, {'�','�',45}, {'�','�',142}, {'�','�',184}, ++{'�','�',161}, {'�','�',445}, {'�','�',83}, {'�','�',269}, {'�','�',75}, {'�','�',111}, {'�','�',40}, {'�','�',169}, {'�','�',251}, {'�','�',322}, {'�','�',195}, {'�','�',50}, {'�','�',258}, {'�','�',203}, {'�','�',380}, {'�','�',442}, ++{'�','�',24}, {'�','�',5}, {'�','�',97}, {'�','�',241}, {'�','�',302}, {'�','�',37}, {'�','�',19}, {'�','�',156}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',21}, {'�','�',45}, {'�','�',47}, {'�','�',14}, {'�','�',2}, ++{'�','�',24}, {'�','�',20}, {'�','�',4}, {'�','�',5}, {'�','�',8}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',2}, {'�','�',7}, {'�','�',7}, {'�','�',4}, {'�','�',3}, {'�','�',3}, {'�','�',5}, {'�','�',4}, ++{'�','�',2}, {'�','�',1}, {'�','�',10}, {'�','�',5}, {'�','�',7}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',5}, {'�','�',2}, {'�','�',5}, {'�','�',135}, {'�','�',21}, {'�','�',72}, {'�','�',27}, {'�','�',21}, ++{'�','�',25}, {'�','�',4}, {'�','�',13}, {'�','�',75}, {'�','�',16}, {'�','�',43}, {'�','�',49}, {'�','�',83}, {'�','�',450}, {'�','�',70}, {'�','�',19}, {'�','�',39}, {'�','�',27}, {'�','�',42}, {'�','�',3}, {'�','�',4}, ++{'�','�',2}, {'�','�',18}, {'�','�',8}, {'�','�',1}, {'�','�',6}, {'�','�',7}, {'�','�',9}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, ++{'�','�',179}, {'�','�',1}, {'�','�',11}, {'�','�',162}, {'�','�',5}, {'�','�',75}, {'�','�',6}, {'�','�',5}, {'�','�',1}, {'�','�',2}, {'�','�',29}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',41}, ++{'�','�',2}, {'�','�',41}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',294}, {'�','�',1}, {'�','�',6}, {'�','�',874}, {'�','�',319}, ++{'�','�',76}, {'�','�',9}, {'�','�',4}, {'�','�',158}, {'�','�',10}, {'�','�',3}, {'�','�',8}, {'�','�',2}, {'�','�',536}, {'�','�',101}, {'�','�',7}, {'�','�',37}, {'�','�',49}, {'�','�',1}, {'�','�',1}, {'�','�',150}, ++{'�','�',5}, {'�','�',388}, {'�','�',188}, {'�','�',201}, {'�','�',68}, {'�','�',4}, {'�','�',78}, {'�','�',53}, {'�','�',8}, {'�','�',3}, {'�','�',2}, {'�','�',17}, {'�','�',45}, {'�','�',1}, {'�','�',1}, {'�','�',200}, ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',68}, {'�','�',242}, {'�','�',96}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',2}, {'�','�',14}, {'�','�',6}, {'�','�',28}, {'�','�',4}, ++{'�','�',8}, {'�','�',6}, {'�','�',1}, {'�','�',4}, {'�','�',12}, {'�','�',10}, {'�','�',2}, {'�','�',15}, {'�','�',16}, {'�','�',13}, {'�','�',6}, {'�','�',3}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',1}, ++{'�','�',4}, {'�','�',5}, {'�','�',1}, {'�','�',10}, {'�','�',10}, {'�','�',66}, {'�','�',248}, {'�','�',37}, {'�','�',65}, {'�','�',138}, {'�','�',21}, {'�','�',45}, {'�','�',74}, {'�','�',264}, {'�','�',83}, {'�','�',305}, ++{'�','�',164}, {'�','�',141}, {'�','�',34}, {'�','�',231}, {'�','�',61}, {'�','�',189}, {'�','�',148}, {'�','�',35}, {'�','�',1}, {'�','�',146}, {'�','�',3}, {'�','�',63}, {'�','�',80}, {'�','�',1}, {'�','�',9}, {'�','�',10}, ++{'�','�',44}, {'�','�',8}, {'�','�',45}, {'�','�',7}, {'�','�',18}, {'�','�',12}, {'�','�',1}, {'�','�',4}, {'�','�',29}, {'�','�',19}, {'�','�',18}, {'�','�',36}, {'�','�',25}, {'�','�',42}, {'�','�',4}, {'�','�',25}, ++{'�','�',24}, {'�','�',10}, {'�','�',1}, {'�','�',3}, {'�','�',14}, {'�','�',1}, {'�','�',4}, {'�','�',21}, {'�','�',38}, {'�','�',63}, {'�','�',196}, {'�','�',35}, {'�','�',110}, {'�','�',154}, {'�','�',11}, {'�','�',58}, ++{'�','�',194}, {'�','�',1}, {'�','�',324}, {'�','�',36}, {'�','�',124}, {'�','�',321}, {'�','�',92}, {'�','�',164}, {'�','�',41}, {'�','�',302}, {'�','�',152}, {'�','�',48}, {'�','�',5}, {'�','�',20}, {'�','�',15}, {'�','�',88}, ++{'�','�',85}, {'�','�',1}, {'�','�',27}, {'�','�',88}, {'�','�',110}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',6}, {'�','�',9}, {'�','�',6}, {'�','�',3}, {'�','�',8}, {'�','�',4}, ++{'�','�',531}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',10}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',17}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',16}, ++{'�','�',4}, {'�','�',5}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',9}, {'�','�',15}, {'�','�',73}, {'�','�',37}, {'�','�',12}, {'�','�',72}, ++{'�','�',12}, {'�','�',6}, {'�','�',11}, {'�','�',45}, {'�','�',1}, {'�','�',30}, {'�','�',9}, {'�','�',28}, {'�','�',58}, {'�','�',17}, {'�','�',58}, {'�','�',14}, {'�','�',71}, {'�','�',139}, {'�','�',10}, {'�','�',5}, ++{'�','�',5}, {'�','�',3}, {'�','�',42}, {'�','�',11}, {'�','�',27}, {'�','�',2}, {'�','�',10}, {'�','�',20}, {'�','�',18}, {'�','�',5}, {'�','�',28}, {'�','�',2}, {'�','�',23}, {'�','�',6}, {'�','�',2}, {'�','�',3}, ++{'�','�',15}, {'�','�',14}, {'�','�',3}, {'�','�',10}, {'�','�',23}, {'�','�',22}, {'�','�',26}, {'�','�',5}, {'�','�',12}, {'�','�',17}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',10}, ++{'�','�',18}, {'�','�',23}, {'�','�',85}, {'�','�',260}, {'�','�',44}, {'�','�',170}, {'�','�',70}, {'�','�',63}, {'�','�',114}, {'�','�',146}, {'�','�',7}, {'�','�',145}, {'�','�',137}, {'�','�',108}, {'�','�',275}, {'�','�',110}, ++{'�','�',204}, {'�','�',43}, {'�','�',259}, {'�','�',457}, {'�','�',64}, {'�','�',2}, {'�','�',37}, {'�','�',18}, {'�','�',75}, {'�','�',13}, {'�','�',33}, {'�','�',22}, {'�','�',31}, {'�','�',47} ++}; ++ ++static const lng_stat koi[]={ ++{'�','�',10}, {'�','�',15}, {'�','�',73}, {'�','�',3}, {'�','�',72}, {'�','�',12}, {'�','�',5}, {'�','�',12}, {'�','�',5}, {'�','�',45}, {'�','�',1}, {'�','�',30}, {'�','�',9}, {'�','�',28}, {'�','�',58}, {'�','�',17}, ++{'�','�',58}, {'�','�',20}, {'�','�',14}, {'�','�',71}, {'�','�',139}, {'�','�',10}, {'�','�',6}, {'�','�',37}, {'�','�',11}, {'�','�',11}, {'�','�',2}, {'�','�',27}, {'�','�',42}, {'�','�',10}, {'�','�',1}, {'�','�',3}, ++{'�','�',1}, {'�','�',3}, {'�','�',17}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',16}, {'�','�',4}, {'�','�',5}, {'�','�',9}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',10}, ++{'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',238}, {'�','�',69}, {'�','�',257}, {'�','�',24}, {'�','�',548}, {'�','�',328}, {'�','�',38}, {'�','�',218}, {'�','�',228}, {'�','�',225}, {'�','�',194}, {'�','�',1285}, ++{'�','�',1288}, {'�','�',771}, {'�','�',911}, {'�','�',166}, {'�','�',421}, {'�','�',384}, {'�','�',368}, {'�','�',802}, {'�','�',874}, {'�','�',98}, {'�','�',262}, {'�','�',701}, {'�','�',626}, {'�','�',121}, {'�','�',63}, {'�','�',27}, ++{'�','�',300}, {'�','�',56}, {'�','�',18}, {'�','�',26}, {'�','�',12}, {'�','�',1}, {'�','�',9}, {'�','�',4}, {'�','�',53}, {'�','�',59}, {'�','�',9}, {'�','�',21}, {'�','�',32}, {'�','�',27}, {'�','�',52}, {'�','�',22}, ++{'�','�',7}, {'�','�',21}, {'�','�',42}, {'�','�',10}, {'�','�',7}, {'�','�',48}, {'�','�',11}, {'�','�',2}, {'�','�',7}, {'�','�',16}, {'�','�',1}, {'�','�',199}, {'�','�',6}, {'�','�',3}, {'�','�',6}, {'�','�',420}, ++{'�','�',11}, {'�','�',142}, {'�','�',7}, {'�','�',137}, {'�','�',7}, {'�','�',31}, {'�','�',327}, {'�','�',4}, {'�','�',134}, {'�','�',172}, {'�','�',18}, {'�','�',1}, {'�','�',288}, {'�','�',13}, {'�','�',10}, {'�','�',561}, ++{'�','�',2}, {'�','�',1}, {'�','�',7}, {'�','�',32}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',179}, {'�','�',1}, {'�','�',162}, {'�','�',75}, {'�','�',6}, {'�','�',5}, {'�','�',1}, {'�','�',2}, ++{'�','�',29}, {'�','�',4}, {'�','�',2}, {'�','�',4}, {'�','�',4}, {'�','�',41}, {'�','�',11}, {'�','�',41}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, ++{'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',795}, {'�','�',7}, {'�','�',138}, {'�','�',14}, {'�','�',839}, {'�','�',1}, {'�','�',6}, {'�','�',11}, {'�','�',487}, {'�','�',1}, {'�','�',79}, {'�','�',78}, {'�','�',10}, ++{'�','�',289}, {'�','�',613}, {'�','�',17}, {'�','�',68}, {'�','�',195}, {'�','�',62}, {'�','�',41}, {'�','�',417}, {'�','�',8}, {'�','�',165}, {'�','�',200}, {'�','�',111}, {'�','�',7}, {'�','�',6}, {'�','�',6}, {'�','�',1}, ++{'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',24}, {'�','�',44}, {'�','�',463}, ++{'�','�',66}, {'�','�',613}, {'�','�',281}, {'�','�',15}, {'�','�',592}, {'�','�',182}, {'�','�',177}, {'�','�',297}, {'�','�',398}, {'�','�',926}, {'�','�',982}, {'�','�',1512}, {'�','�',206}, {'�','�',524}, {'�','�',87}, {'�','�',1231}, ++{'�','�',1082}, {'�','�',1607}, {'�','�',103}, {'�','�',136}, {'�','�',704}, {'�','�',444}, {'�','�',235}, {'�','�',79}, {'�','�',137}, {'�','�',308}, {'�','�',1}, {'�','�',30}, {'�','�',8}, {'�','�',1}, {'�','�',34}, {'�','�',13}, ++{'�','�',4}, {'�','�',7}, {'�','�',2}, {'�','�',21}, {'�','�',21}, {'�','�',4}, {'�','�',13}, {'�','�',20}, {'�','�',21}, {'�','�',31}, {'�','�',25}, {'�','�',2}, {'�','�',13}, {'�','�',10}, {'�','�',7}, {'�','�',1}, ++{'�','�',33}, {'�','�',6}, {'�','�',2}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',21}, {'�','�',45}, {'�','�',6}, {'�','�',47}, {'�','�',14}, {'�','�',2}, {'�','�',24}, {'�','�',20}, {'�','�',4}, {'�','�',5}, ++{'�','�',8}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',210}, {'�','�',18}, {'�','�',191}, {'�','�',83}, {'�','�',3}, {'�','�',1}, {'�','�',103}, {'�','�',20}, {'�','�',205}, {'�','�',3}, {'�','�',62}, ++{'�','�',1340}, {'�','�',7}, {'�','�',4}, {'�','�',180}, {'�','�',13}, {'�','�',4}, {'�','�',88}, {'�','�',6}, {'�','�',2}, {'�','�',1}, {'�','�',15}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',4}, ++{'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',135}, {'�','�',21}, {'�','�',2}, {'�','�',21}, {'�','�',25}, ++{'�','�',3}, {'�','�',27}, {'�','�',4}, {'�','�',75}, {'�','�',16}, {'�','�',43}, {'�','�',49}, {'�','�',83}, {'�','�',450}, {'�','�',70}, {'�','�',9}, {'�','�',19}, {'�','�',39}, {'�','�',27}, {'�','�',42}, {'�','�',4}, ++{'�','�',72}, {'�','�',6}, {'�','�',1}, {'�','�',13}, {'�','�',8}, {'�','�',7}, {'�','�',18}, {'�','�',7}, {'�','�',4}, {'�','�',3}, {'�','�',5}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',10}, {'�','�',5}, ++{'�','�',7}, {'�','�',5}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',7}, {'�','�',5}, {'�','�',2}, {'�','�',3}, {'�','�',22}, {'�','�',90}, {'�','�',242}, {'�','�',123}, {'�','�',435}, {'�','�',280}, {'�','�',17}, ++{'�','�',153}, {'�','�',324}, {'�','�',301}, {'�','�',216}, {'�','�',492}, {'�','�',888}, {'�','�',573}, {'�','�',958}, {'�','�',216}, {'�','�',335}, {'�','�',234}, {'�','�',275}, {'�','�',668}, {'�','�',949}, {'�','�',108}, {'�','�',87}, ++{'�','�',658}, {'�','�',388}, {'�','�',134}, {'�','�',39}, {'�','�',36}, {'�','�',363}, {'�','�',39}, {'�','�',10}, {'�','�',2}, {'�','�',25}, {'�','�',11}, {'�','�',3}, {'�','�',5}, {'�','�',4}, {'�','�',30}, {'�','�',24}, ++{'�','�',5}, {'�','�',36}, {'�','�',39}, {'�','�',25}, {'�','�',49}, {'�','�',28}, {'�','�',3}, {'�','�',22}, {'�','�',25}, {'�','�',8}, {'�','�',2}, {'�','�',46}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',4}, ++{'�','�',21}, {'�','�',36}, {'�','�',11}, {'�','�',100}, {'�','�',15}, {'�','�',6}, {'�','�',26}, {'�','�',7}, {'�','�',88}, {'�','�',1}, {'�','�',87}, {'�','�',25}, {'�','�',47}, {'�','�',117}, {'�','�',55}, {'�','�',133}, ++{'�','�',16}, {'�','�',42}, {'�','�',162}, {'�','�',131}, {'�','�',17}, {'�','�',21}, {'�','�',112}, {'�','�',30}, {'�','�',20}, {'�','�',15}, {'�','�',2}, {'�','�',69}, {'�','�',20}, {'�','�',6}, {'�','�',16}, {'�','�',6}, ++{'�','�',1}, {'�','�',6}, {'�','�',1}, {'�','�',22}, {'�','�',18}, {'�','�',2}, {'�','�',12}, {'�','�',13}, {'�','�',15}, {'�','�',13}, {'�','�',8}, {'�','�',7}, {'�','�',9}, {'�','�',15}, {'�','�',3}, {'�','�',20}, ++{'�','�',2}, {'�','�',7}, {'�','�',1}, {'�','�',1358}, {'�','�',49}, {'�','�',5}, {'�','�',48}, {'�','�',93}, {'�','�',7}, {'�','�',15}, {'�','�',10}, {'�','�',678}, {'�','�',28}, {'�','�',81}, {'�','�',22}, {'�','�',151}, ++{'�','�',1337}, {'�','�',64}, {'�','�',25}, {'�','�',273}, {'�','�',131}, {'�','�',238}, {'�','�',384}, {'�','�',22}, {'�','�',147}, {'�','�',2}, {'�','�',55}, {'�','�',2}, {'�','�',13}, {'�','�',31}, {'�','�',11}, {'�','�',1}, ++{'�','�',4}, {'�','�',2}, {'�','�',7}, {'�','�',1}, {'�','�',8}, {'�','�',9}, {'�','�',2}, {'�','�',5}, {'�','�',20}, {'�','�',7}, {'�','�',9}, {'�','�',8}, {'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',1}, ++{'�','�',13}, {'�','�',5}, {'�','�',2}, {'�','�',4}, {'�','�',159}, {'�','�',758}, {'�','�',34}, {'�','�',2}, {'�','�',55}, {'�','�',675}, {'�','�',5}, {'�','�',35}, {'�','�',7}, {'�','�',1300}, {'�','�',1}, {'�','�',114}, ++{'�','�',21}, {'�','�',45}, {'�','�',111}, {'�','�',947}, {'�','�',83}, {'�','�',299}, {'�','�',18}, {'�','�',320}, {'�','�',61}, {'�','�',264}, {'�','�',33}, {'�','�',102}, {'�','�',642}, {'�','�',146}, {'�','�',23}, {'�','�',1}, ++{'�','�',21}, {'�','�',66}, {'�','�',20}, {'�','�',2}, {'�','�',1}, {'�','�',14}, {'�','�',3}, {'�','�',1}, {'�','�',5}, {'�','�',1}, {'�','�',19}, {'�','�',12}, {'�','�',2}, {'�','�',14}, {'�','�',31}, {'�','�',20}, ++{'�','�',20}, {'�','�',19}, {'�','�',1}, {'�','�',20}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',25}, {'�','�',4}, {'�','�',2}, {'�','�',447}, {'�','�',69}, {'�','�',4}, {'�','�',41}, {'�','�',838}, {'�','�',5}, ++{'�','�',33}, {'�','�',13}, {'�','�',484}, {'�','�',2}, {'�','�',71}, {'�','�',74}, {'�','�',96}, {'�','�',497}, {'�','�',761}, {'�','�',132}, {'�','�',82}, {'�','�',41}, {'�','�',137}, {'�','�',74}, {'�','�',411}, {'�','�',27}, ++{'�','�',119}, {'�','�',33}, {'�','�',207}, {'�','�',38}, {'�','�',3}, {'�','�',17}, {'�','�',3}, {'�','�',46}, {'�','�',11}, {'�','�',2}, {'�','�',10}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',12}, {'�','�',10}, ++{'�','�',6}, {'�','�',23}, {'�','�',12}, {'�','�',12}, {'�','�',10}, {'�','�',1}, {'�','�',9}, {'�','�',16}, {'�','�',2}, {'�','�',14}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',30}, {'�','�',1706}, ++{'�','�',28}, {'�','�',65}, {'�','�',97}, {'�','�',1870}, {'�','�',14}, {'�','�',48}, {'�','�',4}, {'�','�',1319}, {'�','�',84}, {'�','�',7}, {'�','�',11}, {'�','�',331}, {'�','�',1483}, {'�','�',54}, {'�','�',396}, {'�','�',27}, ++{'�','�',133}, {'�','�',165}, {'�','�',480}, {'�','�',5}, {'�','�',55}, {'�','�',196}, {'�','�',385}, {'�','�',20}, {'�','�',3}, {'�','�',7}, {'�','�',31}, {'�','�',55}, {'�','�',11}, {'�','�',1}, {'�','�',3}, {'�','�',4}, ++{'�','�',8}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',2}, {'�','�',9}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',5}, {'�','�',1}, {'�','�',2}, ++{'�','�',6}, {'�','�',81}, {'�','�',34}, {'�','�',652}, {'�','�',51}, {'�','�',946}, {'�','�',457}, {'�','�',35}, {'�','�',915}, {'�','�',137}, {'�','�',291}, {'�','�',602}, {'�','�',498}, {'�','�',923}, {'�','�',1073}, {'�','�',1281}, ++{'�','�',241}, {'�','�',588}, {'�','�',154}, {'�','�',921}, {'�','�',1264}, {'�','�',1536}, {'�','�',152}, {'�','�',437}, {'�','�',1590}, {'�','�',279}, {'�','�',223}, {'�','�',89}, {'�','�',56}, {'�','�',459}, {'�','�',34}, {'�','�',5}, ++{'�','�',2}, {'�','�',16}, {'�','�',16}, {'�','�',7}, {'�','�',9}, {'�','�',4}, {'�','�',43}, {'�','�',17}, {'�','�',3}, {'�','�',23}, {'�','�',49}, {'�','�',23}, {'�','�',40}, {'�','�',27}, {'�','�',6}, {'�','�',14}, ++{'�','�',19}, {'�','�',4}, {'�','�',4}, {'�','�',44}, {'�','�',17}, {'�','�',5}, {'�','�',12}, {'�','�',252}, {'�','�',1}, {'�','�',364}, {'�','�',356}, {'�','�',8}, {'�','�',189}, {'�','�',11}, {'�','�',1543}, {'�','�',6}, ++{'�','�',149}, {'�','�',845}, {'�','�',6}, {'�','�',23}, {'�','�',202}, {'�','�',1}, {'�','�',122}, {'�','�',33}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',31}, {'�','�',23}, {'�','�',85}, {'�','�',18}, ++{'�','�',170}, {'�','�',70}, {'�','�',2}, {'�','�',44}, {'�','�',37}, {'�','�',146}, {'�','�',7}, {'�','�',145}, {'�','�',137}, {'�','�',108}, {'�','�',275}, {'�','�',110}, {'�','�',204}, {'�','�',47}, {'�','�',43}, {'�','�',259}, ++{'�','�',457}, {'�','�',64}, {'�','�',63}, {'�','�',260}, {'�','�',114}, {'�','�',13}, {'�','�',22}, {'�','�',33}, {'�','�',75}, {'�','�',18}, {'�','�',5}, {'�','�',1}, {'�','�',23}, {'�','�',6}, {'�','�',2}, {'�','�',2}, ++{'�','�',15}, {'�','�',14}, {'�','�',3}, {'�','�',10}, {'�','�',23}, {'�','�',22}, {'�','�',26}, {'�','�',18}, {'�','�',5}, {'�','�',12}, {'�','�',17}, {'�','�',1}, {'�','�',2}, {'�','�',28}, {'�','�',3}, {'�','�',2}, ++{'�','�',10}, {'�','�',7}, {'�','�',59}, {'�','�',1264}, {'�','�',29}, {'�','�',23}, {'�','�',129}, {'�','�',936}, {'�','�',8}, {'�','�',53}, {'�','�',36}, {'�','�',777}, {'�','�',1}, {'�','�',54}, {'�','�',29}, {'�','�',39}, ++{'�','�',181}, {'�','�',1126}, {'�','�',38}, {'�','�',159}, {'�','�',34}, {'�','�',76}, {'�','�',122}, {'�','�',355}, {'�','�',41}, {'�','�',98}, {'�','�',98}, {'�','�',169}, {'�','�',20}, {'�','�',49}, {'�','�',7}, {'�','�',5}, ++{'�','�',9}, {'�','�',3}, {'�','�',4}, {'�','�',6}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',5}, {'�','�',8}, {'�','�',10}, {'�','�',6}, {'�','�',2}, {'�','�',4}, {'�','�',3}, ++{'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',27}, {'�','�',306}, {'�','�',33}, {'�','�',6}, {'�','�',41}, {'�','�',973}, {'�','�',18}, {'�','�',18}, {'�','�',34}, {'�','�',295}, {'�','�',691}, {'�','�',519}, {'�','�',222}, ++{'�','�',236}, {'�','�',403}, {'�','�',274}, {'�','�',680}, {'�','�',58}, {'�','�',222}, {'�','�',1721}, {'�','�',146}, {'�','�',8}, {'�','�',259}, {'�','�',401}, {'�','�',79}, {'�','�',11}, {'�','�',20}, {'�','�',10}, {'�','�',2}, ++{'�','�',48}, {'�','�',6}, {'�','�',2}, {'�','�',2}, {'�','�',4}, {'�','�',2}, {'�','�',3}, {'�','�',4}, {'�','�',5}, {'�','�',3}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, ++{'�','�',4}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',13}, {'�','�',1225}, {'�','�',56}, {'�','�',10}, {'�','�',67}, {'�','�',864}, {'�','�',1}, {'�','�',25}, {'�','�',24}, {'�','�',758}, {'�','�',1}, {'�','�',180}, ++{'�','�',52}, {'�','�',65}, {'�','�',329}, {'�','�',2625}, {'�','�',119}, {'�','�',103}, {'�','�',616}, {'�','�',337}, {'�','�',117}, {'�','�',398}, {'�','�',17}, {'�','�',436}, {'�','�',1292}, {'�','�',398}, {'�','�',33}, {'�','�',3}, ++{'�','�',37}, {'�','�',4}, {'�','�',87}, {'�','�',1}, {'�','�',29}, {'�','�',5}, {'�','�',11}, {'�','�',9}, {'�','�',2}, {'�','�',3}, {'�','�',3}, {'�','�',16}, {'�','�',21}, {'�','�',20}, {'�','�',24}, {'�','�',10}, ++{'�','�',23}, {'�','�',16}, {'�','�',2}, {'�','�',10}, {'�','�',13}, {'�','�',2}, {'�','�',2}, {'�','�',25}, {'�','�',5}, {'�','�',1}, {'�','�',3}, {'�','�',11}, {'�','�',156}, {'�','�',45}, {'�','�',142}, {'�','�',445}, ++{'�','�',83}, {'�','�',5}, {'�','�',161}, {'�','�',97}, {'�','�',111}, {'�','�',40}, {'�','�',169}, {'�','�',251}, {'�','�',322}, {'�','�',195}, {'�','�',50}, {'�','�',258}, {'�','�',31}, {'�','�',203}, {'�','�',380}, {'�','�',442}, ++{'�','�',24}, {'�','�',269}, {'�','�',184}, {'�','�',75}, {'�','�',302}, {'�','�',19}, {'�','�',37}, {'�','�',241}, {'�','�',22}, {'�','�',4}, {'�','�',20}, {'�','�',3}, {'�','�',2}, {'�','�',4}, {'�','�',5}, {'�','�',25}, ++{'�','�',9}, {'�','�',6}, {'�','�',12}, {'�','�',18}, {'�','�',13}, {'�','�',18}, {'�','�',17}, {'�','�',1}, {'�','�',18}, {'�','�',12}, {'�','�',2}, {'�','�',19}, {'�','�',2}, {'�','�',3}, {'�','�',5}, {'�','�',6}, ++{'�','�',187}, {'�','�',4}, {'�','�',145}, {'�','�',690}, {'�','�',1}, {'�','�',1}, {'�','�',214}, {'�','�',35}, {'�','�',1}, {'�','�',8}, {'�','�',96}, {'�','�',13}, {'�','�',14}, {'�','�',7}, {'�','�',7}, {'�','�',4}, ++{'�','�',8}, {'�','�',69}, {'�','�',4}, {'�','�',9}, {'�','�',6}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',989}, {'�','�',26}, ++{'�','�',11}, {'�','�',74}, {'�','�',911}, {'�','�',1}, {'�','�',43}, {'�','�',9}, {'�','�',387}, {'�','�',1}, {'�','�',105}, {'�','�',122}, {'�','�',65}, {'�','�',231}, {'�','�',1220}, {'�','�',118}, {'�','�',48}, {'�','�',97}, ++{'�','�',851}, {'�','�',123}, {'�','�',116}, {'�','�',26}, {'�','�',62}, {'�','�',33}, {'�','�',518}, {'�','�',88}, {'�','�',53}, {'�','�',44}, {'�','�',2}, {'�','�',47}, {'�','�',11}, {'�','�',1}, {'�','�',1}, {'�','�',5}, ++{'�','�',5}, {'�','�',7}, {'�','�',8}, {'�','�',6}, {'�','�',14}, {'�','�',2}, {'�','�',9}, {'�','�',13}, {'�','�',17}, {'�','�',51}, {'�','�',4}, {'�','�',7}, {'�','�',18}, {'�','�',4}, {'�','�',2}, {'�','�',6}, ++{'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',88}, {'�','�',38}, {'�','�',63}, {'�','�',15}, {'�','�',110}, {'�','�',154}, {'�','�',5}, {'�','�',35}, {'�','�',20}, {'�','�',194}, {'�','�',1}, {'�','�',324}, {'�','�',36}, ++{'�','�',124}, {'�','�',321}, {'�','�',92}, {'�','�',164}, {'�','�',110}, {'�','�',41}, {'�','�',302}, {'�','�',152}, {'�','�',48}, {'�','�',11}, {'�','�',196}, {'�','�',58}, {'�','�',85}, {'�','�',27}, {'�','�',1}, {'�','�',88}, ++{'�','�',44}, {'�','�',8}, {'�','�',18}, {'�','�',12}, {'�','�',1}, {'�','�',7}, {'�','�',3}, {'�','�',29}, {'�','�',19}, {'�','�',18}, {'�','�',36}, {'�','�',25}, {'�','�',42}, {'�','�',21}, {'�','�',4}, {'�','�',25}, ++{'�','�',24}, {'�','�',10}, {'�','�',1}, {'�','�',45}, {'�','�',4}, {'�','�',1}, {'�','�',4}, {'�','�',14}, {'�','�',10}, {'�','�',66}, {'�','�',3}, {'�','�',65}, {'�','�',138}, {'�','�',1}, {'�','�',37}, {'�','�',146}, ++{'�','�',74}, {'�','�',264}, {'�','�',83}, {'�','�',305}, {'�','�',164}, {'�','�',141}, {'�','�',34}, {'�','�',231}, {'�','�',10}, {'�','�',61}, {'�','�',189}, {'�','�',148}, {'�','�',35}, {'�','�',21}, {'�','�',248}, {'�','�',45}, ++{'�','�',80}, {'�','�',9}, {'�','�',1}, {'�','�',63}, {'�','�',1}, {'�','�',14}, {'�','�',6}, {'�','�',8}, {'�','�',6}, {'�','�',1}, {'�','�',4}, {'�','�',12}, {'�','�',10}, {'�','�',2}, {'�','�',15}, {'�','�',16}, ++{'�','�',13}, {'�','�',6}, {'�','�',10}, {'�','�',3}, {'�','�',7}, {'�','�',10}, {'�','�',1}, {'�','�',1}, {'�','�',28}, {'�','�',4}, {'�','�',5}, {'�','�',4}, {'�','�',889}, {'�','�',39}, {'�','�',174}, {'�','�',56}, ++{'�','�',67}, {'�','�',3}, {'�','�',113}, {'�','�',16}, {'�','�',33}, {'�','�',64}, {'�','�',302}, {'�','�',109}, {'�','�',31}, {'�','�',43}, {'�','�',53}, {'�','�',21}, {'�','�',7}, {'�','�',109}, {'�','�',18}, {'�','�',147}, ++{'�','�',20}, {'�','�',71}, {'�','�',11}, {'�','�',4}, {'�','�',4}, {'�','�',11}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',4}, ++{'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',150}, {'�','�',1}, {'�','�',388}, {'�','�',1}, {'�','�',188}, {'�','�',201}, {'�','�',68}, {'�','�',4}, {'�','�',78}, {'�','�',53}, {'�','�',8}, {'�','�',3}, ++{'�','�',2}, {'�','�',17}, {'�','�',45}, {'�','�',5}, {'�','�',200}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',6}, {'�','�',9}, {'�','�',6}, {'�','�',3}, ++{'�','�',8}, {'�','�',4}, {'�','�',531}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',68}, {'�','�',242}, {'�','�',96}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',20}, {'�','�',2}, {'�','�',1}, ++{'�','�',1}, {'�','�',294}, {'�','�',1}, {'�','�',874}, {'�','�',319}, {'�','�',76}, {'�','�',9}, {'�','�',4}, {'�','�',158}, {'�','�',10}, {'�','�',3}, {'�','�',1}, {'�','�',8}, {'�','�',2}, {'�','�',536}, {'�','�',101}, ++{'�','�',6}, {'�','�',49}, {'�','�',37}, {'�','�',7}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, ++{'�','�',6}, {'�','�',7}, {'�','�',23}, {'�','�',1}, {'�','�',5}, {'�','�',6}, {'�','�',4}, {'�','�',1}, {'�','�',27}, {'�','�',15}, {'�','�',10}, {'�','�',25}, {'�','�',21}, {'�','�',51}, {'�','�',20}, {'�','�',12}, ++{'�','�',13}, {'�','�',31}, {'�','�',15}, {'�','�',2}, {'�','�',60}, {'�','�',6}, {'�','�',8}, {'�','�',17}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',8}, {'�','�',2}, ++{'�','�',2}, {'�','�',1}, {'�','�',4}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',3}, {'�','�',16}, {'�','�',1}, {'�','�',4}, {'�','�',3}, {'�','�',25}, {'�','�',10}, ++{'�','�',9}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',167}, {'�','�',17}, {'�','�',3}, {'�','�',7}, ++{'�','�',3}, {'�','�',1}, {'�','�',20}, {'�','�',9}, {'�','�',19}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',5}, {'�','�',2}, {'�','�',19}, {'�','�',69}, {'�','�',9}, ++{'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',1}, {'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',16}, {'�','�',1}, {'�','�',6}, {'�','�',16}, {'�','�',26}, {'�','�',4}, {'�','�',3}, {'�','�',1}, ++{'�','�',31}, {'�','�',7}, {'�','�',2}, {'�','�',2}, {'�','�',8}, {'�','�',3}, {'�','�',20}, {'�','�',14}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',8}, {'�','�',20}, {'�','�',7}, {'�','�',2}, {'�','�',7}, ++{'�','�',3}, {'�','�',3}, {'�','�',27}, {'�','�',15}, {'�','�',11}, {'�','�',21}, {'�','�',26}, {'�','�',21}, {'�','�',12}, {'�','�',4}, {'�','�',21}, {'�','�',37}, {'�','�',5}, {'�','�',1}, {'�','�',78}, {'�','�',17}, ++{'�','�',1}, {'�','�',2}, {'�','�',8}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',2}, {'�','�',1}, {'�','�',100}, {'�','�',4}, {'�','�',1}, {'�','�',14}, {'�','�',4}, {'�','�',62}, {'�','�',2}, {'�','�',24}, ++{'�','�',10}, {'�','�',62}, {'�','�',3}, {'�','�',3}, {'�','�',7}, {'�','�',11}, {'�','�',4}, {'�','�',12}, {'�','�',1}, {'�','�',15}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',31}, {'�','�',10}, {'�','�',1}, ++{'�','�',44}, {'�','�',1}, {'�','�',19}, {'�','�',93}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',29}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',56}, {'�','�',111}, {'�','�',47}, {'�','�',2}, ++{'�','�',98}, {'�','�',100}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',18}, {'�','�',20}, {'�','�',4}, {'�','�',2}, {'�','�',7}, {'�','�',9}, {'�','�',2}, {'�','�',96}, {'�','�',5}, {'�','�',17}, {'�','�',16}, ++{'�','�',21}, {'�','�',35}, {'�','�',1}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',13}, {'�','�',2}, {'�','�',6}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',18}, {'�','�',148}, ++{'�','�',11}, {'�','�',9}, {'�','�',186}, {'�','�',43}, {'�','�',38}, {'�','�',4}, {'�','�',3}, {'�','�',4}, {'�','�',8}, {'�','�',9}, {'�','�',5}, {'�','�',3}, {'�','�',4}, {'�','�',10}, {'�','�',5}, {'�','�',6}, ++{'�','�',28}, {'�','�',24}, {'�','�',39}, {'�','�',4}, {'�','�',30}, {'�','�',7}, {'�','�',5}, {'�','�',2}, {'�','�',49}, {'�','�',12}, {'�','�',3}, {'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',22}, {'�','�',13}, ++{'�','�',5}, {'�','�',13}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',19}, {'�','�',1}, {'�','�',1}, {'�','�',56}, {'�','�',2}, {'�','�',16}, {'�','�',22}, {'�','�',11}, {'�','�',12}, {'�','�',6}, {'�','�',26}, ++{'�','�',6}, {'�','�',1}, {'�','�',6}, {'�','�',3}, {'�','�',35}, {'�','�',4}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',2}, {'�','�',3}, {'�','�',1}, {'�','�',79}, {'�','�',18}, {'�','�',17}, ++{'�','�',34}, {'�','�',3}, {'�','�',11}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',55}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',3}, {'�','�',2}, {'�','�',1}, {'�','�',2}, ++{'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',2}, {'�','�',9}, {'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',9}, {'�','�',8}, {'�','�',4}, {'�','�',3}, {'�','�',4}, {'�','�',1}, {'�','�',1}, {'�','�',5}, ++{'�','�',1}, {'�','�',14}, {'�','�',5}, {'�','�',2}, {'�','�',1}, {'�','�',15}, {'�','�',5}, {'�','�',98}, {'�','�',3}, {'�','�',7}, {'�','�',5}, {'�','�',8}, {'�','�',7}, {'�','�',6}, {'�','�',107}, {'�','�',18}, ++{'�','�',2}, {'�','�',74}, {'�','�',6}, {'�','�',1}, {'�','�',2}, {'�','�',39}, {'�','�',5}, {'�','�',8}, {'�','�',3}, {'�','�',1}, {'�','�',1}, {'�','�',2}, {'�','�',1}, {'�','�',3}, {'�','�',8}, {'�','�',2}, ++{'�','�',2}, {'�','�',31}, {'�','�',1}, {'�','�',2}, {'�','�',23}, {'�','�',2}, {'�','�',12}, {'�','�',3}, {'�','�',1}, {'�','�',3}, {'�','�',7}, {'�','�',8}, {'�','�',8}, {'�','�',7}, {'�','�',1}, {'�','�',1}, ++{'�','�',5}, {'�','�',1}, {'�','�',1}, {'�','�',4}, {'�','�',1}, {'�','�',10}, {'�','�',1}, {'�','�',61}, {'�','�',1}, {'�','�',1}, {'�','�',46}, {'�','�',60}, {'�','�',8}, {'�','�',1} ++}; ++ ++static int ++is_win_charset(char *txt, int len){ ++ int j,ki=0,wi=0,d=839,l,koi_stat=0,win_stat=0,ws,ks; ++ unsigned char a,b; ++ l=strlen(txt); ++ if ((len)&&(l>len)) l=len; ++ for(j=0;j<l-1;j++){ ++ a=txt[j]; ++ b=txt[j+1]; ++ //skip bottom half of table ++ if(a<128 || b<128) ++ continue; ++ wi=839; ++ ki=839; ++ ws=0; ++ ks=0; ++ d=839; ++ //binary search through lng_stats ++ do{ ++ d>>=1; ++ if(!ws){ ++ if(a==win[wi].a){ ++ if(b==win[wi].b){ ++ ws=1; ++ win_stat+=win[wi].rate; ++ }else if(b<win[wi].b){ ++ wi-=d; ++ }else{ //b>win[wi].b ++ wi+=d; ++ } ++ }else if(a<win[wi].a){ ++ wi-=d; ++ }else{ //a>win[wi].a ++ wi+=d; ++ } ++ } ++ if(!ks){ ++ if(a==koi[ki].a){ ++ if(b==koi[ki].b){ ++ ks=1; ++ koi_stat+=koi[ki].rate; ++ }else if(b<koi[ki].b){ ++ ki-=d; ++ }else{ //b>win[wi].b ++ ki+=d; ++ } ++ }else if(a<koi[ki].a){ ++ ki-=d; ++ }else{ //a>win[wi].a ++ ki+=d; ++ } ++ } ++ }while(d); ++ } ++// fprintf(stderr,"\nwin %i, koi %i\n",win_stat,koi_stat); ++ if(win_stat>koi_stat) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Only CP1251 and KOI8-R still */ ++int autocharset_russian(char *buf,int len) { ++ if (is_win_charset(buf,len)) return 0; ++ return 1; ++} +diff -Naur xmms-1.2.8/libxmms/charset.c xmms-1.2.8-new/libxmms/charset.c +--- xmms-1.2.8/libxmms/charset.c 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/libxmms/charset.c 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,512 @@ ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <errno.h> ++#include <glib.h> ++#include <iconv.h> ++#include <assert.h> ++ ++#include "charset.h" ++#include "charset_config.h" ++ ++#define LOCALE_STRING_LENGTH 64 ++static char charset_default[LOCALE_STRING_LENGTH]; ++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE]; ++ ++static int xmms_charset_changed = -1; ++ ++static int xmms_charset_id3; ++static int xmms_charset_output; ++static int xmms_charset_fs; ++static int xmms_charset_pl; ++ ++static iconv_t iconv_id3=(iconv_t)-1; /* id3 to output */ ++static iconv_t iconv_output=(iconv_t)-1; /* output to id3 */ ++static iconv_t iconv_fs=(iconv_t)-1; /* fs to output */ ++static iconv_t iconv_utf=(iconv_t)-1; /* id3 to UTF-8 */ ++static iconv_t iconv_pl2fs=(iconv_t)-1; /* playlist to fs */ ++static iconv_t iconv_fs2pl=(iconv_t)-1; /* fs to playlist */ ++ ++static int xmms_autocharset_mode = 0; ++static int xmms_autocharset_changed = 0; ++static iconv_t *xmms_autocharset = NULL; ++static iconv_t *xmms_autocharset_id2id = NULL; ++static iconv_t *xmms_autocharset_id2fs = NULL; ++static int xmms_autocharset_number = -1; ++ ++static int recode_fsout; ++static int recode_fs2utf; ++#include "charset_auto_russian.h" ++ ++extern int errno; ++ ++/***************************************************************************** ++************************ Auto Recoding *************************************** ++*****************************************************************************/ ++static int xmms_autocharset_number_charsets(int m) { ++ const char **encodings; ++ int i; ++ ++ if (!m) m=xmms_autocharset_mode; ++ encodings=autocharset_list[m].encodings; ++ for (i=0;encodings[i];i++); ++ return i; ++} ++ ++ ++static void xmms_autocharset_iconv_close() { ++ int i; ++ ++ for (i=0;i<xmms_autocharset_number;i++) { ++ if ((xmms_autocharset)&&(xmms_autocharset[i] != (iconv_t)-1)&&(xmms_autocharset[i] != (iconv_t)-2)) ++ iconv_close(xmms_autocharset[i]); ++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_id2id[i] != (iconv_t)-1)&&(xmms_autocharset_id2id[i] != (iconv_t)-2)) ++ iconv_close(xmms_autocharset_id2id[i]); ++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_id2fs[i] != (iconv_t)-1)&&(xmms_autocharset_id2fs[i] != (iconv_t)-2)) ++ iconv_close(xmms_autocharset_id2fs[i]); ++ } ++ if (xmms_autocharset) free(xmms_autocharset); ++ if (xmms_autocharset_id2id) free(xmms_autocharset_id2id); ++ if (xmms_autocharset_id2fs) free(xmms_autocharset_id2fs); ++ ++ xmms_autocharset=NULL; ++ xmms_autocharset_id2id=NULL; ++ xmms_autocharset_id2fs=NULL; ++ xmms_autocharset_number = -1; ++} ++ ++void xmms_autocharset_new_mode(int m) { ++ xmms_autocharset_mode=m; ++ xmms_autocharset_changed=1; ++} ++ ++void xmms_autocharset_init(int m) { ++ xmms_autocharset_mode=m; ++ xmms_autocharset_changed=1; ++} ++ ++void xmms_autocharset_free() { ++ xmms_autocharset_iconv_close(); ++} ++ ++static void xmms_autocharset_iconv_open() { ++ const char **encodings; ++ const char *output, *id3, *fs; ++ int i; ++ ++ if ((!xmms_autocharset_changed)&&(!xmms_charset_changed)) return; ++ xmms_autocharset_iconv_close(); ++ xmms_autocharset_changed=0; ++ ++ xmms_autocharset_number = xmms_autocharset_number_charsets(0); ++ if (!xmms_autocharset_number) return; ++ ++ xmms_autocharset = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t)); ++ xmms_autocharset_id2id = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t)); ++ xmms_autocharset_id2fs = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t)); ++ if ((!xmms_autocharset)||(!xmms_autocharset_id2id)||(!xmms_autocharset_id2fs)) { ++ if (xmms_autocharset) { ++ free(xmms_autocharset); ++ xmms_autocharset=NULL; ++ } ++ if (xmms_autocharset_id2id) { ++ free(xmms_autocharset_id2id); ++ xmms_autocharset_id2id=NULL; ++ } ++ if (xmms_autocharset_id2fs) { ++ free(xmms_autocharset_id2fs); ++ xmms_autocharset_id2fs=NULL; ++ } ++ xmms_autocharset_number = -1; ++ perror("iconv: allocate memory"); ++ return; ++ } ++ ++ ++ if (xmms_charset_output) output=charset_list[xmms_charset_output]; ++ else output=charset_default; ++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3]; ++ else id3=charset_default; ++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs]; ++ else fs=output; ++ ++ encodings = autocharset_list[xmms_autocharset_mode].encodings; ++ ++ for (i=0;encodings[i];i++) { ++ if (strcmp(output,encodings[i])) ++ xmms_autocharset[i] = iconv_open(output,encodings[i]); ++ else ++ xmms_autocharset[i] = (iconv_t)-2; ++ ++ if (strcmp(id3,encodings[i])) ++ xmms_autocharset_id2id[i] = iconv_open(id3,encodings[i]); ++ else ++ xmms_autocharset_id2id[i] = (iconv_t)-2; ++ ++ if (strcmp(fs,encodings[i])) ++ xmms_autocharset_id2fs[i] = iconv_open(fs,encodings[i]); ++ else ++ xmms_autocharset_id2fs[i] = (iconv_t)-2; ++ } ++} ++ ++ ++static iconv_t xmms_autocharset_get(char *buf, int len) { ++ int i; ++ xmms_autocharset_iconv_open(); ++ if ((xmms_autocharset)&&(xmms_autocharset_number>0)) { ++ i=autocharset_list[xmms_autocharset_mode].func(buf,len); ++ if (i<xmms_autocharset_number) return xmms_autocharset[i]; ++ } ++ return (iconv_t)-1; ++} ++ ++static iconv_t xmms_autocharset_get_id2id(char *buf, int len) { ++ int i; ++ xmms_autocharset_iconv_open(); ++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_number>0)) { ++ i=autocharset_list[xmms_autocharset_mode].func(buf,len); ++ if (i<xmms_autocharset_number) return xmms_autocharset_id2id[i]; ++ } ++ return (iconv_t)-1; ++} ++ ++static iconv_t xmms_autocharset_get_id2fs(char *buf, int len) { ++ int i; ++ xmms_autocharset_iconv_open(); ++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_number>0)) { ++ i=autocharset_list[xmms_autocharset_mode].func(buf,len); ++ if (i<xmms_autocharset_number) return xmms_autocharset_id2fs[i]; ++ } ++ return (iconv_t)-1; ++} ++ ++/***************************************************************************** ++************************ Recoding ******************************************** ++*****************************************************************************/ ++ ++static void xmms_charset_iconv_close() { ++ if (iconv_id3 != (iconv_t)-1) { ++ if (iconv_close(iconv_id3)<0) perror("iconv close: id3"); ++ iconv_id3 = (iconv_t)-1; ++ } ++ if (iconv_output != (iconv_t)-1) { ++ if (iconv_close(iconv_output)<0) perror("iconv close: output"); ++ iconv_output = (iconv_t)-1; ++ } ++ if (iconv_fs != (iconv_t)-1) { ++ if (iconv_close(iconv_fs)<0) perror("iconv close: fs"); ++ iconv_fs = (iconv_t)-1; ++ } ++ if (iconv_fs2pl != (iconv_t)-1) { ++ if (iconv_close(iconv_fs2pl)<0) perror("iconv close: fs2pl"); ++ iconv_fs2pl = (iconv_t)-1; ++ } ++ if (iconv_pl2fs != (iconv_t)-1) { ++ if (iconv_close(iconv_pl2fs)<0) perror("iconv close: pl2fs"); ++ iconv_pl2fs = (iconv_t)-1; ++ } ++ if (iconv_utf != (iconv_t)-1) { ++ if (iconv_close(iconv_utf)<0) perror("iconv close: utf"); ++ iconv_utf=(iconv_t)-1; ++ } ++} ++ ++static void xmms_charset_iconv_open() { ++ const char *id3, *output, *fs, *pl; ++ assert(xmms_charset_changed>=0); ++ if (!xmms_charset_changed) return; ++ ++ xmms_charset_iconv_close(); ++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3]; ++ else id3=charset_default; ++ if (xmms_charset_output) output=charset_list[xmms_charset_output]; ++ else output=charset_default; ++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs]; ++ else fs=output; ++ if (xmms_charset_pl) pl=charset_list[xmms_charset_pl]; ++ else pl=id3; ++ ++ ++ if ((xmms_charset_id3 != xmms_charset_output)&&(strcmp(id3,output))) { ++ iconv_id3 = iconv_open(output, id3); ++ iconv_output = iconv_open(id3, output); ++ if (!xmms_charset_fs) iconv_fs = iconv_open(id3, fs); ++ } ++ ++ if (strcmp(fs,id3)) { ++ iconv_fs = iconv_open(id3, fs); ++ } ++ ++ if (strcmp(fs,pl)) { ++ iconv_fs2pl = iconv_open(pl, fs); ++ iconv_pl2fs = iconv_open(fs,pl); ++ } ++ ++ if (strcmp(id3, "UTF-8")) { ++ iconv_utf = iconv_open("UTF-8",id3); ++ } ++ ++ if ((xmms_charset_fs)&&(xmms_charset_fs!=xmms_charset_output)) { ++ recode_fsout=1; ++ } else { ++ recode_fsout=0; ++ } ++ ++ if (strcmp(fs, "UTF-8")) { ++ recode_fs2utf=1; ++ } else { ++ recode_fs2utf=0; ++ } ++ ++ xmms_charset_changed = 0; ++} ++ ++ ++static int xmms_charset_number(char *c) { ++ int i; ++ if (!c) return 0; ++ for (i=0;charset_list[i];i++) ++ if (strcmp(charset_list[i], c)==0) return i; ++ return 0; /* Default, - if not found */ ++} ++ ++static void xmms_charset_setdefault() { ++ char *str1, *str2; ++ str1 = getenv(ENV_VARIABLE); ++ if (str1) { ++ str2 = strrchr(str1,'.'); ++ if (str2) strncpy(charset_default,str2+1,LOCALE_STRING_LENGTH); ++ else strncpy(charset_default,str1,LOCALE_STRING_LENGTH); ++ charset_default[LOCALE_STRING_LENGTH-1]=0; ++ str2 = strchr(charset_default,'@'); ++ if (str2) *str2 = 0; ++ } else { ++ strcpy(charset_default,"UTF-8"); ++ } ++} ++ ++void xmms_charset_init(char *id3, char *output, char *fs, char *pl) { ++ if (xmms_charset_changed<0) xmms_charset_setdefault(); ++ ++ xmms_charset_changed = 1; ++ xmms_charset_id3 = xmms_charset_number(id3); ++ xmms_charset_output = xmms_charset_number(output); ++ xmms_charset_fs = xmms_charset_number(fs); ++ xmms_charset_pl = xmms_charset_number(pl); ++} ++ ++void xmms_charset_free() { ++ xmms_charset_iconv_close(); ++} ++ ++void xmms_charset_new_charsets(int id3, int output, int fs, int pl) { ++ if (xmms_charset_changed<0) ++ xmms_charset_setdefault(); ++ else ++ if ((xmms_charset_id3==id3)&&(xmms_charset_output==output)&&(xmms_charset_fs==fs)&&(xmms_charset_pl==pl)) return; ++ ++ xmms_charset_changed = 1; ++ xmms_charset_id3 = id3; ++ xmms_charset_output = output; ++ xmms_charset_fs = fs; ++ xmms_charset_pl = pl; ++} ++ ++// Multi-byte strings still unsupported ++static void iconv_copysymbol(char **in_buf, int *in_left, char **out_buf, int *out_left) { ++ if ((out_left>0)&&(in_left>0)) { ++ (**out_buf)=(**in_buf); ++ (*out_buf)++; ++ (*in_buf)++; ++ (*in_left)--; ++ (*out_left)--; ++ } ++} ++ ++static char *xmms_charset_recode(iconv_t icnv, char *buf, int len, int *rlen) { ++ char *in_buf, *out_buf, *res, err; ++ int in_left, out_left, olen; ++ int errors=0; ++ ++ assert(buf); ++ ++ if (!len) { ++ len=strlen(buf); ++ } else { ++ olen=strlen(buf); ++ if (olen<len) len=olen; ++ } ++ ++ if (iconv(icnv, NULL, NULL, NULL, NULL) == -1) { ++ perror( "iconv convert: initialize" ); ++ return NULL; ++ } ++ ++ in_buf = buf; ++ in_left = len; ++ out_buf = &xmms_charset_tmp; ++ out_left = CHARSET_MAX_STRING_SIZE; ++ ++loop: ++ err=iconv(icnv, &in_buf, &in_left, &out_buf, &out_left); ++// printf("%s, %i %u\n",buf,err,errno); ++ if (err<0) { ++ if (errno==E2BIG) { ++ *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0; ++ } else if (errno==EILSEQ) { ++ if (errors<CHARSET_MAX_ERRORS) { ++ errors++; ++ iconv_copysymbol(&in_buf, &in_left, &out_buf, &out_left); ++ if (in_left>0) goto loop; ++ } else { ++ perror("iconv convert: invalid encoding?"); ++ return NULL; ++ } ++ } else { ++ perror("iconv convert: convert"); ++// perror(buf); ++ return NULL; ++ } ++ } ++ ++ olen = CHARSET_MAX_STRING_SIZE - out_left; ++ res = g_malloc(olen+1); ++ if (!res) { ++ perror( "iconv convert: malloc" ); ++ return NULL; ++ } ++ ++ memcpy(res,xmms_charset_tmp,olen); ++ res[olen]=0; ++ if (rlen) *rlen=olen; ++ return res; ++} ++ ++char *xmms_charset_recode_id3(char *buf, int len, int *rlen) { ++ iconv_t icnv; ++ ++ xmms_charset_iconv_open(); ++ icnv = xmms_autocharset_get(buf,len); ++ ++ if (icnv == (iconv_t)-2) return NULL; ++ ++ if (icnv != (iconv_t)-1) ++ return xmms_charset_recode(icnv, buf, len, rlen); ++ ++ if (iconv_id3 != (iconv_t)-1) ++ return xmms_charset_recode(iconv_id3, buf, len, rlen); ++ return NULL; ++} ++ ++char *xmms_charset_recode_output(char *buf, int len, int *rlen) { ++ xmms_charset_iconv_open(); ++ if (iconv_output != (iconv_t)-1) ++ return xmms_charset_recode(iconv_output, buf, len, rlen); ++ return NULL; ++} ++ ++char *xmms_charset_recode_fs(char *buf, int len, int *rlen) { ++ xmms_charset_iconv_open(); ++ if (iconv_fs != (iconv_t)-1) ++ return xmms_charset_recode(iconv_fs, buf, len, rlen); ++ return NULL; ++} ++ ++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen) { ++ int olen; ++ char *str = NULL; ++ ++ xmms_charset_iconv_open(); ++ if (recode_fsout) { ++ if (iconv_fs != (iconv_t)-1) { ++ str = xmms_charset_recode(iconv_fs, buf, len, &olen); ++ if (!str) return NULL; ++ len=olen; ++ } ++ if (iconv_id3 != (iconv_t)-1) { ++ buf = xmms_charset_recode(iconv_id3, str ? str : buf, len, &olen); ++ if (str) free(str); ++ } else if (str) { ++ buf = str; ++ } else { ++ buf = NULL; ++ } ++ if (rlen) *rlen=olen; ++ return buf; ++ } ++ return NULL; ++} ++ ++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen) { ++ xmms_charset_iconv_open(); ++ if (iconv_fs2pl != (iconv_t)-1) ++ return xmms_charset_recode(iconv_fs2pl, buf, len, rlen); ++ return NULL; ++} ++ ++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen) { ++ iconv_t icnv; ++ ++ xmms_charset_iconv_open(); ++ icnv = xmms_autocharset_get_id2fs(buf,len); ++ ++ if (icnv==(iconv_t)-2) return NULL; ++ ++ if (icnv != (iconv_t)-1) ++ return xmms_charset_recode(icnv, buf, len, rlen); ++ ++ if (iconv_pl2fs != (iconv_t)-1) ++ return xmms_charset_recode(iconv_pl2fs, buf, len, rlen); ++ return NULL; ++} ++ ++ ++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen) { ++ char *str = NULL; ++ int olen; ++ iconv_t icnv; ++ ++ xmms_charset_iconv_open(); ++ ++ icnv = xmms_autocharset_get_id2id(buf,len); ++ if ((icnv != (iconv_t)-1)&&(icnv != (iconv_t)-2)) { ++ str = xmms_charset_recode(icnv, buf, len, &olen); ++ if (!str) return NULL; ++ len = olen; ++ } ++ ++ xmms_charset_iconv_open(); ++ if (iconv_utf != (iconv_t)-1) { ++ buf=xmms_charset_recode(iconv_utf, str ? str : buf, len, rlen); ++ if (str) free(str); ++ return buf; ++ } ++ ++ return NULL; ++} ++ ++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen) { ++ int olen; ++ char *str = NULL; ++ ++ xmms_charset_iconv_open(); ++ if (recode_fs2utf) { ++ if (iconv_fs != (iconv_t)-1) { ++ str = xmms_charset_recode(iconv_fs, buf, len, &olen); ++ if (!str) return NULL; ++ len=olen; ++ } ++ if (iconv_utf != (iconv_t)-1) { ++ buf = xmms_charset_recode(iconv_utf, str ? str : buf, len, &olen); ++ if (str) free(str); ++ } else if (str) { ++ buf = str; ++ } else { ++ buf = NULL; ++ } ++ if (rlen) *rlen=olen; ++ return buf; ++ } ++ return NULL; ++} +diff -Naur xmms-1.2.8/libxmms/charset_config.h xmms-1.2.8-new/libxmms/charset_config.h +--- xmms-1.2.8/libxmms/charset_config.h 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/libxmms/charset_config.h 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,13 @@ ++/* Misc configuration options */ ++ ++#define ENV_VARIABLE "LANG" ++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */ ++#define CHARSET_MAX_ERRORS 3 ++ ++const char *charset_list[] = { "Default", "KOI8-R", "CP1251", "CP866", "UTF-8", NULL }; ++ ++const int autocharset_list_ni=2; ++const struct autocharset_list_t autocharset_list[] = { ++ {"Off", NULL, {NULL}}, ++ {"Russian", &autocharset_russian, {"CP1251","KOI8-R"} }, ++}; +diff -Naur xmms-1.2.8/libxmms/charset.h xmms-1.2.8-new/libxmms/charset.h +--- xmms-1.2.8/libxmms/charset.h 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/libxmms/charset.h 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,36 @@ ++#ifndef XMMS_CHARSET_H ++#define XMMS_CHARSET_H ++ ++#define AUTOCHARSET_MAX_ENCODINGS 5 ++int autocharset_russian(char *buf,int len); ++ ++struct autocharset_list_t { ++ char *title; ++ int (*func)(char *buf, int len); ++ char *encodings[AUTOCHARSET_MAX_ENCODINGS]; ++}; ++ ++extern const char *charset_list[]; ++extern const int autocharset_list_ni; ++extern const struct autocharset_list_t autocharset_list[]; ++ ++/* Initialization functions */ ++void xmms_autocharset_new_mode(int m); ++void xmms_autocharset_init(int m); ++void xmms_autocharset_free(); ++ ++void xmms_charset_init(char *id3, char *output, char *fs, char *pl); ++void xmms_charset_free(); ++void xmms_charset_new_charsets(int id3, int output, int fs, int pl); ++ ++/* Recoding functions */ ++char *xmms_charset_recode_id3(char *buf, int len, int *rlen); ++char *xmms_charset_recode_output(char *buf, int len, int *rlen); ++char *xmms_charset_recode_fs(char *buf, int len, int *rlen); ++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen); ++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen); ++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen); ++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen); ++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen); ++ ++#endif /* XMMS_CHARSET_H */ +diff -Naur xmms-1.2.8/libxmms/Makefile.am xmms-1.2.8-new/libxmms/Makefile.am +--- xmms-1.2.8/libxmms/Makefile.am 2003-09-04 23:00:18.000000000 +0200 ++++ xmms-1.2.8-new/libxmms/Makefile.am 2003-09-06 20:11:31.000000000 +0200 +@@ -15,7 +15,9 @@ + formatter.c formatter.h \ + titlestring.c titlestring.h \ + xentry.c xentry.h \ +-xconvert.c xconvert.h ++xconvert.c xconvert.h \ ++charset.c charset.h \ ++charset_auto_russian.h charset_config.h + +-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h ++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h + +diff -Naur xmms-1.2.8/libxmms/Makefile.in xmms-1.2.8-new/libxmms/Makefile.in +--- xmms-1.2.8/libxmms/Makefile.in 2003-09-04 23:01:19.000000000 +0200 ++++ xmms-1.2.8-new/libxmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200 +@@ -101,10 +101,12 @@ + formatter.c formatter.h \ + titlestring.c titlestring.h \ + xentry.c xentry.h \ +-xconvert.c xconvert.h ++xconvert.c xconvert.h \ ++charset.c charset.h \ ++charset_auto_russian.h charset_config.h + ++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h + +-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs + CONFIG_HEADER = config.h +@@ -118,7 +120,7 @@ + LIBS = @LIBS@ + libxmms_la_DEPENDENCIES = + libxmms_la_OBJECTS = configfile.lo xmmsctrl.lo dirbrowser.lo util.lo \ +-formatter.lo titlestring.lo xentry.lo xconvert.lo ++formatter.lo titlestring.lo xentry.lo xconvert.lo charset.lo + CFLAGS = @CFLAGS@ + COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +diff -Naur xmms-1.2.8/libxmms/titlestring.c xmms-1.2.8-new/libxmms/titlestring.c +--- xmms-1.2.8/libxmms/titlestring.c 2002-01-08 15:09:08.000000000 +0100 ++++ xmms-1.2.8-new/libxmms/titlestring.c 2003-09-06 19:42:29.000000000 +0200 +@@ -40,6 +40,8 @@ + gchar *xmms_get_titlestring(gchar *fmt, TitleInput *input) + { + gchar c, *p, outbuf[256], convert[16], *string; ++ gchar *cstring = NULL; ++ gint stringlen; + gint numdigits, numpr, val, size, i; + gint f_left, f_space, f_zero, someflag, width, precision; + gboolean did_output = FALSE; +@@ -164,9 +166,17 @@ + goto Print_string; + case 'f': + string = VS(input, file_name); ++ if (string) { ++ cstring=xmms_charset_recode_fs(string,0,&stringlen); ++ if (cstring) string=cstring; ++ } + goto Print_string; + case 'F': + string = VS(input, file_path); ++ if (string) { ++ cstring=xmms_charset_recode_fs(string,0,&stringlen); ++ if (cstring) string=cstring; ++ } + goto Print_string; + case 'g': + string = VS(input, genre); +@@ -208,6 +218,10 @@ + while ((c = *string++) != '\0') + PUTCH(c); + } ++ if (cstring) { ++ g_free(cstring); ++ cstring=NULL; ++ } + + RIGHTPAD(width - numpr); + break; +diff -Naur xmms-1.2.8/xmms/charset.c xmms-1.2.8-new/xmms/charset.c +--- xmms-1.2.8/xmms/charset.c 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/xmms/charset.c 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,393 @@ ++#include <stdio.h> ++#include <locale.h> ++#include <string.h> ++#include <gtk/gtk.h> ++#include "xmms.h" ++#include "libxmms/charset.h" ++#include "charset.h" ++ ++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */ ++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE]; ++ ++extern TextBox *playlistwin_sinfo; ++ ++static gint selected_id3_charset, selected_output_charset, selected_fs_charset; ++static gint selected_pl_charset; ++static gint selected_autocharset; ++static GtkWidget *prefswin_options_font_shade_entry, *prefswin_options_font_shade_browse; ++ ++static void prefswin_font_shade_browse_ok(GtkWidget * w, gpointer data) ++{ ++ GtkFontSelectionDialog *fontsel = GTK_FONT_SELECTION_DIALOG(data); ++ gchar *fontname; ++ ++ fontname = gtk_font_selection_dialog_get_font_name(fontsel); ++ ++ if (fontname) ++ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_shade_entry), fontname); ++ ++ gtk_widget_destroy(GTK_WIDGET(fontsel)); ++} ++ ++static void prefswin_font_shade_browse_cb(GtkWidget * w, gpointer data) ++{ ++ static GtkWidget *fontsel; ++ ++ if (fontsel != NULL) ++ return; ++ ++ fontsel = gtk_font_selection_dialog_new(_("Select playlist font:")); ++ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fontsel), gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_shade_entry))); ++ gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_browse_ok), fontsel); ++ gtk_signal_connect_object(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(fontsel)); ++ gtk_signal_connect(GTK_OBJECT(fontsel), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &fontsel); ++ gtk_widget_show(fontsel); ++} ++ ++ ++static void prefswin_charset_cb(GtkWidget * w, gpointer item) ++{ ++ int i; ++ i = GPOINTER_TO_INT(item); ++ if (i>=0x3FFFF) selected_pl_charset=i-0x3FFFF; ++ else if (i>=0x2FFFF) selected_autocharset=i-0x2FFFF; ++ else if (i>=0x1FFFF) selected_fs_charset=i-0x1FFFF; ++ else if (i>=0xFFFF) selected_output_charset=i-0xFFFF; ++ else selected_id3_charset=i; ++} ++ ++static void charset_options_add(Config *cfg, GtkOptionMenu *cmenu1, GtkOptionMenu *cmenu2, GtkOptionMenu *cmenu3, GtkOptionMenu *cmenu4) ++{ ++ int i; ++ GtkWidget *menu1, *item1; ++ GtkWidget *menu2, *item2; ++ GtkWidget *menu3, *item3; ++ GtkWidget *menu4, *item4; ++ ++ menu1 = gtk_menu_new(); ++ gtk_option_menu_remove_menu(cmenu1); ++ gtk_option_menu_set_menu(cmenu1, menu1); ++ ++ menu2 = gtk_menu_new(); ++ gtk_option_menu_remove_menu(cmenu2); ++ gtk_option_menu_set_menu(cmenu2, menu2); ++ ++ menu3 = gtk_menu_new(); ++ gtk_option_menu_remove_menu(cmenu3); ++ gtk_option_menu_set_menu(cmenu3, menu3); ++ ++ menu4 = gtk_menu_new(); ++ gtk_option_menu_remove_menu(cmenu4); ++ gtk_option_menu_set_menu(cmenu4, menu4); ++ ++ selected_id3_charset=0; ++ selected_output_charset=0; ++ selected_fs_charset=0; ++ selected_pl_charset=0; ++ ++ for (i=0;charset_list[i];i++) { ++ item1 = gtk_menu_item_new_with_label(charset_list[i]); ++ gtk_signal_connect(GTK_OBJECT(item1), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i)); ++ gtk_widget_show(item1); ++ gtk_menu_append(GTK_MENU(menu1), item1); ++ item2 = gtk_menu_item_new_with_label(charset_list[i]); ++ gtk_signal_connect(GTK_OBJECT(item2), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0xFFFF)); ++ gtk_widget_show(item2); ++ gtk_menu_append(GTK_MENU(menu2), item2); ++ item3 = gtk_menu_item_new_with_label(charset_list[i]); ++ gtk_signal_connect(GTK_OBJECT(item3), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x1FFFF)); ++ gtk_widget_show(item3); ++ gtk_menu_append(GTK_MENU(menu3), item3); ++ item4 = gtk_menu_item_new_with_label(charset_list[i]); ++ gtk_signal_connect(GTK_OBJECT(item4), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x3FFFF)); ++ gtk_widget_show(item4); ++ gtk_menu_append(GTK_MENU(menu4), item4); ++ if ((cfg->charset_id3)&&(!strcmp(cfg->charset_id3,charset_list[i]))) { ++ selected_id3_charset=i; ++ } ++ if ((cfg->charset_output)&&(!strcmp(cfg->charset_output,charset_list[i]))) { ++ selected_output_charset=i; ++ } ++ if ((cfg->charset_fs)&&(!strcmp(cfg->charset_fs,charset_list[i]))) { ++ selected_fs_charset=i; ++ } ++ if ((cfg->charset_pl)&&(!strcmp(cfg->charset_pl,charset_list[i]))) { ++ selected_pl_charset=i; ++ } ++ } ++ ++ gtk_option_menu_set_history(cmenu1, selected_id3_charset); ++ gtk_option_menu_set_history(cmenu2, selected_output_charset); ++ gtk_option_menu_set_history(cmenu3, selected_fs_charset); ++ gtk_option_menu_set_history(cmenu4, selected_pl_charset); ++} ++ ++static void autocharset_options_add(Config *cfg, GtkOptionMenu *cmenu) ++{ ++ int i; ++ GtkWidget *menu, *item; ++ ++ menu = gtk_menu_new(); ++ gtk_option_menu_remove_menu(cmenu); ++ gtk_option_menu_set_menu(cmenu, menu); ++ ++ selected_autocharset=0; ++ ++ ++ for (i=0;i<autocharset_list_ni;i++) { ++ item = gtk_menu_item_new_with_label(autocharset_list[i].title); ++ gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x2FFFF)); ++ gtk_widget_show(item); ++ gtk_menu_append(GTK_MENU(menu), item); ++ if ((cfg->autocharset)&&(cfg->autocharset==i)) { ++ selected_autocharset=i; ++ } ++ } ++ ++ gtk_option_menu_set_history(cmenu, selected_autocharset); ++} ++ ++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox) { ++ GtkWidget *prefswin_encodings_vbox; ++ GtkWidget *prefswin_fonts_shade_frame; ++ GtkWidget *options_font_shade_hbox, *options_font_shade_vbox; ++ GtkWidget *prefswin_charset_frame, *prefswin_charset_vbox; ++ GtkWidget *prefswin_charset_list1, *prefswin_charset_list2, *prefswin_charset_list3, *prefswin_charset_list4; ++ GtkWidget *prefswin_charset_hbox1, *prefswin_charset_hbox2, *prefswin_charset_hbox3, *prefswin_charset_hbox4; ++ GtkWidget *prefswin_charset_label1, *prefswin_charset_label2, *prefswin_charset_label3, *prefswin_charset_label4; ++ GtkWidget *prefswin_autocharset_frame, *prefswin_autocharset_vbox; ++ GtkWidget *prefswin_autocharset_list; ++ GtkWidget *prefswin_autocharset_hbox; ++ GtkWidget *prefswin_autocharset_label; ++ ++ prefswin_fonts_shade_frame = gtk_frame_new(_("Shade")); ++ gtk_container_set_border_width(GTK_CONTAINER(prefswin_fonts_shade_frame), 5); ++ gtk_box_pack_start(GTK_BOX(prefswin_fonts_vbox), prefswin_fonts_shade_frame, FALSE, FALSE, 0); ++ options_font_shade_vbox = gtk_vbox_new(FALSE, 5); ++ gtk_container_border_width(GTK_CONTAINER(options_font_shade_vbox), 5); ++ gtk_container_add(GTK_CONTAINER(prefswin_fonts_shade_frame), options_font_shade_vbox); ++ options_font_shade_hbox = gtk_hbox_new(FALSE, 5); ++ gtk_box_pack_start_defaults(GTK_BOX(options_font_shade_vbox), options_font_shade_hbox); ++ prefswin_options_font_shade_entry = gtk_entry_new(); ++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_entry, TRUE, TRUE, 0); ++ prefswin_options_font_shade_browse = gtk_button_new_with_label(_("Browse")); ++ gtk_signal_connect(GTK_OBJECT(prefswin_options_font_shade_browse), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_browse_cb), NULL); ++ gtk_widget_set_usize(prefswin_options_font_shade_browse, 85, 17); ++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_browse, FALSE, TRUE, 0); ++ ++ prefswin_encodings_vbox = gtk_vbox_new(FALSE, 0); ++ ++ prefswin_charset_frame = gtk_frame_new(_("Encodings")); ++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_charset_frame, FALSE, FALSE, 0); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_frame), 5); ++ prefswin_charset_vbox = gtk_vbox_new(FALSE, 3); ++ gtk_container_add(GTK_CONTAINER(prefswin_charset_frame), prefswin_charset_vbox); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_vbox), 5); ++ ++ prefswin_charset_hbox1 = gtk_hbox_new(FALSE, 10); ++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox1); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox1), 5); ++ prefswin_charset_label1 = gtk_label_new(_("ID3 Encoding")); ++ gtk_widget_set_usize(prefswin_charset_label1, 120, 17); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_label1, FALSE, FALSE, 0); ++ prefswin_charset_list1 = gtk_option_menu_new(); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_list1, TRUE, TRUE, 0); ++ ++ prefswin_charset_hbox3 = gtk_hbox_new(FALSE, 10); ++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox3); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox3), 5); ++ prefswin_charset_label3 = gtk_label_new(_("FileSystem Encoding")); ++ gtk_widget_set_usize(prefswin_charset_label3, 120, 17); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_label3, FALSE, FALSE, 0); ++ prefswin_charset_list3 = gtk_option_menu_new(); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_list3, TRUE, TRUE, 0); ++ ++ prefswin_charset_hbox4 = gtk_hbox_new(FALSE, 10); ++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox4); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox4), 5); ++ prefswin_charset_label4 = gtk_label_new(_("Playlist Encoding")); ++ gtk_widget_set_usize(prefswin_charset_label4, 120, 17); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_label4, FALSE, FALSE, 0); ++ prefswin_charset_list4 = gtk_option_menu_new(); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_list4, TRUE, TRUE, 0); ++ ++ prefswin_charset_hbox2 = gtk_hbox_new(FALSE, 10); ++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox2); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox2), 5); ++ prefswin_charset_label2 = gtk_label_new(_("Output Encoding")); ++ gtk_widget_set_usize(prefswin_charset_label2, 120, 17); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_label2, FALSE, FALSE, 0); ++ prefswin_charset_list2 = gtk_option_menu_new(); ++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_list2, TRUE, TRUE, 0); ++ ++ charset_options_add(cfg, GTK_OPTION_MENU(prefswin_charset_list1),GTK_OPTION_MENU(prefswin_charset_list2),GTK_OPTION_MENU(prefswin_charset_list3), GTK_OPTION_MENU(prefswin_charset_list4)); ++ ++ prefswin_autocharset_frame = gtk_frame_new(_("Encoding Autoselection")); ++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_autocharset_frame, FALSE, FALSE, 0); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_frame), 5); ++ prefswin_autocharset_vbox = gtk_vbox_new(FALSE, 3); ++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_frame), prefswin_autocharset_vbox); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_vbox), 5); ++ ++ prefswin_autocharset_hbox = gtk_hbox_new(FALSE, 10); ++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_vbox), prefswin_autocharset_hbox); ++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_hbox), 5); ++ prefswin_autocharset_label = gtk_label_new(_("Language")); ++ gtk_widget_set_usize(prefswin_autocharset_label, 120, 17); ++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_label, FALSE, FALSE, 0); ++ prefswin_autocharset_list = gtk_option_menu_new(); ++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_list, TRUE, TRUE, 0); ++ ++ autocharset_options_add(cfg, GTK_OPTION_MENU(prefswin_autocharset_list)); ++ ++ gtk_notebook_append_page(prefswin_notebook, prefswin_encodings_vbox, gtk_label_new(_("Encodings"))); ++} ++ ++void charset_prefswin_apply_changes(Config *cfg) { ++ if (cfg->charset_id3) g_free(cfg->charset_id3); ++ if (cfg->charset_output) g_free(cfg->charset_output); ++ if (cfg->charset_fs) g_free(cfg->charset_fs); ++ if (cfg->charset_pl) g_free(cfg->charset_pl); ++ cfg->charset_id3=g_strdup(charset_list[selected_id3_charset]); ++ cfg->charset_output=g_strdup(charset_list[selected_output_charset]); ++ cfg->charset_fs=g_strdup(charset_list[selected_fs_charset]); ++ cfg->charset_pl=g_strdup(charset_list[selected_pl_charset]); ++ xmms_charset_new_charsets(selected_id3_charset, selected_output_charset, selected_fs_charset, selected_pl_charset); ++ cfg->autocharset=selected_autocharset; ++ xmms_autocharset_new_mode(selected_autocharset); ++ ++ if (cfg->shade_font) g_free(cfg->shade_font); ++ cfg->shade_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_shade_entry))); ++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font); ++} ++ ++void charset_show_prefs_window(Config *cfg) { ++ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_shade_entry), cfg->shade_font); ++} ++ ++void charset_create_playlist_window(Config *cfg) { ++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font); ++} ++/***************************************************************************** ++************************ Initialization / Deinitilization ******************** ++*****************************************************************************/ ++void charset_free(Config *cfg) { ++ xmms_charset_free(); ++ xmms_autocharset_free(); ++} ++ ++void charset_init(Config *cfg) { ++ xmms_charset_init(cfg->charset_id3, cfg->charset_output, cfg->charset_fs, cfg->charset_pl); ++ xmms_autocharset_init(cfg->autocharset); ++} ++ ++void charset_read_config(ConfigFile *cfgfile, Config *cfg) { ++ cfg->autocharset=0; ++ xmms_cfg_read_string(cfgfile, "xmms", "shade_font", &cfg->shade_font); ++ xmms_cfg_read_string(cfgfile, "xmms", "charset_id3", &cfg->charset_id3); ++ xmms_cfg_read_string(cfgfile, "xmms", "charset_output", &cfg->charset_output); ++ xmms_cfg_read_string(cfgfile, "xmms", "charset_fs", &cfg->charset_fs); ++ xmms_cfg_read_string(cfgfile, "xmms", "charset_pl", &cfg->charset_pl); ++ xmms_cfg_read_int(cfgfile,"xmms","autocharset",&cfg->autocharset); ++} ++ ++void charset_write_config(ConfigFile *cfgfile, Config *cfg) { ++ xmms_cfg_write_string(cfgfile, "xmms", "shade_font", cfg->shade_font); ++ xmms_cfg_write_string(cfgfile, "xmms", "charset_id3", cfg->charset_id3); ++ xmms_cfg_write_string(cfgfile, "xmms", "charset_output", cfg->charset_output); ++ xmms_cfg_write_string(cfgfile, "xmms", "charset_fs", cfg->charset_fs); ++ xmms_cfg_write_string(cfgfile, "xmms", "charset_pl", cfg->charset_pl); ++ xmms_cfg_write_int(cfgfile,"xmms","autocharset",cfg->autocharset); ++} ++ ++void charset_default_config(Config *cfg) { ++ if (cfg->shade_font && strlen(cfg->shade_font) == 0) ++ { ++ g_free(cfg->shade_font); ++ cfg->shade_font = NULL; ++ } ++ if (cfg->shade_font == NULL) ++ cfg->shade_font = g_strdup("-misc-fixed-medium-r-*-*-7-*"); ++} ++ ++/***************************************************************************** ++*********************************** Sort ************************************* ++*****************************************************************************/ ++gchar *saved_locale; ++ ++static void charset_sort_null_entry(gpointer entry, gpointer data) { ++ ((PlaylistEntry*)entry)->sort=NULL; ++} ++ ++static void charset_sort_free_entry(gpointer entry, gpointer data) { ++ if (((PlaylistEntry*)entry)->sort) ++ g_free(((PlaylistEntry*)entry)->sort); ++} ++ ++void charset_sort_prepare(GList *playlist) { ++ g_list_foreach(playlist, charset_sort_null_entry, NULL); ++ saved_locale=g_strdup(setlocale(LC_COLLATE,NULL)); ++ setlocale(LC_COLLATE,"UTF-8"); ++} ++ ++void charset_sort_free(GList *playlist) { ++ g_list_foreach(playlist, charset_sort_free_entry, NULL); ++ if (saved_locale) { ++ setlocale(LC_COLLATE,saved_locale); ++ g_free(saved_locale); ++ } ++} ++ ++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs) { ++ char *str; ++ if (entry->sort) return entry->sort; ++ if (fs) ++ str=xmms_charset_recode_fs2utf(title,0,NULL); ++ else ++ str=xmms_charset_recode_id2utf(title,0,NULL); ++ ++ if (!str) ++ entry->sort=g_strdup(title); ++ else { ++ strxfrm(xmms_charset_tmp,str,CHARSET_MAX_STRING_SIZE); ++// *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0; ++ xmms_charset_tmp[CHARSET_MAX_STRING_SIZE-1]=0; ++ entry->sort=g_strdup(xmms_charset_tmp); ++ g_free(str); ++ } ++ ++ if (entry->sort) ++ return entry->sort; ++ ++ return title; ++} ++ ++/***************************************************************************** ++*********************************** Misc ************************************* ++*****************************************************************************/ ++ ++void textbox_set_ctext(TextBox * tb, gchar * text) ++{ ++ gchar *rtext; ++ ++ lock_widget(tb); ++ ++ rtext=xmms_charset_recode_id3(text,0,NULL); ++ if (rtext) text=rtext; ++ if (tb->tb_text) ++ { ++ if (!strcmp(text, tb->tb_text)) ++ { ++ if (rtext) free(rtext); ++ unlock_widget(tb); ++ return; ++ } ++ g_free(tb->tb_text); ++ } ++ ++ tb->tb_text = g_strdup(text); ++ if (rtext) free(rtext); ++ ++ unlock_widget(tb); ++ draw_widget(tb); ++} +diff -Naur xmms-1.2.8/xmms/charset.h xmms-1.2.8-new/xmms/charset.h +--- xmms-1.2.8/xmms/charset.h 1970-01-01 01:00:00.000000000 +0100 ++++ xmms-1.2.8-new/xmms/charset.h 2003-09-06 19:42:29.000000000 +0200 +@@ -0,0 +1,19 @@ ++#include "libxmms/configfile.h" ++#include "libxmms/charset.h" ++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox); ++void charset_show_prefs_window(Config *cfg); ++void charset_prefswin_apply_changes(Config *cfg); ++void charset_create_playlist_window(Config *cfg); ++ ++void charset_init(Config *cfg); ++void charset_free(Config *cfg); ++ ++void charset_read_config(ConfigFile *cfgfile, Config *cfg); ++void charset_default_config(Config *cfg); ++void charset_write_config(ConfigFile *cfgfile, Config *cfg); ++ ++void charset_sort_prepare(GList *playlist); ++void charset_sort_free(GList *playlist); ++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs); ++ ++void textbox_set_ctext(TextBox * tb, gchar * text); +diff -Naur xmms-1.2.8/xmms/input.c xmms-1.2.8-new/xmms/input.c +--- xmms-1.2.8/xmms/input.c 2003-07-14 15:24:28.000000000 +0200 ++++ xmms-1.2.8-new/xmms/input.c 2003-09-06 19:42:29.000000000 +0200 +@@ -22,6 +22,7 @@ + #include "libxmms/titlestring.h" + #include "libxmms/util.h" + #include "libxmms/xentry.h" ++#include "libxmms/charset.h" + + static pthread_mutex_t vis_mutex = PTHREAD_MUTEX_INITIALIZER; + +@@ -343,7 +344,7 @@ + { + GList *node; + InputPlugin *ip = NULL; +- ++ + node = get_input_list(); + while (node) + { +@@ -371,8 +372,11 @@ + + (*title) = xmms_get_titlestring(xmms_get_gentitle_format(), + input); +- if ( (*title) == NULL ) +- (*title) = g_strdup(input->file_name); ++ if ( (*title) == NULL ) { ++ (*title) = xmms_charset_recode_fs(input->file_name,0,NULL); ++ if (!*title) (*title) = g_strdup(input->file_name); ++ } ++ + (*length) = -1; + g_free(temp); + g_free(input); +diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c +--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200 ++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 19:42:29.000000000 +0200 +@@ -35,6 +35,7 @@ + #include "libxmms/xmmsctrl.h" + #include "libxmms/util.h" + #include "libxmms/dirbrowser.h" ++#include "charset.h" + #include "xmms_mini.xpm" + + GtkWidget *mainwin, *mainwin_url_window = NULL, *mainwin_dir_browser = NULL; +@@ -377,7 +378,6 @@ + + cfg.gentitle_format = NULL; + +- + filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL); + cfgfile = xmms_cfg_open_file(filename); + if (cfgfile) +@@ -428,6 +428,7 @@ + xmms_cfg_read_boolean(cfgfile, "xmms", "use_fontsets", &cfg.use_fontsets); + xmms_cfg_read_boolean(cfgfile, "xmms", "mainwin_use_xfont", &cfg.mainwin_use_xfont); + xmms_cfg_read_string(cfgfile, "xmms", "mainwin_font", &cfg.mainwin_font); ++ charset_read_config(cfgfile,&cfg); + xmms_cfg_read_int(cfgfile, "xmms", "playlist_position", &cfg.playlist_position); + xmms_cfg_read_int(cfgfile, "xmms", "equalizer_x", &cfg.equalizer_x); + xmms_cfg_read_int(cfgfile, "xmms", "equalizer_y", &cfg.equalizer_y); +@@ -514,6 +515,8 @@ + if (cfg.eqpreset_extension == NULL) + cfg.eqpreset_extension = g_strdup("preset"); + ++ charset_default_config(&cfg); ++ + g_free(filename); + } + +@@ -594,6 +597,7 @@ + xmms_cfg_write_boolean(cfgfile, "xmms", "use_fontsets", cfg.use_fontsets); + xmms_cfg_write_boolean(cfgfile, "xmms", "mainwin_use_xfont", cfg.mainwin_use_xfont); + xmms_cfg_write_string(cfgfile, "xmms", "mainwin_font", cfg.mainwin_font); ++ charset_write_config(cfgfile,&cfg); + xmms_cfg_write_int(cfgfile, "xmms", "playlist_position", get_playlist_position()); + /* dock_get_widget_pos(equalizerwin, &cfg.equalizer_x, &cfg.equalizer_y); */ + xmms_cfg_write_int(cfgfile, "xmms", "equalizer_x", cfg.equalizer_x); +@@ -893,6 +897,7 @@ + gtk_timeout_remove(mainwin_timeout_tag); + util_set_cursor(NULL); + save_config(); ++ charset_free(&cfg); + cleanup_ctrlsocket(); + playlist_stop_get_info_thread(); + playlist_clear(); +@@ -991,11 +996,11 @@ + + if ((text = input_get_info_text()) != NULL) + { +- textbox_set_text(mainwin_info, text); ++ textbox_set_ctext(mainwin_info, text); + } + else if ((text = playlist_get_info_text()) != NULL) + { +- textbox_set_text(mainwin_info, text); ++ textbox_set_ctext(mainwin_info, text); + g_free(text); + } + else +@@ -3491,6 +3496,7 @@ + #endif + + read_config(); ++ charset_init(&cfg); + + if (geteuid() == 0) + { +diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h +--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200 ++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 19:42:29.000000000 +0200 +@@ -19,6 +19,7 @@ + */ + #ifndef MAIN_H + #define MAIN_H ++#include "libxmms/charset.h" + + typedef enum + { +@@ -60,6 +61,9 @@ + gint mouse_change; + gboolean playlist_transparent; + gchar *gentitle_format; ++ gchar *charset_id3, *charset_output, *charset_fs, *charset_pl; ++ gint autocharset; ++ gchar *shade_font; + } + Config; + +diff -Naur xmms-1.2.8/xmms/Makefile.am xmms-1.2.8-new/xmms/Makefile.am +--- xmms-1.2.8/xmms/Makefile.am 2003-08-11 15:47:41.000000000 +0200 ++++ xmms-1.2.8-new/xmms/Makefile.am 2003-09-06 19:42:29.000000000 +0200 +@@ -56,7 +56,8 @@ + getopt.c getopt1.c getopt.h \ + urldecode.c urldecode.h \ + dnd.h \ +-mkdtemp.c ++mkdtemp.c \ ++charset.c charset.h + + EXTRA_DIST= xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig + +diff -Naur xmms-1.2.8/xmms/Makefile.in xmms-1.2.8-new/xmms/Makefile.in +--- xmms-1.2.8/xmms/Makefile.in 2003-09-04 23:01:20.000000000 +0200 ++++ xmms-1.2.8-new/xmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200 +@@ -228,8 +228,8 @@ + getopt.c getopt1.c getopt.h \ + urldecode.c urldecode.h \ + dnd.h \ +-mkdtemp.c +- ++mkdtemp.c \ ++charset.c charset.h + + EXTRA_DIST = xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig + +@@ -257,7 +257,7 @@ + main.$(OBJEXT) skinwin.$(OBJEXT) prefswin.$(OBJEXT) \ + playlistwin.$(OBJEXT) equalizer.$(OBJEXT) hints.$(OBJEXT) \ + about.$(OBJEXT) sm.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \ +-urldecode.$(OBJEXT) mkdtemp.$(OBJEXT) ++urldecode.$(OBJEXT) mkdtemp.$(OBJEXT) charset.$(OBJEXT) + xmms_DEPENDENCIES = $(top_builddir)/libxmms/libxmms.la + CFLAGS = @CFLAGS@ + COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +diff -Naur xmms-1.2.8/xmms/playlist.c xmms-1.2.8-new/xmms/playlist.c +--- xmms-1.2.8/xmms/playlist.c 2003-09-02 15:01:40.000000000 +0200 ++++ xmms-1.2.8-new/xmms/playlist.c 2003-09-06 19:42:29.000000000 +0200 +@@ -20,6 +20,7 @@ + #include "xmms.h" + #include <time.h> + #include "libxmms/util.h" ++#include "charset.h" + #include <sys/stat.h> + #include <unistd.h> + +@@ -939,6 +940,7 @@ + char *playlist_get_info_text(void) + { + char *text, *title, *tmp, *numbers, *length; ++ char *ctitle; + + PL_LOCK(); + if (!playlist_position) +@@ -947,10 +949,17 @@ + return NULL; + } + +- if (playlist_position->title) ++ if (playlist_position->title) { ++ ctitle = xmms_charset_recode_id3(playlist_position->title,0,NULL); ++ if (ctitle) title=ctitle; ++ else + title = playlist_position->title; +- else ++ } else { ++ ctitle = xmms_charset_recode_fsout(g_basename(playlist_position->filename),0,NULL); ++ if (ctitle) title=ctitle; ++ else + title = g_basename(playlist_position->filename); ++ } + + /* + * If the user don't want numbers in the playlist, don't +@@ -972,6 +981,7 @@ + text = g_strdup_printf("%s%s%s", numbers, title, length); + g_free(numbers); + g_free(length); ++ if (ctitle) g_free(ctitle); + + PL_UNLOCK(); + +@@ -1007,6 +1017,7 @@ + { + GList *node; + FILE *file; ++ gchar *fn, *cfn; + + if ((file = fopen(filename, "w")) == NULL) + return FALSE; +@@ -1025,10 +1036,13 @@ + while (node) + { + PlaylistEntry *entry = node->data; ++ cfn=xmms_charset_recode_fs2pl(entry->filename,0,NULL); ++ if (cfn) fn=cfn; ++ else fn=entry->filename; + if (is_pls) + fprintf(file, "File%d=%s\n", + g_list_position(playlist, node) + 1, +- entry->filename); ++ fn); + else + { + if (entry->title && cfg.use_pl_metadata) +@@ -1043,8 +1057,9 @@ + fprintf(file, "#EXTINF:%d,%s\n", + seconds, entry->title); + } +- fprintf(file, "%s\n", entry->filename); ++ fprintf(file, "%s\n", fn); + } ++ if (cfn) g_free(cfn); + node = g_list_next(node); + } + PL_UNLOCK(); +@@ -1121,6 +1136,7 @@ + { + FILE *file; + char *line, *ext; ++ char *cline; + guint entries = 0; + int linelen = 1024; + gboolean extm3u = FALSE; +@@ -1147,8 +1163,10 @@ + "playlist", key); + if (line != NULL) + { +- playlist_load_ins_file(line, filename, pos, ++ cline = xmms_charset_recode_pl2fs(line,0,NULL); ++ playlist_load_ins_file(cline ? cline : line, filename, pos, + NULL, -1); ++ if (cline) g_free(cline); + entries++; + if (pos >= 0) + pos++; +@@ -1215,7 +1233,9 @@ + ext_info = NULL; + } + +- playlist_load_ins_file(line, filename, pos, ext_title, ext_len); ++ cline = xmms_charset_recode_pl2fs(line,linelen,NULL); ++ playlist_load_ins_file(cline ? cline : line, filename, pos, ext_title, ext_len); ++ if (cline) g_free(cline); + + g_free(ext_title); + ext_title = NULL; +@@ -1308,19 +1328,27 @@ + + if (entry->title == NULL && entry->length == -1) + { +- if (playlist_get_info_entry(entry)) ++ if (playlist_get_info_entry(entry)) { ++ title = xmms_charset_recode_id3(entry->title,0,NULL); ++ if (!title) + title = g_strdup(entry->title); ++ } + + PL_UNLOCK(); + } + else + { ++ title = xmms_charset_recode_id3(entry->title,0,NULL); ++ if (!title) + title = g_strdup(entry->title); + PL_UNLOCK(); + } + +- if (title == NULL) ++ if (title == NULL) { ++ title = xmms_charset_recode_fsout(g_basename(filename),0,NULL); ++ if (!title) + title = g_strdup(g_basename(filename)); ++ } + + g_free(filename); + +@@ -1366,6 +1394,7 @@ + static int playlist_sort_by_title_cmpfunc(PlaylistEntry * a, PlaylistEntry * b) + { + char *a_title, *b_title; ++ int a_fs=0, b_fs=0; + + if (a->title) + a_title = a->title; +@@ -1375,6 +1404,7 @@ + a_title = strrchr(a->filename, '/') + 1; + else + a_title = a->filename; ++ a_fs=1; + } + + if (b->title) +@@ -1385,15 +1415,21 @@ + b_title = strrchr(b->filename, '/') + 1; + else + b_title = b->filename; +- ++ b_fs=1; + } ++ ++ a_title=charset_sort_convert(a,a_title,a_fs); ++ b_title=charset_sort_convert(b,b_title,b_fs); ++ + return strcasecmp(a_title, b_title); + } + + void playlist_sort_by_title(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + +@@ -1411,13 +1447,18 @@ + else + b_filename = b->filename; + ++ a_filename=charset_sort_convert(a,a_filename,1); ++ b_filename=charset_sort_convert(b,b_filename,1); ++ + return strcasecmp(a_filename, b_filename); + } + + void playlist_sort_by_filename(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + +@@ -1453,13 +1494,15 @@ + + static int playlist_sort_by_path_cmpfunc(PlaylistEntry * a, PlaylistEntry * b) + { +- return playlist_sort_str_by_path_cmpfunc(a->filename, b->filename); ++ return playlist_sort_str_by_path_cmpfunc(charset_sort_convert(a,a->filename,1), charset_sort_convert(b,b->filename,1)); + } + + void playlist_sort_by_path(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + +@@ -1549,21 +1592,27 @@ + void playlist_sort_selected_by_title(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + + void playlist_sort_selected_by_filename(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + + void playlist_sort_selected_by_path(void) + { + PL_LOCK(); ++ charset_sort_prepare(playlist); + playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc); ++ charset_sort_free(playlist); + PL_UNLOCK(); + } + +diff -Naur xmms-1.2.8/xmms/playlist.h xmms-1.2.8-new/xmms/playlist.h +--- xmms-1.2.8/xmms/playlist.h 2003-06-09 15:22:10.000000000 +0200 ++++ xmms-1.2.8-new/xmms/playlist.h 2003-09-06 19:42:29.000000000 +0200 +@@ -24,6 +24,7 @@ + { + gchar *filename; + gchar *title; ++ gchar *sort; + gint length; + gboolean selected; + } +diff -Naur xmms-1.2.8/xmms/playlist_list.c xmms-1.2.8-new/xmms/playlist_list.c +--- xmms-1.2.8/xmms/playlist_list.c 2003-06-09 15:22:10.000000000 +0200 ++++ xmms-1.2.8-new/xmms/playlist_list.c 2003-09-06 19:42:29.000000000 +0200 +@@ -287,6 +287,7 @@ + { + GdkWChar *wtext; + int len, newlen; ++ gchar *rtext; + /* + * Convert the string to a wide character string to avoid + * destroying multibyte strings, when converting underscores, +@@ -296,6 +297,10 @@ + * Allocate some extra space, we might extend it by one + * character below + */ ++ ++ rtext = xmms_charset_recode_id3(text,0,NULL); ++ if (rtext) text = rtext; ++ + wtext = g_malloc((strlen(text) + 3) * sizeof(GdkWChar)); + len = gdk_mbstowcs(wtext, text, strlen(text) + 1); + if (len == -1) +@@ -312,6 +317,7 @@ + if (wtext[i] == L'_') + wtext[i] = L' '; + } ++ if (rtext) g_free(rtext); + + if (cfg.convert_twenty && len > 2) + { +@@ -361,6 +367,7 @@ + { + int len; + char *tmp; ++ gchar *rtext; + + if (cfg.convert_underscore) + while ((tmp = strchr(text, '_')) != NULL) +@@ -384,7 +391,10 @@ + text[len] = '\0'; + } + ++ rtext = xmms_charset_recode_id3(text,0,NULL); ++ if (rtext) text = rtext; + gdk_draw_text(pl->pl_widget.parent, font, pl->pl_widget.gc, pl->pl_widget.x, pl->pl_widget.y + line * pl->pl_fheight + font->ascent, text, len); ++ if (rtext) g_free(rtext); + } + + void playlist_list_draw(Widget * w) +@@ -396,6 +406,7 @@ + int width, height; + char *text, *title; + int i, tw, max_first; ++ gchar *ctitle = NULL; + + gc = pl->pl_widget.gc; + width = pl->pl_widget.width; +@@ -453,8 +464,11 @@ + + if (entry->title) + title = entry->title; +- else +- title = g_basename(entry->filename); ++ else { ++ ctitle = xmms_charset_recode_fs(g_basename(entry->filename),0,NULL); ++ if (ctitle) title = ctitle; ++ else title = g_basename(entry->filename); ++ } + + pos = playlist_get_queue_position(entry); + +@@ -497,6 +511,10 @@ + playlist_list_draw_string(pl, playlist_list_font, + i - pl->pl_first, tw, text); + g_free(text); ++ if (ctitle) { ++ g_free(ctitle); ++ ctitle=NULL; ++ } + } + PL_UNLOCK(); + } +diff -Naur xmms-1.2.8/xmms/playlistwin.c xmms-1.2.8-new/xmms/playlistwin.c +--- xmms-1.2.8/xmms/playlistwin.c 2003-04-04 19:42:51.000000000 +0200 ++++ xmms-1.2.8-new/xmms/playlistwin.c 2003-09-06 20:24:02.000000000 +0200 +@@ -38,7 +38,8 @@ + PButton *playlistwin_shade, *playlistwin_close; + static PlaylistSlider *playlistwin_slider = NULL; + static TextBox *playlistwin_time_min, *playlistwin_time_sec; +-static TextBox *playlistwin_info, *playlistwin_sinfo; ++static TextBox *playlistwin_info; ++ TextBox *playlistwin_sinfo; + static SButton *playlistwin_srew, *playlistwin_splay; + static SButton *playlistwin_spause, *playlistwin_sstop; + static SButton *playlistwin_sfwd, *playlistwin_seject; +@@ -1846,6 +1847,7 @@ + static void playlistwin_create_widgets(void) + { + playlistwin_sinfo = create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, 4, 4, cfg.playlist_width - 35, FALSE, SKIN_TEXT); ++ charset_create_playlist_window(&cfg); + if (!cfg.playlist_shaded) + hide_widget(playlistwin_sinfo); + if (cfg.playlist_shaded) +diff -Naur xmms-1.2.8/xmms/prefswin.c xmms-1.2.8-new/xmms/prefswin.c +--- xmms-1.2.8/xmms/prefswin.c 2003-07-16 15:17:47.000000000 +0200 ++++ xmms-1.2.8-new/xmms/prefswin.c 2003-09-06 19:42:29.000000000 +0200 +@@ -1,4 +1,4 @@ +-/* XMMS - Cross-platform multimedia player ++/* XMMS - Cdross-platform multimedia player + * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas, + * Thomas Nilsson and 4Front Technologies + * Copyright (C) 1999-2001 Haavard Kvaalen +@@ -20,6 +20,7 @@ + #include "xmms.h" + #include "libxmms/util.h" + #include "libxmms/titlestring.h" ++#include "charset.h" + + static GtkWidget *prefswin, *prefswin_notebook, *prefswin_ok; + static GtkWidget *prefswin_audio_ie_cbox; +@@ -47,6 +48,7 @@ + extern PButton *playlistwin_shade, *playlistwin_close, *equalizerwin_close; + extern PButton *mainwin_menubtn, *mainwin_minimize, *mainwin_shade, *mainwin_close; + extern TextBox *mainwin_info; ++extern TextBox *playlistwin_sinfo; + extern gboolean mainwin_focus, equalizerwin_focus, playlistwin_focus; + + static gboolean is_opening = FALSE; +@@ -351,6 +353,8 @@ + cfg.pause_between_songs_time = CLAMP(atoi(gtk_entry_get_text(GTK_ENTRY(prefswin_options_pbs_entry))), 0, 1000); + cfg.mouse_change = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(prefswin_options_mouse_spin)); + ++ charset_prefswin_apply_changes(&cfg); ++ + set_current_output_plugin(selected_oplugin); + + equalizerwin_set_doublesize(cfg.doublesize && cfg.eq_doublesize_linked); +@@ -384,7 +388,6 @@ + textbox_set_scroll(mainwin_info, FALSE); + textbox_set_scroll(mainwin_info, TRUE); + } +- + if (show_wm_old != cfg.show_wm_decorations) + prefswin_toggle_wm_decorations(); + +@@ -1089,6 +1092,8 @@ + + gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox, gtk_label_new(_("Fonts"))); + ++ charset_create_prefs_window(&cfg,GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox); ++ + /* + * Title page + */ +@@ -1358,6 +1363,8 @@ + gtk_entry_set_text(GTK_ENTRY(prefswin_options_pbs_entry), temp); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(prefswin_options_mouse_spin), cfg.mouse_change); + ++ charset_show_prefs_window(&cfg); ++ + gtk_widget_show_all(prefswin); + gtk_widget_grab_default(prefswin_ok); + diff --git a/media-sound/xmms/files/xmms-1.2.8-sigterm.patch b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch new file mode 100644 index 000000000000..001b3df13209 --- /dev/null +++ b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch @@ -0,0 +1,31 @@ +diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c +--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200 ++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:15:15.000000000 +0200 +@@ -3343,6 +3343,18 @@ + exit(1); + } + ++/* Try to exit nicely when receiving a nice exit signal */ ++void sigterm_handler(int sig) ++{ ++ /* Original author of patch said that you should not use mainwin_quit_cb(), ++ * but his way deadlock xmms, and it anyhow just calls ctrlsocket stuff with ++ * with CMD_QUIT, which anyhow calls mainwin_quit_cb() in turn. This is not ++ * entirely clean, but works. ++ * <azarah@gentoo.org> (19 Jan 2003) ++ xmms_remote_quit(ctrlsocket_get_session_id()); */ ++ mainwin_quit_cb(); ++} ++ + static gboolean pposition_configure(GtkWidget *w, GdkEventConfigure *event, gpointer data) + { + gint x,y; +@@ -3473,6 +3485,8 @@ + #endif + + signal(SIGPIPE, SIG_IGN); /* for controlsocket.c */ ++ signal(SIGTERM, sigterm_handler); ++ signal(SIGINT, sigterm_handler); + signal(SIGSEGV, segfault_handler); + g_thread_init(NULL); + if (!g_thread_supported()) diff --git a/media-sound/xmms/xmms-1.2.8-r1.ebuild b/media-sound/xmms/xmms-1.2.8-r1.ebuild new file mode 100644 index 000000000000..dbbf773c9c40 --- /dev/null +++ b/media-sound/xmms/xmms-1.2.8-r1.ebuild @@ -0,0 +1,139 @@ +# Copyright 1999-2003 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/xmms-1.2.8-r1.ebuild,v 1.1 2003/09/13 03:46:59 vapier Exp $ + +inherit flag-o-matic eutils +filter-flags -fforce-addr -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE + +DESCRIPTION="X MultiMedia System" +HOMEPAGE="http://www.xmms.org/" +SRC_URI="http://www.xmms.org/files/1.2.x/${P}.tar.gz + mirror://gentoo/gentoo_ice.zip" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~x86 ~ppc ~sparc ~alpha ~hppa ~mips" +IUSE="xml nls esd gnome opengl mmx oggvorbis 3dnow mikmod directfb ipv6 cjk" + +DEPEND="app-arch/unzip + =x11-libs/gtk+-1.2* + mikmod? ( >=media-libs/libmikmod-3.1.6 ) + esd? ( >=media-sound/esound-0.2.22 ) + xml? ( >=dev-libs/libxml-1.8.15 ) + gnome? ( <gnome-base/gnome-panel-1.5.0 ) + opengl? ( virtual/opengl ) + oggvorbis? ( >=media-libs/libvorbis-1.0_beta4 )" +RDEPEND="${DEPEND} + directfb? ( dev-libs/DirectFB ) + nls? ( dev-util/intltool )" + +src_unpack() { + unpack ${P}.tar.gz + cd ${S} + + # Patch to allow external programmes to have the "jump to" dialog box + epatch ${FILESDIR}/${P}-jump.patch + + # Save playlist, etc on SIGTERM and SIGINT, bug #13604. + epatch ${FILESDIR}/${P}-sigterm.patch + + # Patch for mpg123 to convert Japanese character code of MP3 tag info + # the Japanese patch and the Russian one overlap, so its one or the other + if use cjk; then + epatch ${FILESDIR}/${P}-mpg123j.patch + else + # add russian charset support + epatch ${FILESDIR}/${P}-russian-charset.patch + fi + + # Add dynamic taste detection patch + epatch ${FILESDIR}/${P}-dtd.patch + + if [ ! -f ${S}/config.rpath ] ; then + touch ${S}/config.rpath + chmod +x ${S}/config.rpath + fi + + # Add /usr/local/share/xmms/Skins to the search path for skins + epatch ${FILESDIR}/${PN}-fhs-skinsdir.patch + + # This patch passes audio output through the output plugin + # before recording via the diskwriter plugin + # http://forum.xmms.org/viewtopic.php?t=500&sid=c286e1c01fb924a2f81f519969f33764 + epatch ${FILESDIR}/xmms-diskwriter-audio.patch + + export WANT_AUTOCONF_2_5=1 + for x in . libxmms ; do + cd ${S}/${x} + automake --gnu --add-missing --include-deps Makefile || die + done +} + +src_compile() { + local myconf="" + + # Allow configure to detect mipslinux systems + use mips && gnuconfig_update + + if [ `use 3dnow` ] || [ `use mmx` ] ; then + myconf="${myconf} --enable-simd" + else + myconf="${myconf} --disable-simd" + fi + + use xml || myconf="${myconf} --disable-cdindex" + + econf \ + --with-dev-dsp=/dev/sound/dsp \ + --with-dev-mixer=/dev/sound/mixer \ + `use_with gnome` \ + `use_enable oggvorbis vorbis` \ + `use_enable oggvorbis oggtest` \ + `use_enable oggvorbis vorbistest` \ + `use_enable esd` \ + `use_enable esd esdtest` \ + `use_enable mikmod` \ + `use_enable mikmod mikmodtest` \ + `use_with mikmod libmikmod` \ + `use_enable opengl` \ + `use_enable nls` \ + `use_enable ipv6` \ + ${myconf} \ + || die + + ### emake seems to break some compiles, please keep @ make + make || die +} + +src_install() { + einstall \ + incdir=${D}/usr/include \ + sysdir=${D}/usr/share/applets/Multimedia \ + GNOME_SYSCONFDIR=${D}/etc \ + install || die "make install failed" + + dodoc AUTHORS ChangeLog COPYING FAQ NEWS README TODO + + keepdir /usr/share/xmms/Skins + insinto /usr/share/pixmaps/ + donewins gnomexmms/gnomexmms.xpm xmms.xpm + doins xmms/xmms_logo.xpm + insinto /usr/share/pixmaps/mini + doins xmms/xmms_mini.xpm + + insinto /etc/X11/wmconfig + donewins xmms/xmms.wmconfig xmms + + if [ `use gnome` ] ; then + insinto /usr/share/gnome/apps/Multimedia + doins xmms/xmms.desktop + dosed "s:xmms_mini.xpm:mini/xmms_mini.xpm:" \ + /usr/share/gnome/apps/Multimedia/xmms.desktop + else + rm ${D}/usr/share/man/man1/gnomexmms* + fi + + # Add the sexy Gentoo Ice skin + insinto /usr/share/xmms/Skins + doins ${DISTDIR}/gentoo_ice.zip +} |