Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 28587
Collapse All | Expand All

(-)xmms-1.2.8-orig/AUTHORS (+1 lines)
Lines 42-47 Link Here
42
                         Chris Wilson
42
                         Chris Wilson
43
                         Dave Yearke
43
                         Dave Yearke
44
                         Stephan K. Zitz
44
                         Stephan K. Zitz
45
                         Johan Walles (Dynamic Taste Detection)
45
46
46
           Default skin: Leonard "Blayde" Tan
47
           Default skin: Leonard "Blayde" Tan
47
                         Robin Sylvestre (Equalizer and Playlist)
48
                         Robin Sylvestre (Equalizer and Playlist)
(-)xmms-1.2.8-orig/README (+29 lines)
Lines 57-62 Link Here
57
5. Features
57
5. Features
58
   5.1 Supported File formats
58
   5.1 Supported File formats
59
   5.2 Supported Features
59
   5.2 Supported Features
60
       5.2.1 Dynamic Taste Detection
60
6. Obtaining XMMS
61
6. Obtaining XMMS
61
7. Misc
62
7. Misc
62
   7.1 Shoutcast support
63
   7.1 Shoutcast support
Lines 1121-1126 Link Here
1121
Compiles and works on other UNIX's
1122
Compiles and works on other UNIX's
1122
Proxy authentication support
1123
Proxy authentication support
1123
1124
1125
5.2.1 Dynamic Taste Detection
1126
-----------------------------
1127
Basically, Dynamic Taste Detection collects statistics on how you
1128
listen to songs, and then adapts the shuffle play and randomize
1129
playlist functions to better follow your taste.  It takes care of
1130
itself entirely, so that's all you really need to know about it ;-).
1131
1132
For those of you who are curious, here is how it works.  Two kinds of
1133
statistics are collected:
1134
1135
- What songs you don't like
1136
- What songs you like to hear next to each other
1137
1138
The first one is easy.  When you skip a song, it looses a point.
1139
Songs with low scores are placed at the end of the playlist.
1140
1141
What songs you want to hear next to each other are a bit more
1142
intricate.  Let's say you have two songs, A and B.  A (or any song
1143
above it in the playlist) is playing.  You move song B to the position
1144
right after A.  A finishes playing, and B starts.  This is interpreted
1145
as "you want to hear A and B next to each other".
1146
1147
In this example, A and B will then *tend* to (i.e. not always) end up
1148
next to each other in the playlist (though not necessarily in that
1149
order).
1150
1151
To explicitly change your opinion of a song, or disconnect one song
1152
from another, you can do so from the right-click menu in the playlist.
1124
1153
1125
6. Obtaining XMMS
1154
6. Obtaining XMMS
1126
-------------------
1155
-------------------
(-)xmms-1.2.8-orig/xmms/dtd.c (+624 lines)
Line 0 Link Here
1
/*  XMMS - Cross-platform multimedia player
2
 *  Copyright (C) 2001  Johan Walles, d92-jwa@nada.kth.se
3
 *
4
 *  This program is free software; you can redistribute it and/or modify
5
 *  it under the terms of the GNU General Public License as published by
6
 *  the Free Software Foundation; either version 2 of the License, or
7
 *  (at your option) any later version.
8
 *
9
 *  This program is distributed in the hope that it will be useful,
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  GNU General Public License for more details.
13
 *
14
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program; if not, write to the Free Software
16
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
 */
18
19
/* Hint: DTD = Dynamic Taste Detection */
20
21
#include <assert.h>
22
23
#include "xmms.h"
24
25
static GList *dtd_recommendations_list;
26
27
static DtdNextSong *dtd_find_next_song(DtdStartSong *start_song,
28
				       const gchar *filename)
29
{
30
	GList *iterator;
31
32
	for (iterator = start_song->next_song;
33
	     iterator != NULL;
34
	     iterator = g_list_next(iterator))
35
	{
36
		if (strcmp(((DtdNextSong *)(iterator->data))->filename,
37
			   filename) == 0)
38
		{
39
			return (DtdNextSong *)(iterator->data);
40
		}
41
	}
42
43
	return NULL;
44
}
45
46
static DtdStartSong *dtd_find_start_song(const gchar *filename)
47
{
48
	GList *iterator;
49
50
	if (filename == NULL)
51
	{
52
		return NULL;
53
	}
54
	
55
	for (iterator = dtd_recommendations_list;
56
	     iterator != NULL;
57
	     iterator = g_list_next(iterator))
58
	{
59
		if (strcmp(((DtdStartSong *)(iterator->data))->filename,
60
			   filename) == 0)
61
		{
62
			return (DtdStartSong *)(iterator->data);
63
		}
64
	}
65
66
	return NULL;
67
}
68
69
/*
70
 * Memorize that after playing the file named start, the user might
71
 * want to hear the file named follow.
72
 */
73
static void __dtd_recommend_next(int weight, const gchar *start, const gchar *follow)
74
{
75
	DtdStartSong *start_song;
76
	DtdNextSong *next_song;
77
78
	/* Check if the start song already has recommendations. */
79
	if ((start_song = dtd_find_start_song(start)) ==
80
	    NULL)
81
	{
82
		/* Nope, create a new recommendation start node */
83
		start_song = g_new(DtdStartSong, 1);
84
		
85
		start_song->filename = g_strdup(start);
86
		start_song->next_song = NULL;
87
		start_song->score = 0;
88
89
		/* Add the recommendation start node to the
90
		   recommendation start node list. */
91
		dtd_recommendations_list =
92
			g_list_append(dtd_recommendations_list,
93
				      start_song);
94
	}
95
96
	/* Check if the next song is already recommended for this
97
	   start song */
98
	if ((next_song = dtd_find_next_song(start_song,
99
					    follow)) == NULL)
100
	{
101
		/* It's not.  Create a recommendation next node */
102
		next_song = g_new(DtdNextSong, 1);
103
104
		next_song->filename = g_strdup(follow);
105
106
		next_song->weight = 0;
107
108
		/* Add the new recommendation to the start node */
109
		start_song->next_song =
110
			g_list_append(start_song->next_song,
111
				      next_song);
112
	}
113
	
114
	next_song->weight += weight;
115
}
116
117
/*
118
 * Memorize that after playing the file named start, the user might
119
 * want to hear the file named follow.
120
 */
121
void dtd_recommend_next(const gchar *start, const gchar *follow)
122
{
123
	/* Recommend symmetrically */
124
	__dtd_recommend_next(1, start, follow);
125
	__dtd_recommend_next(1, follow, start);
126
}
127
128
void __dtd_dissociate(const gchar *file1, const gchar *file2)
129
{
130
	DtdStartSong *startSong;
131
	DtdNextSong *nextSong = NULL;
132
	GList *nextSongIterator;
133
	
134
	// Find the start pointer for file1
135
	startSong = dtd_find_start_song(file1);
136
	if (startSong == NULL)
137
	{
138
		// No such recommendation exists
139
		return;
140
	}
141
	
142
	// Find its next pointer for file2
143
	for (nextSongIterator = startSong->next_song;
144
	     nextSongIterator != NULL;
145
	     nextSongIterator = g_list_next(nextSongIterator))
146
	{
147
		nextSong = (DtdNextSong *)(nextSongIterator->data);
148
		if (strcmp(file2, nextSong->filename) == 0)
149
		{
150
			break;
151
		}
152
	}
153
	if (nextSong == NULL)
154
	{
155
		// No such recommendation exists
156
		return;
157
	}
158
	
159
	// Remove the next pointer
160
	startSong->next_song =
161
		g_list_remove_link(startSong->next_song, nextSongIterator);
162
	g_free(nextSong->filename);
163
	g_free(nextSong);
164
	g_list_free_1(nextSongIterator);
165
}
166
167
void dtd_dissociate(const gchar *file1, const gchar *file2)
168
{
169
	// The slave's chain is heavy in both ends, so let's set 'em
170
	// both free :-)
171
	__dtd_dissociate(file1, file2);
172
	__dtd_dissociate(file2, file1);
173
}
174
175
/*
176
 * Modify a song's score.
177
 */
178
void dtd_set_score(const gchar *filename, gint score)
179
{
180
	DtdStartSong *start_song = dtd_find_start_song(filename);
181
182
	if (start_song == NULL)
183
	{
184
		start_song = g_new(DtdStartSong, 1);
185
		
186
		start_song->filename = g_strdup(filename);
187
		start_song->next_song = NULL;
188
		start_song->score = 0;
189
190
		/* Add the new recommendation start node to the
191
		   recommendation start node list. */
192
		dtd_recommendations_list =
193
			g_list_append(dtd_recommendations_list,
194
				      start_song);
195
	}
196
197
	start_song->score = score;
198
	/* Scores > 0 means "always put this song at the start of the
199
	   list".  Probably we don't want that.
200
	*/
201
	if (start_song->score > 0)
202
	{
203
		start_song->score = 0;
204
	}
205
}
206
207
void dtd_change_score(const gchar *filename, gint delta)
208
{
209
	DtdStartSong *start_song = dtd_find_start_song(filename);
210
211
	if (start_song == NULL)
212
	{
213
		start_song = g_new(DtdStartSong, 1);
214
		
215
		start_song->filename = g_strdup(filename);
216
		start_song->next_song = NULL;
217
		start_song->score = 0;
218
219
		/* Add the new recommendation start node to the
220
		   recommendation start node list. */
221
		dtd_recommendations_list =
222
			g_list_append(dtd_recommendations_list,
223
				      start_song);
224
	}
225
226
	/* Scores > 0 means "always put this song at the start of the
227
	   list".  Probably we don't want that.
228
	*/
229
	start_song->score += delta;
230
	if (start_song->score > 0)
231
	{
232
		start_song->score = 0;
233
	}
234
}
235
236
/*
237
 * Retrieve a song's score.
238
 */
239
gint dtd_get_score(const gchar *filename)
240
{
241
	DtdStartSong *start_song = dtd_find_start_song(filename);
242
243
	if (start_song == NULL)
244
	{
245
		return 0;
246
	}
247
	else
248
	{
249
		return start_song->score;
250
	}
251
}
252
253
const GList *dtd_get_recommendations(const gchar *start)
254
{
255
	DtdStartSong *dtdStartSong = dtd_find_start_song(start);
256
257
	return dtdStartSong ? dtdStartSong->next_song : NULL;
258
}
259
260
static void __dtd_set_score(gchar *filename, gint score)
261
{
262
	DtdStartSong *start_song = dtd_find_start_song(filename);
263
264
	if (start_song == NULL)
265
	{
266
		start_song = g_new(DtdStartSong, 1);
267
		
268
		start_song->filename = g_strdup(filename);
269
		start_song->next_song = NULL;
270
		start_song->score = 0;
271
272
		/* Add the new recommendation start node to the
273
		   recommendation start node list. */
274
		dtd_recommendations_list =
275
			g_list_append(dtd_recommendations_list,
276
				      start_song);
277
	}
278
279
	start_song->score = score;
280
}
281
282
static guint dtd_sum_recommendations(DtdStartSong *start_song)
283
{
284
	GList *iterator;
285
	guint sum = 0;
286
287
	assert(start_song != NULL);
288
289
	for (iterator = start_song->next_song;
290
	     iterator != NULL;
291
	     iterator = g_list_next(iterator))
292
	{
293
		sum += ((DtdNextSong *)(iterator->data))->weight;
294
	}
295
296
	assert(sum != 0);
297
	
298
	return sum;
299
}
300
301
static const gchar *dtd_get_nth_recommendation(DtdStartSong *start_song,
302
					       guint n)
303
{
304
	GList *iterator;
305
	guint sum = 0;
306
307
	assert(start_song != NULL);
308
309
	if (n == 0)
310
		return NULL;
311
	
312
	for (iterator = start_song->next_song;
313
	     iterator != NULL;
314
	     iterator = g_list_next(iterator))
315
	{
316
		sum += ((DtdNextSong *)(iterator->data))->weight;
317
318
		if (sum >= n)
319
			return ((DtdNextSong *)(iterator->data))->filename;
320
	}
321
322
	/* If we get here then the n passed to this function was too
323
	   large.  It can be no larger than the number calculated by
324
	   dtd_sum_recommendations(). */
325
	assert(FALSE);
326
	
327
	return NULL;
328
}
329
330
/*
331
  Try to recommend a song to play after first_filename.  NULL means
332
  that no recommendation is given (for whatever reason).
333
334
  FIXME: This method should accept a list of songs *not* to choose
335
  between.  If the user has asked for a recommendation and received an
336
  answer that is not in the current playlist, they must be able to try
337
  again.
338
*/
339
const gchar *dtd_get_recommendation(const gchar *first_filename)
340
{
341
	DtdStartSong *start_song;
342
	guint recommendation_total;
343
344
	if (first_filename == NULL)
345
	{
346
		return NULL;
347
	}
348
	
349
	if ((start_song = dtd_find_start_song(first_filename)) ==
350
	    NULL)
351
	{
352
		/* There are no recommendations for first_filename */
353
		return NULL;
354
	}
355
356
	if (start_song->next_song == NULL)
357
	{
358
		return NULL;
359
	}
360
	
361
	recommendation_total = dtd_sum_recommendations(start_song);
362
363
	return dtd_get_nth_recommendation(start_song,
364
					  rand() % (recommendation_total + 1));
365
}
366
367
static void dtd_clear(void)
368
{
369
	GList *start_iterator;
370
	GList *next_iterator;
371
372
	if (dtd_recommendations_list == NULL)
373
	{
374
		return;
375
	}
376
	
377
	/* Free all memory used by the recommendations */
378
	for (start_iterator = dtd_recommendations_list;
379
	     start_iterator != NULL;
380
	     start_iterator = g_list_next(start_iterator))
381
	{
382
		/* Free all file names in the next list */
383
		for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
384
		     next_iterator != NULL;
385
		     next_iterator = g_list_next(next_iterator))
386
		{
387
			free(((DtdNextSong *)(next_iterator->data))->filename);
388
			((DtdNextSong *)(next_iterator->data))->filename = NULL;
389
			
390
			free(next_iterator->data);
391
			next_iterator->data = NULL;
392
		}
393
		/* Free the next list */
394
		g_list_free(((DtdStartSong *)(start_iterator->data))->next_song);
395
		((DtdStartSong *)(start_iterator->data))->next_song = NULL;
396
397
		/* Free the file name */
398
		free(((DtdStartSong *)(start_iterator->data))->filename);
399
		((DtdStartSong *)(start_iterator->data))->filename = NULL;
400
401
		/* Free the struct */
402
		free(start_iterator->data);
403
		start_iterator->data = NULL;
404
	}
405
	g_list_free(dtd_recommendations_list);
406
	dtd_recommendations_list = NULL;
407
}
408
409
void dtd_init(void)
410
{
411
	gchar *dtd_data_file_name;
412
	FILE *dtd_data_file;
413
414
	gchar start[950], follow[950];
415
	gchar line[999];
416
	int weight;
417
418
	enum { NEXT, SCORES } section = NEXT;
419
420
	gboolean failure = FALSE;
421
422
	/* We have no recommendations to begin with */
423
	dtd_recommendations_list = NULL;
424
425
	/* Make up a filename for the dtd data file */
426
	dtd_data_file_name = g_strconcat(g_get_home_dir(),
427
					 "/.xmms/",
428
					 DTD_DATA_FILE_NAME,
429
					 NULL);
430
431
	/* Open the dtd data file for input */
432
	dtd_data_file = fopen(dtd_data_file_name,
433
			      "r");
434
	if (dtd_data_file == NULL)
435
	{
436
		return;
437
	}
438
439
	/* Parse the dtd data file */
440
	start[0] = '\0';	
441
	while (!feof(dtd_data_file))
442
	{
443
		if (fgets(line, 990, dtd_data_file) != NULL)
444
		{
445
			if (sscanf(line, "%d %940[^\n]\n", &weight, follow) == 2)
446
			{
447
				/* Found a line with both number and name */
448
449
				switch (section)
450
				{
451
				case NEXT:
452
					if (start[0] != '\0')
453
					{
454
						if (weight < 0)
455
						{
456
							failure= TRUE;
457
						}
458
						else if (weight > 0)
459
						{
460
							__dtd_recommend_next(weight, start, follow);
461
						}
462
					}
463
					else
464
					{
465
						/* Parse error! */
466
						failure = TRUE;
467
					}
468
					break;
469
470
				case SCORES:
471
					if (weight != 0)
472
					{
473
						__dtd_set_score(follow, weight);
474
					}
475
					break;
476
				}
477
478
				if (failure) break;
479
			}
480
			else if (sscanf(line, "%940[^\n]\n", start) == 1)
481
			{
482
				if (strcmp(start, "scores") == 0)
483
				{
484
					/* The scores section starts here */
485
					section = SCORES;
486
					start[0] = '\0';
487
				}
488
				else
489
				{
490
					/* New start song read OK */
491
					section = NEXT;
492
				}
493
			}
494
			else
495
			{
496
				/* Parse error! */
497
				failure = TRUE;
498
				break;
499
			}
500
		}
501
	}
502
503
	if (failure)
504
	{
505
		/* Free all space occupied by the recommendations list
506
		   in favour of remembering a broken list. */
507
		dtd_clear();
508
	}
509
	
510
	fclose(dtd_data_file);
511
	dtd_data_file = NULL;
512
}
513
514
void dtd_persist(void)
515
{
516
	gchar *dtd_data_file_name;
517
	FILE *dtd_data_file;
518
519
	GList *start_iterator;
520
	GList *next_iterator;
521
522
	gboolean failure = FALSE;
523
524
	/* Are there any recommendations to store */
525
	if (dtd_recommendations_list == NULL)
526
	{
527
		/* Nope. */
528
		return;
529
	}
530
	
531
	/* Make up a filename for the dtd data file */
532
	dtd_data_file_name = g_strconcat(g_get_home_dir(),
533
					 "/.xmms/",
534
					 DTD_DATA_FILE_NAME,
535
					 NULL);
536
537
	/* Open the dtd data file for output */
538
	dtd_data_file = fopen(dtd_data_file_name,
539
			      "w");
540
	if (dtd_data_file == NULL)
541
	{
542
		/* FIXME: We fail silently.  Should we output a
543
		 * warning somehow? */
544
		return;
545
	}
546
	
547
	/* Loop through all recommendation starts, storing their
548
	   recommended followers */
549
	for (start_iterator = dtd_recommendations_list;
550
	     start_iterator != NULL;
551
	     start_iterator = g_list_next(start_iterator))
552
	{
553
		if (((DtdStartSong *)(start_iterator->data))->next_song == NULL)
554
		{
555
			/* Don't store songs with only a score (yet). */
556
			continue;
557
		}
558
559
		if (failure ||
560
		    (fprintf(dtd_data_file,
561
			     "%s\n",
562
			     ((DtdStartSong *)(start_iterator->data))->filename) == 0))
563
		{
564
			failure = TRUE;
565
			break;
566
		}
567
568
		for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
569
		     next_iterator != NULL;
570
		     next_iterator = g_list_next(next_iterator))
571
		{
572
			if (fprintf(dtd_data_file, "%d %s\n",
573
				    ((DtdNextSong *)(next_iterator->data))->weight,
574
				    ((DtdNextSong *)(next_iterator->data))->filename) == 0)
575
			{
576
				failure = TRUE;
577
				break;
578
			}
579
		}
580
	}
581
582
	if (!failure)
583
	{
584
		/* Output the "here starts the scoring section" marker. */
585
		if (fprintf(dtd_data_file, "scores\n") == 0)
586
		{
587
			failure = TRUE;
588
		}
589
590
		/* Output all (non-zero) scores */
591
		for (start_iterator = dtd_recommendations_list;
592
		     start_iterator != NULL;
593
		     start_iterator = g_list_next(start_iterator))
594
		{
595
			if (((DtdStartSong *)(start_iterator->data))->score == 0)
596
			{
597
				/* Don't store songs with zero score. */
598
				continue;
599
			}
600
601
			if (failure ||
602
			    (fprintf(dtd_data_file,
603
				     "%d %s\n",
604
				     ((DtdStartSong *)(start_iterator->data))->score,
605
				     ((DtdStartSong *)(start_iterator->data))->filename) == 0))
606
			{
607
				failure = TRUE;
608
				break;
609
			}
610
		}
611
       }
612
613
	fclose(dtd_data_file);
614
	dtd_data_file = NULL;
615
616
	if (failure)
617
	{
618
		/* If writing of the data file failed, attempt to
619
		   remove it rather than leaving a broken data file
620
		   behind. */
621
622
		unlink(dtd_data_file_name);
623
	}
624
}
(-)xmms-1.2.8-orig/xmms/dtd.h (+73 lines)
Line 0 Link Here
1
/*  XMMS - Cross-platform multimedia player
2
 *  Copyright (C) 2001  Johan Walles, d92-jwa@nada.kth.se
3
 *
4
 *  This program is free software; you can redistribute it and/or modify
5
 *  it under the terms of the GNU General Public License as published by
6
 *  the Free Software Foundation; either version 2 of the License, or
7
 *  (at your option) any later version.
8
 *
9
 *  This program is distributed in the hope that it will be useful,
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  GNU General Public License for more details.
13
 *
14
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program; if not, write to the Free Software
16
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
 */
18
19
/* Hint: DTD = Dynamic Taste Detection */
20
21
/* FIXME: Should lists prove to be too slow we could use balanced
22
   trees for the DtdStartSong collection.  The DtdNextSong is scanned
23
   through however, so tree-ifying will do no good. */
24
25
#ifndef DTD_H
26
#define DTD_H
27
28
#define DTD_DATA_FILE_NAME "dtd-stats"
29
30
typedef struct
31
{
32
	gchar *filename;
33
	guint weight;
34
}
35
DtdNextSong;
36
37
typedef struct
38
{
39
	gchar *filename;
40
	gint score;
41
	GList *next_song;
42
}
43
DtdStartSong;
44
45
void dtd_init(void);
46
void dtd_persist(void);
47
48
// Tell DTD that after start, the user tends to want to hear follow
49
void dtd_recommend_next(const gchar *start,
50
			const gchar *follow);
51
52
// Tell DTD to break the association between file1 and file2 (if one
53
// exists)
54
void dtd_dissociate(const gchar *file1,
55
		    const gchar *file2);
56
57
58
// Let DTD recommend what to play after start.  NULL = No
59
// recommendation = anything goes.
60
const gchar *dtd_get_recommendation(const gchar *start);
61
62
// Change the absolute score for a file
63
void dtd_change_score(const gchar *filename, gint delta);
64
void dtd_set_score(const gchar *filename, gint score);
65
66
// Get the score for a file.  Unknown file = score 0.
67
gint dtd_get_score(const gchar *filename);
68
69
// Get the list of recommendations for a given file.  NULL = no
70
// recommendations for that file.
71
const GList *dtd_get_recommendations(const gchar *start);
72
73
#endif
(-)xmms-1.2.8-orig/xmms/main.c (+10 lines)
Lines 475-480 Link Here
475
			}
475
			}
476
		}
476
		}
477
		xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format);
477
		xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format);
478
479
		xmms_cfg_read_boolean(cfgfile, "xmms", "dynamic_taste_detection", &cfg.dynamic_taste_detection);
478
		
480
		
479
		xmms_cfg_free(cfgfile);
481
		xmms_cfg_free(cfgfile);
480
	}
482
	}
Lines 670-675 Link Here
670
		g_free(str);
672
		g_free(str);
671
	}
673
	}
672
	xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format);
674
	xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format);
675
676
	xmms_cfg_write_boolean(cfgfile, "xmms", "dynamic_taste_detection", cfg.dynamic_taste_detection);
673
	
677
	
674
	xmms_cfg_write_file(cfgfile, filename);
678
	xmms_cfg_write_file(cfgfile, filename);
675
	xmms_cfg_free(cfgfile);
679
	xmms_cfg_free(cfgfile);
Lines 898-903 Link Here
898
	playlist_clear();
902
	playlist_clear();
899
	cleanup_plugins();
903
	cleanup_plugins();
900
	sm_cleanup();
904
	sm_cleanup();
905
	dtd_persist();
901
	gtk_exit(0);
906
	gtk_exit(0);
902
}
907
}
903
908
Lines 3120-3125 Link Here
3120
		GDK_THREADS_LEAVE();
3125
		GDK_THREADS_LEAVE();
3121
	}
3126
	}
3122
3127
3128
        cfg.dynamic_taste_detection = TRUE;
3123
3129
3124
	GDK_THREADS_ENTER();
3130
	GDK_THREADS_ENTER();
3125
	check_ctrlsocket();
3131
	check_ctrlsocket();
Lines 3583-3588 Link Here
3583
	mainwin_timeout_tag = gtk_timeout_add(10, idle_func, NULL); 
3589
	mainwin_timeout_tag = gtk_timeout_add(10, idle_func, NULL); 
3584
	playlist_start_get_info_thread();
3590
	playlist_start_get_info_thread();
3585
3591
3592
3593
	/* Initialize dynamic taste detection */
3594
	dtd_init();
3595
3586
	enable_x11r5_session_management(argc, argv);
3596
	enable_x11r5_session_management(argc, argv);
3587
	sm_init(argc, argv, options.previous_session_id);
3597
	sm_init(argc, argv, options.previous_session_id);
3588
	gtk_main();
3598
	gtk_main();
(-)xmms-1.2.8-orig/xmms/main.h (+1 lines)
Lines 45-50 Link Here
45
	gboolean use_backslash_as_dir_delimiter;
45
	gboolean use_backslash_as_dir_delimiter;
46
	gboolean random_skin_on_play, use_fontsets;
46
	gboolean random_skin_on_play, use_fontsets;
47
	gboolean mainwin_use_xfont;
47
	gboolean mainwin_use_xfont;
48
	gboolean dynamic_taste_detection;
48
	gfloat equalizer_preamp, equalizer_bands[10];
49
	gfloat equalizer_preamp, equalizer_bands[10];
49
	gchar *skin, *outputplugin, *filesel_path, *playlist_path;
50
	gchar *skin, *outputplugin, *filesel_path, *playlist_path;
50
	gchar *playlist_font, *mainwin_font;
51
	gchar *playlist_font, *mainwin_font;
(-)xmms-1.2.8-orig/xmms/Makefile.am (+1 lines)
Lines 34-39 Link Here
34
menurow.c menurow.h \
34
menurow.c menurow.h \
35
hslider.c hslider.h \
35
hslider.c hslider.h \
36
monostereo.c monostereo.h \
36
monostereo.c monostereo.h \
37
dtd.c dtd.h \
37
vis.c vis.h \
38
vis.c vis.h \
38
svis.c svis.h \
39
svis.c svis.h \
39
number.c number.h \
40
number.c number.h \
(-)xmms-1.2.8-orig/xmms/Makefile.in (-2 / +3 lines)
Lines 184-190 Link Here
184
-I$(top_builddir)/intl -I$(top_srcdir)
184
-I$(top_builddir)/intl -I$(top_srcdir)
185
185
186
186
187
xmms_SOURCES = bmp.c bmp.h \
187
xmms_SOURCES = dtd.c dtd.h \
188
bmp.c bmp.h \
188
skin.c skin.h \
189
skin.c skin.h \
189
util.c util.h \
190
util.c util.h \
190
output.c output.h \
191
output.c output.h \
Lines 245-251 Link Here
245
CPPFLAGS = @CPPFLAGS@
246
CPPFLAGS = @CPPFLAGS@
246
LDFLAGS = @LDFLAGS@
247
LDFLAGS = @LDFLAGS@
247
LIBS = @LIBS@
248
LIBS = @LIBS@
248
xmms_OBJECTS =  bmp.$(OBJEXT) skin.$(OBJEXT) util.$(OBJEXT) \
249
xmms_OBJECTS = dtd.$(OBJEXT) bmp.$(OBJEXT) skin.$(OBJEXT) util.$(OBJEXT) \
249
output.$(OBJEXT) fft.$(OBJEXT) input.$(OBJEXT) effect.$(OBJEXT) \
250
output.$(OBJEXT) fft.$(OBJEXT) input.$(OBJEXT) effect.$(OBJEXT) \
250
general.$(OBJEXT) visualization.$(OBJEXT) fullscreen.$(OBJEXT) \
251
general.$(OBJEXT) visualization.$(OBJEXT) fullscreen.$(OBJEXT) \
251
pluginenum.$(OBJEXT) playlist.$(OBJEXT) controlsocket.$(OBJEXT) \
252
pluginenum.$(OBJEXT) playlist.$(OBJEXT) controlsocket.$(OBJEXT) \
(-)xmms-1.2.8-orig/xmms/playlist.c (-23 / +410 lines)
Lines 22-27 Link Here
22
#include "libxmms/util.h"
22
#include "libxmms/util.h"
23
#include <sys/stat.h>
23
#include <sys/stat.h>
24
#include <unistd.h>
24
#include <unistd.h>
25
#include <assert.h>
25
26
26
GList *playlist = NULL;
27
GList *playlist = NULL;
27
GList *shuffle_list = NULL;
28
GList *shuffle_list = NULL;
Lines 652-657 Link Here
652
653
653
	plist_pos_list = find_playlist_position_list();
654
	plist_pos_list = find_playlist_position_list();
654
655
656
        if ((plist_pos_list != NULL) &&
657
            (cfg.repeat || (g_list_next(plist_pos_list) != NULL)))
658
        {
659
                /*
660
                  The user is skipping a song.  We interpret this as
661
                  meaning "I don't like this song".
662
                */
663
                dtd_change_score(((PlaylistEntry *)(plist_pos_list->data))->filename,
664
                                 -1);
665
        }
666
        
655
	if (!cfg.repeat && !g_list_next(plist_pos_list))
667
	if (!cfg.repeat && !g_list_next(plist_pos_list))
656
	{
668
	{
657
		PL_UNLOCK();
669
		PL_UNLOCK();
Lines 724-730 Link Here
724
	
736
	
725
	plist_pos_list = find_playlist_position_list();
737
	plist_pos_list = find_playlist_position_list();
726
	if (g_list_previous(plist_pos_list))
738
	if (g_list_previous(plist_pos_list))
727
		playlist_position = plist_pos_list->prev->data;
739
        {
740
                playlist_position =
741
                        g_list_previous(plist_pos_list)->data;
742
743
                /*
744
                  The user has gone back to a song.  We interpret that
745
                  as meaning "I like this song so much that I want to
746
                  hear it again".
747
                */
748
                dtd_change_score(playlist_position->filename,
749
                                 1);
750
        }
728
	else if (cfg.repeat)
751
	else if (cfg.repeat)
729
	{
752
	{
730
		GList *node;
753
		GList *node;
Lines 739-745 Link Here
739
	}
762
	}
740
	PL_UNLOCK();
763
	PL_UNLOCK();
741
	playlist_check_pos_current();
764
	playlist_check_pos_current();
742
765
        
743
	if (restart_playing)
766
	if (restart_playing)
744
		playlist_play();
767
		playlist_play();
745
	else
768
	else
Lines 855-860 Link Here
855
878
856
	playlist_position = node->data;
879
	playlist_position = node->data;
857
	PL_UNLOCK();
880
	PL_UNLOCK();
881
        ((PlaylistEntry *)playlist_position)->moved = FALSE;
858
	playlist_check_pos_current();
882
	playlist_check_pos_current();
859
883
860
	if (restart_playing)
884
	if (restart_playing)
Lines 874-885 Link Here
874
898
875
void playlist_eof_reached(void)
899
void playlist_eof_reached(void)
876
{
900
{
901
        /*
902
         * This function is called whenever a song ends and the next
903
         * one should start playing.
904
         */
905
        
877
	GList *plist_pos_list;
906
	GList *plist_pos_list;
907
	GList *previous_plist_pos;
878
908
879
	input_stop();
909
	input_stop();
880
910
881
	PL_LOCK();
911
	PL_LOCK();
882
	plist_pos_list = find_playlist_position_list();
912
	plist_pos_list = find_playlist_position_list();
913
        previous_plist_pos = plist_pos_list->data;
883
914
884
	if (cfg.no_playlist_advance)
915
	if (cfg.no_playlist_advance)
885
	{
916
	{
Lines 911-916 Link Here
911
	else
942
	else
912
		playlist_position = plist_pos_list->next->data;
943
		playlist_position = plist_pos_list->next->data;
913
	PL_UNLOCK();
944
	PL_UNLOCK();
945
946
        if (!cfg.shuffle && (((PlaylistEntry *)playlist_position)->moved))
947
        {
948
                /*
949
                 * Song A has stopped playing.  Song B will start
950
                 * playing now.  Song B had moved.  Thus, we conclude
951
                 * that the user wants to hear Song B after song A.
952
                 * Therefore, Song B should be added to Song A's
953
                 * next-song-preferences.
954
                 *
955
                 * Song A is previous_plist_pos.  Song B is
956
                 * playlist_position.  */
957
                
958
                dtd_recommend_next(((PlaylistEntry *)previous_plist_pos)->filename,
959
                                   ((PlaylistEntry *)playlist_position)->filename);
960
961
                ((PlaylistEntry *)playlist_position)->moved = FALSE;
962
        }
963
        
914
	playlist_check_pos_current();
964
	playlist_check_pos_current();
915
	playlist_play();
965
	playlist_play();
916
	mainwin_set_info_text();
966
	mainwin_set_info_text();
Lines 1130-1135 Link Here
1130
	ext = strrchr(filename, '.');
1180
	ext = strrchr(filename, '.');
1131
	if (ext && !strcasecmp(ext, ".pls"))
1181
	if (ext && !strcasecmp(ext, ".pls"))
1132
	{
1182
	{
1183
		/* It's a playlist, let's parse it. */
1133
		int noe, i;
1184
		int noe, i;
1134
		char key[10];
1185
		char key[10];
1135
		
1186
		
Lines 1263-1268 Link Here
1263
	char *ret;
1314
	char *ret;
1264
	PlaylistEntry *entry;
1315
	PlaylistEntry *entry;
1265
	GList *node;
1316
	GList *node;
1317
1318
	if (pos < 0)
1319
	{
1320
		return NULL;
1321
	}
1266
	
1322
	
1267
	PL_LOCK();
1323
	PL_LOCK();
1268
	if (!playlist)
1324
	if (!playlist)
Lines 1270-1275 Link Here
1270
		PL_UNLOCK();
1326
		PL_UNLOCK();
1271
		return NULL;
1327
		return NULL;
1272
	}
1328
	}
1329
	if (pos >= __get_playlist_length())
1330
	{
1331
		PL_UNLOCK();
1332
		return NULL;
1333
	}
1273
	node = g_list_nth(playlist, pos);
1334
	node = g_list_nth(playlist, pos);
1274
	if (!node)
1335
	if (!node)
1275
	{
1336
	{
Lines 1327-1332 Link Here
1327
	return title;
1388
	return title;
1328
}
1389
}
1329
1390
1391
int playlist_get_score(gint pos)
1392
{
1393
	PlaylistEntry *entry;
1394
	GList *node;
1395
	gint score;
1396
1397
	PL_LOCK();
1398
	if (!playlist)
1399
	{
1400
		PL_UNLOCK();
1401
		return 0;
1402
	}
1403
	node = g_list_nth(playlist, pos);
1404
	if (!node)
1405
	{
1406
		PL_UNLOCK();
1407
		return 0;
1408
	}
1409
	entry = node->data;
1410
	score = dtd_get_score(entry->filename);
1411
	PL_UNLOCK();
1412
1413
	return score;
1414
}
1415
1416
void playlist_set_score(gint pos, gint score)
1417
{
1418
	PlaylistEntry *entry;
1419
	GList *node;
1420
1421
	PL_LOCK();
1422
	if (!playlist)
1423
	{
1424
		PL_UNLOCK();
1425
		return;
1426
	}
1427
	node = g_list_nth(playlist, pos);
1428
	if (!node)
1429
	{
1430
		PL_UNLOCK();
1431
		return;
1432
	}
1433
	entry = node->data;
1434
	dtd_set_score(entry->filename, score);
1435
	PL_UNLOCK();
1436
}
1437
1438
const gchar* playlist_filename2songtitle(const gchar *filename)
1439
{
1440
	GList *iterator;
1441
	
1442
	if (filename == NULL)
1443
	{
1444
		return NULL;
1445
	}
1446
1447
	// Scan loaded songs for filename
1448
	PL_LOCK();
1449
	for (iterator = get_playlist();
1450
	     iterator != NULL;
1451
	     iterator = g_list_next(iterator))
1452
	{
1453
		PlaylistEntry *currentEntry =
1454
			(PlaylistEntry *)(iterator->data);
1455
1456
		if (strcmp(currentEntry->filename, filename) == 0)
1457
		{
1458
			// FIXME: Race condition
1459
			PL_UNLOCK();
1460
			return currentEntry->title;
1461
		}
1462
	}
1463
	PL_UNLOCK();
1464
1465
	// Not found / don't know
1466
	return NULL;
1467
}
1468
1330
gint playlist_get_songtime(gint pos)
1469
gint playlist_get_songtime(gint pos)
1331
{
1470
{
1332
	int retval = -1;
1471
	int retval = -1;
Lines 1567-1572 Link Here
1567
	PL_UNLOCK();
1706
	PL_UNLOCK();
1568
}
1707
}
1569
1708
1709
static guint playlist_filename_to_index(PlaylistEntry *playlist_entries[],
1710
                                        guint n_entries,
1711
                                        const gchar *filename)
1712
{
1713
        guint i;
1714
1715
        for (i = 0; i < n_entries; i++)
1716
        {
1717
                if (strcmp(playlist_entries[i]->filename,
1718
                           filename) == 0)
1719
                {
1720
                        return i;
1721
                }
1722
        }
1723
1724
        return -1;
1725
}
1726
1727
/* This function is used by the qsort() call in
1728
   smart_playlist_shuffle_list() (below). */
1570
void playlist_sort_selected_by_date(void)
1729
void playlist_sort_selected_by_date(void)
1571
{
1730
{
1572
	PL_LOCK();
1731
	PL_LOCK();
Lines 1581-1587 Link Here
1581
	PL_UNLOCK();
1740
	PL_UNLOCK();
1582
}
1741
}
1583
1742
1584
static GList *playlist_shuffle_list(GList *list)
1743
static int playlist_entry_score_comparator(const void *a,
1744
                                           const void *b)
1745
{
1746
1747
	int score_a = dtd_get_score((*((PlaylistEntry **)a))->filename);
1748
	int score_b = dtd_get_score((*((PlaylistEntry **)b))->filename);
1749
1750
	/* We want high scores before low, so if score_a > score_b we
1751
	want to return something negative. */
1752
1753
	return score_b - score_a;
1754
}
1755
1756
static GList *smart_playlist_shuffle_list(GList *list)
1585
{
1757
{
1586
	/* Caller should hold playlist mutex */
1758
	/* Caller should hold playlist mutex */
1587
	/*
1759
	/*
Lines 1590-1632 Link Here
1590
	 * fuction is run.
1762
	 * fuction is run.
1591
	 */
1763
	 */
1592
	int len = g_list_length(list);
1764
	int len = g_list_length(list);
1593
	int i, j;
1765
	gint i;
1594
	GList *node, **ptrs;
1766
	gint next_score_section = -1;
1767
	GList *iterator;
1768
	PlaylistEntry **ptrs;
1595
1769
1596
	if (!len)
1770
	if (len == 0)
1597
		return NULL;
1771
		return NULL;
1598
1772
1599
	ptrs = g_new(GList *, len);
1773
	ptrs = g_new(PlaylistEntry *, len);
1600
1774
1601
	for (node = list, i = 0; i < len; node = g_list_next(node), i++)
1775
	/* Convert the list into an array of pointers */
1602
		ptrs[i] = node;
1776
	for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
1777
        {
1778
              ptrs[i] = (PlaylistEntry *)(iterator->data);
1779
1780
                // Shuffling songs voids information about user listening preferences
1781
                ptrs[i]->moved = FALSE;
1782
        }
1783
        g_list_free(list);
1784
        list = NULL;
1785
1786
        /* Sort the array by score */
1787
        qsort(ptrs, len, sizeof(PlaylistEntry *), playlist_entry_score_comparator);
1788
1789
        /* Shuffle the pointer array */
1790
        for (i = 0; i < len; i++)
1791
        {
1792
                PlaylistEntry *swap_ptr;
1793
                gint j;
1794
1795
                const gchar *previous_filename;
1796
                const gchar *new_filename;
1797
1798
                if (i >= next_score_section)
1799
                {
1800
                        /* We are in a new score section; find out
1801
                           where the next one starts */
1802
                        gint current_score =
1803
                                dtd_get_score(ptrs[i]->filename);
1804
1805
                        for (next_score_section = i;
1806
                             next_score_section < len;
1807
                             next_score_section++)
1808
                        {
1809
                                if (dtd_get_score(ptrs[next_score_section]->filename) !=
1810
                                    current_score)
1811
                                {
1812
                                        break;
1813
                                }
1814
                        }
1815
                }
1816
1817
                if (i == 0)
1818
                {
1819
                        previous_filename = NULL;
1820
                }
1821
                else
1822
                {
1823
                        previous_filename = ptrs[i - 1]->filename;
1824
                }
1825
1826
                /* Find out from which index we should get the next song */
1827
                do {
1828
                        if ((new_filename = dtd_get_recommendation(previous_filename)) !=
1829
                            NULL)
1830
                        {
1831
                                j = playlist_filename_to_index(&(ptrs[i]),
1832
                                                               len - i,
1833
                                                               new_filename);
1834
                                if (j != -1)
1835
                                        j += i;
1836
                        }
1837
                        else
1838
                        {
1839
                                /* Only find new songs in our current
1840
                                   score section */
1841
                                j = (rand() % (next_score_section - i)) + i;
1842
1843
                                assert(j < next_score_section);
1844
                        }
1845
                } while (j == -1);
1846
1847
                assert(j < len);
1848
                assert(j >= i);
1849
               
1850
                if (j < next_score_section)
1851
                {
1852
                        /* Swap pointer #i and pointer #j */
1853
                        swap_ptr = ptrs[i];
1854
                        ptrs[i] = ptrs[j];
1855
                        ptrs[j] = swap_ptr;
1856
                }
1857
                else
1858
                {
1859
                        /*
1860
                          We need to preserve the score section
1861
                          ordering, so we do it the slow way if i and
1862
                          j are in different scoring sections.
1863
                        */
1864
                        gint k;
1865
                        swap_ptr = ptrs[i];
1866
                        ptrs[i] = ptrs[j];
1867
1868
                        for (k = j; k >= (i + 2); k--)
1869
                        {
1870
                                ptrs[k] = ptrs[k - 1];
1871
                        }
1872
1873
                        ptrs[i + 1] = swap_ptr;
1874
                }
1875
        }
1876
1877
        /* Create a new list from the pointer array */
1878
        for (i = 0; i < len; i++)
1879
        {
1880
                list = g_list_append(list, ptrs[i]);
1881
        }
1603
1882
1604
	j = random() % len;
1883
	g_free(ptrs);
1605
	list = ptrs[j];
1606
	ptrs[j]->next = NULL;
1607
	ptrs[j] = ptrs[0];
1608
1884
1609
	for (i = 1; i < len; i++)
1885
	return list;
1610
	{
1886
}
1611
		j = random() % (len - i);
1887
1612
		list->prev = ptrs[i + j];
1888
static GList *old_playlist_shuffle_list(GList *list)
1613
		ptrs[i + j]->next = list;
1889
{
1614
		list = ptrs[i + j];
1890
	/* Caller should hold playlist mutex */
1615
		ptrs[i + j] = ptrs[i];
1891
	/*
1616
	}
1892
	 * Note that this doesn't make a copy of the original list.
1617
	list->prev = NULL;
1893
	 * The pointer to the original list is not valid after this
1894
	 * fuction is run.
1895
	 */
1896
	int len = g_list_length(list);
1897
	gint i;
1898
	GList *iterator;
1899
	  PlaylistEntry **ptrs;
1900
1901
	if (len == 0)
1902
		return NULL;
1903
1904
	ptrs = g_new(PlaylistEntry *, len);
1905
1906
	/* Convert the list into an array of pointers */
1907
	for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
1908
        {
1909
              ptrs[i] = (PlaylistEntry *)(iterator->data);
1910
1911
                // Shuffling songs voids information about user listening preferences
1912
                ptrs[i]->moved = FALSE;
1913
        }
1914
        g_list_free(list);
1915
        list = NULL;
1916
1917
	/* Shuffle the pointer array */
1918
        for (i = 0; i < len; i++)
1919
        {
1920
                PlaylistEntry *swap_ptr;
1921
                gint j;
1922
1923
                /* Pick a random song among the ones that are left */
1924
                j = (rand() % (len - i)) + i;
1925
1926
                /* Swap pointer #i and pointer #j */
1927
                swap_ptr = ptrs[i];
1928
                ptrs[i] = ptrs[j];
1929
                ptrs[j] = swap_ptr;
1930
        }
1931
1932
        /* Create a new list from the pointer array */
1933
        for (i = 0; i < len; i++)
1934
        {
1935
                list = g_list_append(list, ptrs[i]);
1936
        }
1618
1937
1619
	g_free(ptrs);
1938
	g_free(ptrs);
1620
1939
1621
	return list;
1940
	return list;
1622
}
1941
}
1623
1942
1943
static GList *playlist_shuffle_list(GList *list)
1944
{
1945
        if (cfg.dynamic_taste_detection)
1946
        {
1947
                return smart_playlist_shuffle_list(list);
1948
        }
1949
        else
1950
        {
1951
                return old_playlist_shuffle_list(list);
1952
        }
1953
}
1954
1624
void playlist_random(void)
1955
void playlist_random(void)
1625
{
1956
{
1957
	GList *for_each;
1958
1626
	PL_LOCK();
1959
	PL_LOCK();
1627
1960
1628
	playlist = playlist_shuffle_list(playlist);
1961
	playlist = playlist_shuffle_list(playlist);
1629
1962
1963
        /* Remove all moved-marks from the playlist */
1964
1965
        for (for_each = playlist;
1966
             for_each != NULL;
1967
             for_each = g_list_next(for_each))
1968
        {
1969
                ((PlaylistEntry *) for_each->data)->moved = FALSE;
1970
        }
1971
1630
	PL_UNLOCK();
1972
	PL_UNLOCK();
1631
}
1973
}
1632
1974
Lines 1661-1667 Link Here
1661
	PL_UNLOCK();
2003
	PL_UNLOCK();
1662
	return num;
2004
	return num;
1663
}
2005
}
1664
	
2006
2007
gint playlist_get_single_selection(void)
2008
{
2009
	// Returns the playlist position of the current selection.  If
2010
	// 0 or > 1 songs are selected, this method returns -1.
2011
	GList *iterator;
2012
	gint currentPos;
2013
2014
	gint selectedPos = -1;
2015
2016
	for (iterator = get_playlist(), currentPos = 0;
2017
	     iterator != NULL;
2018
	     iterator = g_list_next(iterator), currentPos++)
2019
	{
2020
		if (((PlaylistEntry *)(iterator->data))->selected)
2021
		{
2022
			if (selectedPos == -1)
2023
			{
2024
				// First selected song found
2025
				selectedPos = currentPos;
2026
			}
2027
			else
2028
			{
2029
				// > 1 selected song found
2030
				return -1;
2031
			}
2032
		}
2033
	}
2034
2035
	return selectedPos;
2036
}
2037
2038
gboolean playlist_is_selected(gint pos)
2039
{
2040
	GList *node;
2041
	gboolean isSelected = 0;
2042
2043
	PL_LOCK();
2044
	if ((node = g_list_nth(get_playlist(), pos)) != NULL)
2045
	{
2046
		isSelected = ((PlaylistEntry *)(node->data))->selected;
2047
	}
2048
	PL_UNLOCK();
2049
2050
	return isSelected;
2051
}
1665
2052
1666
static void playlist_generate_shuffle_list(void)
2053
static void playlist_generate_shuffle_list(void)
1667
{
2054
{
(-)xmms-1.2.8-orig/xmms/playlist.h (-1 / +7 lines)
Lines 25-31 Link Here
25
	gchar *filename;
25
	gchar *filename;
26
	gchar *title;
26
	gchar *title;
27
	gint length;
27
	gint length;
28
	gboolean selected;
28
        gboolean selected;
29
	gboolean moved;
29
}
30
}
30
PlaylistEntry;
31
PlaylistEntry;
31
32
Lines 80-89 Link Here
80
void playlist_delete_filenames(GList *filenames);
81
void playlist_delete_filenames(GList *filenames);
81
gchar* playlist_get_filename(gint pos);
82
gchar* playlist_get_filename(gint pos);
82
gchar* playlist_get_songtitle(gint pos);
83
gchar* playlist_get_songtitle(gint pos);
84
gint playlist_get_score(gint pos);
85
void playlist_set_score(gint pos, gint score);
86
const gchar* playlist_filename2songtitle(const gchar *filename);
83
gint playlist_get_songtime(gint pos);
87
gint playlist_get_songtime(gint pos);
84
GList * playlist_get_selected(void);
88
GList * playlist_get_selected(void);
85
GList * playlist_get_selected_list(void);
89
GList * playlist_get_selected_list(void);
86
int playlist_get_num_selected(void);
90
int playlist_get_num_selected(void);
91
gboolean playlist_is_selected(gint pos);
92
gint playlist_get_single_selection(void);
87
void playlist_get_total_time(gulong *total_time, gulong *selection_time, gboolean *total_more, gboolean *selection_more);
93
void playlist_get_total_time(gulong *total_time, gulong *selection_time, gboolean *total_more, gboolean *selection_more);
88
void playlist_select_all(gboolean set);
94
void playlist_select_all(gboolean set);
89
void playlist_select_range(int min, int max, gboolean sel);
95
void playlist_select_range(int min, int max, gboolean sel);
(-)xmms-1.2.8-orig/xmms/playlist_list.c (-4 / +55 lines)
Lines 24-29 Link Here
24
#endif
24
#endif
25
#include <X11/Xatom.h>
25
#include <X11/Xatom.h>
26
26
27
static gint playlist_move_delta = 0;
28
static PlaylistEntry *playlist_last_moved_song = NULL;
27
static GdkFont *playlist_list_font = NULL;
29
static GdkFont *playlist_list_font = NULL;
28
30
29
static int playlist_list_auto_drag_down_func(gpointer data)
31
static int playlist_list_auto_drag_down_func(gpointer data)
Lines 61-84 Link Here
61
	GList *list;
63
	GList *list;
62
64
63
	PL_LOCK();
65
	PL_LOCK();
66
67
        /* If the first song is selected... */
64
	if ((list = get_playlist()) == NULL)
68
	if ((list = get_playlist()) == NULL)
65
	{
69
	{
66
		PL_UNLOCK();
70
		PL_UNLOCK();
67
		return;
71
		return;
68
	}
72
	}
73
69
	if (((PlaylistEntry *) list->data)->selected)
74
	if (((PlaylistEntry *) list->data)->selected)
70
	{
75
	{
71
		/* We are at the top */
76
                /* ... don't move. */
72
		PL_UNLOCK();
77
		PL_UNLOCK();
73
		return;
78
		return;
74
	}
79
	}
75
	while (list)
80
81
        while (list)
76
	{
82
	{
77
		if (((PlaylistEntry *) list->data)->selected)
83
                if (((PlaylistEntry *) list->data)->selected)
84
                {
85
                        playlist_last_moved_song =
86
                                (PlaylistEntry *) list->data;
78
			glist_moveup(list);
87
			glist_moveup(list);
88
                }
79
		list = g_list_next(list);
89
		list = g_list_next(list);
80
	}
90
	}
81
	PL_UNLOCK();
91
	PL_UNLOCK();
92
93
        playlist_move_delta--;
94
82
	if (pl->pl_prev_selected != -1)
95
	if (pl->pl_prev_selected != -1)
83
		pl->pl_prev_selected--;
96
		pl->pl_prev_selected--;
84
	if (pl->pl_prev_min != -1)
97
	if (pl->pl_prev_min != -1)
Lines 92-115 Link Here
92
	GList *list;
105
	GList *list;
93
106
94
	PL_LOCK();
107
	PL_LOCK();
108
109
        /* If the last song is selected... */
95
	if ((list = g_list_last(get_playlist())) == NULL)
110
	if ((list = g_list_last(get_playlist())) == NULL)
96
	{
111
	{
97
		PL_UNLOCK();
112
		PL_UNLOCK();
98
		return;
113
		return;
99
	}
114
	}
115
100
	if (((PlaylistEntry *) list->data)->selected)
116
	if (((PlaylistEntry *) list->data)->selected)
101
	{
117
	{
102
		/* We are at the bottom */
118
                /* ... don't move. */
103
		PL_UNLOCK();
119
		PL_UNLOCK();
104
		return;
120
		return;
105
	}
121
	}
122
106
	while (list)
123
	while (list)
107
	{
124
	{
108
		if (((PlaylistEntry *) list->data)->selected)
125
		if (((PlaylistEntry *) list->data)->selected)
126
                {
127
                        playlist_last_moved_song =
128
                                (PlaylistEntry *) list->data;
109
			glist_movedown(list);
129
			glist_movedown(list);
130
                }
110
		list = g_list_previous(list);
131
		list = g_list_previous(list);
111
	}
132
	}
133
112
	PL_UNLOCK();
134
	PL_UNLOCK();
135
136
        playlist_move_delta++;
137
113
	if (pl->pl_prev_selected != -1)
138
	if (pl->pl_prev_selected != -1)
114
		pl->pl_prev_selected++;
139
		pl->pl_prev_selected++;
115
	if (pl->pl_prev_min != -1)
140
	if (pl->pl_prev_min != -1)
Lines 177-182 Link Here
177
				playlist_play();
202
				playlist_play();
178
		}
203
		}
179
		pl->pl_dragging = TRUE;
204
		pl->pl_dragging = TRUE;
205
		playlist_move_delta = 0;
180
		playlistwin_update_list();
206
		playlistwin_update_list();
181
	}
207
	}
182
}
208
}
Lines 244-254 Link Here
244
	}
270
	}
245
}
271
}
246
272
273
int playlist_n_selected_songs(void)
274
{
275
        int n_selected = 0;
276
        GList *list;
277
        
278
	for (list = get_playlist();
279
             list != NULL;
280
             list = g_list_next(list))
281
	{
282
                if (((PlaylistEntry *) list->data)->selected)
283
                        n_selected++;
284
	}
285
286
        return n_selected;
287
}
288
247
void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl)
289
void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl)
248
{
290
{
249
	pl->pl_dragging = FALSE;
291
	pl->pl_dragging = FALSE;
250
	pl->pl_auto_drag_down = FALSE;
292
	pl->pl_auto_drag_down = FALSE;
251
	pl->pl_auto_drag_up = FALSE;
293
	pl->pl_auto_drag_up = FALSE;
294
295
        if ((playlist_move_delta < 0) && (playlist_n_selected_songs() == 1))
296
        {
297
                /* Exactly one song has been moved upwards, mark it as moved. */
298
                playlist_last_moved_song->moved = TRUE;
299
        }
300
        
301
        playlist_move_delta = 0;
302
        playlist_last_moved_song = NULL;
252
}
303
}
253
304
254
#ifdef HAVE_WCHAR_H
305
#ifdef HAVE_WCHAR_H
(-)xmms-1.2.8-orig/xmms/playlistwin.c (-7 / +301 lines)
Lines 17-22 Link Here
17
 *  along with this program; if not, write to the Free Software
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
 */
19
 */
20
#include <assert.h>
20
#include "xmms.h"
21
#include "xmms.h"
21
#include "libxmms/dirbrowser.h"
22
#include "libxmms/dirbrowser.h"
22
#include "libxmms/util.h"
23
#include "libxmms/util.h"
Lines 24-30 Link Here
24
GtkWidget *playlistwin;
25
GtkWidget *playlistwin;
25
static GtkWidget *playlistwin_url_window = NULL;
26
static GtkWidget *playlistwin_url_window = NULL;
26
static GtkItemFactory *playlistwin_sort_menu, *playlistwin_sub_menu;
27
static GtkItemFactory *playlistwin_sort_menu, *playlistwin_sub_menu;
27
static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu;
28
static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu, *playlistwin_dtd_menu;
28
29
29
static GdkPixmap *playlistwin_bg;
30
static GdkPixmap *playlistwin_bg;
30
static GdkBitmap *playlistwin_mask;
31
static GdkBitmap *playlistwin_mask;
Lines 67-79 Link Here
67
	SEL_INV, SEL_ZERO, SEL_ALL,
68
	SEL_INV, SEL_ZERO, SEL_ALL,
68
	MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS,
69
	MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS,
69
	PLIST_NEW, PLIST_SAVE, PLIST_LOAD,
70
	PLIST_NEW, PLIST_SAVE, PLIST_LOAD,
70
	SEL_LOOKUP,
71
	SEL_LOOKUP, DTD_GOODSONG, DTD_BADSONG,
72
	DTD_DISSOCIATE
71
};
73
};
72
74
73
void playlistwin_sort_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
75
void playlistwin_sort_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
74
void playlistwin_sub_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
76
void playlistwin_sub_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
75
void playlistwin_save_type_cb(gpointer cb_data, guint action, GtkWidget * w);
77
void playlistwin_save_type_cb(gpointer cb_data, guint action, GtkWidget * w);
76
void playlistwin_set_hints(void);
78
void playlistwin_set_hints(void);
79
void playlistwin_dissociate_callback(GtkMenuItem *menuitem, gpointer ignored);
77
80
78
enum
81
enum
79
{
82
{
Lines 150-156 Link Here
150
GtkItemFactoryEntry playlistwin_popup_menu_entries[] =
153
GtkItemFactoryEntry playlistwin_popup_menu_entries[] =
151
{
154
{
152
	{N_("/View File Info"), NULL, playlistwin_popup_menu_callback, MISC_FILEINFO, "<Item>"},
155
	{N_("/View File Info"), NULL, playlistwin_popup_menu_callback, MISC_FILEINFO, "<Item>"},
156
153
	{N_("/-"), NULL, NULL, 0, "<Separator>"},
157
	{N_("/-"), NULL, NULL, 0, "<Separator>"},
158
159
	{N_("/Dynamic Taste Detection"), NULL, NULL, 0, "<Item>"},
160
154
	{N_("/Add"), NULL, NULL, 0, "<Branch>"},
161
	{N_("/Add"), NULL, NULL, 0, "<Branch>"},
155
	{N_("/Add/File"), NULL, playlistwin_popup_menu_callback, ADD_FILE, "<Item>"},
162
	{N_("/Add/File"), NULL, playlistwin_popup_menu_callback, ADD_FILE, "<Item>"},
156
	{N_("/Add/Directory"), NULL, playlistwin_popup_menu_callback, ADD_DIR, "<Item>"},
163
	{N_("/Add/Directory"), NULL, playlistwin_popup_menu_callback, ADD_DIR, "<Item>"},
Lines 181-186 Link Here
181
	sizeof(playlistwin_popup_menu_entries) / 
188
	sizeof(playlistwin_popup_menu_entries) / 
182
	sizeof(playlistwin_popup_menu_entries[0]);
189
	sizeof(playlistwin_popup_menu_entries[0]);
183
190
191
GtkItemFactoryEntry playlistwin_dtd_menu_entries[] =
192
{
193
	{N_("/I Like this Song"),
194
	 NULL, playlistwin_popup_menu_callback, DTD_GOODSONG,
195
	 "<RadioItem>"},
196
	{N_("/I Don't Like this Song"),
197
	 NULL, playlistwin_popup_menu_callback, DTD_BADSONG,
198
	 /* <RadioItem> */ "/I Like this Song"},
199
	{N_("/Dissociate From"), NULL, NULL, 0, "<Item>"}
200
};
201
202
static const int playlistwin_dtd_menu_entries_num = 
203
	sizeof(playlistwin_dtd_menu_entries) / 
204
	sizeof(playlistwin_dtd_menu_entries[0]);
205
184
void playlistwin_draw_frame(void);
206
void playlistwin_draw_frame(void);
185
207
186
static void playlistwin_update_info(void)
208
static void playlistwin_update_info(void)
Lines 1029-1034 Link Here
1029
		case PLIST_LOAD:
1051
		case PLIST_LOAD:
1030
			playlistwin_show_load_filesel();
1052
			playlistwin_show_load_filesel();
1031
			break;
1053
			break;
1054
1055
			/* DTD button */
1056
		case DTD_GOODSONG: {
1057
			gint score;
1058
			gint selected_playlist_pos = playlist_get_single_selection();
1059
		
1060
			assert(selected_playlist_pos > -1);
1061
			score = playlist_get_score(selected_playlist_pos);
1062
			if (score < 0)
1063
			{
1064
				playlist_set_score(selected_playlist_pos, 0);
1065
			}
1066
1067
			break;
1068
		}
1069
1070
		case DTD_BADSONG: {
1071
			gint score;
1072
			gint selected_playlist_pos = playlist_get_single_selection();
1073
		
1074
			assert(selected_playlist_pos > -1);
1075
			score = playlist_get_score(selected_playlist_pos);
1076
			if (score >= 0)
1077
			{
1078
				playlist_set_score(selected_playlist_pos, -1);
1079
			}
1080
			
1081
			break;
1082
		}
1032
	}
1083
	}
1033
}
1084
}
1034
1085
Lines 1052-1057 Link Here
1052
		inside_widget(x, y, playlistwin_sscroll_down));
1103
		inside_widget(x, y, playlistwin_sscroll_down));
1053
}
1104
}
1054
1105
1106
gchar *playlistwin_fileName2songName(const gchar *fileName)
1107
{
1108
	const gchar *afterLastSlash, *beforeLastDot, *c;
1109
	gchar *returnMe;
1110
	gint i;
1111
	const gchar *loadedTitle =
1112
		playlist_filename2songtitle(fileName);
1113
1114
	if (loadedTitle != NULL)
1115
	{
1116
		return g_strdup(loadedTitle);
1117
	}
1118
1119
	// Ditch the last slash and everything before it, as well as
1120
	// the last dot and everything after it before returning it.
1121
	afterLastSlash = strrchr(fileName, '/');
1122
	if (afterLastSlash != NULL)
1123
	{
1124
		afterLastSlash++;
1125
	}
1126
	else
1127
	{
1128
		afterLastSlash = fileName;
1129
	}
1130
1131
	beforeLastDot = strrchr(fileName, '.');
1132
	if ((beforeLastDot != NULL) && (beforeLastDot > afterLastSlash))
1133
	{
1134
		beforeLastDot--;
1135
	}
1136
	else
1137
	{
1138
		beforeLastDot = fileName + strlen(fileName) - 1;
1139
	}
1140
1141
	if ((afterLastSlash >= beforeLastDot) ||
1142
	    (beforeLastDot <= fileName) ||
1143
	    ((afterLastSlash - fileName) >= strlen(fileName)))
1144
	{
1145
		return g_strdup(fileName);
1146
	}
1147
1148
	// Copy from after last slash to before last dot
1149
	returnMe = g_new(gchar, (beforeLastDot - afterLastSlash) + 1);
1150
	i = 0;
1151
	for (c = afterLastSlash; c <= beforeLastDot; c++)
1152
	{
1153
		returnMe[i++] = *c;
1154
	}
1155
	returnMe[i] = '\0';
1156
1157
	return returnMe;
1158
}
1159
1160
void playlistwin_remove_dissociation_menu(GtkMenuItem *attachPoint)
1161
{
1162
	// Nuke any previous dissociations menu
1163
1164
	// FIXME: I have absolutely no clue to how GTK+ refcounting
1165
	// works.  This implementation may or may not leak memory.
1166
	// Candidates for memory leaks are the user_data fields of the
1167
	// labels (they should be freed).
1168
	// /Johan Walles - jan 05 / 2002
1169
	gtk_menu_item_remove_submenu(attachPoint);
1170
}
1171
1172
void playlistwin_setup_dissociation_menu(GtkMenuItem *attachPoint,
1173
					 gint selected_playlist_pos)
1174
{
1175
	const GList *recommendations = NULL;
1176
	const gchar *filename = playlist_get_filename(selected_playlist_pos);
1177
	
1178
	// Ditch the previous dissociations menu (if any)
1179
	playlistwin_remove_dissociation_menu(attachPoint);
1180
	
1181
	recommendations =
1182
		dtd_get_recommendations(filename);
1183
1184
	if (recommendations != NULL)
1185
	{
1186
		// Create a new menu
1187
		GtkWidget *dissociateMenu = gtk_menu_new();
1188
		const GList *iterator;
1189
1190
		// Associate it with the selected song
1191
		gtk_object_set_user_data(GTK_OBJECT(dissociateMenu), g_strdup(filename));
1192
1193
		// For all recommendations...
1194
		for (iterator = recommendations;
1195
		     iterator != NULL;
1196
		     iterator = g_list_next(iterator))
1197
		{
1198
			const gchar *filename = ((DtdNextSong *)(iterator->data))->filename;
1199
			gchar *songName = playlistwin_fileName2songName(filename);
1200
			GtkWidget *newMenuItem;
1201
1202
			// ... create a new menu entry...
1203
			newMenuItem = gtk_menu_item_new_with_label(songName);
1204
			g_free(songName);
1205
1206
			// ... with its user_data set to point
1207
			// to the associated file name...
1208
			gtk_object_set_user_data(GTK_OBJECT(newMenuItem), g_strdup(filename));
1209
1210
			// ... tell it what to do on receiving a click...
1211
			gtk_signal_connect(GTK_OBJECT(newMenuItem),
1212
					   "activate",
1213
					   GTK_SIGNAL_FUNC(playlistwin_dissociate_callback),
1214
					   NULL);
1215
1216
			// ... and add it to the new menu.
1217
			gtk_menu_append(GTK_MENU(dissociateMenu), newMenuItem);
1218
		}
1219
		
1220
		// Enable our new dissociations menu
1221
		gtk_widget_show_all(dissociateMenu);
1222
		gtk_menu_item_set_submenu(attachPoint, dissociateMenu);
1223
		gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 1);
1224
	}
1225
	else
1226
	{
1227
		// ... or disable the dissociation menu heading
1228
		// if there is nothing to dissociate from.
1229
		gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 0);
1230
	}
1231
}
1232
1055
#define REGION_L(x1,x2,y1,y2)			\
1233
#define REGION_L(x1,x2,y1,y2)			\
1056
(event->x >= (x1) && event->x < (x2) &&		\
1234
(event->x >= (x1) && event->x < (x2) &&		\
1057
 event->y >= cfg.playlist_height - (y1) &&	\
1235
 event->y >= cfg.playlist_height - (y1) &&	\
Lines 1226-1243 Link Here
1226
	else if (event->button == 3 &&
1404
	else if (event->button == 3 &&
1227
		 inside_widget(event->x, event->y, playlistwin_list))
1405
		 inside_widget(event->x, event->y, playlistwin_list))
1228
	{
1406
	{
1229
		int pos, sensitive;
1407
		// FIXME: This block is much too long, put it in its
1408
		// own function
1409
1410
		gint clicked_playlist_pos, selected_playlist_pos;
1411
		gboolean sensitive;
1412
1230
		GtkWidget *w;
1413
		GtkWidget *w;
1231
		pos = playlist_list_get_playlist_position(playlistwin_list,
1414
1232
							  event->x, event->y);
1415
		clicked_playlist_pos =
1233
		sensitive = pos != -1;
1416
			playlist_list_get_playlist_position(playlistwin_list,
1417
							    event->x, event->y);
1418
		
1419
		// Unless the clicked song is part of a (multiple)
1420
		// selection...
1421
		if (!playlist_is_selected(clicked_playlist_pos))
1422
		{
1423
			// ... select just the current song before
1424
			// displaying the menu
1425
			playlist_select_all(0);
1426
			playlist_select_range(clicked_playlist_pos, clicked_playlist_pos, 1);
1427
			playlistwin_update_list();
1428
		}
1429
		
1430
		selected_playlist_pos = playlist_get_single_selection();
1431
		sensitive = (selected_playlist_pos != -1);
1432
		
1433
		// Disable "View File Info" if not exactly one song is selected
1234
		w = gtk_item_factory_get_widget(playlistwin_popup_menu,
1434
		w = gtk_item_factory_get_widget(playlistwin_popup_menu,
1235
						"/View File Info");
1435
						"/View File Info");
1236
		gtk_widget_set_sensitive(w, sensitive);
1436
		gtk_widget_set_sensitive(w, sensitive);
1437
		
1438
		// Disable the DTD menu hierarchy if not exactly one
1439
		// song is selected
1440
		// FIXME: or if it's disabled in the prefs
1441
		w = gtk_item_factory_get_widget(playlistwin_popup_menu,
1442
						"/Dynamic Taste Detection");
1443
		gtk_widget_set_sensitive(w, sensitive);
1444
1445
		if (sensitive)
1446
		{
1447
			GtkWidget *playlistwin_dtd_menu_widget =
1448
				gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>");
1449
			
1450
			gtk_widget_show_all(playlistwin_dtd_menu_widget);
1451
			gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), playlistwin_dtd_menu_widget);
1452
		}
1453
		else
1454
		{
1455
			gtk_menu_item_remove_submenu(GTK_MENU_ITEM(w));
1456
			gtk_widget_hide_all(gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>"));
1457
		}
1458
1459
		if (selected_playlist_pos != -1)
1460
		{
1461
			// Enable either "/I Like this Song" (if the
1462
			// song scores >= 0) or "Bad Song" (if the
1463
			// song scores < 0).
1464
			gint score = dtd_get_score(playlist_get_filename(selected_playlist_pos));
1465
1466
			w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
1467
							"/I Like this Song");
1468
			if (score >= 0)
1469
			{
1470
				gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
1471
			}
1472
			gtk_widget_set_sensitive(w, 1);
1473
			w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
1474
							"/I Don't Like this Song");
1475
			if (score < 0)
1476
			{
1477
				gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
1478
			}
1479
			gtk_widget_set_sensitive(w, 1);
1480
		}
1481
		else
1482
		{
1483
			// We don't do ratings of multiple songs at once
1484
			w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
1485
							"/I Like this Song");
1486
			gtk_widget_set_sensitive(w, 0);
1487
			gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
1488
			w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
1489
							"/I Don't Like this Song");
1490
			gtk_widget_set_sensitive(w, 0);
1491
			gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
1492
		}
1237
1493
1494
		// Create the dissociation menu
1495
		w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
1496
						"/Dissociate From");
1497
		playlistwin_setup_dissociation_menu(GTK_MENU_ITEM(w), selected_playlist_pos);
1498
		
1238
		playlistwin_set_sensitive_sortmenu();
1499
		playlistwin_set_sensitive_sortmenu();
1239
		util_item_factory_popup_with_data(playlistwin_popup_menu,
1500
		util_item_factory_popup_with_data(playlistwin_popup_menu,
1240
						  GINT_TO_POINTER(pos), NULL,
1501
						  GINT_TO_POINTER(selected_playlist_pos), NULL,
1241
						  event->x_root,
1502
						  event->x_root,
1242
						  event->y_root + 5,
1503
						  event->y_root + 5,
1243
						  3, event->time);
1504
						  3, event->time);
Lines 1944-1949 Link Here
1944
					cfg.playlist_height,
2205
					cfg.playlist_height,
1945
					gdk_rgb_get_visual()->depth);
2206
					gdk_rgb_get_visual()->depth);
1946
2207
2208
	playlistwin_dtd_menu = gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
2209
	gtk_item_factory_create_items(GTK_ITEM_FACTORY(playlistwin_dtd_menu),
2210
				      playlistwin_dtd_menu_entries_num,
2211
				      playlistwin_dtd_menu_entries, NULL);
2212
1947
	playlistwin_popup_menu =
2213
	playlistwin_popup_menu =
1948
		gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
2214
		gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
1949
	gtk_item_factory_set_translate_func(playlistwin_popup_menu,
2215
	gtk_item_factory_set_translate_func(playlistwin_popup_menu,
Lines 2020-2025 Link Here
2020
	tbutton_set_toggled(mainwin_pl, FALSE);
2286
	tbutton_set_toggled(mainwin_pl, FALSE);
2021
}
2287
}
2022
2288
2289
void playlistwin_dissociate_callback(GtkMenuItem *menuitem,
2290
				     gpointer ignored)
2291
{
2292
	gchar *first, *second;
2293
2294
	// Find out "first" by checking the user data of the menuitem
2295
	// that was activated
2296
	first = (gchar *)gtk_object_get_user_data(GTK_OBJECT(menuitem));
2297
2298
	// Find out "second" by checking the user data of the menu
2299
	// that is the parent of the menuitem that was activated
2300
	second = (gchar *)gtk_object_get_user_data(GTK_OBJECT(GTK_WIDGET(menuitem)->parent));
2301
2302
	dtd_dissociate(first, second);
2303
}
2304
2305
2023
void playlistwin_popup_menu_callback(gpointer cb_data, guint action, GtkWidget * w)
2306
void playlistwin_popup_menu_callback(gpointer cb_data, guint action, GtkWidget * w)
2024
{
2307
{
2025
	int pos = GPOINTER_TO_INT(gtk_item_factory_popup_data_from_widget(w));
2308
	int pos = GPOINTER_TO_INT(gtk_item_factory_popup_data_from_widget(w));
Lines 2032-2037 Link Here
2032
		case SEL_LOOKUP:
2315
		case SEL_LOOKUP:
2033
			playlist_read_info_selection();
2316
			playlist_read_info_selection();
2034
			break;
2317
			break;
2318
		case DTD_DISSOCIATE:
2319
			
2320
			break;
2321
		case DTD_GOODSONG:
2322
		case DTD_BADSONG:
2323
			// Act only upon the active item
2324
			if (GTK_CHECK_MENU_ITEM(w)->active)
2325
			{
2326
				playlistwin_popup_handler(action);
2327
			}
2328
			break;
2035
		default:
2329
		default:
2036
			playlistwin_popup_handler(action);
2330
			playlistwin_popup_handler(action);
2037
	}
2331
	}
(-)xmms-1.2.8-orig/xmms/prefswin.c (+2 lines)
Lines 1039-1044 Link Here
1039
1039
1040
	gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_options_vbox, gtk_label_new(_("Options")));
1040
	gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_options_vbox, gtk_label_new(_("Options")));
1041
1041
1042
	prefswin_option_new_with_label_to_table(&cfg.dynamic_taste_detection, _("Dynamic taste detection"), GTK_TABLE(options_table), 1, 10);
1043
1042
	/*
1044
	/*
1043
	 * Fonts page
1045
	 * Fonts page
1044
	 */
1046
	 */
(-)xmms-1.2.8-orig/xmms/xmms.h (+1 lines)
Lines 81-86 Link Here
81
#include "sm.h"
81
#include "sm.h"
82
#include "dnd.h"
82
#include "dnd.h"
83
#include "urldecode.h"
83
#include "urldecode.h"
84
#include "dtd.h"
84
85
85
#include "config.h"
86
#include "config.h"
86
87

Return to bug 28587