moved 2.1-staging to trunk @ rev 1765
[ardour.git] / tools / session_exchange.py
1 #! /usr/bin/python
2
3 # Session Exchange
4 # By Taybin Rutkin
5 # Copyright 2004-2005, under the GPL
6
7 VERSION='0.1.2'
8
9 #twisted libraries
10 from twisted.internet import gtk2reactor
11 gtk2reactor.install()
12 from twisted.internet import reactor, protocol
13 import twisted.internet.error
14
15 #pygtk libraries
16 import gobject
17 import gtk
18
19 #standard python2.2 libraries
20 import getopt
21 import os
22 import os.path
23 import re
24 import shelve
25 import string
26 import sys
27 import xml.dom.minidom
28
29 def get_header_size(filename):
30         size = 0
31         file = open(filename, 'r')
32         while True:
33                 chunk = file.read(4)
34                 size += 4
35                 if chunk == "data":
36                         file.close()
37                         return size + 4 #include the size chunk after "data"
38                 if not chunk:
39                         file.close()
40                         return None
41
42 def append_empty_data(self, filename, size):
43         file = open(filename, 'a')
44         file.seek(size-1)
45         file.write('\x00')
46         file.close()
47         
48 def get_sound_list(snapshot):
49         doc = xml.dom.minidom.parse(snapshot)
50         
51         regionlist = []
52         playlists_tag = doc.getElementsByTagName('Playlists')
53         playlists = playlists_tag[0].getElementsByTagName('Playlist')
54         for play in playlists:
55                 regions = play.getElementsByTagName('Region')
56                 for region in regions:
57                         regionlist.append(region.getAttribute('source-0'))
58                         regionlist.append(region.getAttribute('source-1'))
59                         regionlist.append(region.getAttribute('source-2'))
60                         regionlist.append(region.getAttribute('source-3'))
61                         regionlist.append(region.getAttribute('source-4'))
62                         regionlist.append(region.getAttribute('source-5'))
63         
64         sourcelist = {}
65         sources = doc.getElementsByTagName('Source')
66         for source in sources:
67                 sourcelist[source.getAttribute('id')] = str(source.getAttribute('name'))
68
69         soundlist = []
70         for id in regionlist:
71                 if sourcelist.has_key(id):
72                         soundlist.append(sourcelist[id])
73         
74         return soundlist
75
76 def raise_error(string, parent):
77         dialog = gtk.MessageDialog(parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
78         gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, string)
79         
80         dialog.run()
81         dialog.destroy()
82
83 class Data(object):
84         def delete_snap(self, session, collab, snap):
85                 sessions = self._data['sessions']
86                 sessions[session]['collabs'][collab]['snaps'].remove(snap)
87                 self._data['sessions'] = sessions
88         
89         def delete_collab(self,session, collab):
90                 sessions = self._data['sessions']
91                 del sessions[session]['collabs'][collab]
92                 self._data['sessions'] = sessions
93         
94         def delete_session(self, session):
95                 sessions = self._data['sessions']
96                 del sessions[session]
97                 self._data['sessions'] = sessions
98         
99         def add_snap(self, session_name, collab_name, snap_name):
100                 sessions = self._data['sessions']
101                 sessions[session_name]['collabs'][collab_name]['snaps'].append(snap_name)
102                 sessions[session_name]['collabs'][collab_name]['snaps'].sort()
103                 self._data['sessions'] = sessions
104                 
105                 g_display.update_snap_view()
106         
107         def add_collab(self, session_name, collab_name, ip_address, port):
108                 sessions = self._data['sessions']
109                 sessions[session_name]['collabs'][collab_name] = {}
110                 sessions[session_name]['collabs'][collab_name]['snaps'] = []
111                 sessions[session_name]['collabs'][collab_name]['sounds'] = []
112                 sessions[session_name]['collabs'][collab_name]['ip'] = ip_address
113                 sessions[session_name]['collabs'][collab_name]['port'] = port
114                 sessions[session_name]['collabs'][collab_name]['v2paths'] = true
115                 self._data['sessions'] = sessions
116                 
117                 client = ExchangeClientFactory(session_name, collab_name, None, self.debug_mode)
118                 reactor.connectTCP(ip_address, port, client)
119                 g_display.show_status("connecting")
120                 
121                 g_display.update_collab_view()
122         
123         def add_session(self, session_path):
124                 sessions = self._data['sessions']
125                 
126                 session_name = session_path[session_path.rfind('/', 0, len(session_path)-2)+1: -1]
127                 sessions[session_name] = {}
128                 sessions[session_name]['path'] = session_path 
129                 sessions[session_name]['collabs'] = {}
130                 sessions[session_name]['collabs'][self._data['user']] = {}
131                 sessions[session_name]['collabs'][self._data['user']]['snaps'] = []
132                 sessions[session_name]['collabs'][self._data['user']]['sounds'] = []
133                 if os.path.test (os.path.join (session_path,'sounds')):
134                         sessions[session_name]['collabs'][collab_name]['v2paths'] = False
135                 else:
136                         sessions[session_name]['collabs'][collab_name]['v2paths'] = True
137                 
138                 self._data['sessions'] = sessions
139                 
140                 self.rescan_session(session_name)
141
142         def rescan_session(self, session_name):
143                 sessions = self._data['sessions']
144                 
145                 session_path = sessions[session_name]['path']
146                 sessions[session_name]['collabs'][self._data['user']]['snaps'] = self._scan_snapshots(session_path)
147                 sessions[session_name]['collabs'][self._data['user']]['sounds'] = self._scan_sounds(session_path)
148                 
149                 self._data['sessions'] = sessions
150                 
151                 g_display.update_snap_view()
152                 
153                 print self._data['sessions']
154         
155         def create_session(self, session_path):
156                 try:
157                         os.mkdir(session_path)
158                         os.mkdir(os.path.join (session_path,'interchange',session_name,'audiofiles'))
159
160                 except OSError:
161                         raise_error("Could not create session directory", g_display.window)
162                         return
163                 
164                 sessions = self._data['sessions']
165                 
166                 session_name = session_path[session_path.rfind('/', 0, len(session_path)-2)+1: ]
167                 sessions[session_name] = {}
168                 sessions[session_name]['path'] = session_path
169                 sessions[session_name]['collabs'] = {}
170                 sessions[session_name]['collabs'][self._data['user']] = {}
171                 sessions[session_name]['collabs'][self._data['user']]['snaps'] = []
172                 sessions[session_name]['collabs'][self._data['user']]['sounds'] = []
173                 
174                 self._data['sessions'] = sessions
175                 print self._data['sessions']
176         
177         def get_session_path(self, session):
178                 sessions = self._data['sessions']
179                 return sessions[session]['path']
180         
181         def get_user(self):
182                 return self._data['user']
183         
184         def set_user(self, username):
185                 self._data['user'] = username
186         
187         def get_collab_ip(self, session, collab):
188                 sessions = self._data['sessions']
189                 return sessions[session]['collabs'][collab]['ip']
190         
191         def close(self):
192                 self._data.close()
193         
194         def get_sessions(self):
195                 sessions = self._data['sessions']
196                 sess = sessions.keys()
197                 sess.sort()
198                 return sess
199         
200         def get_collabs(self, session):
201                 if session:
202                         sessions = self._data['sessions']
203                         collabs = sessions[session]['collabs'].keys()
204                         collabs.sort()
205                         return collabs
206                 else:
207                         return []
208         
209         def get_snaps(self, session, collab):
210                 if session and collab:
211                         sessions = self._data['sessions']
212                         snaps = sessions[session]['collabs'][collab]['snaps']
213                         snaps.sort()
214                         return snaps
215                 else:
216                         return []
217         
218         def get_sounds(self, session, collab):
219                 if session and collab:
220                         sessions = self._data['sessions']
221                         sounds = sessions[session]['collabs'][self._data['user']]['sounds']
222                         sounds.sort()
223                         return sounds
224                 else:
225                         return []
226                 
227         def _scan_snapshots(self, session):
228                 snaps = []
229                 files = os.listdir(session)
230                 pattern = re.compile(r'\.ardour$')
231                 for file in files:
232                         if pattern.search(file):
233                                 snaps.append(file[0:-7])
234                                 print file[0:-7]
235                 return snaps
236         
237         def _scan_sounds(self, session):
238                 sounds = []
239                 if v2paths:
240                         files = os.listdir(os.path.join (session,'interchange', session, 'audiofiles'))
241                 else:
242                         files = os.listdir(os.path.join (session,'sounds'))
243                 pattern = re.compile(r'\.peak$')
244                 for file in files:
245                         if not pattern.search(file):
246                                 sounds.append(file)
247                 return sounds
248         
249         def __init__(self, *args):
250                 self._data = shelve.open(os.path.expanduser('~/.session_exchange'), 'c')
251                 self.port = 8970
252                 self.debug_mode = False
253                 if len(self._data.keys()) < 1:
254                         self._data['sessions'] = {}
255                         self._data['user'] = ''
256                 
257                 self._collabs = {}
258
259 from twisted.protocols.basic import FileSender
260 class FileSenderLimited(FileSender):
261         def beginFileTransfer(self, file, consumer, limit, transform = None):
262                 self.file = file
263                 self.consumer = consumer
264                 self.CHUNK_SIZE = limit
265                 self.transform = transform
266                 
267                 self.consumer.registerProducer(self, False)
268                 self.deferred = defer.Deferred()
269                 return self.deferred
270         
271         def resumeProducing(self):
272                 chunk = ''
273                 chunk = self.file.read(self.CHUNK_SIZE)
274                 
275                 if self.transform:
276                         chunk = self.transform(chunk)
277
278                 self.consumer.write(chunk)
279                 self.lastSent = chunk[-1]
280                 self.file = None
281                 self.consumer.unregisterProducer()
282                 self.deferred.callback(self.lastSent)
283                 self.deferred = None
284
285 from twisted.protocols.basic import LineReceiver
286 class ExchangeServer (LineReceiver):
287         def __init__(self):
288                 self.state = "IDLE"
289         
290         def error(self, message):
291                 self.sendLine("ERROR")
292                 self.sendLine(message)
293                 self.transport.loseConnection()
294         
295         def connectionLost(self, reason):
296                 print "server: connection lost: ", reason
297         
298         def connectionMade(self):
299                 print "server: connection made"
300         
301         def lineReceived(self, data):
302                 print "server: ", data
303                 
304                 if self.state == "SESSION":
305                         if g_data.get_sessions().count(data):
306                                 self.session_name = data
307                                 self.state = "IDLE"
308                                 self.sendLine("OK")
309                         else:
310                                 self.error(data + " doesn't exist on server")
311                 elif self.state == "SNAPSHOT":
312                         if g_data.get_snaps(self.session_name, g_data.get_user()).count(data):
313                                 filename = g_data.get_session_path(self.session_name)+data+'.ardour'
314                                 print filename
315                                 self.sendLine(str(os.stat(filename).st_size))
316                                 self.sendLine("OK")
317                                 self.file = open(filename, 'r')
318                                 file_sender = FileSender()
319                                 cb = file_sender.beginFileTransfer(self.file, self.transport)
320                                 cb.addCallback(self.file_done)
321                         else:
322                                 self.error("snapshot: " + data + " doesn't exist on server")
323                 elif self.state == "SOUNDFILE" or self.state == "SOUNDFILE_HEADER":
324                         if g_data.get_sounds(self.session_name, g_data.get_user()).count(data):
325                                 filename = g_data.get_session_path(self.session_name)+"/sounds/"+data
326                                 print filename
327                                 if self.state == "SOUNDFILE":
328                                         self.sendLine(str(os.stat(filename).st_size))
329                                 else:   #SOUNDFILE_HEADER
330                                         header_size = get_header_size(filename)
331                                         if header_size:
332                                                 self.sendLine(str(header_size))
333                                         else:
334                                                 self.error('soundfile: ' + data + 'doesn\'t have "data" chunk')
335                                 self.sendLine("OK")
336                                 self.file = open(filename, 'r')
337                                 if self.state == "SOUNDFILE":
338                                         file_sender = FileSender()
339                                         cb = file_sender.beginFileTransfer(self.file, self.transport)
340                                 else:   # SOUNDFILE_HEADER
341                                         file_sender = FileSenderLimited()
342                                         cb = file_sender.beginFileTransfer(self.file, self.transport, header_size)
343                                 cb.addCallback(self.file_done)
344                         else:
345                                 self.error("soundfile: " + data + "doesn't exist on server")
346                 elif self.state == "SOUNDFILE_SIZE":
347                         if g_data.get_sounds(self.session_name, g_data.get_user()).count(data):
348                                 filename = g_data.get_session_path(self.session_name)+"/sounds/"+data
349                                 print filename
350                                 self.sendLine(str(os.stat(filename).st_size))
351                                 self.state = "IDLE"
352                 elif data == "SESSION":
353                         self.state = "SESSION"
354                 elif data == "SNAPS":
355                         self.state = "SNAPS"
356                         for snap in g_data.get_snaps(self.session_name, g_data.get_user()):
357                                 self.sendLine(snap)
358                         self.sendLine("OK")
359                         self.state = "IDLE"
360                 elif data == "SNAPSHOT":
361                         self.state = "SNAPSHOT"
362                 elif data == "SOUNDFILE":
363                         self.state = "SOUNDFILE"
364                 elif data == "SOUNDFILE_HEADER":
365                         self.state = "SOUNDFILE_HEADER"
366                 elif data == "SOUNDFILE_SIZE":
367                         self.state = "SOUNDFILE_SIZE"
368         
369         def file_done(self, data):
370                 print "server: file done"
371                 self.file.close()
372                 self.state = "IDLE"
373         
374 class ExchangeServerFactory(protocol.ServerFactory):
375         protocol = ExchangeServer
376         
377         def __init__(self):
378                 pass
379
380 class ExchangeClient (LineReceiver):
381         def __init__(self, session_name, collab_name, snap_name, debug_mode):
382                 self.session_name = session_name
383                 self.collab_name = collab_name
384                 self.snap_name = snap_name
385                 self.debug_mode = debug_mode
386                 self.state = "IDLE"
387         
388         def connectionLost(self, reason):
389                 g_display.show_status("Connection lost")
390         
391         def connectionMade(self):
392                 g_display.show_status("Connection made")
393                 self.state = "SESSION"
394                 self.sendLine("SESSION")
395                 self.sendLine(self.session_name)
396         
397         def rawDataReceived(self, data):
398                 self.file.write(data)
399                 self.received += len(data)
400                 print self.received, self.filesize
401                 if self.received >= self.filesize:
402                         self.setLineMode()
403                         self.file.close()
404                         g_data.rescan_session(self.session_name)
405                         if self.state == "SNAPSHOT":
406                                 self.sounds = get_sound_list(self.filename)
407                                 if len(self.sounds):
408                                         self.sound_index = 0
409                                         if self.debug_mode:
410                                                 self.state = "SOUNDFILE_HEADER"
411                                                 self.sendLine("SOUNDFILE_HEADER")
412                                         else:
413                                                 self.state = "SOUNDFILE"
414                                                 self.sendLine("SOUNDFILE")
415                                         self.sendLine(self.sounds[self.sound_index])
416                                 else:
417                                         self.transport.loseConnection()
418                         elif self.state == "SOUNDFILE":
419                                 self.sound_index += 1
420                                 if self.sound_index > len(self.sounds)-1:
421                                         self.transport.loseConnection()
422                                 else:
423                                         self.sendLine("SOUNDFILE")
424                                         self.sendLine(self.sounds[self.sound_index])
425                         elif self.state == "SOUNDFILE_HEADER":
426                                 self.state = "SOUNDFILE_SIZE"
427                                 self.sendLine("SOUNDFILE_SIZE")
428                                 self.sendLine(self.sounds[self.sound_index])
429         
430         def lineReceived(self, data):
431                 print "client: ", data
432                 
433                 if data == "ERROR":
434                         self.state = "ERROR"
435                 elif data == "OK":
436                         if self.state == "SESSION":
437                                 if self.snap_name:
438                                         self.state = "SNAPSHOT"
439                                         self.sendLine("SNAPSHOT")
440                                         self.sendLine(self.snap_name)
441                                 else:
442                                         self.state = "SNAPS"
443                                         self.sendLine("SNAPS")
444                         elif self.state == "SNAPS":
445                                 self.transport.loseConnection()
446                         elif self.state == "SNAPSHOT":
447                                 self.setRawMode()
448                                 self.filename = g_data.get_session_path(self.session_name)+'/'+self.snap_name+'.ardour'
449                                 self.file = open(self.filename, 'w')
450                                 self.received = 0
451                         elif self.state == "SOUNDFILE" or self.state == "SOUNDFILE_HEADER":
452                                 self.setRawMode()
453                                 self.filename = g_data.get_session_path(self.session_name)+'/sounds/'+self.sounds[self.sound_index]
454                                 self.file = open(self.filename, 'w')
455                                 self.received = 0
456                 elif self.state == "ERROR":
457                         raise_error(data, g_display.window)
458                 elif self.state == "SNAPS":
459                         g_data.add_snap(self.session_name, self.collab_name, data)
460                 elif self.state == "SNAPSHOT":
461                         self.filesize = int(data)
462                 elif self.state == "SOUNDFILE":
463                         self.filesize = int(data)
464                 elif self.state == "SOUNDFILE_HEADER":
465                         self.filesize = int(data)
466                 elif self.state == "SOUNDFILE_SIZE":
467                         append_empty_data(self.filename, int(data))
468                         self.sound_index += 1
469                         if self.sound_index > len(self.sounds)-1:
470                                 self.transport.loseConnection()
471                         else:
472                                 self.state = "SOUNDFILE_HEADER"
473                                 self.sendLine("SOUNDFILE_HEADER")
474                                 self.sendLine(self.sounds[self.sound_index])
475
476 class ExchangeClientFactory(protocol.ClientFactory):
477         def buildProtocol(self, addr):
478                 return ExchangeClient(self.session_name, self.collab_name, self.snap_name, self.debug_mode)
479         
480         def clientConnectionFailed(self, connector, reason):
481                 raise_error('Connection failed: ' + reason.getErrorMessage(), g_display.window)
482                 g_display.show_status('Connection failed')
483         
484         def __init__(self, session_name, collab_name, snap_name, debug_mode):
485                 self.session_name = session_name
486                 self.collab_name = collab_name
487                 self.snap_name = snap_name
488                 self.debug_mode = debug_mode
489
490 class HelperWin(object):
491         def delete_me(self, window):
492                 self = 0
493
494 class Preferences(HelperWin):
495         def __init__(self):
496                 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
497                 self.window.set_title('Preferences')
498                 self.window.connect('destroy', self.delete_me)
499                 self.window.set_position(gtk.WIN_POS_MOUSE)
500                 
501                 main_box = gtk.VBox()
502                 self.window.add(main_box)
503                 
504                 hbox1 = gtk.HBox()
505                 label1 = gtk.Label("User")
506                 self.user = gtk.Entry()
507                 self.user.set_text(g_data.get_user())
508                 hbox1.pack_start(label1)
509                 hbox1.pack_start(self.user)
510                 main_box.pack_start(hbox1)
511                 
512                 ok_btn = gtk.Button("Ok")
513                 ok_btn.connect('clicked', self.ok_clicked)
514                 main_box.pack_start(ok_btn)
515                 
516                 self.window.show_all()
517                 
518         def ok_clicked(self, btn):
519                 g_data.set_user(self.user.get_text())
520                 self.window.hide_all()
521                 
522         def show_all(self):
523                 self.window.show_all()
524
525 class AddCollaborator(HelperWin):
526         def __init__(self, session):
527                 self.session_name = session
528                 
529                 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
530                 self.window.set_title('Fetch Session')
531                 self.window.connect('destroy', self.delete_me)
532                 self.window.set_position(gtk.WIN_POS_MOUSE)
533                 
534                 main_box = gtk.VBox()
535                 self.window.add(main_box)
536                 
537                 hbox0 = gtk.HBox()
538                 label0 = gtk.Label("Collaborator")
539                 self.collab = gtk.Entry()
540                 self.collab.connect('key-release-event', self.key_press)
541                 hbox0.pack_start(label0)
542                 hbox0.pack_start(self.collab)
543                 main_box.pack_start(hbox0)
544                 
545                 hbox1 = gtk.HBox()
546                 label1 = gtk.Label("IP Address")
547                 self.address = gtk.Entry()
548                 self.address.connect('key-release-event', self.key_press)
549                 hbox1.pack_start(label1)
550                 hbox1.pack_start(self.address)
551                 main_box.pack_start(hbox1)
552                 
553                 hbox2 = gtk.HBox()
554                 label2 = gtk.Label("Port Number")
555                 self.port = gtk.Entry()
556                 self.port.connect('key-release-event', self.key_press)
557                 self.port.set_text(str(g_data.port))
558                 hbox2.pack_start(label2)
559                 hbox2.pack_start(self.port)
560                 main_box.pack_start(hbox2)
561                 
562                 hbox3 = gtk.HBox()
563                 label3 = gtk.Label("Username")
564                 label3.set_sensitive(False)
565                 self.username = gtk.Entry()
566                 self.username.set_sensitive(False)
567                 hbox3.pack_start(label3)
568                 hbox3.pack_start(self.username)
569                 main_box.pack_start(hbox3)
570                 
571                 hbox4 = gtk.HBox()
572                 label4 = gtk.Label("Password")
573                 label4.set_sensitive(False)
574                 self.password = gtk.Entry()
575                 self.password.set_sensitive(False)
576                 hbox4.pack_start(label4)
577                 hbox4.pack_start(self.password)
578                 main_box.pack_start(hbox4)
579                 
580                 self.ok_btn = gtk.Button(gtk.STOCK_OK)
581                 self.ok_btn.set_use_stock(True)
582                 self.ok_btn.connect('clicked', self.ok_clicked)
583                 self.ok_btn.set_sensitive(False)
584                 main_box.pack_start(self.ok_btn)
585                 
586                 self.window.show_all()
587         
588         def key_press(self, event, data):
589                 if self.collab.get_text() and self.address.get_text() and self.port.get_text():
590                         self.ok_btn.set_sensitive(True)
591                 else:
592                         self.ok_btn.set_sensitive(False)
593                 return True
594         
595         def ok_clicked(self, btn):
596                 self.window.hide_all()
597                 g_data.add_collab(self.session_name, self.collab.get_text(), self.address.get_text(), int(self.port.get_text()))
598                 self.collab.set_text('')
599                 self.address.set_text('')
600                 self.port.set_text('')
601                 self.username.set_text('')
602                 self.password.set_text('')
603         
604         def show_all(self):
605                 self.window.show_all()
606
607 class ArdourShareWindow(object):
608         def menuitem_cb(self, window, action, widget):
609                 print self, window, action, widget
610         
611         def add_collaborator_cb(self, window, action, widget):
612                 if self.session:
613                         self.add_session = AddCollaborator(self.session)
614         
615         def fetch_snapshot_cb(self, window, action, widget):
616                 if self.session and self.collab and self.collab != g_data.get_user():
617                         client = ExchangeClientFactory(self.session, self.collab, self.snap, g_data.debug_mode)
618                         reactor.connectTCP(g_data.get_collab_ip(self.session, self.collab), g_data.port, client)
619         
620         def preferences_cb(self, window, action, widget):
621                 self.preferences = Preferences()
622         
623         def add_session_ok_file_btn_clicked(self, w):
624                 filename = self.file_sel.get_filename()
625                 if filename.endswith(".ardour"):
626                         g_data.add_session(filename[0:filename.rfind("/")+1])
627                         self.update_session_view()
628                 else:
629                         raise_error("Not an Ardour session", self.window)
630                 self.file_sel.destroy()
631         
632         def add_session_cb(self, window, action, widget):
633                 if g_data.get_user():
634                         self.file_sel = gtk.FileSelection("Add Session...")
635                         self.file_sel.ok_button.connect("clicked", self.add_session_ok_file_btn_clicked)
636                         self.file_sel.cancel_button.connect("clicked", lambda w: self.file_sel.destroy())
637                         self.file_sel.connect("destroy", lambda w: self.file_sel.destroy())
638                         self.file_sel.show()
639                 else:
640                         raise_error("Set the user name in the preferences first", self.window)
641         
642         def create_session_cb(self, window, action, widget):
643                 if g_data.get_user():
644                         self.file_sel = gtk.FileSelection("Create Session...")
645                         self.file_sel.ok_button.connect("clicked", self.create_file_ok_btn_clicked)
646                         self.file_sel.cancel_button.connect("clicked", lambda w: self.file_sel.destroy())
647                         self.file_sel.connect("destroy", lambda w: self.file_sel.destroy())
648                         self.file_sel.show()
649                 else:
650                         raise_error("Set the user name in the preferences first", self.window)
651         
652         def create_file_ok_btn_clicked(self, w):
653                 filename = self.file_sel.get_filename()
654                 if len(filename) > 0:
655                         g_data.create_session(filename)
656                         self.update_session_view()
657                 else:
658                         raise_error("Not an Ardour session", self.window)
659                 self.file_sel.destroy()
660         
661         def update_session_view(self):
662                 self.session_model.clear()
663                 for session in g_data.get_sessions():
664                         self.session_model.set(self.session_model.append(), 0, session)
665         
666         def update_collab_view(self):
667                 self.collab_model.clear()
668                 for collab in g_data.get_collabs(self.session):
669                         self.collab_model.set(self.collab_model.append(), 0, collab)
670         
671         def update_snap_view(self):
672                 self.snap_model.clear()
673                 for snap in g_data.get_snaps(self.session, self.collab):
674                         self.snap_model.set(self.snap_model.append(), 0, snap)
675         
676         def cb_session_selection_changed(self, selection_object):
677                 selected = []
678                 selection_object.selected_foreach(lambda model, path, iter, sel = selected: sel.append(path))
679                 for x in selected:
680                         self.session = self.session_model[x][0]
681                 self.selected_type = "session"
682                 self.update_collab_view()
683         
684         def cb_collab_selection_changed(self, selection_object):
685                 selected = []
686                 selection_object.selected_foreach(lambda model, path, iter, sel = selected: sel.append(path))
687                 for x in selected:
688                         self.collab = self.collab_model[x][0]
689                 self.selected_type = "collab"
690                 self.update_snap_view()
691         
692         def cb_snap_selection_changed(self, selection_object):
693                 selected = []
694                 selection_object.selected_foreach(lambda model, path, iter, sel = selected: sel.append(path))
695                 for x in selected:
696                         self.snap = self.snap_model[x][0]
697                 self.selected_type = "snap"
698         
699         def delete_cb(self, window, action, widget):
700                 if self.selected_type == "session":
701                         g_data.delete_session(self.session)
702                         self.session = ""
703                         self.collab = ""
704                         self.snap = ""
705                 elif self.selected_type == "collab":
706                         g_data.delete_collab(self.session, self.collab)
707                         self.collab = ""
708                         self.snap = ""
709                 elif self.selected_type == "snap":
710                         g_data.delete_snap(self.session, self.collab, self.snap)
711                         self.snap = ""
712                 
713                 self.update_session_view()
714                 self.update_collab_view()
715                 self.update_snap_view()
716                 self.selected_type = ""
717                 
718         def show_status(self, text):
719                 mid = self.status_bar.push(self._status_cid, text)
720                 if self._status_mid:
721                         self.status_bar.remove(self._status_cid, self._status_mid)
722                 self._status_mid = mid
723         
724         def __init__(self):
725                 self.selected_type = ""
726                 self.session = ""
727                 self.collab = g_data.get_user()
728                 self.snap = ""
729                 
730                 self.preferences = 0
731                 self.add_collab = 0
732                 self.add_session = 0
733                 
734                 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
735                 self.window.set_title('Session Exchange')
736                 self.window.set_size_request(400, 200)
737                 self.window.connect('destroy', lambda win: gtk.main_quit())
738                 self.window.set_position(gtk.WIN_POS_MOUSE)
739                 
740                 accel_group = gtk.AccelGroup()
741                 self.window.add_accel_group(accel_group)
742                 
743                 main_box = gtk.VBox()
744                 self.window.add(main_box)
745                 
746                 menu_items = (
747                         ('/_File',            None,         None,             0, '<Branch>'),
748                         ('/File/_Add Session...','<control>A', self.add_session_cb, 0, ''),
749                         ('/File/Create _Session...', '<control>S', self.create_session_cb, 0, ''),
750                         ('/File/sep1',        None,         None,             0, '<Separator>'),
751                         ('/File/_Quit',       '<control>Q', gtk.main_quit,     0, '<StockItem>', gtk.STOCK_QUIT),
752                         ('/_Edit',            None,         None,             0, '<Branch>' ),
753                         ('/Edit/Cu_t',        '<control>X', self.menuitem_cb, 0, '<StockItem>', gtk.STOCK_CUT),
754                         ('/Edit/_Copy',       '<control>C', self.menuitem_cb, 0, '<StockItem>', gtk.STOCK_COPY),
755                         ('/Edit/_Paste',      '<control>V', self.menuitem_cb, 0, '<StockItem>', gtk.STOCK_PASTE),
756                         ('/Edit/_Delete',     None,         self.delete_cb, 0, '<StockItem>', gtk.STOCK_DELETE),
757                         ('/Edit/sep1',        None,         None,             0, '<Separator>'),
758                         ('/Edit/Add Colla_borator...','<control>B', self.add_collaborator_cb,0,''),
759                         ('/Edit/_Fetch Snapshot','<control>F', self.fetch_snapshot_cb,0,''),
760                         ('/Edit/sep1',        None,         None,             0, '<Separator>'),
761                         ('/Edit/_Preferences...','<control>P', self.preferences_cb, 0, '')
762                 )
763                 
764                 #need to hold a reference to the item_factory or the menubar will disappear.
765                 self.item_factory = gtk.ItemFactory(gtk.MenuBar, '<main>', accel_group)
766                 self.item_factory.create_items(menu_items, self.window)
767                 main_box.pack_start(self.item_factory.get_widget('<main>'), False)
768                 
769                 pane1 = gtk.HPaned()
770                 pane2 = gtk.HPaned()
771                 pane1.pack2(pane2, True, False)
772                 
773                 scroll1 = gtk.ScrolledWindow()
774                 scroll1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
775                 pane1.pack1(scroll1, True, False)
776                 scroll2 = gtk.ScrolledWindow()
777                 scroll2.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
778                 pane2.pack1(scroll2, True, False)
779                 scroll3 = gtk.ScrolledWindow()
780                 scroll3.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
781                 pane2.pack2(scroll3, True, False)
782                 
783                 self.session_model = gtk.ListStore(gobject.TYPE_STRING)
784                 view1 = gtk.TreeView(self.session_model)
785                 column1 = gtk.TreeViewColumn('Sessions', gtk.CellRendererText(), text=0)
786                 view1.append_column(column1)
787                 self.session_selection = view1.get_selection()
788                 self.session_selection.connect("changed", self.cb_session_selection_changed)
789                 scroll1.add(view1)
790                 
791                 self.update_session_view()
792                 
793                 self.collab_model = gtk.ListStore(gobject.TYPE_STRING)
794                 view2 = gtk.TreeView(self.collab_model)
795                 column2 = gtk.TreeViewColumn('Collaborators', gtk.CellRendererText(), text=0)
796                 view2.append_column(column2)
797                 self.collab_selection = view2.get_selection()
798                 self.collab_selection.connect("changed", self.cb_collab_selection_changed)
799                 scroll2.add(view2)
800                 
801                 self.snap_model = gtk.ListStore(gobject.TYPE_STRING)
802                 view3 = gtk.TreeView(self.snap_model)
803                 column3 = gtk.TreeViewColumn('Snapshots', gtk.CellRendererText(), text=0)
804                 view3.append_column(column3)
805                 self.snap_selection = view3.get_selection()
806                 self.snap_selection.connect("changed", self.cb_snap_selection_changed)
807                 scroll3.add(view3)
808                 
809                 main_box.pack_start(pane1, True, True)
810                 
811                 self.status_bar = gtk.Statusbar()
812                 main_box.pack_start(self.status_bar, False)
813                 self._status_cid = self.status_bar.get_context_id('display')
814                 self._status_mid = ''
815                 
816                 self.window.show_all()
817
818 def print_help():
819         print """
820         -h, --help
821         -n, --no-server          Only act as a client
822         -p, --port <port number> Defaults to 8970
823         -d, --debug              Infers audio files.  For debugging Ardour.
824         -v, --version            Version
825         """
826         sys.exit(2)
827
828 def main():
829         try:
830                 opts, args = getopt.getopt(sys.argv[1:], "hp:ndv", ["help", "port=", "no-server", "debug", "version"])
831         except getopt.GetoptError:
832                 print_help()
833         
834         server = True
835         for o, a in opts:
836                 if o in ("-h", "--help"):
837                         print_help()
838                 if o in ("-d", "--debug"):
839                         g_display.window.set_title('Session Exchange: Debug Mode')
840                         g_data.debug_mode = True
841                 if o in ("-p", "--port"):
842                         g_data.port = int(a)
843                 if o in ("-n", "--no-server"):
844                         server = False
845                 if o in ("-v", "--version"):
846                         print VERSION
847                         sys.exit(2)
848         
849         if (server):
850                 try:
851                         reactor.listenTCP(g_data.port, ExchangeServerFactory())
852                 except twisted.internet.error.CannotListenError:
853                         print "Can not listen on a port number under 1024 unless run as root"
854                         sys.exit(2)
855
856         reactor.run()
857
858         g_data.close()
859
860 # global objects
861 g_data = Data()
862 g_display = ArdourShareWindow()
863
864 if __name__ == '__main__':
865         main()