1
# IDJCmedia.py: GUI code for main media players in IDJC
2
# Copyright (C) 2005 Stephen Fairchild
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29
from IDJCmultitagger import*
30
from IDJCfree import *
31
from idjc_config import *
32
from langheader import *
34
# Arrow button creation helper function
35
def make_arrow_button(self, arrow_type, shadow_type, data):
36
button = gtk.Button();
37
arrow = gtk.Arrow(arrow_type, shadow_type);
39
button.connect("clicked", self.callback, data)
44
def get_number_for(token, string):
46
end = string.rindex(token)
48
while start >= 0 and (string[start].isdigit() or string[start] == "."):
50
return int(float(string[start+1:end]))
54
class nice_listen_togglebutton(gtk.ToggleButton):
55
def __init__(self, label = None, use_underline = True):
56
gtk.ToggleButton.__init__(self, label, use_underline)
58
return gtk.ToggleButton.__str__() + " auto inconsistent when insensitive"
59
def set_sensitive(self, bool):
61
gtk.ToggleButton.set_sensitive(self, False)
62
gtk.ToggleButton.set_inconsistent(self, True)
64
gtk.ToggleButton.set_sensitive(self, True)
65
gtk.ToggleButton.set_inconsistent(self, False)
67
class IDJC_Media_Player:
68
def get_media_metadata(self, filename):
69
# This is the third incarnation of this function.
77
# Strip away any file:// prefix
78
if filename.count("file://", 0, 7):
79
filename = filename[7:]
80
elif filename.count("file:", 0, 5):
81
filename = filename[5:]
82
# Attempt to get the file extension
84
filext = os.path.splitext(filename)[1]
85
if [ ".mp3", ".ogg", ".flac", ".wma", ".m4a", ".wav", ".avi", ".mp4" ].count(filext) != 1:
87
if os.path.isfile(filename) == False:
90
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
92
# Use this name for metadata when we can't get anything from the ID3 tags
93
# The name will also appear grey
94
meta_name = os.path.basename(filename)
95
enclist = self.parent.fenc.split(",")
99
each = self.parent.denc
101
meta_name = unicode(os.path.splitext(meta_name)[0], each)
106
rsmeta_name = u'<span foreground="gray">' + rich_safe(meta_name) + '</span>'
107
title_retval = meta_name
110
encoding = "latin1" # Have to default to something.
111
meta_name = u'Unknown'
112
rsmeta_name = u'<span foreground="gray">Unknown encoding</span>'
113
title_retval = u"Unknown"
115
filext = filext.lower()
116
if filext == ".mp3" or filext == ".flac" or filext == ".wma" or filext == ".ogg":
118
if eyeD3.isMp3File(filename):
119
audiofile = eyeD3.Mp3AudioFile(filename, eyeD3.ID3_V2)
120
length = audiofile.getPlayTime()
121
tag = audiofile.getTag()
123
tag = eyeD3.Tag() # Attempt to read an id3 tag from a flac file
124
tag.link(filename, eyeD3.ID3_V2)
125
artist = unicode(tag.getArtist())
126
title = unicode(tag.getTitle())
128
print "Problem with reading ID3v2 tag"
130
if eyeD3.isMp3File(filename) and length == 0:
131
audiofile = eyeD3.Mp3AudioFile(filename, eyeD3.ID3_V1)
132
length = audiofile.getPlayTime()
133
tag = audiofile.getTag()
135
tag = eyeD3.Tag() # Attempt to read an id3 tag from a flac file
136
tag.link(filename, eyeD3.ID3_V1)
137
except eyeD3.tag.TagException:
139
print "Could not get length for the file", filename, "Using a length of zero instead"
140
except eyeD3.tag.InvalidAudioFormatException:
141
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
142
if tag is not None and (artist == u"" or title == u""):
143
artist = unicode(tag.getArtist())
144
title = unicode(tag.getTitle())
146
while artist != u"" and artist[-1] == u"\x00":
148
artist = artist.strip()
149
while title != u"" and title[-1] == u"\x00":
151
title = title.strip()
152
if artist != u"" and title != u"":
153
rsmeta_name = meta_name = artist + u" - " + title
155
artist_retval = artist
157
if filext == ".ogg" and os.environ.get("ogginfo") != "missing":
158
self.parent.mixer_write("OGGP=%s\nACTN=ogginforequest\nend\n" % filename, True)
160
line = self.parent.mixer_read()
161
if line == "OIR:NOT VALID\n" or line == "":
162
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
163
if line.startswith("OIR:ARTIST="):
164
artist = line[11:].strip()
165
if line.startswith("OIR:TITLE="):
166
title = line[10:].strip()
167
if line.startswith("OIR:LENGTH="):
168
length = int(float(line[11:].strip()))
169
if line == "OIR:end\n":
172
if artist != "" and title != "":
173
artist = artist.decode("utf-8")
174
title = title.decode("utf-8")
175
rsmeta_name = meta_name = artist + u" - " + title
177
artist_retval = artist
179
elif filext == ".flac" and os.environ.get("metaflac") != "missing":
180
(stdin,stdout)=os.popen2([ os.environ.get("metaflac") ,"--no-utf8-convert", "--show-sample-rate","--show-total-samples", "--show-tag=TITLE", "--show-tag=ARTIST",filename])
182
data = unicode(stdout.read())
184
data = data.splitlines()
186
length = int(data[1]) / int(data[0])
188
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
191
if each[:7] == "ARTIST=" or each[:7] == "artist":
192
artist = each[7:].strip()
193
if each[:6] == "TITLE=" or each[:6] == "title":
194
title = each[6:].strip()
195
if artist != u"" and title != u"":
196
rsmeta_name = meta_name = artist + " - " + title
198
artist_retval = artist
199
elif (filext == ".mp4" or filext == ".m4a") and mp4enabled:
200
print "getting info for mp4 file"
201
self.parent.mixer_write("MP4P=%s\nACTN=mp4inforequest\nend\n" % filename, True)
203
line = self.parent.mixer_read()
204
if line == "idjcmixer: mp4fileinfo Not Valid\n" or line == "":
205
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
206
if line.startswith("idjcmixer: mp4fileinfo artist="):
207
artist = line[30:].strip()
208
if line.startswith("idjcmixer: mp4fileinfo title="):
209
title = line[29:].strip()
210
if line.startswith("idjcmixer: mp4fileinfo length="):
211
length = int(float(line[30:].strip()))
212
if line == "idjcmixer: mp4fileinfo end\n":
214
if artist != "" and title != "":
215
artist = artist.decode("utf-8")
216
title = title.decode("utf-8")
217
rsmeta_name = meta_name = artist + u" - " + title
219
artist_retval = artist
221
self.parent.mixer_write("PLRP=%s\nACTN=askxine\nend\n" % filename, True)
223
line = self.parent.mixer_read()
225
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
226
if line == "askxine: done\n":
228
if line == "askxine: error\n":
230
if line.startswith("askxine: length="):
231
length = int(line[16:-1])
232
if line.startswith("askxine: artist="):
234
if line.startswith("askxine: title="):
237
return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
238
if artist != "" and title != "":
239
artist = artist.decode("utf-8")
240
title = title.decode("utf-8")
241
rsmeta_name = meta_name = artist + u" - " + title
242
artist_retval = artist
245
if rsmeta_name == meta_name:
246
return [ rich_safe(rsmeta_name), filename, length, meta_name, encoding, title_retval, artist_retval ]
248
return [ rsmeta_name, filename, length, meta_name, encoding, title_retval, artist_retval ]
250
# Update playlist entries for a given filename e.g. when tag has been edited
251
def update_playlist(self, newdata):
253
for item in self.liststore:
254
if item[1] == newdata[1]:
255
if item[0][:3] == "<b>":
256
item[0] = u"<b>" + newdata[0] + u"</b>"
264
self.songname = newdata[3] # update metadata on server
265
self.title = newdata[5].encode("utf-8")
266
self.artist = newdata[6].encode("utf-8")
267
self.parent.send_new_mixer_stats()
269
# Shut down our media players when we exit.
271
self.exiting = True # I added this because, well the reason totally sucks TBH
272
if self.player_is_playing:
276
def save_session(self):
277
fh = open(self.session_filename, "w")
278
fh.write("digiprogress_type=" + str(int(self.digiprogress_type)) + "\n")
279
fh.write("stream_button=" + str(int(self.stream.get_active())) + "\n")
280
fh.write("listen_button=" + str(int(self.listen.get_active())) + "\n")
281
fh.write("playlist_mode=" + str(self.pl_mode.get_active()) + "\n")
282
for entry in self.liststore:
286
if type(item) == int:
291
fh.write(str(len(item)) + ":" + item)
293
model, iter = self.treeview.get_selection().get_selected()
295
fh.write("select=" + str(model.get_path(iter)[0]) + "\n")
298
def restore_session(self):
300
fh = open(self.session_filename, "r")
311
if line.startswith("digiprogress_type="):
312
if int(line[18]) != self.digiprogress_type:
313
self.digiprogress_click()
314
if line.startswith("stream_button="):
315
self.stream.set_active(int(line[14]))
316
if line.startswith("listen_button="):
317
self.listen.set_active(int(line[14]))
318
if line.startswith("playlist_mode="):
319
self.pl_mode.set_active(int(line[14]))
320
if line.startswith("pe="):
321
playlist_entry = self.pl_unpack(line[3:])
323
self.liststore.append(playlist_entry)
324
except: # playlist data may not be compatible across IDJC versions
325
playlist_entry = self.get_media_metadata(playlist_entry[1])
326
if playlist_entry[0] != "Not a valid file":
327
self.liststore.append(playlist_entry)
328
if line.startswith("select="):
331
self.treeview.get_selection().select_path(path)
332
self.treeview.scroll_to_cell(path, None, False)
338
def pl_unpack(self, text): # converts a string encoded list to a python list
342
while text[start] != "\n":
344
while text[end] != ":":
346
nextstart = int(text[start + 1 : end]) + end + 1
347
if (text[start] == "s"):
348
reply.append(text[ end+1 : nextstart ])
349
elif (text[start] == "i"):
351
reply.append(int(text[ end+1 : nextstart ]))
353
print "pl_unpack: bad integer data"
356
print "pl_unpack: unknown data type:", text[start]
361
def handle_stop_button(self, widget):
362
self.restart_cancel = True
363
if self.is_playing == True:
364
self.is_playing = False
365
if self.timeout_source_id:
366
gobject.source_remove(self.timeout_source_id)
367
# This will make our play button code branch to its shutdown code.
368
self.is_stopping = True
369
# This will emit a signal which will trigger the play button handler code.
370
self.play.set_active(False)
371
# Must do pause as well if it is pressed.
372
if self.pause.get_active() == True:
373
self.pause.set_active(False)
374
self.parent.send_new_mixer_stats()
376
def handle_pause_button(self, widget, selected):
377
if self.is_playing == True:
378
if self.is_paused == False:
379
# Player pause code goes here
380
print "Player paused"
381
self.is_paused = True
382
self.parent.send_new_mixer_stats()
384
# Player unpause code goes here
385
print "Player unpaused"
386
self.is_paused = False
387
self.parent.send_new_mixer_stats()
389
# Prevent the pause button going into its on state when not playing.
391
# We must unselect it.
392
widget.set_active(False)
394
self.is_paused = False
396
def handle_play_button(self, widget, selected):
397
if selected == False:
398
# Prevent the button from being toggled off by clicking on a selected play button.
399
# This emits a toggled signal so we will handle this eventuality too.
400
# Note that when flag is_stopping is set we don't want to reactivate this button.
401
if self.is_stopping == False:
402
widget.set_active(True)
404
self.is_stopping = False
405
if self.player_is_playing == True:
406
self.player_shutdown()
408
if self.is_playing == True:
409
if self.new_title == True:
410
self.new_title = False
411
self.player_shutdown()
412
self.parent.send_new_mixer_stats()
413
self.player_is_playing = self.player_startup()
414
if self.player_is_playing == False:
415
self.player_is_playing = True
417
#self.invoke_end_of_track_policy()
419
self.pause.set_active(False)
421
print "Someone probably clicked Play when we were already playing"
423
self.is_playing = True
424
self.new_title = False
425
if self.player_startup():
426
self.player_is_playing = True
427
print "Player has started"
431
def player_startup(self):
432
if self.player_is_playing == True:
433
# Use the current song if one is playing.
434
model = self.model_playing
435
iter = self.iter_playing
437
# Get our next playlist item.
438
treeselection = self.treeview.get_selection()
439
(model, iter) = treeselection.get_selected()
441
print "Nothing selected in the playlist - trying the first entry."
443
iter = model.get_iter(0)
445
print "Playlist is empty"
447
print "We start at the beginning"
448
treeselection.select_iter(iter)
450
self.treeview.scroll_to_cell(model.get_path(iter)[0], None, False)
452
# Songname is used for metadata for mp3
453
self.songname = unicode(model.get_value(iter, 3))
454
# These two are used for ogg metadata
455
self.title = unicode(model.get_value(iter, 5)).encode("utf-8", "replace")
456
self.artist = unicode(model.get_value(iter, 6)).encode("utf-8", "replace")
457
self.parent.send_new_mixer_stats()
458
self.music_filename = model.get_value(iter, 1)
459
# rt is the run time in seconds of our song
460
rt = model.get_value(iter, 2)
462
rt = 0 # playlist controls have negative numbers
463
# Calculate our seek time scaling from old slider settings.
464
# Used for when seek is moved before play is pressed.
465
if os.path.isfile(self.music_filename):
467
self.start_time = int(self.progressadj.get_value() / self.max_seek * float(rt))
468
except ZeroDivisionError:
471
self.start_time = rt # Seek to the end when file is missing - looks very slick in action.
472
print "Seek time is %d seconds" % self.start_time
474
# Now we recalibrate the progress bar to the current song length
475
self.digiprogress_f = True
476
self.progressadj.set_all(float (self.start_time) , 0.0, rt, rt/1000.0, rt/100.0, 0.0)
477
self.progressadj.emit("changed")
478
# Set the stop figure used by the progress bar's timeout function
479
self.progress_stop_figure = model.get_value(iter, 2)
480
self.progress_current_figure = self.start_time
482
self.player_is_playing = True
484
# Bold highlight the file we are playing
485
text = model.get_value(iter, 0)
486
text = "<b>" + text + "</b>"
487
model.set_value(iter, 0, text)
488
self.iter_playing = iter
489
self.model_playing = model
491
self.silence_count = 0
493
if self.music_filename != "":
494
if self.gapless == False:
495
self.parent.mixer_write("PLRP=%s\nSEEK=%d\nACTN=play%s\nend\n" % (
496
self.music_filename, self.start_time, self.playername), True)
498
print "playing without flush"
499
self.parent.mixer_write("PLRP=%s\nSEEK=%d\nACTN=playnoflush%s\nend\n" % (
500
self.music_filename, self.start_time, self.playername), True)
502
line = self.parent.mixer_read()
503
if line.startswith("context_id="):
504
self.player_cid = int(line[11:-1])
510
print "skipping play for empty filename"
512
if self.player_cid == -1:
513
print "player startup was unsuccessful for file", self.music_filename
514
# we will treat this as successful and not return false so that end of track processing will take care of it
515
print "player context id is %d\n" % self.player_cid
516
if self.player_cid % 2:
517
self.timeout_source_id = gobject.timeout_add(100, self.cb_play_progress_timeout, self.player_cid)
520
def player_shutdown(self):
521
print "player shutdown code was called"
523
if self.iter_playing:
524
# Unhighlight this track
525
text = self.model_playing.get_value(self.iter_playing, 0)
526
if text[:3] == "<b>":
528
self.model_playing.set_value(self.iter_playing, 0, text)
529
self.file_iter_playing = 0
531
self.player_is_playing = False
532
if self.timeout_source_id:
533
gobject.source_remove(self.timeout_source_id)
535
self.playtime_elapsed.set_value(0)
536
self.progressadj.set_value(0.0)
537
self.progressadj.value_changed()
539
if self.gapless == False:
540
self.parent.mixer_write("ACTN=stop%s\nend\n" % self.playername, True)
542
self.digiprogress_f = False
543
self.other_player_initiated = False
544
self.crossfader_initiated = False
546
def player_restart(self):
547
gobject.source_remove(self.timeout_source_id)
548
self.start_time = int (self.progressadj.get_value())
549
self.silence_count = 0
550
self.parent.mixer_write("PLRP=%s\nSEEK=%d\nACTN=play%s\nend\n" % (
551
self.music_filename, self.start_time, self.playername), True)
553
line = self.parent.mixer_read()
554
if line.startswith("context_id="):
555
self.player_cid = int(line[11:-1])
560
if self.player_cid == -1:
561
print "player startup was unsuccessful for file", self.music_filename
564
print "player context id is %d\n" % self.player_cid
566
# Restart a callback to update the progressbar.
567
self.timeout_source_id = gobject.timeout_add(100, self.cb_play_progress_timeout, self.player_cid)
570
def invoke_end_of_track_policy(self):
571
# This is where we implement the playlist modes.
572
mode_text = self.pl_mode.get_active_text()
573
if self.is_playing == False:
574
print "Assertion failed in: invoke_end_of_track_policy"
577
if mode_text == manual_text:
578
# For Manual mode just stop the player at the end of the track.
579
print "Stopping in accordance with manual mode"
581
elif mode_text == play_all_text:
582
if self.music_filename == "":
583
self.handle_playlist_control()
586
treeselection = self.treeview.get_selection()
587
if self.is_playing == False:
588
treeselection.select_path(0) # park on the first menu item
589
elif mode_text == loop_all_text or mode_text == cue_up_text:
590
path = self.model_playing.get_path(self.iter_playing)[0]+1
593
self.model_playing.get_iter(path)
594
print "Picking the next element in the playlist"
596
print "We are at the bottom. Picking the first element"
598
treeselection = self.treeview.get_selection()
599
treeselection.select_path(path)
600
if mode_text == loop_all_text or (mode_text == cue_up_text and self.model_playing[path][0][0] == ">"):
602
elif mode_text == random_text:
603
# Count the number of tracks
608
self.model_playing.get_iter(count)
613
print "There are", count, "elements in the playlist"
614
# Pick a new track almost at random
616
new_path = random.randint(0, count -1)
618
# Prevent repetition of songs when there are more than 3 in the playlist
619
current_path = self.model_playing.get_path(self.iter_playing)[0]
620
while new_path == current_path:
622
new_path = random.randint(0, count -1)
624
print "New track is:", new_path
625
treeselection = self.treeview.get_selection()
626
treeselection.select_path(new_path)
629
print 'The mode "%s" is not currently supported - stopping' % mode_text
632
def handle_playlist_control(self):
633
treeselection = self.treeview.get_selection()
634
model = self.model_playing
635
iter = self.iter_playing
636
control = model.get_value(iter, 0)
637
print "control is", control
638
if control == "<b>>stopplayer</b>":
639
print "player", self.playername, "stopping due to playlist control"
641
if model.iter_next(iter):
642
treeselection.select_iter(model.iter_next(iter))
644
treeselection.select_iter(model.get_iter_first())
645
if control == "<b>>crossfade</b>":
646
print "player", self.playername, "stopping, crossfade complete"
648
if model.iter_next(iter):
649
treeselection.select_iter(model.iter_next(iter))
651
treeselection.select_path(0)
652
if control == "<b>>stopstreaming</b>":
654
if self.parent.server_window.connect_button.get_active():
655
print "stopping streaming due to", self.playername, "playlist control"
656
self.parent.server_window.connect_button.set_active(False)
657
if self.is_playing == False:
658
treeselection.select_path(0)
659
if control == "<b>>stoprecording</b>":
661
if self.parent.server_window.record_button.get_active():
662
print "stopping recording due to", self.playername, "playlist control"
663
self.parent.server_window.stop_button.clicked()
664
if self.is_playing == False:
665
treeselection.select_path(0)
666
if control == "<b>>transfer</b>":
667
if self.playername == "left":
668
otherplayer = self.parent.player_right
669
self.parent.passright.clicked()
671
otherplayer = self.parent.player_left
672
self.parent.passleft.clicked()
673
print "transferring to player", otherplayer.playername
674
otherplayer.play.clicked()
676
if model.iter_next(iter):
677
treeselection.select_iter(model.iter_next(iter))
679
treeselection.select_path(0)
681
def get_pl_block_size(self, iter):
683
while iter is not None:
684
length = self.liststore.get_value(iter, 2)
689
iter = self.liststore.iter_next(iter)
692
def update_time_stats(self):
693
if self.pl_mode.get_active() != 0: # optimisation -- this function uses a lot of cpu
695
if self.player_is_playing:
696
tr = int(self.max_seek - self.progressadj.value)
697
model = self.model_playing
698
iter = model.iter_next(self.iter_playing)
699
tr += self.get_pl_block_size(iter)
702
selection = self.treeview.get_selection()
703
model, iter = selection.get_selected()
708
iter = model.get_iter_first()
709
bs = self.get_pl_block_size(iter)
711
if model.get_value(iter, 0)[0:3] == "<b>":
714
bs = self.get_pl_block_size(iter)
716
bsm = (bs - bss) / 60
719
trm = (tr - trs) / 60
720
tm_end = time.localtime(int(time.time()) + tr)
725
self.statusbar_update("%s -%2d:%02d | %s %02d:%02d:%02d" % (remaining_text, trm, trs, finish_text, tm_end_h, tm_end_m, tm_end_s))
727
self.statusbar_update("%s -%2d:%02d | %s %02d:%02d:%02d | %s %2d:%02d" % (remaining_text, trm, trs, finish_text, tm_end_h, tm_end_m, tm_end_s, block_size_text, bsm, bss))
730
self.statusbar_update("")
732
bft = time.localtime(time.time() + bs)
736
self.statusbar_update("%s %2d:%02d | %s %02d:%02d:%02d" % (block_size_text, bsm, bss, finish_text, bf_h, bf_m, bf_s))
738
def statusbar_update(self, newtext): # optimisation -- only update the status bars when the text changes
739
if newtext != self.oldstatusbartext:
740
self.pl_statusbar.push(1, newtext)
741
self.oldstatusbartext = newtext
743
def check_mixer_signal(self):
744
if self.progress_press == False and self.progressadj.upper - self.progress_current_figure < 5.0 and self.progressadj.upper > 10.0:
745
if self.mixer_signal_f.value == 0 and int(self.mixer_cid) == self.player_cid:
746
print "termination by check mixer signal"
747
self.invoke_end_of_track_policy()
749
def cb_play_progress_timeout(self, cid):
750
gtk.gdk.threads_enter()
751
if self.reselect_cursor_please:
752
treeselection = self.treeview.get_selection()
753
(model, iter) = treeselection.get_selected()
755
self.treeview.scroll_to_cell(model.get_path(iter)[0], None, False)
757
self.reselect_please = True
758
self.reselect_cursor_please = False
759
if self.reselect_please:
760
print "Set cursor on track playing"
761
# This code reselects the playing track after a drag operation.
762
treeselection = self.treeview.get_selection()
764
treeselection.select_iter(self.iter_playing)
766
print "Iter was cancelled probably due to song dragging"
767
self.reselect_please = False
768
if self.progress_press == False:
769
if self.runout.value and self.is_paused == False and self.mixer_cid.value > self.player_cid:
771
print "termination due to end of track"
772
self.invoke_end_of_track_policy()
774
gtk.gdk.threads_leave()
776
if self.mixer_signal_f.value == False:
777
self.silence_count += 1
778
if self.silence_count == 90:
779
print "termination due to excessive silence"
780
self.invoke_end_of_track_policy()
781
gtk.gdk.threads_leave()
784
self.silence_count = 0
785
self.progress_current_figure = self.playtime_elapsed.value
786
self.progressadj.set_value(self.playtime_elapsed.value)
787
if self.max_seek == 0:
788
self.progressadj.emit("value_changed")
789
self.update_time_stats()
791
# we stop monitoring the play progress during the progress bar drag operation
792
# by cancelling this timeout
793
gtk.gdk.threads_leave()
795
# Calclulate when to sound the DJ alarm (end of music notification)
796
# Bugs: does not deep scan the playlist controls for >stopplayer so the alarm will not sound if
797
# preceeded by another playlist control
798
if self.progress_current_figure == self.progress_stop_figure -9 and self.progressadj.upper > 10 and self.parent.prefs_window.djalarm.get_active():
799
if ((self.playername == "left" and self.parent.crossadj.get_value() < 50) or (self.playername == "right" and self.parent.crossadj.get_value() >= 50)) and (self.pl_mode.get_active() == 3 or self.pl_mode.get_active() == 4 or (self.pl_mode.get_active() == 0 and (self.model_playing.iter_next(self.iter_playing) is None or (self.pl_mode.get_active() == 0 and self.model_playing.get_value(self.model_playing.iter_next(self.iter_playing), 0) == ">stopplayer")))):
800
self.parent.alarm = True
801
self.parent.send_new_mixer_stats()
802
# Initial autocrossfade -- start the other player.
803
if self.model_playing.iter_next(self.iter_playing) is not None and self.model_playing.get_value(self.model_playing.iter_next(self.iter_playing), 0) == ">crossfade" and self.pl_mode.get_active() == 0 and int(self.progress_current_figure) >= int(self.progress_stop_figure) -15:
804
if self.other_player_initiated == False:
805
if self.playername == "left":
806
self.parent.player_right.play.clicked()
808
self.parent.player_left.play.clicked()
809
self.other_player_initiated = True
810
# Now we do the crossfade
811
if self.model_playing.iter_next(self.iter_playing) is not None and self.model_playing.get_value(self.model_playing.iter_next(self.iter_playing), 0) == ">crossfade" and self.pl_mode.get_active() == 0 and int(self.progress_current_figure) >= int(self.progress_stop_figure) -9:
812
if self.crossfader_initiated == False:
813
self.parent.passbutton.clicked()
814
self.crossfader_initiated = True
815
desired_direction = (self.playername == "left")
816
if desired_direction != self.parent.crossdirection:
817
self.parent.passbutton.clicked()
819
gtk.gdk.threads_leave()
822
def islastinplaylist(self):
823
ifself.model_playing.iter_next(self.iter_playing)
830
treeselection = self.treeview.get_selection()
831
(model, iter) = treeselection.get_selected()
833
print "Nothing is selected"
835
path = model.get_path(iter)
837
other_iter = model.get_iter(path[0]-1)
838
self.liststore.swap(iter, other_iter)
839
self.treeview.scroll_to_cell(path[0]-1, None, False)
841
def arrow_down(self):
842
treeselection = self.treeview.get_selection()
843
(model, iter) = treeselection.get_selected()
845
print "Nothing is selected"
847
path = model.get_path(iter)
849
other_iter = model.get_iter(path[0]+1)
850
self.liststore.swap(iter, other_iter)
851
self.treeview.scroll_to_cell(path[0]+1, None, False)
857
path = self.model_playing.get_path(self.iter_playing)[0]+1
859
treeselection = self.treeview.get_selection()
860
treeselection.select_path(path)
861
self.treeview.scroll_to_cell(path, None, False)
865
def callback(self, widget, data):
866
print "%s was pressed on player %s" % (data, self.playername)
868
if data == "Arrow Up":
871
if data == "Arrow Dn":
875
self.handle_stop_button(widget)
879
path = self.model_playing.get_path(self.iter_playing)[0]+1
883
self.model_playing.get_iter(path)
887
treeselection = self.treeview.get_selection()
888
treeselection.select_path(path)
889
self.new_title = True
894
treeselection = self.treeview.get_selection()
895
path = self.model_playing.get_path(self.iter_playing)
898
treeselection.select_path(path[0]-1)
899
self.new_title = True
902
# This is for adding files to the playlist using the file requester.
903
if data == "Add Files":
904
if self.showing_file_requester == False:
905
if self.playername == "left":
906
filerqtext = left_playlist_addition_text
908
filerqtext = right_playlist_addition_text
909
self.filerq = gtk.FileSelection(filerqtext)
910
self.filerq.set_select_multiple(True)
911
self.filerq.set_icon_from_file(pkgdatadir + "icon" + gfext)
912
self.filerq.set_filename(str(self.file_requester_start_dir))
913
self.filerq.hide_fileop_buttons()
914
self.filerq.ok_button.connect("clicked", self.file_ok_sel)
915
self.filerq.cancel_button.connect("clicked", self.file_notok)
916
self.filerq.connect("destroy", self.file_destroy)
918
self.showing_file_requester = True
920
print "refused to open file requester"
922
def file_ok_sel(self, widget):
923
chosenfiles = self.filerq.get_selections()
924
# Destroy the requester window as soon as possible to make our app look snappy :)
925
self.filerq.destroy()
926
if len (chosenfiles) == 1 and os.path.splitext(chosenfiles[0])[1] == ".m3u":
927
self.parse_m3u(chosenfiles[0])
929
for chosenfile in chosenfiles:
930
if os.path.isdir(chosenfile):
931
files = os.listdir(chosenfile)
934
path = chosenfile + "/" + each
935
media_data = self.get_media_metadata(path)
936
if media_data[0] != "Not a valid file":
937
self.liststore.append(media_data)
938
while gtk.events_pending():
941
media_data = self.get_media_metadata(chosenfile)
942
if media_data[0] != "Not a valid file":
943
self.liststore.append(media_data)
944
# Set the directories start path to be the same next time around.
945
chosenfile = chosenfiles[0]
946
if os.path.isdir(chosenfile):
947
# make sure there is a trailing slash
948
if chosenfile[-1:] == '/':
949
self.file_requester_start_dir.set_text(chosenfile)
951
self.file_requester_start_dir.set_text(chosenfile + '/')
953
chosenfile = os.path.dirname(chosenfile)
954
if os.path.isdir(chosenfile):
955
self.file_requester_start_dir.set_text(chosenfile + '/')
956
# else the user typed in crap so we will reuse the current setting next time around
958
# Clear the flag last to prevent signal handling problems
959
self.showing_file_requester = False
961
def file_notok(self, widget):
962
print "Cancel was clicked on player", self.playername
963
self.filerq.destroy()
964
self.showing_file_requester = False
966
def file_destroy(self, widget):
967
self.filerq.destroy()
968
self.showing_file_requester = False
970
def plfile_ok_sel(self, widget):
971
chosenfile = self.plfilerq.get_filename()
972
self.plfilerq.destroy()
973
if chosenfile[-1] == "/":
974
chosenfile = chosenfile + "idjcplaylist.m3u"
975
elif os.path.splitext(chosenfile)[1] != ".m3u":
976
chosenfile = chosenfile + ".m3u"
977
print "Chosenfile is", chosenfile
979
pl = open(chosenfile, "w")
981
print "Can't open file for writing. Permissions problem?"
984
extm3u = self.parent.prefs_window.extm3u.get_active()
986
pl.write("#EXTM3U\n")
989
if self.liststore[count][0][0] != ">":
991
pl.write("#EXTINF:%d,%s\n" % (self.liststore[count][2], self.liststore[count][3]))
992
pl.write(self.liststore[count][1] + "\n")
998
print "That was odd\n"
999
self.showing_pl_save_requester = False
1001
def plfile_notok(self, widget):
1002
print "Cancel was clicked"
1003
self.plfilerq.destroy()
1004
self.showing_pl_save_requester = False
1006
def plfile_destroy(self, widget):
1007
self.plfilerq.destroy()
1008
self.showing_pl_save_requester = False
1010
def cb_toggle(self, widget, data):
1011
print "Toggle %s recieved for signal: %s" % (("OFF","ON")[widget.get_active()], data)
1012
# Delete mode is for removing items from the playlist.
1013
if data == "Delete":
1014
self.delete_mode = widget.get_active()
1015
# This indirectly selects our song after delete mode is cancelled
1016
if self.delete_mode == 0:
1017
self.reselect_please = True
1020
self.handle_play_button(widget, widget.get_active())
1022
self.handle_pause_button(widget, widget.get_active())
1023
if data == "Stream":
1024
self.parent.send_new_mixer_stats();
1025
if data == "Listen":
1026
self.parent.send_new_mixer_stats();
1028
def cb_progress(self, progress):
1029
if self.digiprogress_f:
1030
if self.max_seek > 0:
1031
if self.digiprogress_type == 0 or self.player_is_playing == False:
1032
count = int(progress.value)
1034
count = self.max_seek - int(progress.value)
1036
count = self.progress_current_figure
1037
hours = int(count / 3600)
1038
count = count - (hours * 3600)
1039
minutes = count / 60
1040
seconds = count - (minutes * 60)
1041
if self.digiprogress_type == 0:
1042
self.digiprogress.set_text("%d:%02d:%02d" % (hours, minutes, seconds))
1044
if self.max_seek != 0:
1045
self.digiprogress.set_text(" -%02d:%02d " % (minutes, seconds))
1047
self.digiprogress.set_text(" -00:00 ")
1048
if self.handle_motion_as_drop:
1049
self.handle_motion_as_drop = False
1050
if self.player_restart() == False:
1053
if self.pause.get_active():
1054
self.pause.set_active(False)
1056
def digiprogress_click(self):
1057
self.digiprogress_type = not self.digiprogress_type
1058
if not self.digiprogress_f:
1059
if self.digiprogress_type == 0:
1060
self.digiprogress.set_text("0:00:00")
1062
self.digiprogress.set_text(" -00:00 ")
1064
self.cb_progress(self.progressadj)
1066
def cb_event(self, widget, event, callback_data):
1067
# Handle click to the play progress indicator
1068
if callback_data == "DigitalProgressPress":
1069
if event.button == 1:
1070
self.digiprogress_click()
1071
if event.button == 3:
1072
self.parent.app_menu.popup(None, None, None, event.button, event.time)
1073
return True # Prevent any focus therefore any cursor appearing
1074
if event.button == 1:
1075
# Handle click to the play progress bar
1076
if callback_data == "ProgressPress":
1077
self.progress_press = True
1078
if self.timeout_source_id:
1079
gobject.source_remove(self.timeout_source_id)
1080
elif callback_data == "ProgressRelease":
1081
self.progress_press = False
1082
if self.player_is_playing:
1083
self.progress_current_figure = self.progressadj.get_value()
1084
self.handle_motion_as_drop = True
1085
gobject.idle_add(self.player_progress_value_changed_emitter)
1088
# This is really a very convoluted workaround to achieve the effect of a connect_after
1089
# method on a button_release_event on the player progress bar to run player_restart
1090
# I tried using connect_after but no such luck hence this retarded idle function.
1091
def player_progress_value_changed_emitter(self):
1092
gtk.gdk.threads_enter()
1093
self.progressadj.emit("value_changed")
1094
gtk.gdk.threads_leave()
1097
def cb_menu_select(self, widget, data):
1098
print "The %s was chosen from the %s menu" % (data, self.playername)
1100
def delete_event(self, widget, event, data=None):
1103
def parse_m3u(self, filename):
1105
file = open(filename, "r")
1109
print "Problem reading file", filename
1111
basepath = os.path.split(filename)[0] + "/"
1112
data = data.splitlines()
1113
if data[0].rstrip() == "#EXTM3U" and self.parent.prefs_window.extm3u.get_active():
1114
print "Playlist is extended"
1118
if data[index][:8] == "#EXTINF:":
1119
line1 = data[index][8:].split(",")
1120
line2 = data[index+1]
1122
line2 = basepath + line2
1123
if [ ".mp3", ".ogg", ".flac", ".wma", ".wav", ".m4a" ].count(os.path.splitext(line2)[1]) == 1:
1124
self.liststore.append([rich_safe(line1[1]), line2, int(line1[0]), line1[1], self.parent.denc, "", line1[1]])
1131
print "Invalid play duration value"
1135
each = basepath + each
1136
meta = self.get_media_metadata(each)
1137
if meta[0] != "Not a valid file":
1138
self.liststore.append(meta)
1139
while gtk.events_pending():
1140
gtk.main_iteration()
1142
def drag_data_delete(self, treeview, context):
1143
if context.action == gtk.gdk.ACTION_MOVE:
1144
treeselection = treeview.get_selection()
1145
model, iter = treeselection.get_selected()
1146
data = model.get_value(iter, 0)
1147
if data[:3] == "<b>":
1148
self.iter_playing = 0
1151
def drag_data_get_data(self, treeview, context, selection, target_id, etime):
1152
treeselection = treeview.get_selection()
1153
model, iter = treeselection.get_selected()
1154
if model.get_value(iter, 1) != "":
1155
data = "file://" + model.get_value(iter, 1)
1157
data = "idjcplayercontrol://" + model.get_value(iter, 0)
1158
print "data for drag_get =", data
1159
selection.set(selection.target, 8, data)
1160
self.reselect_please = True
1163
def drag_data_received_data(self, treeview, context, x, y, dragged, info, etime):
1165
text = str(dragged.data)
1166
if text.count("\x00"): # OMG where did these come from!!!
1168
# Strip out any Null characters which may be due to the dragged
1169
# text being composed of unicode and convert back to the
1170
# filesystem encoding.
1171
# If fenc is wrong dnd will likely fail on filenames with non ASCII.
1172
# The names must ultimately match what is written on your hard drive.
1173
# This can be a problem when you have multiple encodings registered.
1174
text = unicode(text)
1175
fe = self.parent.fenc.split(",")
1177
if each == "@locale" or each == "locale":
1178
each = self.parent.de
1180
result = text.encode(each, "strict")
1182
continue # failed to encode text with the encoding in each
1184
text = result # encoding succeeded even if it is wrong
1187
print "Unable to encode drag and drop filenames with the supplied list:", self.parent.fenc
1189
pass # Not unicode aparrently or we wouldn't be here.
1190
# Finally a blanket removal of any NULLs because they really need to go
1191
text = text.replace("\x00", "")
1192
if text[:20] == "idjcplayercontrol://":
1193
drop_info = treeview.get_dest_row_at_pos(x, y)
1194
model = treeview.get_model()
1195
if drop_info == None:
1196
model.append([ text[20:], "", -11, "" ,"", "", "" ])
1198
path, position = drop_info
1199
dest_iter = model.get_iter(path)
1200
if(position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
1201
model.insert_before(dest_iter, [ text[20:], "", -11, "" ,"", "", "" ])
1203
model.insert_after(dest_iter, [ text[20:], "", -11, "" ,"", "", "" ])
1204
if context.action == gtk.gdk.ACTION_MOVE:
1205
context.finish(True, True, etime)
1207
if context.action == gtk.gdk.ACTION_MOVE:
1208
context.finish(True, True, etime)
1209
gobject.idle_add(self.drag_data_received_data_idle, treeview, x, y, text)
1211
treeselection = treeview.get_selection()
1212
model, iter = treeselection.get_selected()
1213
drop_info = treeview.get_dest_row_at_pos(x, y)
1214
if drop_info == None:
1215
self.liststore.move_before(iter, None)
1217
path, position = drop_info
1218
dest_iter = model.get_iter(path)
1219
if(position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
1220
self.liststore.move_before(iter, dest_iter)
1222
self.liststore.move_after(iter, dest_iter)
1223
if context.action == gtk.gdk.ACTION_MOVE:
1224
context.finish(False, False, etime)
1227
def drag_data_received_data_idle(self, treeview, x, y, dragged):
1228
gtk.gdk.threads_enter()
1229
model = treeview.get_model()
1230
gtk.gdk.threads_leave()
1232
dragged = url_unescape(dragged) # parse out %xx escape sequences
1233
if dragged.count("\n") == 1: # for nautilus compat
1234
dragged = dragged.rstrip() # konqeror omits \n for single entity drag 'n drop
1236
if dragged.count("\n") == 0:
1237
filename = dragged.replace("file://", "")
1238
filename = filename.replace("file:", "")
1239
# This will produce a list of sorted directory contents
1240
if os.path.isdir(filename):
1241
selection = os.listdir(filename)
1243
for value in range (len (selection)):
1244
selection[value] = filename + "/" + selection[value]
1245
# This will attempt to parse m3u files provided they are the only thing dragged
1246
elif os.path.splitext(filename)[1] == ".m3u":
1247
path = os.path.split(filename)[0] + "/"
1248
file = open(filename, "r")
1252
line = file.readline()
1255
if line[0] == "/" or line[0] == "#":
1256
selection = selection + line # handle absolute path m3u files
1258
selection = selection + (path + line) # handle relocatable m3u files
1262
selection = selection.splitlines()
1264
selection = dragged.splitlines()
1266
selection = dragged.splitlines()
1268
if len(selection) > 0 and selection[0].rstrip() == "#EXTM3U" and self.parent.prefs_window.extm3u.get_active():
1272
if selection[index][:8] == "#EXTINF:":
1273
line1 = selection[index][8:].split(",")
1274
line2 = selection[index+1]
1275
if [ ".mp3", ".ogg", ".flac", ".wma", ".wav", ".m4a" ].count(os.path.splitext(line2)[1]) == 1:
1276
media_data = [rich_safe(line1[1]), line2, int(line1[0]), line1[1], self.parent.denc, "", line1[1]]
1277
gtk.gdk.threads_enter()
1278
drop_info = treeview.get_dest_row_at_pos(x, y)
1279
gtk.gdk.threads_leave()
1281
path, position = drop_info
1282
gtk.gdk.threads_enter()
1283
iter = model.get_iter(path)
1284
gtk.gdk.threads_leave()
1285
if(position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
1286
gtk.gdk.threads_enter()
1287
model.insert_before(iter, media_data)
1288
gtk.gdk.threads_leave()
1290
gtk.gdk.threads_enter()
1291
model.insert_after(iter, media_data)
1292
gtk.gdk.threads_leave()
1294
gtk.gdk.threads_enter()
1295
model.append(media_data)
1296
gtk.gdk.threads_leave()
1301
print "Bad value for duration"
1302
gtk.gdk.threads_leave()
1305
for line in selection:
1306
media_data = self.get_media_metadata(line)
1307
if media_data[0] != 'Not a valid file':
1309
gtk.gdk.threads_enter()
1310
drop_info = treeview.get_dest_row_at_pos(x, y)
1311
gtk.gdk.threads_leave()
1313
path, position = drop_info
1314
gtk.gdk.threads_enter()
1315
iter = model.get_iter(path)
1316
gtk.gdk.threads_leave()
1317
if(position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
1318
gtk.gdk.threads_enter()
1319
iter = model.insert_before(iter, media_data)
1320
gtk.gdk.threads_leave()
1322
gtk.gdk.threads_enter()
1323
iter = model.insert_after(iter, media_data)
1324
gtk.gdk.threads_leave()
1326
gtk.gdk.threads_enter()
1327
iter = model.append(media_data)
1328
gtk.gdk.threads_leave()
1331
gtk.gdk.threads_enter()
1332
iter = model.insert_after(iter, media_data)
1333
gtk.gdk.threads_leave()
1334
gtk.gdk.threads_enter()
1335
while gtk.events_pending():
1336
gtk.gdk.threads_leave()
1337
gtk.gdk.threads_enter()
1338
gtk.main_iteration()
1339
gtk.gdk.threads_leave()
1340
gtk.gdk.threads_enter()
1341
gtk.gdk.threads_leave()
1342
self.reselect_please = True
1346
('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
1347
('text/plain', 0, 1),
1352
def cb_doubleclick(self, treeview, path, tvcolumn, user_data):
1353
if self.delete_mode == 0:
1354
# Our play button handler will manage to get the song title.
1355
# All we need to do is set a flag and issue a play button started signal.
1357
# The new_title flag allows a new song to be played when the player is going.
1358
self.new_title = True
1363
print "Double click does nothing in delete mode"
1365
def cb_singleclick(self, treeview):
1366
treeselection = treeview.get_selection()
1367
(model, iter) = treeselection.get_selected()
1369
if self.delete_mode == 1:
1370
treeselection.unselect_all()
1371
# Include a debouce delay for deletion of list entries
1372
if time.time() > self.last_time + 0.1:
1373
deleted_text = model.get_value(iter, 0)
1374
if deleted_text[:3] == "<b>":
1375
print "We deleted the song we are playing - stopping"
1376
self.file_iter_playing = 0
1379
print "We deleted a song"
1381
self.last_time = time.time()
1383
print "Was protected by my debounce code"
1384
self.update_time_stats()
1386
def menu_activate(self, widget, event):
1387
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
1388
self.menu_model = self.treeview.get_model()
1389
row_info = self.treeview.get_dest_row_at_pos(int(event.x + 0.5), int(event.y + 0.5))
1392
path, position = row_info
1393
selection = self.treeview.get_selection()
1394
selection.select_path(path)
1395
self.menu_iter = self.menu_model.get_iter(path) # store the context of the menu action
1396
self.item_tag.set_sensitive(multitag.is_supported(multitag(), self.menu_model.get_value(self.menu_iter, 1)) != False)
1398
self.menu_iter = None
1400
self.item_duplicate.set_sensitive(sens)
1401
self.remove_this.set_sensitive(sens)
1402
self.remove_from_here.set_sensitive(sens)
1403
self.remove_to_here.set_sensitive(sens)
1404
model = self.treeview.get_model()
1405
if model.get_iter_first() == None:
1409
self.pl_menu_item.set_sensitive(sens2)
1410
self.playlist_save.set_sensitive(sens2)
1411
self.playlist_copy.set_sensitive(sens2)
1412
self.playlist_transfer.set_sensitive(sens2)
1413
self.playlist_empty.set_sensitive(sens2)
1414
if self.pl_mode.get_active() != 0:
1415
self.pl_menu_control.set_sensitive(False)
1417
self.pl_menu_control.set_sensitive(True)
1419
if self.playername == "left": # determine if anything is selected in the other playlist
1420
tv = self.parent.player_right.treeview.get_selection()
1422
tv = self.parent.player_left.treeview.get_selection()
1423
model, iter = tv.get_selected()
1428
self.copy_append_cursor.set_sensitive(sens3)
1429
self.copy_prepend_cursor.set_sensitive(sens3)
1430
self.transfer_append_cursor.set_sensitive(sens3)
1431
self.transfer_prepend_cursor.set_sensitive(sens3)
1433
widget.popup(None, None, None, event.button, event.time)
1437
def menuitem_response(self, widget, text):
1438
print "The %s menu option was chosen" % text
1439
model = self.menu_model
1440
iter = self.menu_iter
1443
"Stop Control" : ">stopplayer",
1444
"Transfer Control" : ">transfer",
1445
"Crossfade Control" : ">crossfade",
1446
"Stream Disconnect Control" : ">stopstreaming",
1447
"Stop Recording Control" : ">stoprecording"
1449
if dict.has_key(text):
1450
if iter is not None:
1451
iter = model.insert_after(iter)
1453
iter = model.append()
1454
model.set_value(iter, 0, dict[text])
1455
model.set_value(iter, 1, "")
1456
model.set_value(iter, 2, -11)
1457
model.set_value(iter, 3, "")
1458
model.set_value(iter, 4, "")
1459
model.set_value(iter, 5, "")
1460
model.set_value(iter, 6, "")
1461
self.treeview.get_selection().select_iter(iter)
1464
if text == "MetaTag":
1466
pathname = model.get_value(iter, 1)
1470
multitag(pathname, model.get_value(iter, 4) , self.parent)
1472
if text == "Add File":
1473
if self.showing_file_requester == False:
1474
if self.playername == "left":
1475
filerqtext = left_playlist_addition_text
1477
filerqtext = right_playlist_addition_text
1478
self.filerq = gtk.FileSelection(filerqtext)
1479
self.filerq.set_select_multiple(True)
1480
self.filerq.set_icon_from_file(pkgdatadir + "icon" + gfext)
1481
self.filerq.set_filename(str(self.file_requester_start_dir))
1482
self.filerq.hide_fileop_buttons()
1483
self.filerq.ok_button.connect("clicked", self.file_ok_sel)
1484
self.filerq.cancel_button.connect("clicked", self.file_notok)
1485
self.filerq.connect("destroy", self.file_destroy)
1487
self.showing_file_requester = True
1489
print "refused to open file requester"
1491
if text == "Playlist Save":
1492
if self.showing_pl_save_requester == False:
1493
if self.playername == "left":
1494
filerqtext = left_playlist_save_text
1496
filerqtext = right_playlist_save_text
1497
self.plfilerq = gtk.FileSelection(filerqtext)
1498
self.plfilerq.set_icon_from_file(pkgdatadir + "icon" + gfext)
1499
self.plfilerq.set_filename(self.home + "/idjcplaylist.m3u")
1500
self.plfilerq.hide_fileop_buttons()
1501
self.plfilerq.ok_button.connect("clicked", self.plfile_ok_sel)
1502
self.plfilerq.cancel_button.connect("clicked", self.plfile_notok)
1503
self.plfilerq.connect("destroy", self.plfile_destroy)
1504
self.plfilerq.show()
1505
self.showing_pl_save_requester = True
1507
print "refused to open file requester"
1509
if text == "Remove All":
1512
self.liststore.clear()
1514
if text == "Remove This" and iter != None:
1515
name = model.get_value(iter, 0)
1516
if name[:3] == "<b>":
1518
self.liststore.remove(iter)
1520
if text == "Remove From Here" and iter != None:
1521
path = model.get_path(iter)
1524
iter = model.get_iter(path)
1525
if model.get_value(iter, 0)[:3] == "<b>":
1527
self.liststore.remove(iter)
1529
print "Nothing more to delete"
1531
if text == "Remove To Here" and iter != None:
1532
path = model.get_path(iter)[0] -1
1534
iter = model.get_iter(path)
1535
if model.get_value(iter, 0)[:3] == "<b>":
1537
self.liststore.remove(iter)
1540
if text == "Duplicate" and iter != None:
1541
row = list(model[model.get_path(iter)])
1542
if row[0][:3] == "<b>": # strip off any bold tags
1543
row[0] = row[0][3:-4]
1544
model.insert_after(iter, row)
1546
if text == "Playlist Exchange":
1547
if self.playername == "left":
1548
opposite = self.parent.player_right
1550
opposite = self.parent.player_left
1552
opposite.stop.clicked()
1556
self.templist.append(self.liststore[i])
1560
self.liststore.clear()
1564
self.liststore.append(opposite.liststore[i])
1568
opposite.liststore.clear()
1572
opposite.liststore.append(self.templist[i])
1576
self.templist.clear()
1578
if text == "Copy Append":
1579
self.copy_playlist("end")
1581
if text == "Transfer Append":
1582
self.copy_playlist("end")
1584
self.liststore.clear()
1586
if text == "Copy Prepend":
1587
self.copy_playlist("start")
1589
if text == "Transfer Prepend":
1590
self.copy_playlist("start")
1592
self.liststore.clear()
1594
if text == "Copy Append Cursor":
1595
self.copy_playlist("after")
1597
if text == "Transfer Append Cursor":
1598
self.copy_playlist("after")
1600
self.liststore.clear()
1602
if text == "Copy Prepend Cursor":
1603
self.copy_playlist("before")
1605
if text == "Transfer Prepend Cursor":
1606
self.copy_playlist("before")
1608
self.liststore.clear()
1610
if text == "ToJingles":
1611
source = model.get_value(iter, 1)
1612
dest = self.parent.idjc + "jingles/" + os.path.split(source)[1]
1614
source = open(source, "r")
1615
dest = open(dest, "w")
1617
data = source.read(4096)
1619
if len(data) < 4096:
1622
print "IOError occurred"
1625
self.parent.jingles.refresh.clicked()
1627
if self.player_is_playing: # put the cursor on the file playing after a brief pause.
1628
self.reselect_please = True
1630
def stripbold(self, playlist_item):
1631
copy = list(playlist_item)
1632
if copy[0][:3] == "<b>":
1633
copy[0] = copy[0][3:-4]
1636
def copy_playlist(self, dest):
1637
if self.playername == "left":
1638
other = self.parent.player_right
1640
other = self.parent.player_left
1645
other.liststore.insert(i, self.stripbold(self.liststore[i]))
1649
other.liststore.append(self.stripbold(self.liststore[i]))
1652
(model, iter) = other.treeview.get_selection().get_selected()
1656
iter = other.liststore.insert_after(iter, self.stripbold(self.liststore[i]))
1658
if dest == "before":
1660
other.liststore.insert_before(iter, self.stripbold(self.liststore[i]))
1665
def cb_keypress(self, widget, event):
1666
if self.delete.get_active():
1667
print "Keyboard shortcuts disabled in delete mode"
1669
# Handle shifted arrow keys for rearranging stuff in the playlist.
1670
if event.state & gtk.gdk.SHIFT_MASK:
1671
if event.keyval == 65362:
1674
if event.keyval == 65364:
1677
if event.keyval == 65361 and self.playername == "right":
1678
if self.parent.player_left.delete.get_active() == False:
1679
treeselection = widget.get_selection()
1680
s_model, s_iter = treeselection.get_selected()
1681
if s_iter is not None:
1682
name = s_model.get_value(s_iter, 0)
1683
if name[:3] == "<b>":
1685
otherselection = self.parent.player_left.treeview.get_selection()
1686
d_model, d_iter = otherselection.get_selected()
1687
row = list(s_model[s_model.get_path(s_iter)])
1688
path = s_model.get_path(s_iter)
1689
s_model.remove(s_iter)
1690
treeselection.select_path(path)
1692
d_iter = d_model.append(row)
1694
d_iter = d_model.insert_after(d_iter, row)
1695
otherselection.select_iter(d_iter)
1696
self.parent.player_left.treeview.set_cursor(d_model.get_path(d_iter))
1697
self.parent.player_left.treeview.scroll_to_cell(d_model.get_path(d_iter), None, False)
1699
print "Cannot transfer to the other playlist in delete mode"
1701
if event.keyval == 65363 and self.playername == "left":
1702
if self.parent.player_left.delete.get_active() == False:
1703
treeselection = widget.get_selection()
1704
s_model, s_iter = treeselection.get_selected()
1705
if s_iter is not None:
1706
name = s_model.get_value(s_iter, 0)
1707
if name[:3] == "<b>":
1709
otherselection = self.parent.player_right.treeview.get_selection()
1710
d_model, d_iter = otherselection.get_selected()
1711
row = list(s_model[s_model.get_path(s_iter)])
1712
path = s_model.get_path(s_iter)
1713
s_model.remove(s_iter)
1714
treeselection.select_path(path)
1716
d_iter = d_model.append(row)
1718
d_iter = d_model.insert_after(d_iter, row)
1719
otherselection.select_iter(d_iter)
1720
self.parent.player_right.treeview.set_cursor(d_model.get_path(d_iter))
1721
self.parent.player_right.treeview.scroll_to_cell(d_model.get_path(d_iter), None, False)
1723
print "Cannot transfer to the other playlist in delete mode"
1725
if event.keyval == 65361 and self.playername == "right":
1726
if self.parent.player_left.delete.get_active() == False:
1727
treeselection = self.parent.player_left.treeview.get_selection()
1728
model, iter = treeselection.get_selected()
1729
if iter is not None:
1730
self.parent.player_left.treeview.set_cursor(model.get_path(iter))
1732
treeselection.select_path(0)
1733
self.parent.player_left.treeview.grab_focus()
1735
print "Cannot switch to other player in delete mode"
1737
if event.keyval == 65363 and self.playername == "left":
1738
if self.parent.player_right.delete.get_active() == False:
1739
treeselection = self.parent.player_right.treeview.get_selection()
1740
model, iter = treeselection.get_selected()
1741
if iter is not None:
1742
self.parent.player_right.treeview.set_cursor(model.get_path(iter))
1744
treeselection.select_path(0)
1745
self.parent.player_right.treeview.grab_focus()
1747
print "Cannot switch to other player in delete mode"
1749
if event.keyval == 65535 or event.keyval == 65439: # The Main and NK Delete keys
1750
treeselection = widget.get_selection() # Delete to cause playlist entry removal
1751
model, iter = treeselection.get_selected()
1752
if iter is not None:
1753
path = model.get_path(iter)
1755
prev = model.get_iter(path[0]-1)
1759
next = model.get_iter(path[0]+1)
1762
name = model.get_value(iter, 0)
1763
if name[:3] == "<b>":
1765
self.liststore.remove(iter)
1766
if next is not None:
1767
treeselection.select_iter(next)
1768
widget.set_cursor(model.get_path(next))
1769
self.treeview.scroll_to_cell(model.get_path(next), None, False)
1770
elif prev is not None:
1771
treeselection.select_iter(prev)
1772
widget.set_cursor(model.get_path(prev))
1773
self.treeview.scroll_to_cell(model.get_path(prev), None, False)
1775
print "Playlist is empty!"
1777
if event.string == "1":
1778
self.parent.passleft.clicked()
1780
if event.string == "2":
1781
self.parent.passright.clicked()
1783
if event.string == "c" or event.string == "C":
1784
self.parent.passbutton.clicked()
1786
if (event.string == "m" or event.string == "M") and self.parent.mic_select.flags() & gtk.VISIBLE:
1787
self.parent.mic_select.set_active(not self.parent.mic_select.get_active())
1789
if (event.string == "<" or event.string == ",") and not self.parent.mic_select.flags() & gtk.VISIBLE:
1790
self.parent.mic_lselect.set_active(not self.parent.mic_lselect.get_active())
1792
if (event.string == ">" or event.string == ".") and not self.parent.mic_select.flags() & gtk.VISIBLE:
1793
self.parent.mic_rselect.set_active(not self.parent.mic_rselect.get_active())
1795
if event.keyval == 65288: # backspace key stops the player
1797
if event.string == "t" or event.string == "T": # t key does metadata tagging
1798
model, iter = self.treeview.get_selection().get_selected()
1799
if iter is not None:
1800
pathname = model.get_value(iter, 1)
1801
if multitag.is_supported(multitag(), pathname):
1802
multitag(pathname, model.get_value(iter, 4), self.parent)
1804
print "File type is not supported by the idjc metadata tagger"
1805
if (event.string == "s" or event.string == "S") and self.pl_mode.get_active() == 0:
1806
self.menu_model, self.menu_iter = self.treeview.get_selection().get_selected()
1807
self.menuitem_response(None, "Stop Control")
1809
if (event.string == "a" or event.string == "A") and self.pl_mode.get_active() == 0:
1810
self.menu_model, self.menu_iter = self.treeview.get_selection().get_selected()
1811
self.menuitem_response(None, "Transfer Control")
1813
if (event.string == "f" or event.string == "F") and self.pl_mode.get_active() == 0:
1814
self.menu_model, self.menu_iter = self.treeview.get_selection().get_selected()
1815
self.menuitem_response(None, "Crossfade Control")
1818
# Allow certain key presses to work but not allow a text entry box to appear.
1819
if event.string ==" " or event.string =="\r":
1823
if event.string == "":
1827
def playtimerowconfig(self, tv_column, cell_renderer, model, iter):
1830
playtime = model.get_value(iter, 2)
1831
self.rowconfig(tv_column, cell_renderer, model, iter)
1833
cell_renderer.set_property("text", "")
1835
secs = playtime % 60
1837
mins = playtime / 60
1838
text = "%d:%02d" % (mins, secs)
1839
cell_renderer.set_property("xalign", 1.0)
1840
cell_renderer.set_property("text", text)
1842
def rowconfig(self, tv_column, cell_renderer, model, iter):
1845
celltext = model.get_value(iter, 0)
1846
if celltext[:4] == "<b>>":
1847
celltext = celltext[3:-4]
1848
if celltext[0] == ">" and self.pl_mode.get_active() == 0:
1849
cell_renderer.set_property("xalign", 0.40)
1850
cell_renderer.set_property("ypad", 0)
1851
cell_renderer.set_property("scale", 0.65)
1852
cell_renderer.set_property("cell-background-set", True)
1853
cell_renderer.set_property("background-set", True)
1854
cell_renderer.set_property("foreground-set", True)
1855
if celltext == ">stopplayer":
1856
cell_renderer.set_property("cell-background", "red")
1857
cell_renderer.set_property("background", "gray")
1858
cell_renderer.set_property("foreground", "red")
1859
cell_renderer.set_property("text", stop_control_element_text)
1860
if celltext == ">stopstreaming":
1861
cell_renderer.set_property("cell-background", "black")
1862
cell_renderer.set_property("background", "gray")
1863
cell_renderer.set_property("foreground", "black")
1864
cell_renderer.set_property("text", stream_disconnect_element_text)
1865
if celltext == ">stoprecording":
1866
cell_renderer.set_property("cell-background", "black")
1867
cell_renderer.set_property("background", "gray")
1868
cell_renderer.set_property("foreground", "black")
1869
cell_renderer.set_property("text", stop_recording_element_text)
1870
if celltext == ">transfer":
1871
cell_renderer.set_property("cell-background", "magenta")
1872
cell_renderer.set_property("background", "gray")
1873
cell_renderer.set_property("foreground", "magenta")
1874
if self.playername == "left":
1875
cell_renderer.set_property("text", transfer_control_ltr_element_text)
1877
cell_renderer.set_property("text", transfer_control_rtl_element_text)
1878
if celltext == ">crossfade":
1879
cell_renderer.set_property("cell-background", "blue")
1880
cell_renderer.set_property("background", "gray")
1881
cell_renderer.set_property("foreground", "blue")
1882
if self.playername == "left":
1883
cell_renderer.set_property("text", crossfade_control_ltr_element_text)
1885
cell_renderer.set_property("text", crossfade_control_rtl_element_text)
1887
if celltext[0] == ">":
1888
cell_renderer.set_property("visible", False)
1890
cell_renderer.set_property("visible", True)
1891
cell_renderer.set_property("foreground-set", False)
1892
cell_renderer.set_property("cell-background-set", False)
1893
cell_renderer.set_property("background-set", False)
1894
cell_renderer.set_property("scale", 1.0)
1895
cell_renderer.set_property("xalign", 0.0)
1896
cell_renderer.set_property("ypad", 2)
1898
def cb_playlist_mode(self, widget):
1899
# uses a side effect that the entire playlist is re-rendered
1900
self.tvcolumn.add_attribute(self.cellrender, "markup", 0)
1901
if widget.get_active() == 0:
1902
self.pl_statusbar.show()
1904
self.pl_statusbar.hide()
1906
def __init__(self, pbox, name, parent):
1907
self.exiting = False # used to stop the running of problem code - don't ask
1908
# A box for the Stop/Start/Pause widgets
1909
self.hbox1 = gtk.HBox(True, 0)
1910
self.hbox1.set_border_width(2)
1911
self.hbox1.set_spacing(3)
1913
frame.set_border_width(3)
1914
frame.set_shadow_type(gtk.SHADOW_IN)
1915
frame.add(self.hbox1)
1917
pbox.pack_start(frame, False, False, 0)
1919
# A box for the progress bar, which may at a later date contain other stuff
1920
# Like an elapsed timer for instance.
1921
self.progressbox = gtk.HBox(False, 0)
1922
self.progressbox.set_border_width(3)
1923
self.progressbox.set_spacing(4)
1924
pbox.pack_start(self.progressbox, False, False, 0)
1926
# The numerical play progress box
1927
self.digiprogress = gtk.Entry(7)
1928
self.digiprogress.set_text("0:00:00")
1929
self.digiprogress.set_width_chars(6)
1930
self.digiprogress.set_editable(False)
1931
self.digiprogress.connect("button_press_event", self.cb_event, "DigitalProgressPress")
1932
self.progressbox.pack_start(self.digiprogress, False, False, 1)
1933
self.digiprogress.show()
1935
# The play progress bar and "needle shifter"
1936
self.progressadj = gtk.Adjustment(0.0, 0.0, 100.0, 0.1, 1.0, 0.0)
1937
self.progressadj.connect("value_changed", self.cb_progress)
1938
self.progressbar = gtk.HScale(self.progressadj)
1939
self.progressbar.set_update_policy(gtk.UPDATE_CONTINUOUS)
1940
self.progressbar.set_digits(1)
1941
self.progressbar.set_value_pos(gtk.POS_TOP)
1942
self.progressbar.set_draw_value(False)
1943
# These events may not be the best ones to use. Others may work better.
1944
self.progressbar.connect("button_press_event", self.cb_event, "ProgressPress")
1945
self.progressbar.connect("button_release_event", self.cb_event, "ProgressRelease")
1946
self.progressbox.pack_start(self.progressbar, True, True, 0)
1947
self.progressbar.show()
1949
# Finished filling the progress box so lets show it.
1950
self.progressbox.show()
1952
# A frame for our playlist
1954
plframe = gtk.Frame(" " + playlist_text + " 1 ")
1956
plframe = gtk.Frame(" " + playlist_text + " 2 ")
1957
plframe.set_border_width(4)
1958
plframe.set_shadow_type(gtk.SHADOW_ETCHED_IN)
1963
# The scrollable window box that will contain our playlist.
1964
self.scrolllist = gtk.ScrolledWindow()
1965
self.scrolllist.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
1966
self.scrolllist.set_size_request(-1, 217)
1967
self.scrolllist.set_border_width(4)
1968
self.scrolllist.set_shadow_type(gtk.SHADOW_IN)
1969
# A liststore object for our playlist
1970
# The first one gets rendered and is derived from id3 tags or just is the filename
1971
# when the id3 tag is not sufficient.
1972
# The second one always is the filename and is passed to the player.
1973
self.liststore = gtk.ListStore(str, str, int, str, str, str, str)
1974
self.templist = gtk.ListStore(str, str, int, str, str, str, str)
1975
self.treeview = gtk.TreeView(self.liststore)
1976
self.playtimecellrender = gtk.CellRendererText()
1977
self.cellrender = gtk.CellRendererText()
1978
self.playtimetvcolumn = gtk.TreeViewColumn("Time", self.playtimecellrender)
1979
self.tvcolumn = gtk.TreeViewColumn("Playlist", self.cellrender)
1980
self.playtimetvcolumn.set_cell_data_func(self.playtimecellrender, self.playtimerowconfig)
1981
self.tvcolumn.set_cell_data_func(self.cellrender, self.rowconfig)
1982
self.playtimetvcolumn.add_attribute(self.playtimecellrender, 'text', 2)
1983
self.playtimetvcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
1984
self.tvcolumn.add_attribute(self.cellrender, 'markup', 0)
1985
self.tvcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
1986
self.tvcolumn.set_expand(True)
1987
self.treeview.append_column(self.tvcolumn)
1988
self.treeview.append_column(self.playtimetvcolumn)
1989
self.treeview.set_search_column(0)
1990
self.treeview.set_headers_visible(False)
1991
self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
1993
gtk.gdk.ACTION_DEFAULT |
1994
gtk.gdk.ACTION_MOVE)
1995
self.treeview.enable_model_drag_dest( self.TARGETS,
1996
gtk.gdk.ACTION_DEFAULT)
1998
self.treeview.connect("drag_data_get", self.drag_data_get_data)
1999
self.treeview.connect("drag_data_received", self.drag_data_received_data)
2000
self.treeview.connect("drag_data_delete", self.drag_data_delete)
2002
self.treeview.connect("row_activated", self.cb_doubleclick, "Double click")
2003
self.treeview.connect("cursor_changed", self.cb_singleclick)
2005
self.treeview.connect("key_press_event", self.cb_keypress)
2007
self.scrolllist.add(self.treeview)
2008
self.treeview.show()
2010
plvbox.pack_start(self.scrolllist, True, True, 0)
2011
self.scrolllist.show()
2013
# An information display for playlist stats
2014
self.pl_statusbar = gtk.Statusbar()
2015
self.pl_statusbar.set_has_resize_grip(False)
2016
plvbox.pack_start(self.pl_statusbar, False, False, 0)
2017
self.pl_statusbar.show()
2019
pbox.pack_start(plframe, True, True, 0)
2021
# The box for the mute widgets.
2022
self.hbox2 = gtk.HBox(False, 0)
2023
self.hbox2.set_border_width(4)
2024
pbox.pack_start(self.hbox2, False, False, 0)
2026
# A set of buttons for hbox1 namely Prev/Play/Pause/Stop/Next/Playlist : XMMS order
2028
image.set_from_file(pkgdatadir + "prev" + gfext)
2030
self.prev = gtk.Button()
2031
self.prev.add(image)
2032
self.prev.connect("clicked", self.callback, "Prev")
2033
self.hbox1.add(self.prev)
2036
pixbuf = gtk.gdk.pixbuf_new_from_file(pkgdatadir + "play2" + gfext)
2037
pixbuf = pixbuf.scale_simple(14, 14, gtk.gdk.INTERP_BILINEAR)
2039
image.set_from_pixbuf(pixbuf)
2041
self.play = gtk.ToggleButton()
2042
self.play.add(image)
2043
self.play.connect("toggled", self.cb_toggle, "Play")
2044
self.hbox1.add(self.play)
2048
image.set_from_file(pkgdatadir + "pause" + gfext)
2050
self.pause = gtk.ToggleButton()
2051
self.pause.add(image)
2052
self.pause.connect("toggled", self.cb_toggle, "Pause")
2053
self.hbox1.add(self.pause)
2057
image.set_from_file(pkgdatadir + "stop" + gfext)
2059
self.stop = gtk.Button()
2060
self.stop.add(image)
2061
self.stop.connect("clicked", self.callback, "Stop")
2062
self.hbox1.add(self.stop)
2066
image.set_from_file(pkgdatadir + "next" + gfext)
2068
self.next = gtk.Button()
2069
self.next.add(image)
2070
self.next.connect("clicked", self.callback, "Next")
2071
self.hbox1.add(self.next)
2074
pixbuf = gtk.gdk.pixbuf_new_from_file(pkgdatadir + "add3" + gfext)
2075
pixbuf = pixbuf.scale_simple(14, 14, gtk.gdk.INTERP_HYPER)
2077
image.set_from_pixbuf(pixbuf)
2079
self.add = gtk.Button()
2081
self.add.connect("clicked", self.callback, "Add Files")
2082
self.hbox1.add(self.add)
2085
# hbox1 is done so it is time to show it
2088
# The playlist mode dropdown menu.
2089
self.pl_mode = gtk.combo_box_new_text()
2090
self.pl_mode.append_text(play_all_text)
2091
self.pl_mode.append_text(loop_all_text)
2092
self.pl_mode.append_text(random_text)
2093
self.pl_mode.append_text(manual_text)
2094
self.pl_mode.append_text(cue_up_text)
2095
self.pl_mode.set_active(0)
2096
self.pl_mode.connect("changed", self.cb_playlist_mode)
2098
self.hbox2.pack_start(self.pl_mode, True, True, 0)
2101
# Up and Down arrows for moving items in the playlist
2102
self.uparrow = make_arrow_button(self, gtk.ARROW_UP, gtk.SHADOW_IN, "Arrow Up")
2103
self.hbox2.pack_start(self.uparrow, True, True, 0)
2105
self.dnarrow = make_arrow_button(self, gtk.ARROW_DOWN, gtk.SHADOW_IN, "Arrow Dn")
2106
self.hbox2.pack_start(self.dnarrow, True, True, 0)
2108
# Delete mode toggle button for playlist
2110
image.set_from_file(pkgdatadir + "del" + gfext)
2112
self.delete = gtk.ToggleButton()
2113
self.delete.add(image)
2114
self.delete_mode = self.delete.get_active()
2115
self.delete.connect("toggled", self.cb_toggle, "Delete")
2116
self.hbox2.pack_start(self.delete, True, True, 0)
2120
self.stream = gtk.ToggleButton(stream_text)
2121
self.stream.set_active(True)
2122
self.stream.connect("toggled", self.cb_toggle, "Stream")
2123
self.hbox2.pack_start(self.stream, True, True, 0)
2126
self.listen = nice_listen_togglebutton(listen_text)
2127
self.listen.set_active(True)
2128
self.listen.connect("toggled", self.cb_toggle, "Listen")
2129
self.hbox2.pack_start(self.listen, True, True, 0)
2131
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
2132
sizegroup.add_widget(self.stream)
2133
sizegroup.add_widget(self.listen)
2135
# hbox2 is now filled so lets show it
2138
# Popup menu code here
2141
self.pl_menu = gtk.Menu()
2143
self.pl_menu_control = gtk.MenuItem(control_menu_text)
2144
self.pl_menu.append(self.pl_menu_control)
2145
self.pl_menu_control.show()
2147
separator = gtk.SeparatorMenuItem()
2148
self.pl_menu.append(separator)
2151
self.pl_menu_item = gtk.MenuItem(item_menu_text)
2152
self.pl_menu.append(self.pl_menu_item)
2153
self.pl_menu_item.show()
2155
self.pl_menu_playlist = gtk.MenuItem(playlist_menu_text)
2156
self.pl_menu.append(self.pl_menu_playlist)
2157
self.pl_menu_playlist.show()
2161
# Control element submenu of main popup menu
2163
self.control_menu = gtk.Menu()
2165
self.control_menu_stop_control = gtk.MenuItem(stop_control_menu_text)
2166
self.control_menu_stop_control.connect("activate", self.menuitem_response, "Stop Control")
2167
self.control_menu.append(self.control_menu_stop_control)
2168
self.control_menu_stop_control.show()
2170
self.control_menu_transfer_control = gtk.MenuItem(transfer_control_menu_text)
2171
self.control_menu_transfer_control.connect("activate", self.menuitem_response, "Transfer Control")
2172
self.control_menu.append(self.control_menu_transfer_control)
2173
self.control_menu_transfer_control.show()
2175
self.control_menu_crossfade_control = gtk.MenuItem(crossfade_control_menu_text)
2176
self.control_menu_crossfade_control.connect("activate", self.menuitem_response, "Crossfade Control")
2177
self.control_menu.append(self.control_menu_crossfade_control)
2178
self.control_menu_crossfade_control.show()
2180
separator = gtk.SeparatorMenuItem()
2181
self.control_menu.append(separator)
2184
self.control_menu_stream_disconnect_control = gtk.MenuItem(stream_disconnect_menu_text)
2185
self.control_menu_stream_disconnect_control.connect("activate", self.menuitem_response, "Stream Disconnect Control")
2186
self.control_menu.append(self.control_menu_stream_disconnect_control)
2187
self.control_menu_stream_disconnect_control.show()
2189
self.control_menu_stop_recording_control = gtk.MenuItem(stop_recording_menu_text)
2190
self.control_menu_stop_recording_control.connect("activate", self.menuitem_response, "Stop Recording Control")
2191
self.control_menu.append(self.control_menu_stop_recording_control)
2192
self.control_menu_stop_recording_control.show()
2194
self.pl_menu_control.set_submenu(self.control_menu)
2195
self.control_menu.show()
2197
# Item submenu of main popup menu
2198
self.item_menu = gtk.Menu()
2200
self.item_tag = gtk.MenuItem(meta_tag_text)
2201
self.item_tag.connect("activate", self.menuitem_response, "MetaTag")
2202
self.item_menu.append(self.item_tag)
2203
self.item_tag.show()
2205
self.item_duplicate = gtk.MenuItem(duplicate_text)
2206
self.item_duplicate.connect("activate", self.menuitem_response, "Duplicate")
2207
self.item_menu.append(self.item_duplicate)
2208
self.item_duplicate.show()
2210
self.item_remove = gtk.MenuItem(remove_text)
2211
self.item_menu.append(self.item_remove)
2212
self.item_remove.show()
2214
self.item_tojingles = gtk.MenuItem(add_to_jingles_text)
2215
self.item_tojingles.connect("activate", self.menuitem_response, "ToJingles")
2216
self.item_menu.append(self.item_tojingles)
2217
self.item_tojingles.show()
2219
self.pl_menu_item.set_submenu(self.item_menu)
2220
self.item_menu.show()
2222
# Remove submenu of Item submenu
2223
self.remove_menu = gtk.Menu()
2225
self.remove_this = gtk.MenuItem(this_text)
2226
self.remove_this.connect("activate", self.menuitem_response, "Remove This")
2227
self.remove_menu.append(self.remove_this)
2228
self.remove_this.show()
2230
self.remove_all = gtk.MenuItem(all_text)
2231
self.remove_all.connect("activate", self.menuitem_response, "Remove All")
2232
self.remove_menu.append(self.remove_all)
2233
self.remove_all.show()
2235
self.remove_from_here = gtk.MenuItem(from_here_text)
2236
self.remove_from_here.connect("activate", self.menuitem_response, "Remove From Here")
2237
self.remove_menu.append(self.remove_from_here)
2238
self.remove_from_here.show()
2240
self.remove_to_here = gtk.MenuItem(to_here_text)
2241
self.remove_to_here.connect("activate", self.menuitem_response, "Remove To Here")
2242
self.remove_menu.append(self.remove_to_here)
2243
self.remove_to_here.show()
2245
self.item_remove.set_submenu(self.remove_menu)
2246
self.remove_menu.show()
2248
# Playlist submenu of main popup menu.
2249
self.playlist_menu = gtk.Menu()
2251
self.playlist_add_file = gtk.MenuItem(add_file_text)
2252
self.playlist_add_file.connect("activate", self.menuitem_response, "Add File")
2253
self.playlist_menu.append(self.playlist_add_file)
2254
self.playlist_add_file.show()
2256
self.playlist_save = gtk.MenuItem(save_text)
2257
self.playlist_save.connect("activate", self.menuitem_response, "Playlist Save")
2258
self.playlist_menu.append(self.playlist_save)
2259
self.playlist_save.show()
2261
separator = gtk.SeparatorMenuItem()
2262
self.playlist_menu.append(separator)
2265
self.playlist_copy = gtk.MenuItem(copy_text)
2266
self.playlist_menu.append(self.playlist_copy)
2267
self.playlist_copy.show()
2269
self.playlist_transfer = gtk.MenuItem(transfer_text)
2270
self.playlist_menu.append(self.playlist_transfer)
2271
self.playlist_transfer.show()
2273
self.playlist_exchange = gtk.MenuItem(exchange_text)
2274
self.playlist_exchange.connect("activate", self.menuitem_response, "Playlist Exchange")
2275
self.playlist_menu.append(self.playlist_exchange)
2276
self.playlist_exchange.show()
2278
self.playlist_empty = gtk.MenuItem(empty_text)
2279
self.playlist_empty.connect("activate", self.menuitem_response, "Remove All")
2280
self.playlist_menu.append(self.playlist_empty)
2281
self.playlist_empty.show()
2283
self.pl_menu_playlist.set_submenu(self.playlist_menu)
2284
self.playlist_menu.show()
2286
# Position Submenu of Playlist-Copy menu item
2288
self.copy_menu = gtk.Menu()
2290
self.copy_append = gtk.MenuItem(append_text)
2291
self.copy_append.connect("activate", self.menuitem_response, "Copy Append")
2292
self.copy_menu.append(self.copy_append)
2293
self.copy_append.show()
2295
self.copy_prepend = gtk.MenuItem(prepend_text)
2296
self.copy_prepend.connect("activate", self.menuitem_response, "Copy Prepend")
2297
self.copy_menu.append(self.copy_prepend)
2298
self.copy_prepend.show()
2300
separator = gtk.SeparatorMenuItem()
2301
self.copy_menu.append(separator)
2304
self.copy_append_cursor = gtk.MenuItem(append_cursor_text)
2305
self.copy_append_cursor.connect("activate", self.menuitem_response, "Copy Append Cursor")
2306
self.copy_menu.append(self.copy_append_cursor)
2307
self.copy_append_cursor.show()
2309
self.copy_prepend_cursor = gtk.MenuItem(prepend_cursor_text)
2310
self.copy_prepend_cursor.connect("activate", self.menuitem_response, "Copy Prepend Cursor")
2311
self.copy_menu.append(self.copy_prepend_cursor)
2312
self.copy_prepend_cursor.show()
2314
self.playlist_copy.set_submenu(self.copy_menu)
2315
self.copy_menu.show()
2317
# Position Submenu of Playlist-Transfer menu item
2319
self.transfer_menu = gtk.Menu()
2321
self.transfer_append = gtk.MenuItem(append_text)
2322
self.transfer_append.connect("activate", self.menuitem_response, "Transfer Append")
2323
self.transfer_menu.append(self.transfer_append)
2324
self.transfer_append.show()
2326
self.transfer_prepend = gtk.MenuItem(prepend_text)
2327
self.transfer_prepend.connect("activate", self.menuitem_response, "Transfer Prepend")
2328
self.transfer_menu.append(self.transfer_prepend)
2329
self.transfer_prepend.show()
2331
separator = gtk.SeparatorMenuItem()
2332
self.transfer_menu.append(separator)
2335
self.transfer_append_cursor = gtk.MenuItem(append_cursor_text)
2336
self.transfer_append_cursor.connect("activate", self.menuitem_response, "Transfer Append Cursor")
2337
self.transfer_menu.append(self.transfer_append_cursor)
2338
self.transfer_append_cursor.show()
2340
self.transfer_prepend_cursor = gtk.MenuItem(prepend_cursor_text)
2341
self.transfer_prepend_cursor.connect("activate", self.menuitem_response, "Transfer Prepend Cursor")
2342
self.transfer_menu.append(self.transfer_prepend_cursor)
2343
self.transfer_prepend_cursor.show()
2345
self.playlist_transfer.set_submenu(self.transfer_menu)
2346
self.transfer_menu.show()
2349
self.treeview.connect_object("event", self.menu_activate, self.pl_menu)
2352
self.playername = name
2353
self.showing_file_requester = False
2354
self.showing_pl_save_requester = False
2356
# Get the users home directory and test that the environment variable is okay.
2357
# Actually this is more than bullet proof because when HOME is wrong I cant even run it.
2358
self.home = os.getenv("HOME")
2359
if os.path.exists(self.home):
2360
if os.path.isdir(self.home) == False:
2361
self.home = os.path.basename(self.home)
2363
# Make sure there is a trailing slash or the filerequester will mess up a bit.
2364
if self.home[-1:] != '/':
2365
self.home = self.home + '/'
2367
self.file_requester_start_dir = int_object(self.home)
2369
# This is used for mouse-click debounce when deleting files in the playlist.
2370
self.last_time = time.time()
2372
# This flag symbolises if we are playing music or not.
2373
self.is_playing = False
2374
self.is_paused = False
2375
self.is_stopping = False
2376
self.player_is_playing = False
2377
self.new_title = False
2378
self.timeout_source_id = 0
2379
self.progress_press = False
2381
# The maximum value from the progress bar at startup
2382
self.max_seek = 100.0
2383
self.reselect_please = False
2384
self.reselect_cursor_please = False
2385
self.parent = parent
2390
self.gapless = False
2391
self.seek_file_valid = False
2392
self.digiprogress_type = 0
2393
self.digiprogress_f = 0
2394
self.handle_motion_as_drop = False
2395
self.tag = eyeD3.Tag()
2396
self.other_player_initiated = False
2397
self.crossfader_initiated = False
2398
self.music_filename = ""
2399
self.session_filename = self.parent.idjc + self.playername + "_session"
2400
self.oldstatusbartext = ""