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