1
# -*- coding: utf-8 -*-
2
# Copyright 2004-2005 Joe Wreschnig, Michael Urman, Iñigo Serna
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License version 2 as
6
# published by the Free Software Foundation
8
# $Id: information.py 4020 2007-04-26 04:37:42Z piman $
19
from qltk.bookmarks import EditBookmarksPane
20
from qltk.cover import CoverImage
21
from qltk.lyrics import LyricsPane
22
from qltk.x import Window
27
l.set_selectable(True)
31
def Frame(name, widget):
33
f.set_shadow_type(gtk.SHADOW_NONE)
35
l.set_markup("<u><b>%s</b></u>" % name)
37
a = gtk.Alignment(xalign=0, yalign=0, xscale=1, yscale=1)
38
a.set_padding(3, 0, 12, 0)
44
swin = gtk.ScrolledWindow()
45
swin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
48
class ErrorPane(gtk.VBox):
49
def __init__(self, song):
50
super(ErrorPane, self).__init__(spacing=6)
51
self.set_border_width(12)
52
sw = gtk.ScrolledWindow()
54
view.get_buffer().set_text(song.get("~errors", ""))
55
view.set_wrap_mode(gtk.WRAP_WORD)
57
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
58
sw.set_shadow_type(gtk.SHADOW_IN)
59
button = qltk.Button(_("_Clear Errors"), gtk.STOCK_DIALOG_ERROR)
60
button.connect('clicked', self.__clear_errors, song, view)
61
view.set_editable(False)
62
self.pack_start(sw, expand=True, fill=True)
63
self.pack_start(button, expand=False, fill=True)
65
def __clear_errors(self, button, song, view):
66
try: del(song["~errors"])
68
view.get_buffer().set_text("")
70
class NoSongs(gtk.Label):
72
super(NoSongs, self).__init__(_("No songs are selected."))
73
self.title = _("No Songs")
75
class OneSong(qltk.Notebook):
76
def __init__(self, library, song):
77
super(OneSong, self).__init__()
78
vbox = gtk.VBox(spacing=12)
79
vbox.set_border_width(12)
80
self._title(song, vbox)
81
self._album(song, vbox)
82
self._people(song, vbox)
83
self._library(song, vbox)
84
self._file(song, vbox)
86
sw.title = _("Information")
87
sw.add_with_viewport(vbox)
89
lyrics = LyricsPane(song)
90
lyrics.title = _("Lyrics")
91
self.append_page(lyrics)
93
bookmarks = EditBookmarksPane(None, song)
94
bookmarks.title = _("Bookmarks")
95
bookmarks.set_border_width(12)
96
self.append_page(bookmarks)
99
errors = ErrorPane(song)
100
errors.title = _("Errors")
101
self.append_page(errors)
103
s = library.connect('changed', self.__check_changed, vbox, song)
104
self.connect_object('destroy', library.disconnect, s)
106
def __check_changed(self, library, songs, vbox, song):
108
for c in vbox.get_children():
111
self._title(song, vbox)
112
self._album(song, vbox)
113
self._people(song, vbox)
114
self._library(song, vbox)
115
self._file(song, vbox)
116
parent = qltk.get_top_parent(self)
118
parent.set_title(self.title + " - Quod Libet")
121
def _title(self, song, box):
123
text = "<big><b>%s</b></big>" % util.escape(song("title"))
124
if "version" in song:
125
text += "\n" + util.escape(song.comma("version"))
127
l.set_ellipsize(pango.ELLIPSIZE_END)
128
box.pack_start(l, expand=False, fill=False)
129
self.title = song("title")
131
def _album(self, song, box):
132
if "album" not in song: return
135
text.append("<i>%s</i>" % util.escape(song.comma("album")))
137
text[-1] += " (%s)" % util.escape(song.comma("date"))
139
if "discnumber" in song:
140
secondary.append(_("Disc %s") % song["discnumber"])
142
secondary.append("<i>%s</i>" %
143
util.escape(song.comma("part")))
144
if "tracknumber" in song:
145
secondary.append(_("Track %s") % song["tracknumber"])
146
if secondary: text.append(" - ".join(secondary))
148
if "organization" in song or "labelid" in song:
149
t = util.escape(song.comma("~organization~labelid"))
152
if "producer" in song:
153
text.append("Produced by %s" %(
154
util.escape(song.comma("producer"))))
156
w.set_markup("\n".join(text))
157
w.set_ellipsize(pango.ELLIPSIZE_END)
158
hb = gtk.HBox(spacing=12)
160
cover = CoverImage([70, 70], song)
161
if cover: hb.pack_start(cover, expand=False)
162
else: cover.destroy()
165
box.pack_start(Frame(tag("album"), hb), expand=False, fill=False)
167
def _people(self, song, box):
170
if len(song.list("artist")) == 1: title = _("artist")
171
else: title = _("artists")
172
title = util.capitalize(title)
173
l = Label(song["artist"])
174
l.set_ellipsize(pango.ELLIPSIZE_END)
175
vb.pack_start(l, expand=False)
177
title = tag("~people")
179
("performers", "performer"),
180
("lyricists", "lyricist"),
181
("arrangers", "arranger"),
182
("composers", "composer"),
183
("conductors", "conductor"),
184
("authors", "author")]:
186
l = Label(song[tag_])
187
l.set_ellipsize(pango.ELLIPSIZE_END)
188
if len(song.list(tag_)) == 1: name = tag(tag_)
189
else: name = _(names)
190
vb.pack_start(Frame(util.capitalize(name), l), expand=False)
191
if not vb.get_children(): vb.destroy()
192
else: box.pack_start(Frame(title, vb), expand=False, fill=False)
194
def _library(self, song, box):
196
if i == 0: return _("Never")
197
else: return ngettext("%d time", "%d times", i) % i
202
timestr = time.strftime("%c", time.localtime(t))
203
return timestr.decode(const.ENCODING)
205
playcount = counter(song.get("~#playcount", 0))
206
skipcount = counter(song.get("~#skipcount", 0))
207
lastplayed = ftime(song.get("~#lastplayed", 0))
208
if lastplayed == _("Unknown"):
209
lastplayed = _("Never")
210
added = ftime(song.get("~#added", 0))
211
rating = song("~rating")
214
t.set_col_spacings(6)
215
t.set_homogeneous(False)
216
table = [(_("added"), added),
217
(_("last played"), lastplayed),
218
(_("plays"), playcount),
219
(_("skips"), skipcount),
220
(_("rating"), rating)]
222
for i, (l, r) in enumerate(table):
223
l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":")
226
t.attach(lab, 0, 1, i + 1, i + 2, xoptions=gtk.FILL)
227
t.attach(Label(r), 1, 2, i + 1, i + 2)
229
box.pack_start(Frame(_("Library"), t), expand=False, fill=False)
231
def _file(self, song, box):
236
timestr = time.strftime("%c", time.localtime(t))
237
return timestr.decode(const.ENCODING)
239
fn = util.fsdecode(util.unexpand(song["~filename"]))
240
length = util.format_time_long(song["~#length"])
241
size = util.format_size(util.size(song["~filename"]))
242
mtime = ftime(util.mtime(song["~filename"]))
243
if "~#bitrate" in song and song["~#bitrate"] != 0:
244
bitrate = _("%d kbps") % int(song["~#bitrate"]/1000)
245
else: bitrate = False
248
t.set_col_spacings(6)
249
t.set_homogeneous(False)
250
table = [(_("length"), length),
251
(_("file size"), size),
252
(_("modified"), mtime)]
254
table.insert(1, (_("bitrate"), bitrate))
256
fnlab.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
257
t.attach(fnlab, 0, 2, 0, 1, xoptions=gtk.FILL)
258
for i, (l, r) in enumerate(table):
259
l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":")
262
t.attach(lab, 0, 1, i + 1, i + 2, xoptions=gtk.FILL)
263
t.attach(Label(r), 1, 2, i + 1, i + 2)
265
box.pack_start(Frame(_("File"), t), expand=False, fill=False)
267
class OneAlbum(qltk.Notebook):
268
def __init__(self, songs):
269
super(OneAlbum, self).__init__()
271
swin.title = _("Information")
272
vbox = gtk.VBox(spacing=12)
273
vbox.set_border_width(12)
274
swin.add_with_viewport(vbox)
275
# Needed to get proper track/disc/part ordering
276
songs = sorted(songs)
277
self._title(songs, vbox)
278
self._album(songs, vbox)
279
self._people(songs, vbox)
280
self._description(songs, vbox)
281
self.append_page(swin)
283
def _title(self, songs, box):
286
l.set_ellipsize(pango.ELLIPSIZE_END)
287
text = "<big><b>%s</b></big>" % util.escape(song["album"])
288
if "date" in song: text += "\n" + song["date"]
290
box.pack_start(l, expand=False, fill=False)
291
self.title = song["album"]
293
def _album(self, songs, box):
299
discs[song("~#disc")] = int(
300
song["tracknumber"].split("/")[1])
301
except (AttributeError, ValueError, IndexError, KeyError):
302
discs[song("~#disc")] = max([
303
song("~#track", discs.get(song("~#disc"), 0))])
304
tracks = sum(discs.values())
306
length = sum([song["~#length"] for song in songs])
308
if tracks == 0 or tracks < len(songs): tracks = len(songs)
313
ngettext("%d disc", "%d discs", discs) % discs)
315
ngettext("%d track", "%d tracks", tracks) % tracks)
316
if tracks != len(songs):
317
parts.append(ngettext("%d selected", "%d selected",
318
len(songs)) % len(songs))
320
text.append(", ".join(parts))
321
text.append(util.format_time_long(length))
323
if "location" in song:
324
text.append(util.escape(song["location"]))
325
if "organization" in song or "labelid" in song:
326
t = util.escape(song.comma("~organization~labelid"))
329
if "producer" in song:
330
text.append(_("Produced by %s") %(
331
util.escape(song.comma("producer"))))
334
w.set_ellipsize(pango.ELLIPSIZE_END)
335
w.set_markup("\n".join(text))
336
hb = gtk.HBox(spacing=12)
338
cover = CoverImage([70, 70], song)
339
if cover: hb.pack_start(cover, expand=False)
340
else: cover.destroy()
343
box.pack_start(hb, expand=False, fill=False)
345
def _people(self, songs, box):
349
artists.update(song.list("artist"))
350
performers.update(song.list("performer"))
352
artists = sorted(artists)
353
performers = sorted(performers)
356
if len(artists) == 1: title = _("artist")
357
else: title = _("artists")
358
title = util.capitalize(title)
359
box.pack_start(Frame(title, Label("\n".join(artists))),
360
expand=False, fill=False)
362
if len(artists) == 1: title = _("performer")
363
else: title = _("performers")
364
title = util.capitalize(title)
365
box.pack_start(Frame(title, Label("\n".join(performers))),
366
expand=False, fill=False)
368
def _description(self, songs, box):
370
cur_disc = songs[0]("~#disc", 1) - 1
372
cur_track = songs[0]("~#track", 1) - 1
374
track = song("~#track", 0)
375
disc = song("~#disc", 0)
376
part = song.get("part")
378
if cur_disc: text.append("")
379
cur_track = song("~#track", 1) - 1
383
text.append("<b>%s</b>" % (_("Disc %s") % disc))
385
ts = " " * bool(disc)
388
text.append("%s<b>%s</b>" %(ts, util.escape(part)))
390
ts = " " * (bool(disc) + bool(part))
391
while cur_track < track:
392
text.append("%s<b>%d.</b> <i>%s</i>" %(
393
ts, cur_track, _("Track unavailable")))
395
text.append("%s<b>%d.</b> %s" %(
396
ts, track, util.escape(song.comma("~title~version"))))
398
l.set_markup("\n".join(text))
399
l.set_ellipsize(pango.ELLIPSIZE_END)
400
box.pack_start(Frame(_("Track List"), l), expand=False, fill=False)
402
class OneArtist(qltk.Notebook):
403
def __init__(self, songs):
404
super(OneArtist, self).__init__()
406
swin.title = _("Information")
407
vbox = gtk.VBox(spacing=12)
408
vbox.set_border_width(12)
409
swin.add_with_viewport(vbox)
410
self._title(songs, vbox)
411
self._album(songs, vbox)
412
self.append_page(swin)
414
def _title(self, songs, box):
416
l.set_ellipsize(pango.ELLIPSIZE_END)
417
artist = util.escape(songs[0]("artist"))
418
l.set_markup("<b><big>%s</big></b>" % artist)
419
box.pack_start(l, expand=False, fill=False)
420
self.title = songs[0]["artist"]
422
def _album(self, songs, box):
427
albums[song.list("album")[0]] = song
429
albums = [(song.get("date"), song, album) for
430
album, song in albums.items()]
432
def format((date, song, album)):
433
if date: return "%s (%s)" % (album, date[:4])
435
covers = [(a, s.find_cover(), s) for d, s, a in albums]
436
albums = map(format, albums)
438
albums.append(ngettext("%d song with no album",
439
"%d songs with no album", noalbum) % noalbum)
440
l = Label("\n".join(albums))
441
l.set_ellipsize(pango.ELLIPSIZE_END)
442
box.pack_start(Frame(_("Selected Discography"), l))
444
tips = qltk.Tooltips(self)
445
covers = [ac for ac in covers if bool(ac[1])]
446
t = gtk.Table(4, (len(covers) // 4) + 1)
447
t.set_col_spacings(12)
448
t.set_row_spacings(12)
450
for i, (album, cover, song) in enumerate(covers):
451
if cover.name in added: continue
452
cov = CoverImage([70, 70], song)
453
tips.set_tip(cov.child, album)
456
t.attach(cov, c, c + 1, r, r + 1,
457
xoptions=gtk.EXPAND, yoptions=0)
458
added.add(cover.name)
461
class ManySongs(qltk.Notebook):
462
def __init__(self, songs):
463
super(ManySongs, self).__init__()
465
swin.title = _("Information")
466
vbox = gtk.VBox(spacing=12)
467
vbox.set_border_width(12)
468
swin.add_with_viewport(vbox)
469
self._title(songs, vbox)
470
self._people(songs, vbox)
471
self._album(songs, vbox)
472
self._file(songs, vbox)
473
self.append_page(swin)
475
def _title(self, songs, box):
477
t = ngettext("%d song", "%d songs", len(songs)) % len(songs)
478
l.set_markup("<big><b>%s</b></big>" % t)
480
box.pack_start(l, expand=False, fill=False)
482
def _people(self, songs, box):
486
if "artist" in song: artists.update(song.list("artist"))
488
artists = sorted(artists)
489
num_artists = len(artists)
491
if none: artists.append(ngettext("%d song with no artist",
492
"%d songs with no artist", none) % none)
493
box.pack_start(Frame(
494
"%s (%d)" % (util.capitalize(_("artists")), num_artists),
495
Label("\n".join(artists))),
496
expand=False, fill=False)
498
def _album(self, songs, box):
502
if "album" in song: albums.update(song.list("album"))
504
albums = sorted(albums)
505
num_albums = len(albums)
507
if none: albums.append(ngettext("%d song with no album",
508
"%d songs with no album", none) % none)
509
box.pack_start(Frame(
510
"%s (%d)" % (util.capitalize(_("albums")), num_albums),
511
Label("\n".join(albums))),
512
expand=False, fill=False)
514
def _file(self, songs, box):
518
time += song["~#length"]
519
try: size += util.size(song["~filename"])
520
except EnvironmentError: pass
521
table = gtk.Table(2, 2)
522
table.set_col_spacings(6)
523
table.attach(Label(_("Total length:")), 0, 1, 0, 1,
526
Label(util.format_time_long(time)), 1, 2, 0, 1)
527
table.attach(Label(_("Total size:")), 0, 1, 1, 2,
529
table.attach(Label(util.format_size(size)), 1, 2, 1, 2)
530
box.pack_start(Frame(_("Files"), table),
531
expand=False, fill=False)
533
class Information(Window):
534
def __init__(self, library, songs):
535
super(Information, self).__init__()
536
self.set_border_width(12)
537
self.set_default_size(400, 400)
539
sig = library.connect('changed', self.__check_changed)
540
self.connect_object('destroy', library.disconnect, sig)
542
sig = library.connect('removed', self.__check_removed)
543
self.connect_object('destroy', library.disconnect, sig)
545
self.__update(library)
548
def __check_changed(self, library, songs):
550
for song in self.__songs:
552
self.__update(library)
555
def __check_removed(self, library, songs):
557
old = len(self.__songs)
558
self.__songs = filter(lambda s: s not in gone, self.__songs)
559
if len(self.__songs) != old: self.__update(library)
561
def __update(self, library):
563
if self.child: self.child.destroy()
565
if not songs: self.add(NoSongs())
566
elif len(songs) == 1: self.add(OneSong(library, songs[0]))
568
tags = [(s.get("artist"), s.get("album")) for s in songs]
569
artists, albums = zip(*tags)
570
if min(albums) == max(albums) and albums[0]:
571
self.add(OneAlbum(songs))
572
elif min(artists) == max(artists) and artists[0]:
573
self.add(OneArtist(songs))
574
else: self.add(ManySongs(songs))
576
self.set_title(self.child.title + " - Quod Libet")
577
self.child.show_all()