diff --git a/data/glade/message_window.glade b/data/glade/message_window.glade index 3220f4812..ad3263915 100644 --- a/data/glade/message_window.glade +++ b/data/glade/message_window.glade @@ -102,7 +102,7 @@ - + True None 1 @@ -333,15 +333,13 @@ - + True True - True - Start audio session + Toggle audio session none - - + True gtk-missing-image 1 @@ -354,15 +352,13 @@ - + True True - True - Stop audio session + Toggle video session none - - + True gtk-missing-image 1 diff --git a/src/chat_control.py b/src/chat_control.py index 8e04e0a32..4c980b544 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -1167,12 +1167,12 @@ class ChatControlBase(MessageControl, CommonCommands): class ChatControl(ChatControlBase, ChatCommands): '''A control for standard 1-1 chat''' ( - AUDIO_STATE_NOT_AVAILABLE, - AUDIO_STATE_AVAILABLE, - AUDIO_STATE_CONNECTING, - AUDIO_STATE_CONNECTION_RECEIVED, - AUDIO_STATE_CONNECTED, - AUDIO_STATE_ERROR + JINGLE_STATE_NOT_AVAILABLE, + JINGLE_STATE_AVAILABLE, + JINGLE_STATE_CONNECTING, + JINGLE_STATE_CONNECTION_RECEIVED, + JINGLE_STATE_CONNECTED, + JINGLE_STATE_ERROR ) = range(6) TYPE_ID = message_control.TYPE_CHAT @@ -1200,15 +1200,13 @@ class ChatControl(ChatControlBase, ChatCommands): self._on_add_to_roster_menuitem_activate) self.handlers[id_] = self._add_to_roster_button - self._start_audio_button = self.xml.get_widget('start_audio_button') - id_ = self._start_audio_button.connect('clicked', - self.on_start_audio_button_activate) - self.handlers[id_] = self._start_audio_button + self._audio_button = self.xml.get_widget('audio_togglebutton') + id_ = self._audio_button.connect('toggled', self.on_audio_button_toggled) + self.handlers[id_] = self._audio_button - self._stop_audio_button = self.xml.get_widget('stop_audio_button') - id_ = self._stop_audio_button.connect('clicked', - self.on_stop_audio_button_activate) - self.handlers[id_] = self._stop_audio_button + self._video_button = self.xml.get_widget('video_togglebutton') + id_ = self._video_button.connect('toggled', self.on_video_button_toggled) + self.handlers[id_] = self._video_button self._send_file_button = self.xml.get_widget('send_file_button') # add a special img for send file button @@ -1252,13 +1250,20 @@ class ChatControl(ChatControlBase, ChatCommands): img.set_from_pixbuf(gtkgui_helpers.load_icon( 'muc_active').get_pixbuf()) - self._audio_image = self.xml.get_widget('audio_image') + self._audio_banner_image = self.xml.get_widget('audio_banner_image') + self._video_banner_image = self.xml.get_widget('video_banner_image') if gajim.capscache.is_supported(contact, NS_JINGLE_RTP_AUDIO) and \ gajim.HAVE_FARSIGHT: self.set_audio_state('available') else: self.set_audio_state('not_available') + if gajim.capscache.is_supported(contact, NS_JINGLE_RTP_VIDEO) and \ + gajim.HAVE_FARSIGHT: + self.set_video_state('available') + else: + self.set_video_state('not_available') self.audio_sid = None + self.video_sid = None self.update_toolbar() @@ -1369,18 +1374,16 @@ class ChatControl(ChatControlBase, ChatCommands): self._add_to_roster_button.hide() # Audio buttons - if self.audio_state == self.AUDIO_STATE_NOT_AVAILABLE: - self._start_audio_button.show() - self._start_audio_button.set_sensitive(False) - self._stop_audio_button.hide() - elif self.audio_state in (self.AUDIO_STATE_AVAILABLE, - self.AUDIO_STATE_ERROR): - self._start_audio_button.show() - self._start_audio_button.set_sensitive(True) - self._stop_audio_button.hide() + if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE: + self._audio_button.set_sensitive(False) else: - self._start_audio_button.hide() - self._stop_audio_button.show() + self._audio_button.set_sensitive(True) + + # Video buttons + if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE: + self._video_button.set_sensitive(False) + else: + self._video_button.set_sensitive(True) # Send file if gajim.capscache.is_supported(self.contact, NS_FILE) and \ @@ -1515,19 +1518,35 @@ class ChatControl(ChatControlBase, ChatCommands): self._tune_image.hide() def update_audio(self): - if self.audio_state in (self.AUDIO_STATE_NOT_AVAILABLE, - self.AUDIO_STATE_AVAILABLE): - self._audio_image.hide() + if self.audio_state in (self.JINGLE_STATE_NOT_AVAILABLE, + self.JINGLE_STATE_AVAILABLE): + self._audio_banner_image.hide() else: - self._audio_image.show() - if self.audio_state == self.AUDIO_STATE_CONNECTING: - self._audio_image.set_from_stock(gtk.STOCK_CONVERT, 1) - elif self.audio_state == self.AUDIO_STATE_CONNECTION_RECEIVED: - self._audio_image.set_from_stock(gtk.STOCK_NETWORK, 1) - elif self.audio_state == self.AUDIO_STATE_CONNECTED: - self._audio_image.set_from_stock(gtk.STOCK_CONNECT, 1) - elif self.audio_state == self.AUDIO_STATE_ERROR: - self._audio_image.set_from_stock(gtk.STOCK_DIALOG_WARNING, 1) + self._audio_banner_image.show() + if self.audio_state == self.JINGLE_STATE_CONNECTING: + self._audio_banner_image.set_from_stock(gtk.STOCK_CONVERT, 1) + elif self.audio_state == self.JINGLE_STATE_CONNECTION_RECEIVED: + self._audio_banner_image.set_from_stock(gtk.STOCK_NETWORK, 1) + elif self.audio_state == self.JINGLE_STATE_CONNECTED: + self._audio_banner_image.set_from_stock(gtk.STOCK_CONNECT, 1) + elif self.audio_state == self.JINGLE_STATE_ERROR: + self._audio_banner_image.set_from_stock(gtk.STOCK_DIALOG_WARNING, 1) + self.update_toolbar() + + def update_video(self): + if self.video_state in (self.JINGLE_STATE_NOT_AVAILABLE, + self.JINGLE_STATE_AVAILABLE): + self._video_banner_image.hide() + else: + self._video_banner_image.show() + if self.video_state == self.JINGLE_STATE_CONNECTING: + self._video_banner_image.set_from_stock(gtk.STOCK_CONVERT, 1) + elif self.video_state == self.JINGLE_STATE_CONNECTION_RECEIVED: + self._video_banner_image.set_from_stock(gtk.STOCK_NETWORK, 1) + elif self.video_state == self.JINGLE_STATE_CONNECTED: + self._video_banner_image.set_from_stock(gtk.STOCK_CONNECT, 1) + elif self.video_state == self.JINGLE_STATE_ERROR: + self._video_banner_image.set_from_stock(gtk.STOCK_DIALOG_WARNING, 1) self.update_toolbar() def change_resource(self, resource): @@ -1550,26 +1569,53 @@ class ChatControl(ChatControlBase, ChatCommands): str += ', ' + _('reason: %s') % reason self.print_conversation(str, 'info') if state == 'not_available': - self.audio_state = self.AUDIO_STATE_NOT_AVAILABLE + self.audio_state = self.JINGLE_STATE_NOT_AVAILABLE self.audio_sid = None elif state == 'available': - self.audio_state = self.AUDIO_STATE_AVAILABLE + self.audio_state = self.JINGLE_STATE_AVAILABLE self.audio_sid = None elif state == 'connecting': - self.audio_state = self.AUDIO_STATE_CONNECTING + self.audio_state = self.JINGLE_STATE_CONNECTING self.audio_sid = sid elif state == 'connection_received': - self.audio_state = self.AUDIO_STATE_CONNECTION_RECEIVED + self.audio_state = self.JINGLE_STATE_CONNECTION_RECEIVED self.audio_sid = sid elif state == 'connected': - self.audio_state = self.AUDIO_STATE_CONNECTED + self.audio_state = self.JINGLE_STATE_CONNECTED elif state == 'stop': - self.audio_state = self.AUDIO_STATE_AVAILABLE + self.audio_state = self.JINGLE_STATE_AVAILABLE self.audio_sid = None elif state == 'error': - self.audio_state = self.AUDIO_STATE_ERROR + self.audio_state = self.JINGLE_STATE_ERROR self.update_audio() + def set_video_state(self, state, sid=None, reason=None): + if state in ('connecting', 'connected', 'stop'): + str = _('Video state : %s') % state + if reason: + str += ', ' + _('reason: %s') % reason + self.print_conversation(str, 'info') + if state == 'not_available': + self.video_state = self.JINGLE_STATE_NOT_AVAILABLE + self.video_sid = None + elif state == 'available': + self.video_state = self.JINGLE_STATE_AVAILABLE + self.video_sid = None + elif state == 'connecting': + self.video_state = self.JINGLE_STATE_CONNECTING + self.video_sid = sid + elif state == 'connection_received': + self.video_state = self.JINGLE_STATE_CONNECTION_RECEIVED + self.video_sid = sid + elif state == 'connected': + self.video_state = self.JINGLE_STATE_CONNECTED + elif state == 'stop': + self.video_state = self.JINGLE_STATE_AVAILABLE + self.video_sid = None + elif state == 'error': + self.video_state = self.JINGLE_STATE_ERROR + self.update_video() + def on_avatar_eventbox_enter_notify_event(self, widget, event): ''' we enter the eventbox area so we under conditions add a timeout @@ -1770,16 +1816,29 @@ class ChatControl(ChatControlBase, ChatCommands): banner_name_label.set_markup(label_text) banner_name_tooltip.set_tip(banner_name_label, label_tooltip) - def on_start_audio_button_activate(self, *things): - sid = gajim.connections[self.account].startVoIP(self.contact.get_full_jid( - )) - self.set_audio_state('connecting', sid) + def on_audio_button_toggled(self, widget): + if widget.get_active(): + sid = gajim.connections[self.account].startVoIP( + self.contact.get_full_jid()) + self.set_audio_state('connecting', sid) + else: + session = gajim.connections[self.account].get_jingle_session( + self.contact.get_full_jid(), self.audio_sid) + if session: + # TODO: end only audio + session.end_session() - def on_stop_audio_button_activate(self, *things): - session = gajim.connections[self.account].get_jingle_session( - self.contact.get_full_jid(), self.audio_sid) - if session: - session.end_session() + def on_video_button_toggled(self, widget): + if widget.get_active(): + sid = gajim.connections[self.account].startVideoIP( + self.contact.get_full_jid()) + self.set_video_state('connecting', sid) + else: + session = gajim.connections[self.account].get_jingle_session( + self.contact.get_full_jid(), self.video_sid) + if session: + # TODO: end only video + session.end_session() def _toggle_gpg(self): if not self.gpg_is_active and not self.contact.keyID: diff --git a/src/common/jingle.py b/src/common/jingle.py index c15067235..9232ae02c 100644 --- a/src/common/jingle.py +++ b/src/common/jingle.py @@ -953,14 +953,42 @@ class ConnectionJingle(object): raise xmpp.NodeProcessed def startVoIP(self, jid): - jingle = JingleSession(self, weinitiate=True, jid=jid) - self.add_jingle(jingle) - jingle.add_content('voice', JingleVoIP(jingle)) - jingle.start_session() + jingle = self.get_jingle_session(jid, media='video') + if jingle: + jingle.add_content('voice', JingleVoIP(jingle)) + else: + jingle = JingleSession(self, weinitiate=True, jid=jid) + self.add_jingle(jingle) + jingle.add_content('voice', JingleVoIP(jingle)) + jingle.start_session() return jingle.sid - def get_jingle_session(self, jid, sid): - try: - return self.__sessions[(jid, sid)] - except KeyError: - return None + def startVideoIP(self, jid): + jingle = self.get_jingle_session(jid, media='audio') + if jingle: + jingle.add_content('video', JingleVideo(jingle)) + else: + jingle = JingleSession(self, weinitiate=True, jid=jid) + self.add_jingle(jingle) + jingle.add_content('video', JingleVideo(jingle)) + jingle.start_session() + return jingle.sid + + def get_jingle_session(self, jid, sid=None, media=None): + if sid: + if (jid, sid) in self.__sessions: + return self.__sessions[(jid, sid)] + else: + return None + elif media: + if media == 'audio': + cls = JingleVoIP + elif media == 'video': + cls = JingleVideo + else: + return None + for session in self.__sessions.values(): + for content in session.contents.values(): + if isinstance(content, cls): + return session + return None