~ubuntu-branches/debian/wheezy/idjc/wheezy

« back to all changes in this revision

Viewing changes to idjcpython/IDJCmedia.py

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Le Folgoc (mr_pouit)
  • Date: 2007-01-15 01:49:58 UTC
  • mto: (1.1.8 upstream) (5.1.1 sid)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20070115014958-j2g4ki53lnlnmnfp
Tags: upstream-0.6.9
ImportĀ upstreamĀ versionĀ 0.6.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#   IDJCmedia.py: GUI code for main media players in IDJC
 
2
#   Copyright (C) 2005 Stephen Fairchild
 
3
#
 
4
#   This program is free software; you can redistribute it and/or modify
 
5
#   it under the terms of the GNU General Public License as published by
 
6
#   the Free Software Foundation; either version 2 of the License, or
 
7
#   (at your option) any later version.
 
8
#
 
9
#   This program is distributed in the hope that it will be useful,
 
10
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#   GNU General Public License for more details.
 
13
#
 
14
#   You should have received a copy of the GNU General Public License
 
15
#   along with this program; if not, write to the Free Software
 
16
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
import pygtk
 
19
pygtk.require('2.0')
 
20
import gtk
 
21
import gobject
 
22
import os
 
23
import sys
 
24
import time
 
25
import random
 
26
import signal
 
27
import eyeD3
 
28
from stat import *
 
29
from IDJCmultitagger import*
 
30
from IDJCfree import *
 
31
from idjc_config import *
 
32
from langheader import *
 
33
 
 
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);
 
38
   button.add(arrow)
 
39
   button.connect("clicked", self.callback, data)
 
40
   button.show()
 
41
   arrow.show()
 
42
   return button
 
43
 
 
44
def get_number_for(token, string):
 
45
   try:
 
46
      end = string.rindex(token)
 
47
      start = end - 1
 
48
      while start >= 0 and (string[start].isdigit() or string[start] == "."):
 
49
         start = start - 1
 
50
      return int(float(string[start+1:end]))
 
51
   except ValueError:
 
52
      return 0
 
53
   
 
54
class nice_listen_togglebutton(gtk.ToggleButton):
 
55
   def __init__(self, label = None, use_underline = True):
 
56
      gtk.ToggleButton.__init__(self, label, use_underline)
 
57
   def __str__(self):
 
58
      return gtk.ToggleButton.__str__() + " auto inconsistent when insensitive"
 
59
   def set_sensitive(self, bool):
 
60
      if bool is False:
 
61
         gtk.ToggleButton.set_sensitive(self, False)
 
62
         gtk.ToggleButton.set_inconsistent(self, True)
 
63
      else:
 
64
         gtk.ToggleButton.set_sensitive(self, True)
 
65
         gtk.ToggleButton.set_inconsistent(self, False)
 
66
         
 
67
class IDJC_Media_Player:
 
68
   def get_media_metadata(self, filename):
 
69
      # This is the third incarnation of this function.
 
70
   
 
71
      artist = u""
 
72
      title = u""
 
73
      length = 0
 
74
      artist_retval = u""
 
75
      title_retval = u""
 
76
      
 
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
 
83
      try:
 
84
         filext = os.path.splitext(filename)[1]
 
85
         if [ ".mp3", ".ogg", ".flac", ".wma", ".m4a", ".wav", ".avi", ".mp4" ].count(filext) != 1:
 
86
            raise
 
87
         if os.path.isfile(filename) == False:
 
88
            raise
 
89
      except:
 
90
         return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]   
 
91
      
 
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(",")
 
96
      for each in enclist:
 
97
         each = each.strip()
 
98
         if each == "@locale":
 
99
            each = self.parent.denc
 
100
         try:
 
101
            meta_name = unicode(os.path.splitext(meta_name)[0], each)
 
102
         except:
 
103
            pass
 
104
         else:
 
105
            encoding = each
 
106
            rsmeta_name = u'<span foreground="gray">' + rich_safe(meta_name) + '</span>'
 
107
            title_retval = meta_name
 
108
            break
 
109
      else:
 
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"
 
114
         
 
115
      filext = filext.lower()
 
116
      if filext == ".mp3" or filext == ".flac" or filext == ".wma" or filext == ".ogg":
 
117
         try:
 
118
            if eyeD3.isMp3File(filename):
 
119
               audiofile = eyeD3.Mp3AudioFile(filename, eyeD3.ID3_V2)
 
120
               length = audiofile.getPlayTime()
 
121
               tag = audiofile.getTag()
 
122
            else:
 
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())
 
127
         except:
 
128
            print "Problem with reading ID3v2 tag"
 
129
         try:
 
130
            if eyeD3.isMp3File(filename) and length == 0:
 
131
               audiofile = eyeD3.Mp3AudioFile(filename, eyeD3.ID3_V1)
 
132
               length = audiofile.getPlayTime()
 
133
               tag = audiofile.getTag()
 
134
            else:
 
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:
 
138
            if length == 0:
 
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())
 
145
            
 
146
         while artist != u"" and artist[-1] == u"\x00":
 
147
            artist = artist[:-1]
 
148
         artist = artist.strip()
 
149
         while title != u"" and title[-1] == u"\x00":
 
150
            title = title[:-1]
 
151
         title = title.strip()
 
152
         if artist != u"" and title != u"":
 
153
            rsmeta_name = meta_name = artist + u" - " + title
 
154
            title_retval = title
 
155
            artist_retval = artist
 
156
      
 
157
      if filext == ".ogg" and os.environ.get("ogginfo") != "missing":
 
158
         self.parent.mixer_write("OGGP=%s\nACTN=ogginforequest\nend\n" % filename, True)
 
159
         while 1:
 
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":
 
170
               break
 
171
 
 
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
 
176
            title_retval = title
 
177
            artist_retval = artist
 
178
 
 
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])
 
181
         stdin.close()
 
182
         data = unicode(stdout.read())
 
183
         stdout.close
 
184
         data = data.splitlines()
 
185
         try:
 
186
            length = int(data[1]) / int(data[0]) 
 
187
         except:
 
188
            return [ "Not a valid file", filename, 0, "", "latin1", "", "" ]
 
189
         artist = title = u""
 
190
         for each in data:
 
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
 
197
            title_retval = 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)
 
202
         while 1:
 
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":
 
213
               break
 
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
 
218
            title_retval = title
 
219
            artist_retval = artist
 
220
      else: 
 
221
         self.parent.mixer_write("PLRP=%s\nACTN=askxine\nend\n" % filename, True)
 
222
         while 1:
 
223
            line = self.parent.mixer_read()
 
224
            if line == "":
 
225
               return [ "Not a valid file", filename, 0, "", "latin1", "", "" ] 
 
226
            if line == "askxine: done\n":
 
227
               break
 
228
            if line == "askxine: error\n":
 
229
               length = None
 
230
            if line.startswith("askxine: length="):
 
231
               length = int(line[16:-1])
 
232
            if line.startswith("askxine: artist="):
 
233
               artist = line[16:-1]
 
234
            if line.startswith("askxine: title="):
 
235
               title = line[15:-1]
 
236
         if length == None:
 
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
 
243
            title_retval = title
 
244
 
 
245
      if rsmeta_name == meta_name:
 
246
         return [ rich_safe(rsmeta_name), filename, length, meta_name, encoding, title_retval, artist_retval ]
 
247
      else:
 
248
         return [ rsmeta_name, filename, length, meta_name, encoding, title_retval, artist_retval ]
 
249
      
 
250
   # Update playlist entries for a given filename e.g. when tag has been edited
 
251
   def update_playlist(self, newdata):
 
252
      update = False
 
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>"
 
257
               update = True
 
258
            else:
 
259
               item[0] = newdata[0]
 
260
            item[3] = newdata[3]
 
261
            item[5] = newdata[5]
 
262
            item[6] = newdata[6]
 
263
      if update:
 
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()
 
268
      
 
269
   # Shut down our media players when we exit.
 
270
   def cleanup(self):
 
271
      self.exiting = True               # I added this because, well the reason totally sucks TBH
 
272
      if self.player_is_playing:
 
273
         self.stop.clicked()
 
274
      self.save_session()
 
275
 
 
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:
 
283
         fh.write("pe=")
 
284
         entry = list(entry)
 
285
         for item in entry:
 
286
            if type(item) == int:
 
287
               item = str(item)
 
288
               fh.write("i")
 
289
            else:
 
290
               fh.write("s")
 
291
            fh.write(str(len(item)) + ":" + item)
 
292
         fh.write("\n")
 
293
      model, iter = self.treeview.get_selection().get_selected()
 
294
      if iter is not None:
 
295
         fh.write("select=" + str(model.get_path(iter)[0]) + "\n")
 
296
      fh.close()
 
297
 
 
298
   def restore_session(self):
 
299
      try:
 
300
         fh = open(self.session_filename, "r")
 
301
      except:
 
302
         return
 
303
      while 1:
 
304
         try:
 
305
            line = fh.readline()
 
306
            if line == "":
 
307
               break
 
308
         except:
 
309
            break
 
310
         try:
 
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:])
 
322
               try:
 
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="):
 
329
               path = line[7:-1]
 
330
               try:
 
331
                  self.treeview.get_selection().select_path(path)
 
332
                  self.treeview.scroll_to_cell(path, None, False) 
 
333
               except:
 
334
                  pass
 
335
         except ValueError:
 
336
            pass
 
337
 
 
338
   def pl_unpack(self, text):           # converts a string encoded list to a python list
 
339
      start = 0 
 
340
      item = 0  
 
341
      reply = list()
 
342
      while text[start] != "\n":
 
343
         end = start
 
344
         while text[end] != ":":
 
345
            end = end + 1                               
 
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"):
 
350
            try:
 
351
               reply.append(int(text[ end+1 : nextstart ]))
 
352
            except:
 
353
               print "pl_unpack: bad integer data"
 
354
               return []
 
355
         else:
 
356
            print "pl_unpack: unknown data type:", text[start]
 
357
            return []
 
358
         start = nextstart
 
359
      return reply
 
360
             
 
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()
 
375
         
 
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()
 
383
         else:
 
384
            # Player unpause code goes here
 
385
            print "Player unpaused"
 
386
            self.is_paused = False
 
387
            self.parent.send_new_mixer_stats()
 
388
      else:
 
389
         # Prevent the pause button going into its on state when not playing.
 
390
         if selected:
 
391
            # We must unselect it.
 
392
            widget.set_active(False)
 
393
         else:
 
394
            self.is_paused = False
 
395
   
 
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)
 
403
         else:
 
404
            self.is_stopping = False
 
405
            if self.player_is_playing == True:
 
406
               self.player_shutdown()
 
407
      else:
 
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
 
416
                  #self.next.clicked()
 
417
                  #self.invoke_end_of_track_policy()
 
418
               if self.is_paused:
 
419
                  self.pause.set_active(False)
 
420
            else:    
 
421
               print "Someone probably clicked Play when we were already playing"
 
422
         else:
 
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"
 
428
            else:
 
429
               self.stop.clicked()
 
430
   
 
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
 
436
      else:
 
437
         # Get our next playlist item.
 
438
         treeselection = self.treeview.get_selection()
 
439
         (model, iter) = treeselection.get_selected()
 
440
      if iter == None:
 
441
         print "Nothing selected in the playlist - trying the first entry."
 
442
         try:
 
443
            iter = model.get_iter(0)
 
444
         except:
 
445
            print "Playlist is empty"
 
446
            return False
 
447
         print "We start at the beginning"
 
448
         treeselection.select_iter(iter)
 
449
         
 
450
      self.treeview.scroll_to_cell(model.get_path(iter)[0], None, False)
 
451
 
 
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)
 
461
      if rt < 0:
 
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):
 
466
         try:
 
467
            self.start_time = int(self.progressadj.get_value() / self.max_seek * float(rt))
 
468
         except ZeroDivisionError:
 
469
            self.start_time = 0
 
470
      else:
 
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
 
473
           
 
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
 
481
      
 
482
      self.player_is_playing = True
 
483
      
 
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
 
490
      self.max_seek = rt
 
491
      self.silence_count = 0
 
492
      
 
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)
 
497
         else:
 
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)
 
501
         while 1:
 
502
            line = self.parent.mixer_read()
 
503
            if line.startswith("context_id="):
 
504
               self.player_cid = int(line[11:-1])
 
505
               break
 
506
            if line == "":
 
507
               self.player_cid = -1
 
508
               break
 
509
      else:
 
510
         print "skipping play for empty filename"
 
511
         self.player_cid = -1
 
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)
 
518
      return True
 
519
 
 
520
   def player_shutdown(self):
 
521
      print "player shutdown code was called"
 
522
      
 
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>":
 
527
            text = text[3:-4]
 
528
            self.model_playing.set_value(self.iter_playing, 0, text)
 
529
         self.file_iter_playing = 0
 
530
 
 
531
      self.player_is_playing = False
 
532
      if self.timeout_source_id:
 
533
         gobject.source_remove(self.timeout_source_id)
 
534
         
 
535
      self.playtime_elapsed.set_value(0)
 
536
      self.progressadj.set_value(0.0)
 
537
      self.progressadj.value_changed()
 
538
      
 
539
      if self.gapless == False:
 
540
         self.parent.mixer_write("ACTN=stop%s\nend\n" % self.playername, True)
 
541
      
 
542
      self.digiprogress_f = False
 
543
      self.other_player_initiated = False
 
544
      self.crossfader_initiated = False
 
545
   
 
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)
 
552
      while 1:
 
553
         line = self.parent.mixer_read()
 
554
         if line.startswith("context_id="):
 
555
            self.player_cid = int(line[11:-1])
 
556
            break
 
557
         if line == "":
 
558
            self.player_cid = -1
 
559
            break
 
560
      if self.player_cid == -1:
 
561
         print "player startup was unsuccessful for file", self.music_filename
 
562
         return False
 
563
      
 
564
      print "player context id is %d\n" % self.player_cid
 
565
      
 
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)
 
568
      return True
 
569
 
 
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"
 
575
         return
 
576
      
 
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"
 
580
         self.stop.clicked()
 
581
      elif mode_text == play_all_text:
 
582
         if self.music_filename == "":
 
583
            self.handle_playlist_control()
 
584
         else:
 
585
            self.next.clicked()
 
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
 
591
         self.stop.clicked()
 
592
         try:
 
593
            self.model_playing.get_iter(path)
 
594
            print "Picking the next element in the playlist"
 
595
         except:
 
596
            print "We are at the bottom. Picking the first element"
 
597
            path = 0
 
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] == ">"):
 
601
            self.play.clicked()
 
602
      elif mode_text == random_text:
 
603
         # Count the number of tracks
 
604
         self.stop.clicked()    
 
605
         count = 0
 
606
         while 1:
 
607
            try:
 
608
               self.model_playing.get_iter(count)
 
609
               count = count + 1 
 
610
            except:
 
611
               break
 
612
         
 
613
         print "There are", count, "elements in the playlist"
 
614
         # Pick a new track almost at random
 
615
         
 
616
         new_path = random.randint(0, count -1)
 
617
         if count > 3:
 
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:
 
621
               # Must try again
 
622
               new_path = random.randint(0, count -1)
 
623
         
 
624
         print "New track is:", new_path
 
625
         treeselection = self.treeview.get_selection()   
 
626
         treeselection.select_path(new_path)
 
627
         self.play.clicked()
 
628
      else:
 
629
         print 'The mode "%s" is not currently supported - stopping' % mode_text
 
630
         self.stop.clicked()
 
631
 
 
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"
 
640
         self.stop.clicked()
 
641
         if model.iter_next(iter):
 
642
            treeselection.select_iter(model.iter_next(iter))
 
643
         else:
 
644
            treeselection.select_iter(model.get_iter_first())
 
645
      if control == "<b>>crossfade</b>":
 
646
         print "player", self.playername, "stopping, crossfade complete"
 
647
         self.stop.clicked()
 
648
         if model.iter_next(iter):
 
649
            treeselection.select_iter(model.iter_next(iter))
 
650
         else:
 
651
            treeselection.select_path(0)
 
652
      if control == "<b>>stopstreaming</b>":
 
653
         self.next.clicked()
 
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>":
 
660
         self.next.clicked()
 
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()
 
670
         else:
 
671
            otherplayer = self.parent.player_left
 
672
            self.parent.passleft.clicked()
 
673
         print "transferring to player", otherplayer.playername
 
674
         otherplayer.play.clicked()
 
675
         self.stop.clicked()
 
676
         if model.iter_next(iter):
 
677
            treeselection.select_iter(model.iter_next(iter))
 
678
         else:
 
679
            treeselection.select_path(0)
 
680
 
 
681
   def get_pl_block_size(self, iter):
 
682
      size = 0
 
683
      while iter is not None:
 
684
         length = self.liststore.get_value(iter, 2)
 
685
         if length == -11:
 
686
            break
 
687
         if length >= 0:
 
688
            size += length
 
689
         iter = self.liststore.iter_next(iter)
 
690
      return size
 
691
 
 
692
   def update_time_stats(self):
 
693
      if self.pl_mode.get_active() != 0:                # optimisation -- this function uses a lot of cpu
 
694
         return
 
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)
 
700
      else:
 
701
         tr = 0
 
702
      selection = self.treeview.get_selection()
 
703
      model, iter = selection.get_selected()
 
704
      if iter is None:
 
705
         if self.is_playing:
 
706
            bs = 0
 
707
         else:
 
708
            iter = model.get_iter_first()
 
709
            bs = self.get_pl_block_size(iter)
 
710
      else:
 
711
         if model.get_value(iter, 0)[0:3] == "<b>":
 
712
            bs = 0
 
713
         else:
 
714
            bs = self.get_pl_block_size(iter)
 
715
      bss = bs % 60
 
716
      bsm = (bs - bss) / 60
 
717
      if self.is_playing:
 
718
         trs = tr % 60
 
719
         trm = (tr - trs) / 60
 
720
         tm_end = time.localtime(int(time.time()) + tr)
 
721
         tm_end_h = tm_end[3]
 
722
         tm_end_m = tm_end[4]
 
723
         tm_end_s = tm_end[5]
 
724
         if bs == 0:
 
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))
 
726
         else:
 
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))
 
728
      else:
 
729
         if bs == 0:
 
730
            self.statusbar_update("")
 
731
         else:
 
732
            bft = time.localtime(time.time() + bs)
 
733
            bf_h = bft[3]
 
734
            bf_m = bft[4]
 
735
            bf_s = bft[5]
 
736
            self.statusbar_update("%s %2d:%02d | %s %02d:%02d:%02d" % (block_size_text, bsm, bss, finish_text, bf_h, bf_m, bf_s))
 
737
            
 
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
 
742
 
 
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()
 
748
 
 
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()
 
754
         if iter is not None:
 
755
            self.treeview.scroll_to_cell(model.get_path(iter)[0], None, False)
 
756
         else:
 
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()
 
763
         try:
 
764
            treeselection.select_iter(self.iter_playing)
 
765
         except:
 
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:
 
770
            self.gapless = True
 
771
            print "termination due to end of track"
 
772
            self.invoke_end_of_track_policy()
 
773
            self.gapless = False
 
774
            gtk.gdk.threads_leave()
 
775
            return False
 
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()
 
782
               return False
 
783
         else:
 
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()
 
790
      else:
 
791
         # we stop monitoring the play progress during the progress bar drag operation
 
792
         # by cancelling this timeout
 
793
         gtk.gdk.threads_leave()
 
794
         return False
 
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()
 
807
            else:
 
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()
 
818
      
 
819
      gtk.gdk.threads_leave()
 
820
      return True
 
821
 
 
822
   def islastinplaylist(self):
 
823
      ifself.model_playing.iter_next(self.iter_playing)
 
824
      if iter == None:
 
825
         return False
 
826
      else:
 
827
         return True
 
828
          
 
829
   def arrow_up(self):
 
830
      treeselection = self.treeview.get_selection()
 
831
      (model, iter) = treeselection.get_selected()
 
832
      if iter == None:
 
833
         print "Nothing is selected"
 
834
      else:
 
835
         path = model.get_path(iter)
 
836
         if path[0]:
 
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)
 
840
            
 
841
   def arrow_down(self):
 
842
      treeselection = self.treeview.get_selection()
 
843
      (model, iter) = treeselection.get_selected()
 
844
      if iter == None:
 
845
         print "Nothing is selected"
 
846
      else:
 
847
         path = model.get_path(iter)
 
848
         try:
 
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)
 
852
         except ValueError:
 
853
            pass
 
854
          
 
855
   def advance(self):
 
856
      if self.is_playing:
 
857
         path = self.model_playing.get_path(self.iter_playing)[0]+1
 
858
         self.stop.clicked()
 
859
         treeselection = self.treeview.get_selection()
 
860
         treeselection.select_path(path)
 
861
         self.treeview.scroll_to_cell(path, None, False)
 
862
      else:
 
863
         self.play.clicked()
 
864
          
 
865
   def callback(self, widget, data):
 
866
      print "%s was pressed on player %s" % (data, self.playername)
 
867
 
 
868
      if data == "Arrow Up":
 
869
         self.arrow_up()
 
870
      
 
871
      if data == "Arrow Dn":
 
872
         self.arrow_down()
 
873
               
 
874
      if data == "Stop":
 
875
         self.handle_stop_button(widget)
 
876
         
 
877
      if data == "Next":
 
878
         if self.is_playing:
 
879
            path = self.model_playing.get_path(self.iter_playing)[0]+1
 
880
            if self.is_paused:
 
881
               self.stop.clicked()
 
882
            try:
 
883
               self.model_playing.get_iter(path)
 
884
            except:
 
885
               self.stop.clicked()
 
886
               return
 
887
            treeselection = self.treeview.get_selection()           
 
888
            treeselection.select_path(path)
 
889
            self.new_title = True
 
890
            self.play.clicked()        
 
891
               
 
892
      if data == "Prev":
 
893
         if self.is_playing:
 
894
            treeselection = self.treeview.get_selection()
 
895
            path = self.model_playing.get_path(self.iter_playing)
 
896
            if self.is_paused:
 
897
               self.stop.clicked()
 
898
            treeselection.select_path(path[0]-1)
 
899
            self.new_title = True
 
900
            self.play.clicked()
 
901
               
 
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
 
907
            else:
 
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)
 
917
            self.filerq.show()
 
918
            self.showing_file_requester = True
 
919
         else:
 
920
            print "refused to open file requester"
 
921
         
 
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])
 
928
      else:
 
929
         for chosenfile in chosenfiles:
 
930
            if os.path.isdir(chosenfile):
 
931
               files = os.listdir(chosenfile)
 
932
               files.sort()
 
933
               for each in files:
 
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():
 
939
                        gtk.main_iteration()
 
940
            else:
 
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)
 
950
         else:
 
951
            self.file_requester_start_dir.set_text(chosenfile + '/')
 
952
      else:
 
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
 
957
       
 
958
      # Clear the flag last to prevent signal handling problems
 
959
      self.showing_file_requester = False 
 
960
    
 
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
 
965
 
 
966
   def file_destroy(self, widget):
 
967
      self.filerq.destroy()
 
968
      self.showing_file_requester = False
 
969
     
 
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
 
978
      try:
 
979
         pl = open(chosenfile, "w")
 
980
      except IOError:
 
981
         print "Can't open file for writing.  Permissions problem?"
 
982
      else:
 
983
         try:
 
984
            extm3u = self.parent.prefs_window.extm3u.get_active()
 
985
            if extm3u:
 
986
               pl.write("#EXTM3U\n")
 
987
            count = 0
 
988
            while 1:
 
989
               if self.liststore[count][0][0] != ">":
 
990
                  if extm3u:
 
991
                     pl.write("#EXTINF:%d,%s\n" % (self.liststore[count][2], self.liststore[count][3]))
 
992
                  pl.write(self.liststore[count][1] + "\n")
 
993
               count = count + 1
 
994
         except IndexError:
 
995
            pl.close()
 
996
         except IOError:
 
997
            pl.close()
 
998
            print "That was odd\n"
 
999
      self.showing_pl_save_requester = False 
 
1000
    
 
1001
   def plfile_notok(self, widget):
 
1002
      print "Cancel was clicked"
 
1003
      self.plfilerq.destroy()
 
1004
      self.showing_pl_save_requester = False
 
1005
 
 
1006
   def plfile_destroy(self, widget):
 
1007
      self.plfilerq.destroy()
 
1008
      self.showing_pl_save_requester = False     
 
1009
         
 
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   
 
1018
         
 
1019
      if data == "Play":
 
1020
         self.handle_play_button(widget, widget.get_active())
 
1021
      if data == "Pause":
 
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();
 
1027
   
 
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)
 
1033
            else:
 
1034
               count = self.max_seek - int(progress.value)
 
1035
         else:
 
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))
 
1043
         else:
 
1044
            if self.max_seek != 0:
 
1045
               self.digiprogress.set_text(" -%02d:%02d " % (minutes, seconds))
 
1046
            else:
 
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:
 
1051
            self.next.clicked()
 
1052
         else:
 
1053
            if self.pause.get_active():
 
1054
               self.pause.set_active(False)
 
1055
 
 
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")
 
1061
         else:
 
1062
            self.digiprogress.set_text(" -00:00 ")
 
1063
      else:
 
1064
         self.cb_progress(self.progressadj)
 
1065
 
 
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)
 
1086
      return False
 
1087
      
 
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()
 
1095
      return False
 
1096
 
 
1097
   def cb_menu_select(self, widget, data):
 
1098
      print "The %s was chosen from the %s menu" % (data, self.playername)   
 
1099
 
 
1100
   def delete_event(self, widget, event, data=None):
 
1101
      return False
 
1102
 
 
1103
   def parse_m3u(self, filename):
 
1104
      try:
 
1105
         file = open(filename, "r")
 
1106
         data = file.read()
 
1107
         file.close()
 
1108
      except IOError:
 
1109
         print "Problem reading file", filename
 
1110
         return
 
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"
 
1115
         try:
 
1116
            index = 1
 
1117
            while 1:
 
1118
               if data[index][:8] == "#EXTINF:":
 
1119
                  line1 = data[index][8:].split(",")
 
1120
                  line2 = data[index+1]
 
1121
                  if line2[0] != "/":
 
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]])
 
1125
                  index = index + 2
 
1126
               else:
 
1127
                  break
 
1128
         except IndexError:
 
1129
            pass
 
1130
         except ValueError:
 
1131
            print "Invalid play duration value"
 
1132
      else:
 
1133
         for each in data:
 
1134
            if each[0] != "/":
 
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()
 
1141
    
 
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
 
1149
            self.stop.clicked()
 
1150
 
 
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)
 
1156
      else:
 
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
 
1161
      return True
 
1162
 
 
1163
   def drag_data_received_data(self, treeview, context, x, y, dragged, info, etime):
 
1164
      if info != 0:
 
1165
         text = str(dragged.data)
 
1166
         if text.count("\x00"): # OMG where did these come from!!!
 
1167
            try:
 
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(",")
 
1176
               for each in fe:
 
1177
                  if each == "@locale" or each == "locale":
 
1178
                     each = self.parent.de
 
1179
                  try:
 
1180
                     result = text.encode(each, "strict")
 
1181
                  except:
 
1182
                     continue           # failed to encode text with the encoding in each
 
1183
                  else:
 
1184
                     text = result      # encoding succeeded even if it is wrong
 
1185
                     break
 
1186
               else:
 
1187
                  print "Unable to encode drag and drop filenames with the supplied list:", self.parent.fenc
 
1188
            except:
 
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, "" ,"", "", "" ])
 
1197
            else:
 
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, "" ,"", "", "" ])
 
1202
               else:
 
1203
                  model.insert_after(dest_iter, [ text[20:], "", -11, "" ,"", "", "" ])
 
1204
            if context.action == gtk.gdk.ACTION_MOVE:
 
1205
               context.finish(True, True, etime)
 
1206
         else:
 
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)
 
1210
      else:
 
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)
 
1216
         else:
 
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)
 
1221
            else:
 
1222
               self.liststore.move_after(iter, dest_iter)
 
1223
         if context.action == gtk.gdk.ACTION_MOVE:
 
1224
            context.finish(False, False, etime)
 
1225
      return True
 
1226
   
 
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()
 
1231
 
 
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
 
1235
      
 
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)
 
1242
            selection.sort()
 
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")
 
1249
            if file:
 
1250
               selection = ""
 
1251
               while 1:
 
1252
                  line = file.readline()
 
1253
                  if line == "":
 
1254
                     break
 
1255
                  if line[0] == "/" or line[0] == "#":
 
1256
                     selection = selection + line               # handle absolute path m3u files
 
1257
                  else:
 
1258
                     selection = selection + (path + line)      # handle relocatable m3u files
 
1259
               file.close()
 
1260
            else:
 
1261
               selection = "wtf\n"
 
1262
            selection = selection.splitlines()
 
1263
         else:
 
1264
            selection = dragged.splitlines()
 
1265
      else:
 
1266
         selection = dragged.splitlines()
 
1267
 
 
1268
      if len(selection) > 0 and selection[0].rstrip() == "#EXTM3U" and self.parent.prefs_window.extm3u.get_active():
 
1269
         index = 1
 
1270
         try:
 
1271
            while 1:
 
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()
 
1280
                     if drop_info:
 
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()
 
1289
                        else:
 
1290
                           gtk.gdk.threads_enter()
 
1291
                           model.insert_after(iter, media_data)
 
1292
                           gtk.gdk.threads_leave()
 
1293
                     else:
 
1294
                        gtk.gdk.threads_enter()
 
1295
                        model.append(media_data)
 
1296
                        gtk.gdk.threads_leave()
 
1297
               index = index + 2
 
1298
         except IndexError:
 
1299
            pass 
 
1300
         except ValueError:
 
1301
            print "Bad value for duration"
 
1302
            gtk.gdk.threads_leave()     
 
1303
      else: 
 
1304
         first = True
 
1305
         for line in selection:
 
1306
            media_data = self.get_media_metadata(line)
 
1307
            if media_data[0] != 'Not a valid file':
 
1308
               if first:
 
1309
                  gtk.gdk.threads_enter()
 
1310
                  drop_info = treeview.get_dest_row_at_pos(x, y)
 
1311
                  gtk.gdk.threads_leave()
 
1312
                  if drop_info:
 
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()
 
1321
                     else:
 
1322
                        gtk.gdk.threads_enter()
 
1323
                        iter = model.insert_after(iter, media_data)
 
1324
                        gtk.gdk.threads_leave()
 
1325
                  else:
 
1326
                     gtk.gdk.threads_enter()
 
1327
                     iter = model.append(media_data)
 
1328
                     gtk.gdk.threads_leave()
 
1329
                  first = False
 
1330
               else:
 
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
 
1343
      return False
 
1344
     
 
1345
   TARGETS = [
 
1346
      ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
 
1347
      ('text/plain', 0, 1),
 
1348
      ('TEXT', 0, 2),
 
1349
      ('STRING', 0, 3),
 
1350
      ]
 
1351
      
 
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.
 
1356
         if self.is_playing:
 
1357
            # The new_title flag allows a new song to be played when the player is going.
 
1358
            self.new_title = True
 
1359
            self.play.clicked()
 
1360
         else:
 
1361
            self.play.clicked()
 
1362
      else:
 
1363
         print "Double click does nothing in delete mode"
 
1364
                           
 
1365
   def cb_singleclick(self, treeview):
 
1366
      treeselection = treeview.get_selection()
 
1367
      (model, iter) = treeselection.get_selected()
 
1368
      if iter:
 
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
 
1377
                  self.stop.clicked()
 
1378
               else:
 
1379
                  print "We deleted a song"
 
1380
               model.remove(iter)
 
1381
               self.last_time = time.time()
 
1382
            else:
 
1383
               print "Was protected by my debounce code"
 
1384
         self.update_time_stats()
 
1385
         
 
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))
 
1390
         if row_info:
 
1391
            sens = True
 
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)
 
1397
         else:
 
1398
            self.menu_iter = None
 
1399
            sens = False
 
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:
 
1406
            sens2 = False
 
1407
         else:
 
1408
            sens2 = True
 
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)
 
1416
         else:
 
1417
            self.pl_menu_control.set_sensitive(True)
 
1418
         
 
1419
         if self.playername == "left":  # determine if anything is selected in the other playlist
 
1420
            tv = self.parent.player_right.treeview.get_selection()
 
1421
         else:
 
1422
            tv = self.parent.player_left.treeview.get_selection()
 
1423
         model, iter = tv.get_selected()
 
1424
         if iter:
 
1425
            sens3 = True
 
1426
         else:
 
1427
            sens3 = False
 
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)   
 
1432
            
 
1433
         widget.popup(None, None, None, event.button, event.time)
 
1434
         return True
 
1435
      return False
 
1436
      
 
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
 
1441
      
 
1442
      dict = {
 
1443
             "Stop Control"              : ">stopplayer",
 
1444
             "Transfer Control"          : ">transfer",
 
1445
             "Crossfade Control"         : ">crossfade",
 
1446
             "Stream Disconnect Control" : ">stopstreaming",
 
1447
             "Stop Recording Control"    : ">stoprecording"
 
1448
      }
 
1449
      if dict.has_key(text):
 
1450
         if iter is not None:
 
1451
            iter = model.insert_after(iter)
 
1452
         else:
 
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)
 
1462
         return
 
1463
      
 
1464
      if text == "MetaTag":
 
1465
         try:
 
1466
            pathname = model.get_value(iter, 1)
 
1467
         except TypeError:
 
1468
            pass
 
1469
         else:
 
1470
            multitag(pathname, model.get_value(iter, 4) , self.parent)
 
1471
      
 
1472
      if text == "Add File":
 
1473
         if self.showing_file_requester == False:
 
1474
            if self.playername == "left":
 
1475
               filerqtext = left_playlist_addition_text
 
1476
            else:
 
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)
 
1486
            self.filerq.show()
 
1487
            self.showing_file_requester = True
 
1488
         else:
 
1489
            print "refused to open file requester"
 
1490
            
 
1491
      if text == "Playlist Save":
 
1492
         if self.showing_pl_save_requester == False:
 
1493
            if self.playername == "left":
 
1494
               filerqtext = left_playlist_save_text
 
1495
            else:
 
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
 
1506
         else:
 
1507
            print "refused to open file requester"
 
1508
       
 
1509
      if text == "Remove All":
 
1510
         if self.is_playing:
 
1511
            self.stop.clicked()
 
1512
         self.liststore.clear()   
 
1513
      
 
1514
      if text == "Remove This" and iter != None:
 
1515
         name = model.get_value(iter, 0)
 
1516
         if name[:3] == "<b>":
 
1517
            self.stop.clicked()
 
1518
         self.liststore.remove(iter)
 
1519
         
 
1520
      if text == "Remove From Here" and iter != None:    
 
1521
         path = model.get_path(iter)
 
1522
         try:
 
1523
            while 1:
 
1524
               iter = model.get_iter(path)
 
1525
               if model.get_value(iter, 0)[:3] == "<b>":
 
1526
                  self.stop.clicked()
 
1527
               self.liststore.remove(iter)
 
1528
         except:
 
1529
            print "Nothing more to delete"
 
1530
         
 
1531
      if text == "Remove To Here" and iter != None:
 
1532
         path = model.get_path(iter)[0] -1
 
1533
         while path >= 0:
 
1534
            iter = model.get_iter(path)
 
1535
            if model.get_value(iter, 0)[:3] == "<b>":
 
1536
               self.stop.clicked()
 
1537
            self.liststore.remove(iter)
 
1538
            path = path -1
 
1539
 
 
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)
 
1545
      
 
1546
      if text == "Playlist Exchange":
 
1547
         if self.playername == "left":
 
1548
            opposite = self.parent.player_right
 
1549
         else:
 
1550
            opposite = self.parent.player_left
 
1551
         self.stop.clicked()
 
1552
         opposite.stop.clicked()
 
1553
         i = 0
 
1554
         try:
 
1555
            while 1:
 
1556
               self.templist.append(self.liststore[i])
 
1557
               i = i + 1
 
1558
         except IndexError:
 
1559
            pass
 
1560
         self.liststore.clear()
 
1561
         i = 0
 
1562
         try:
 
1563
            while 1:
 
1564
               self.liststore.append(opposite.liststore[i])
 
1565
               i = i + 1
 
1566
         except IndexError:
 
1567
            pass
 
1568
         opposite.liststore.clear()
 
1569
         i = 0
 
1570
         try:
 
1571
            while 1:
 
1572
               opposite.liststore.append(self.templist[i])
 
1573
               i = i + 1
 
1574
         except IndexError:
 
1575
            pass
 
1576
         self.templist.clear()
 
1577
         
 
1578
      if text == "Copy Append":
 
1579
         self.copy_playlist("end")
 
1580
         
 
1581
      if text == "Transfer Append":
 
1582
         self.copy_playlist("end")
 
1583
         self.stop.clicked()
 
1584
         self.liststore.clear()
 
1585
                 
 
1586
      if text == "Copy Prepend":
 
1587
         self.copy_playlist("start")
 
1588
         
 
1589
      if text == "Transfer Prepend":
 
1590
         self.copy_playlist("start")
 
1591
         self.stop.clicked()
 
1592
         self.liststore.clear()
 
1593
         
 
1594
      if text == "Copy Append Cursor":
 
1595
         self.copy_playlist("after")
 
1596
         
 
1597
      if text == "Transfer Append Cursor":
 
1598
         self.copy_playlist("after")
 
1599
         self.stop.clicked()
 
1600
         self.liststore.clear()
 
1601
         
 
1602
      if text == "Copy Prepend Cursor":
 
1603
         self.copy_playlist("before")
 
1604
         
 
1605
      if text == "Transfer Prepend Cursor":
 
1606
         self.copy_playlist("before")
 
1607
         self.stop.clicked()
 
1608
         self.liststore.clear()
 
1609
        
 
1610
      if text == "ToJingles":
 
1611
         source = model.get_value(iter, 1)
 
1612
         dest = self.parent.idjc + "jingles/" + os.path.split(source)[1]
 
1613
         try:
 
1614
            source = open(source, "r")
 
1615
            dest = open(dest, "w")
 
1616
            while True:
 
1617
               data = source.read(4096)
 
1618
               dest.write(data)
 
1619
               if len(data) < 4096:
 
1620
                  break;
 
1621
         except IOError:
 
1622
            print "IOError occurred"
 
1623
         source.close
 
1624
         dest.close
 
1625
         self.parent.jingles.refresh.clicked()
 
1626
               
 
1627
      if self.player_is_playing:        # put the cursor on the file playing after a brief pause.
 
1628
         self.reselect_please = True
 
1629
 
 
1630
   def stripbold(self, playlist_item):
 
1631
      copy = list(playlist_item)
 
1632
      if copy[0][:3] == "<b>":
 
1633
         copy[0] = copy[0][3:-4]
 
1634
      return copy
 
1635
                 
 
1636
   def copy_playlist(self, dest):
 
1637
      if self.playername == "left":
 
1638
         other = self.parent.player_right
 
1639
      else:
 
1640
         other = self.parent.player_left
 
1641
      i = 0
 
1642
      try:
 
1643
         if dest == "start":
 
1644
            while 1:
 
1645
               other.liststore.insert(i, self.stripbold(self.liststore[i]))
 
1646
               i = i + 1
 
1647
         if dest == "end":
 
1648
            while 1:
 
1649
               other.liststore.append(self.stripbold(self.liststore[i]))
 
1650
               i = i + 1
 
1651
         
 
1652
         (model, iter) = other.treeview.get_selection().get_selected()         
 
1653
         
 
1654
         if dest == "after":
 
1655
            while 1:
 
1656
               iter = other.liststore.insert_after(iter, self.stripbold(self.liststore[i]))
 
1657
               i = i + 1
 
1658
         if dest == "before":
 
1659
            while 1:
 
1660
               other.liststore.insert_before(iter, self.stripbold(self.liststore[i]))
 
1661
               i = i + 1
 
1662
      except IndexError:
 
1663
         pass
 
1664
            
 
1665
   def cb_keypress(self, widget, event):
 
1666
      if self.delete.get_active():
 
1667
         print "Keyboard shortcuts disabled in delete mode"
 
1668
         return True
 
1669
      # Handle shifted arrow keys for rearranging stuff in the playlist.
 
1670
      if event.state & gtk.gdk.SHIFT_MASK:
 
1671
         if event.keyval == 65362:
 
1672
            self.arrow_up()
 
1673
            return True
 
1674
         if event.keyval == 65364:
 
1675
            self.arrow_down()
 
1676
            return True
 
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>":
 
1684
                     self.stop.clicked()
 
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)
 
1691
                  if d_iter is None:
 
1692
                     d_iter = d_model.append(row)
 
1693
                  else:
 
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)
 
1698
            else:
 
1699
               print "Cannot transfer to the other playlist in delete mode"
 
1700
            return True
 
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>":
 
1708
                     self.stop.clicked()
 
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)
 
1715
                  if d_iter is None:
 
1716
                     d_iter = d_model.append(row)
 
1717
                  else:
 
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)
 
1722
            else:
 
1723
               print "Cannot transfer to the other playlist in delete mode"
 
1724
            return True
 
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))
 
1731
            else:
 
1732
               treeselection.select_path(0)
 
1733
            self.parent.player_left.treeview.grab_focus()
 
1734
         else:
 
1735
            print "Cannot switch to other player in delete mode"
 
1736
         return True
 
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))
 
1743
            else:
 
1744
               treeselection.select_path(0)
 
1745
            self.parent.player_right.treeview.grab_focus()
 
1746
         else:
 
1747
            print "Cannot switch to other player in delete mode"
 
1748
         return True
 
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)
 
1754
            if path[0] > 0:
 
1755
               prev = model.get_iter(path[0]-1)
 
1756
            else:
 
1757
               prev = None
 
1758
            try:
 
1759
               next = model.get_iter(path[0]+1)
 
1760
            except:
 
1761
               next = None
 
1762
            name = model.get_value(iter, 0)
 
1763
            if name[:3] == "<b>":
 
1764
               self.stop.clicked()
 
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)
 
1774
         else:
 
1775
            print "Playlist is empty!"
 
1776
         return True
 
1777
      if event.string == "1":
 
1778
         self.parent.passleft.clicked()
 
1779
         return True
 
1780
      if event.string == "2":
 
1781
         self.parent.passright.clicked()
 
1782
         return True
 
1783
      if event.string == "c" or event.string == "C":
 
1784
         self.parent.passbutton.clicked()
 
1785
         return True
 
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())
 
1788
         return True
 
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())
 
1791
         return True
 
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())
 
1794
         return True
 
1795
      if event.keyval == 65288:         # backspace key stops the player
 
1796
         self.stop.clicked()
 
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)
 
1803
            else:
 
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")
 
1808
         return True
 
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")
 
1812
         return True
 
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")
 
1816
         return True
 
1817
         
 
1818
      # Allow certain key presses to work but not allow a text entry box to appear.
 
1819
      if event.string ==" " or event.string =="\r":
 
1820
         self.stop.clicked()
 
1821
         self.play.clicked()
 
1822
         return True
 
1823
      if event.string == "":
 
1824
         return False
 
1825
      return True
 
1826
            
 
1827
   def playtimerowconfig(self, tv_column, cell_renderer, model, iter):
 
1828
      if self.exiting:
 
1829
         return
 
1830
      playtime = model.get_value(iter, 2)
 
1831
      self.rowconfig(tv_column, cell_renderer, model, iter)
 
1832
      if playtime == -11:
 
1833
         cell_renderer.set_property("text", "")
 
1834
      else:   
 
1835
         secs = playtime % 60
 
1836
         playtime -= secs
 
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)
 
1841
 
 
1842
   def rowconfig(self, tv_column, cell_renderer, model, iter):
 
1843
      if self.exiting:
 
1844
         return
 
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)
 
1876
            else:
 
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)
 
1884
            else:
 
1885
               cell_renderer.set_property("text", crossfade_control_rtl_element_text)
 
1886
      else:
 
1887
         if celltext[0] == ">":
 
1888
            cell_renderer.set_property("visible", False)
 
1889
         else:
 
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)
 
1897
            
 
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()
 
1903
      else:
 
1904
         self.pl_statusbar.hide()
 
1905
      
 
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)
 
1912
      frame = gtk.Frame()
 
1913
      frame.set_border_width(3)
 
1914
      frame.set_shadow_type(gtk.SHADOW_IN)
 
1915
      frame.add(self.hbox1)
 
1916
      frame.show()
 
1917
      pbox.pack_start(frame, False, False, 0)
 
1918
 
 
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)
 
1925
           
 
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()      
 
1934
      
 
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()
 
1948
      
 
1949
      # Finished filling the progress box so lets show it.
 
1950
      self.progressbox.show()
 
1951
     
 
1952
      # A frame for our playlist
 
1953
      if name == "left":
 
1954
         plframe = gtk.Frame(" " + playlist_text + " 1 ")
 
1955
      else:
 
1956
         plframe = gtk.Frame(" " + playlist_text + " 2 ")        
 
1957
      plframe.set_border_width(4)
 
1958
      plframe.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
1959
      plframe.show()
 
1960
      plvbox = gtk.VBox()
 
1961
      plframe.add(plvbox)
 
1962
      plvbox.show()
 
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,
 
1992
                                                self.TARGETS,
 
1993
                                                gtk.gdk.ACTION_DEFAULT |
 
1994
                                                gtk.gdk.ACTION_MOVE)
 
1995
      self.treeview.enable_model_drag_dest( self.TARGETS,
 
1996
                                                gtk.gdk.ACTION_DEFAULT)
 
1997
        
 
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)
 
2001
      
 
2002
      self.treeview.connect("row_activated", self.cb_doubleclick, "Double click")
 
2003
      self.treeview.connect("cursor_changed", self.cb_singleclick)
 
2004
      
 
2005
      self.treeview.connect("key_press_event", self.cb_keypress)
 
2006
      
 
2007
      self.scrolllist.add(self.treeview)
 
2008
      self.treeview.show()
 
2009
      
 
2010
      plvbox.pack_start(self.scrolllist, True, True, 0)
 
2011
      self.scrolllist.show()
 
2012
      
 
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()
 
2018
 
 
2019
      pbox.pack_start(plframe, True, True, 0) 
 
2020
      
 
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)
 
2025
 
 
2026
      # A set of buttons for hbox1 namely Prev/Play/Pause/Stop/Next/Playlist : XMMS order
 
2027
      image = gtk.Image()
 
2028
      image.set_from_file(pkgdatadir + "prev" + gfext)
 
2029
      image.show()
 
2030
      self.prev = gtk.Button()
 
2031
      self.prev.add(image)
 
2032
      self.prev.connect("clicked", self.callback, "Prev")
 
2033
      self.hbox1.add(self.prev)
 
2034
      self.prev.show()
 
2035
      
 
2036
      pixbuf = gtk.gdk.pixbuf_new_from_file(pkgdatadir + "play2" + gfext)
 
2037
      pixbuf = pixbuf.scale_simple(14, 14, gtk.gdk.INTERP_BILINEAR)
 
2038
      image=gtk.Image()
 
2039
      image.set_from_pixbuf(pixbuf)
 
2040
      image.show()
 
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)
 
2045
      self.play.show()
 
2046
      
 
2047
      image=gtk.Image()
 
2048
      image.set_from_file(pkgdatadir + "pause" + gfext)
 
2049
      image.show()
 
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)
 
2054
      self.pause.show()
 
2055
      
 
2056
      image=gtk.Image()
 
2057
      image.set_from_file(pkgdatadir + "stop" + gfext)
 
2058
      image.show()
 
2059
      self.stop = gtk.Button()
 
2060
      self.stop.add(image)
 
2061
      self.stop.connect("clicked", self.callback, "Stop")
 
2062
      self.hbox1.add(self.stop)
 
2063
      self.stop.show()
 
2064
            
 
2065
      image=gtk.Image()
 
2066
      image.set_from_file(pkgdatadir + "next" + gfext)
 
2067
      image.show()
 
2068
      self.next = gtk.Button()
 
2069
      self.next.add(image)
 
2070
      self.next.connect("clicked", self.callback, "Next")
 
2071
      self.hbox1.add(self.next)
 
2072
      self.next.show()
 
2073
      
 
2074
      pixbuf = gtk.gdk.pixbuf_new_from_file(pkgdatadir + "add3" + gfext)
 
2075
      pixbuf = pixbuf.scale_simple(14, 14, gtk.gdk.INTERP_HYPER)
 
2076
      image = gtk.Image()
 
2077
      image.set_from_pixbuf(pixbuf)
 
2078
      image.show()
 
2079
      self.add = gtk.Button()
 
2080
      self.add.add(image)
 
2081
      self.add.connect("clicked", self.callback, "Add Files")
 
2082
      self.hbox1.add(self.add)
 
2083
      self.add.show()
 
2084
      
 
2085
      # hbox1 is done so it is time to show it
 
2086
      self.hbox1.show()
 
2087
 
 
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)
 
2097
      
 
2098
      self.hbox2.pack_start(self.pl_mode, True, True, 0)
 
2099
      self.pl_mode.show()
 
2100
      
 
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)
 
2104
      
 
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)
 
2107
      
 
2108
      # Delete mode toggle button for playlist
 
2109
      image = gtk.Image()
 
2110
      image.set_from_file(pkgdatadir + "del" + gfext)
 
2111
      image.show()
 
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)
 
2117
      self.delete.show()
 
2118
      
 
2119
      # Mute buttons
 
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)
 
2124
      self.stream.show()
 
2125
            
 
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)
 
2130
      self.listen.show()
 
2131
      sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
 
2132
      sizegroup.add_widget(self.stream)
 
2133
      sizegroup.add_widget(self.listen)
 
2134
      
 
2135
      # hbox2 is now filled so lets show it
 
2136
      self.hbox2.show()
 
2137
      
 
2138
      # Popup menu code here
 
2139
      
 
2140
      # Main popup menu
 
2141
      self.pl_menu = gtk.Menu()
 
2142
 
 
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()
 
2146
 
 
2147
      separator = gtk.SeparatorMenuItem()
 
2148
      self.pl_menu.append(separator)
 
2149
      separator.show()
 
2150
      
 
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()
 
2154
      
 
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()
 
2158
      
 
2159
      self.pl_menu.show()
 
2160
      
 
2161
      # Control element submenu of main popup menu
 
2162
      
 
2163
      self.control_menu = gtk.Menu()
 
2164
      
 
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()
 
2169
      
 
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()
 
2174
      
 
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()
 
2179
      
 
2180
      separator = gtk.SeparatorMenuItem()
 
2181
      self.control_menu.append(separator)
 
2182
      separator.show()
 
2183
      
 
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()
 
2188
      
 
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()    
 
2193
        
 
2194
      self.pl_menu_control.set_submenu(self.control_menu)
 
2195
      self.control_menu.show()
 
2196
      
 
2197
      # Item submenu of main popup menu
 
2198
      self.item_menu = gtk.Menu()
 
2199
      
 
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()
 
2204
      
 
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()
 
2209
      
 
2210
      self.item_remove = gtk.MenuItem(remove_text)
 
2211
      self.item_menu.append(self.item_remove)
 
2212
      self.item_remove.show()
 
2213
      
 
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()
 
2218
      
 
2219
      self.pl_menu_item.set_submenu(self.item_menu)
 
2220
      self.item_menu.show()
 
2221
      
 
2222
      # Remove submenu of Item submenu
 
2223
      self.remove_menu = gtk.Menu()
 
2224
 
 
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()
 
2229
      
 
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()
 
2234
      
 
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()
 
2239
      
 
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()
 
2244
      
 
2245
      self.item_remove.set_submenu(self.remove_menu)
 
2246
      self.remove_menu.show()
 
2247
      
 
2248
      # Playlist submenu of main popup menu.
 
2249
      self.playlist_menu = gtk.Menu()
 
2250
      
 
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()
 
2255
      
 
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()
 
2260
      
 
2261
      separator = gtk.SeparatorMenuItem()
 
2262
      self.playlist_menu.append(separator)
 
2263
      separator.show()
 
2264
      
 
2265
      self.playlist_copy = gtk.MenuItem(copy_text)
 
2266
      self.playlist_menu.append(self.playlist_copy)
 
2267
      self.playlist_copy.show()
 
2268
      
 
2269
      self.playlist_transfer = gtk.MenuItem(transfer_text)
 
2270
      self.playlist_menu.append(self.playlist_transfer)
 
2271
      self.playlist_transfer.show()
 
2272
      
 
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()
 
2277
      
 
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()
 
2282
      
 
2283
      self.pl_menu_playlist.set_submenu(self.playlist_menu)
 
2284
      self.playlist_menu.show()
 
2285
      
 
2286
      # Position Submenu of Playlist-Copy menu item
 
2287
      
 
2288
      self.copy_menu = gtk.Menu()
 
2289
      
 
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()
 
2294
      
 
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()
 
2299
      
 
2300
      separator = gtk.SeparatorMenuItem()
 
2301
      self.copy_menu.append(separator)
 
2302
      separator.show()
 
2303
      
 
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()
 
2308
      
 
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()
 
2313
 
 
2314
      self.playlist_copy.set_submenu(self.copy_menu)
 
2315
      self.copy_menu.show()
 
2316
      
 
2317
      # Position Submenu of Playlist-Transfer menu item
 
2318
      
 
2319
      self.transfer_menu = gtk.Menu()
 
2320
      
 
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()
 
2325
      
 
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()
 
2330
      
 
2331
      separator = gtk.SeparatorMenuItem()
 
2332
      self.transfer_menu.append(separator)
 
2333
      separator.show()
 
2334
      
 
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()
 
2339
      
 
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()
 
2344
 
 
2345
      self.playlist_transfer.set_submenu(self.transfer_menu)
 
2346
      self.transfer_menu.show()      
 
2347
 
 
2348
 
 
2349
      self.treeview.connect_object("event", self.menu_activate, self.pl_menu)
 
2350
 
 
2351
      # Initialisations
 
2352
      self.playername = name 
 
2353
      self.showing_file_requester = False
 
2354
      self.showing_pl_save_requester = False
 
2355
 
 
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)
 
2362
         else:
 
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 + '/'
 
2366
               
 
2367
      self.file_requester_start_dir = int_object(self.home)
 
2368
      
 
2369
      # This is used for mouse-click debounce when deleting files in the playlist.
 
2370
      self.last_time = time.time()
 
2371
      
 
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
 
2380
      random.seed()
 
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
 
2386
      self.songname = u""
 
2387
      self.flush = False
 
2388
      self.title = ""
 
2389
      self.artist = ""
 
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 = ""