various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / tools / session_exchange.py
blob4248a4242aad18008b22380bfdabab41a180d6e9
1 #! /usr/bin/python
3 # Session Exchange
4 # By Taybin Rutkin
5 # Copyright 2004-2005, under the GPL
7 VERSION='0.1.2'
9 #twisted libraries
10 from twisted.internet import gtk2reactor
11 gtk2reactor.install()
12 from twisted.internet import reactor, protocol
13 import twisted.internet.error
15 #pygtk libraries
16 import gobject
17 import gtk
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
29 v2paths = True
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
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()
50 def get_sound_list(snapshot):
51 doc = xml.dom.minidom.parse(snapshot)
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'))
66 sourcelist = {}
67 sources = doc.getElementsByTagName('Source')
68 for source in sources:
69 sourcelist[source.getAttribute('id')] = str(source.getAttribute('name'))
71 soundlist = []
72 for id in regionlist:
73 if sourcelist.has_key(id):
74 soundlist.append(sourcelist[id])
76 return soundlist
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)
82 dialog.run()
83 dialog.destroy()
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
91 def delete_collab(self,session, collab):
92 sessions = self._data['sessions']
93 del sessions[session]['collabs'][collab]
94 self._data['sessions'] = sessions
96 def delete_session(self, session):
97 sessions = self._data['sessions']
98 del sessions[session]
99 self._data['sessions'] = sessions
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
107 g_display.update_snap_view()
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
119 client = ExchangeClientFactory(session_name, collab_name, None, self.debug_mode)
120 reactor.connectTCP(ip_address, port, client)
121 g_display.show_status("connecting")
123 g_display.update_collab_view()
125 def add_session(self, session_path):
126 sessions = self._data['sessions']
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
141 self._data['sessions'] = sessions
143 self.rescan_session(session_name)
145 def rescan_session(self, session_name):
146 sessions = self._data['sessions']
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)
152 self._data['sessions'] = sessions
154 g_display.update_snap_view()
156 print self._data['sessions']
158 def create_session(self, session_path):
159 sessions = self._data['sessions']
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
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'] = []
178 self._data['sessions'] = sessions
179 print self._data['sessions']
181 def get_session_path(self, session):
182 sessions = self._data['sessions']
183 return sessions[session]['path']
185 def get_user(self):
186 return self._data['user']
188 def set_user(self, username):
189 self._data['user'] = username
191 def get_collab_ip(self, session, collab):
192 sessions = self._data['sessions']
193 return sessions[session]['collabs'][collab]['ip']
195 def close(self):
196 self._data.close()
198 def get_sessions(self):
199 sessions = self._data['sessions']
200 sess = sessions.keys()
201 sess.sort()
202 return sess
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 []
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 []
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 []
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
241 def _scan_sounds(self, session):
242 sessions = self._data['sessions']
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
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'] = ''
265 self._collabs = {}
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
275 self.consumer.registerProducer(self, False)
276 self.deferred = defer.Deferred()
277 return self.deferred
279 def resumeProducing(self):
280 chunk = ''
281 chunk = self.file.read(self.CHUNK_SIZE)
283 if self.transform:
284 chunk = self.transform(chunk)
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
293 from twisted.protocols.basic import LineReceiver
294 class ExchangeServer (LineReceiver):
295 def __init__(self):
296 self.state = "IDLE"
298 def error(self, message):
299 self.sendLine("ERROR")
300 self.sendLine(message)
301 self.transport.loseConnection()
303 def connectionLost(self, reason):
304 print "server: connection lost: ", reason
306 def connectionMade(self):
307 print "server: connection made"
309 def lineReceived(self, data):
310 print "server: ", data
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"
377 def file_done(self, data):
378 print "server: file done"
379 self.file.close()
380 self.state = "IDLE"
382 class ExchangeServerFactory(protocol.ServerFactory):
383 protocol = ExchangeServer
385 def __init__(self):
386 pass
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"
396 def connectionLost(self, reason):
397 g_display.show_status("Connection lost")
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)
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])
438 def lineReceived(self, data):
439 print "client: ", data
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])
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)
488 def clientConnectionFailed(self, connector, reason):
489 raise_error('Connection failed: ' + reason.getErrorMessage(), g_display.window)
490 g_display.show_status('Connection failed')
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
498 class HelperWin(object):
499 def delete_me(self, window):
500 self = 0
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)
509 main_box = gtk.VBox()
510 self.window.add(main_box)
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)
520 ok_btn = gtk.Button("Ok")
521 ok_btn.connect('clicked', self.ok_clicked)
522 main_box.pack_start(ok_btn)
524 self.window.show_all()
526 def ok_clicked(self, btn):
527 g_data.set_user(self.user.get_text())
528 self.window.hide_all()
530 def show_all(self):
531 self.window.show_all()
533 class AddCollaborator(HelperWin):
534 def __init__(self, session):
535 self.session_name = session
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)
542 main_box = gtk.VBox()
543 self.window.add(main_box)
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)
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)
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)
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)
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)
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)
594 self.window.show_all()
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
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('')
612 def show_all(self):
613 self.window.show_all()
615 class ArdourShareWindow(object):
616 def menuitem_cb(self, window, action, widget):
617 print self, window, action, widget
619 def add_collaborator_cb(self, window, action, widget):
620 if self.session:
621 self.add_session = AddCollaborator(self.session)
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)
628 def preferences_cb(self, window, action, widget):
629 self.preferences = Preferences()
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()
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)
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)
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()
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)
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)
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)
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()
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()
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"
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 = ""
721 self.update_session_view()
722 self.update_collab_view()
723 self.update_snap_view()
724 self.selected_type = ""
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
732 def __init__(self):
733 self.selected_type = ""
734 self.session = ""
735 self.collab = g_data.get_user()
736 self.snap = ""
738 self.preferences = 0
739 self.add_collab = 0
740 self.add_session = 0
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)
748 accel_group = gtk.AccelGroup()
749 self.window.add_accel_group(accel_group)
751 main_box = gtk.VBox()
752 self.window.add(main_box)
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, '')
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)
777 pane1 = gtk.HPaned()
778 pane2 = gtk.HPaned()
779 pane1.pack2(pane2, True, False)
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)
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)
799 self.update_session_view()
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)
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)
817 main_box.pack_start(pane1, True, True)
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 = ''
824 self.window.show_all()
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
834 sys.exit(2)
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()
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)
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)
864 reactor.run()
866 g_data.close()
868 # global objects
869 g_data = Data()
870 g_display = ArdourShareWindow()
872 if __name__ == '__main__':
873 main()