diff --git a/data/glade/preferences_window.glade b/data/glade/preferences_window.glade index 75a1a1a22..98301441a 100644 --- a/data/glade/preferences_window.glade +++ b/data/glade/preferences_window.glade @@ -1714,7 +1714,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -1754,7 +1754,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -1794,7 +1794,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -1823,7 +1823,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -1854,7 +1854,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -1883,7 +1883,7 @@ $T will be replaced by auto-not-available timeout - + True @@ -2391,7 +2391,7 @@ Custom True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 + 4 6 6 @@ -2441,6 +2441,21 @@ Custom 3 + + + Allow my _idle time to be sent + True + True + False + True + True + + + + 3 + 4 + + diff --git a/src/common/config.py b/src/common/config.py index 94078dffc..ad0b77cbc 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -359,6 +359,7 @@ class Config: 'ignore_unknown_contacts': [ opt_bool, False ], 'send_os_info': [ opt_bool, True ], 'log_encrypted_sessions': [opt_bool, True, _('When negotiating an encrypted session, should Gajim assume you want your messages to be logged?')], + 'send_idle_time': [ opt_bool, True ], 'roster_version': [opt_str, ''], }, {}), 'statusmsg': ({ diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index def59cca4..b91fe13b7 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -78,7 +78,8 @@ PRIVACY_ARRIVED = 'privacy_arrived' PEP_CONFIG = 'pep_config' HAS_IDLE = True try: - import idle +# import idle + import common.sleepy except Exception: log.debug(_('Unable to load idle module')) HAS_IDLE = False @@ -906,6 +907,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps, ConnectionHandlersBase, ConnectionJingle): def __init__(self): + global HAS_IDLE ConnectionVcard.__init__(self) ConnectionBytestream.__init__(self) ConnectionCommands.__init__(self) @@ -933,9 +935,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, self.continue_connect_info = None try: - idle.init() + self.sleeper = common.sleepy.Sleepy() +# idle.init() + HAS_IDLE = True except Exception: - global HAS_IDLE HAS_IDLE = False self.gmail_last_tid = None @@ -1100,15 +1103,19 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, raise common.xmpp.NodeProcessed def _LastCB(self, con, iq_obj): + global HAS_IDLE log.debug('LastCB') if not self.connection or self.connected < 2: return - iq_obj = iq_obj.buildReply('result') - qp = iq_obj.getTag('query') - if not HAS_IDLE: - qp.attrs['seconds'] = '0' + if HAS_IDLE and gajim.config.get_per('accounts', self.name, + 'send_idle_time'): + iq_obj = iq_obj.buildReply('result') + qp = iq_obj.getTag('query') + qp.attrs['seconds'] = int(self.sleeper.getIdleSec()) else: - qp.attrs['seconds'] = idle.getIdleSec() + iq_obj = iq_obj.buildReply('error') + err = common.xmpp.ErrorNode(name=common.xmpp.NS_STANZAS+' service-unavailable') + iq_obj.addChild(node=err) self.connection.send(iq_obj) raise common.xmpp.NodeProcessed diff --git a/src/common/contacts.py b/src/common/contacts.py index cd3b5f01b..439e5f5c0 100644 --- a/src/common/contacts.py +++ b/src/common/contacts.py @@ -99,7 +99,7 @@ class Contact(CommonContact): def __init__(self, jid, account, name='', groups=[], show='', status='', sub='', ask='', resource='', priority=0, keyID='', client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None, msg_id= - None, composing_xep=None): + None, composing_xep=None, last_activity_time=None): CommonContact.__init__(self, jid, account, resource, show, status, name, our_chatstate, composing_xep, chatstate, client_caps=client_caps) @@ -114,6 +114,7 @@ class Contact(CommonContact): self.keyID = keyID self.msg_id = msg_id self.last_status_time = last_status_time + self.last_activity_time = last_activity_time self.pep = {} @@ -245,16 +246,18 @@ class LegacyContactsAPI: del self._accounts[account] self._metacontact_manager.remove_account(account) - def create_contact(self, jid, account, name='', groups=[], show='', status='', - sub='', ask='', resource='', priority=0, keyID='', client_caps=None, - our_chatstate=None, chatstate=None, last_status_time=None, - composing_xep=None): - account = self._accounts.get(account, account) # Use Account object if available + def create_contact(self, jid, account, name='', groups=[], show='', + status='', sub='', ask='', resource='', priority=0, keyID='', + client_caps=None, our_chatstate=None, chatstate=None, last_status_time=None, + composing_xep=None, last_activity_time=None): + # Use Account object if available + account = self._accounts.get(account, account) return Contact(jid=jid, account=account, name=name, groups=groups, - show=show, status=status, sub=sub, ask=ask, resource=resource, priority=priority, - keyID=keyID, client_caps=client_caps, our_chatstate=our_chatstate, - chatstate=chatstate, last_status_time=last_status_time, - composing_xep=composing_xep) + show=show, status=status, sub=sub, ask=ask, resource=resource, + priority=priority, keyID=keyID, client_caps=client_caps, + our_chatstate=our_chatstate, chatstate=chatstate, + last_status_time=last_status_time, composing_xep=composing_xep, + last_activity_time=last_activity_time) def create_self_contact(self, jid, account, resource, show, status, priority, name='', keyID=''): @@ -275,12 +278,15 @@ class LegacyContactsAPI: status='', sub='none', keyID=keyID) def copy_contact(self, contact): - return self.create_contact(jid=contact.jid, account=contact.account, - name=contact.name, groups=contact.groups, show=contact.show, status=contact.status, - sub=contact.sub, ask=contact.ask, resource=contact.resource, - priority=contact.priority, keyID=contact.keyID, - client_caps=contact.client_caps, our_chatstate=contact.our_chatstate, - chatstate=contact.chatstate, last_status_time=contact.last_status_time) + return self.create_contact(contact.jid, contact.account, + name=contact.name, groups=contact.groups, show=contact.show, + status=contact.status, sub=contact.sub, ask=contact.ask, + resource=contact.resource, priority=contact.priority, + keyID=contact.keyID, client_caps=contact.client_caps, + our_chatstate=contact.our_chatstate, chatstate=contact.chatstate, + last_status_time=contact.last_status_time, + composing_xep=contact.composing_xep, + last_activity_time=contact.last_activity_time) def add_contact(self, account, contact): if account not in self._accounts: diff --git a/src/common/idle.py b/src/common/idle.py index ccdfa5a79..d3ff4dff7 100644 --- a/src/common/idle.py +++ b/src/common/idle.py @@ -73,6 +73,7 @@ except OSError, e: xss_available = False def getIdleSec(): + global xss_available """ Return the idle time in seconds """ diff --git a/src/config.py b/src/config.py index 821d90dcd..95122baf1 100644 --- a/src/config.py +++ b/src/config.py @@ -509,6 +509,14 @@ class PreferencesWindow: else: w.set_active(st) + # send idle time + w = self.xml.get_widget('send_idle_time_checkbutton') + st = self.get_per_account_option('send_idle_time') + if st == 'mixed': + w.set_inconsistent(True) + else: + w.set_active(st) + # check if gajm is default st = gajim.config.get('check_if_gajim_is_default') self.xml.get_widget('check_default_client_checkbutton').set_active(st) @@ -1120,6 +1128,10 @@ class PreferencesWindow: widget.set_inconsistent(False) self.on_per_account_checkbutton_toggled(widget, 'send_os_info') + def on_send_idle_time_checkbutton_toggled(self, widget): + widget.set_inconsistent(False) + self.on_per_account_checkbutton_toggled(widget, 'send_idle_time') + def on_check_default_client_checkbutton_toggled(self, widget): self.on_checkbutton_toggled(widget, 'check_if_gajim_is_default') diff --git a/src/gui_interface.py b/src/gui_interface.py index a500f939f..6db32db67 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -817,12 +817,18 @@ class Interface: win = self.instances[account]['infos'][array[0] + '/' + array[1]] c = gajim.contacts.get_contact(account, array[0], array[1]) if c: # c can be none if it's a gc contact - c.last_status_time = time.localtime(time.time() - tim) if array[3]: c.status = array[3] self.roster.draw_contact(c.jid, account) # draw offline status + last_time = time.localtime(time.time() - tim) + if c.show == 'offline': + c.last_status_time = last_time + else: + c.last_activity_time = last_time if win: win.set_last_status_time() + if self.roster.tooltip.id and self.roster.tooltip.win: + self.roster.tooltip.update_last_time(last_time) if self.remote_ctrl: self.remote_ctrl.raise_signal('LastStatusTime', (account, array)) diff --git a/src/tooltips.py b/src/tooltips.py index 7faed5c45..aeaf06b1c 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -66,6 +66,8 @@ class BaseTooltip: self.preferred_position = [0, 0] self.win = None self.id = None + self.cur_data = None + self.check_last_time = None def populate(self, data): """ @@ -115,9 +117,8 @@ class BaseTooltip: self.screen.get_width() + half_width: self.preferred_position[0] = self.screen.get_width() - \ requisition.width - else: + elif not self.check_last_time: self.preferred_position[0] -= half_width - self.screen.get_height() if self.preferred_position[1] + requisition.height > \ self.screen.get_height(): # flip tooltip up @@ -148,6 +149,7 @@ class BaseTooltip: widget_height is the height of the widget on which we show the tooltip. widget_y_position is vertical position of the widget on the screen. """ + self.cur_data = data # set tooltip contents self.populate(data) @@ -171,6 +173,8 @@ class BaseTooltip: self.win.destroy() self.win = None self.id = None + self.cur_data = None + self.check_last_time = None class StatusTable: """ @@ -498,6 +502,20 @@ class RosterTooltip(NotificationAreaTooltip): else: # only one resource if contact.show: show = helpers.get_uf_show(contact.show) + if not self.check_last_time and self.account: + if contact.show == 'offline': + if not contact.last_status_time: + gajim.connections[self.account].request_last_status_time( + contact.jid, '') + else: + self.check_last_time = contact.last_status_time + elif contact.resource: + gajim.connections[self.account].request_last_status_time( + contact.jid, contact.resource) + if contact.last_activity_time: + self.check_last_time = contact.last_activity_time + else: + self.check_last_time = None if contact.last_status_time: vcard_current_row += 1 if contact.show == 'offline': @@ -565,6 +583,23 @@ class RosterTooltip(NotificationAreaTooltip): properties.append((_('OpenPGP: '), gobject.markup_escape_text(keyID))) + if contact.last_activity_time: + text = _(' since %s') + + if time.strftime('%j', time.localtime())== \ + time.strftime('%j', contact.last_activity_time): + # it's today, show only the locale hour representation + local_time = time.strftime('%I:%M %p', + contact.last_activity_time) + else: + # time.strftime returns locale encoded string + local_time = time.strftime('%c', + contact.last_activity_time) + local_time = local_time.decode( + locale.getpreferredencoding()) + text = text % local_time + properties.append(('Idle' + text,None)) + while properties: property_ = properties.pop(0) vcard_current_row += 1 @@ -598,6 +633,15 @@ class RosterTooltip(NotificationAreaTooltip): vcard_current_row + 1, gtk.FILL, gtk.FILL | gtk.EXPAND, 3, 3) self.win.add(vcard_table) + def update_last_time(self, last_time): + if not self.check_last_time or time.strftime('%x %I:%M %p', last_time) !=\ + time.strftime('%x %I:%M %p', self.check_last_time): + self.win.destroy() + self.win = None + self.populate(self.cur_data) + self.win.ensure_style() + self.win.show_all() + def _append_pep_info(self, contact, properties): """ Append Tune, Mood, Activity, Location information of the specified contact