From f52afdcbe8380f9c2f0b451a9781034e4ad8bbd1 Mon Sep 17 00:00:00 2001 From: Travis Shirk Date: Sat, 7 Jan 2006 17:25:35 +0000 Subject: [PATCH] Merged revisions 5017-5020,5022-5029 via svnmerge from svn://svn.gajim.org/gajim/trunk ........ r5017 | asterix | 2006-01-06 01:55:51 -0700 (Fri, 06 Jan 2006) | 2 lines use escape for pango markup ........ r5018 | asterix | 2006-01-06 02:21:39 -0700 (Fri, 06 Jan 2006) | 2 lines missing new contacts function ........ r5019 | asterix | 2006-01-06 11:03:07 -0700 (Fri, 06 Jan 2006) | 2 lines handle the click on toggle_gpg_encryption menuitem ........ r5020 | asterix | 2006-01-06 11:14:14 -0700 (Fri, 06 Jan 2006) | 2 lines use the saved size even if a chat window is already opened ........ r5022 | asterix | 2006-01-07 03:43:47 -0700 (Sat, 07 Jan 2006) | 2 lines we can now resume filetransfert ........ r5023 | asterix | 2006-01-07 03:56:31 -0700 (Sat, 07 Jan 2006) | 2 lines [Knuckles] Google E-Mail Notification ........ r5024 | asterix | 2006-01-07 04:02:16 -0700 (Sat, 07 Jan 2006) | 2 lines better string ........ r5025 | asterix | 2006-01-07 04:14:32 -0700 (Sat, 07 Jan 2006) | 2 lines fix a TB ........ r5026 | asterix | 2006-01-07 05:36:55 -0700 (Sat, 07 Jan 2006) | 2 lines we can now drag a file on a contact in the roster to send him a file ........ r5027 | asterix | 2006-01-07 06:26:28 -0700 (Sat, 07 Jan 2006) | 2 lines contact.groups is always a list, even if emtpy ........ r5028 | asterix | 2006-01-07 06:54:30 -0700 (Sat, 07 Jan 2006) | 2 lines make all buttons insensitive on a category row in disco ........ r5029 | asterix | 2006-01-07 07:19:25 -0700 (Sat, 07 Jan 2006) | 2 lines auto open groupchat configuration window when we create a new room ........ --- src/chat.py | 2 +- src/chat_control.py | 14 ++++++++++---- src/common/config.py | 1 + src/common/connection.py | 25 +++++++++++++++++++++++++ src/common/socks5.py | 13 +++++++++++-- src/common/xmpp/protocol.py | 1 + src/config.py | 7 +++++++ src/dialogs.py | 31 ++++++++++++++++++++++++++++++- src/disco.py | 3 +++ src/filetransfers_window.py | 20 ++++++++++++++++++++ src/gajim.py | 7 ++++++- src/groupchat_control.py | 2 ++ src/groupchat_window.py | 3 +++ src/gtkgui.glade | 20 ++++++++++++++++++++ src/notify.py | 3 +++ src/roster_window.py | 33 +++++++++++++++++++++++++++++---- src/tabbed_chat_window.py | 16 +++++++++++----- 17 files changed, 183 insertions(+), 18 deletions(-) diff --git a/src/chat.py b/src/chat.py index 24b2af4f3..0b784b390 100644 --- a/src/chat.py +++ b/src/chat.py @@ -289,7 +289,7 @@ class Chat: close_button.hide() nickname.set_max_width_chars(10) - lbl = self.names[jid] + lbl = gtkgui_helpers.escape_for_pango_markup(self.names[jid]) if num_unread: # if unread, text in the label becomes bold lbl = '' + unread + lbl + '' nickname.set_markup(lbl) diff --git a/src/chat_control.py b/src/chat_control.py index 8f7945185..468e7c2bf 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -661,7 +661,6 @@ class ChatControl(ChatControlBase): self._schedule_activity_timers() # Hook up signals - # FIXME: This does not seem to be working self.parent_win.window.connect('motion-notify-event', self._on_window_motion_notify) message_tv_buffer = self.msg_textview.get_buffer() @@ -673,6 +672,8 @@ class ChatControl(ChatControlBase): self._on_banner_eventbox_button_press_event) xm = gtk.glade.XML(GTKGUI_GLADE, 'avatar_eventbox', APP) xm.signal_autoconnect(self) + xm = gtk.glade.XML(GTKGUI_GLADE, 'gpg_togglebutton', APP) + xm.signal_autoconnect(self) if self.contact.jid in gajim.encrypted_chats[self.account]: self.xml.get_widget('gpg_togglebutton').set_active(True) @@ -833,8 +834,8 @@ class ChatControl(ChatControlBase): else: tb.set_sensitive(False) #we talk about a contact here - tt = _('%s has not broadcasted an OpenPGP key nor you have '\ - 'assigned one') % self.contact.name + tt = _('%s has not broadcast an OpenPGP key, nor has one been assigned') %\ + self.contact.name gtk.Tooltips().set_tip(self.xml.get_widget('gpg_eventbox'), tt) def send_message(self, message, keyID = '', chatstate = None): @@ -1013,7 +1014,7 @@ class ChatControl(ChatControlBase): if self.parent_win.get_active_control() != self: color = self.lighten_color(color) - label_str = self.contact.name + label_str = gtkgui_helpers.escape_for_pango_markup(self.contact.name) if num_unread: # if unread, text in the label becomes bold label_str = '' + unread + label_str + '' return (label_str, color) @@ -1400,3 +1401,8 @@ class ChatControl(ChatControlBase): def _on_contact_information_menuitem_clicked(self, widget): gajim.interface.roster.on_info(widget, self.contact, self.account) + def on_toggle_gpg_menuitem_activate(self, widget): + print "toggling" + jid = self.get_active_jid() + tb = self.xml.get_widget('gpg_togglebutton') + tb.set_active(not tb.get_active()) diff --git a/src/common/config.py b/src/common/config.py index 565a57878..6d6a25237 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -116,6 +116,7 @@ class Config: 'before_nickname': [ opt_str, '' ], 'after_nickname': [ opt_str, ':' ], 'send_os_info': [ opt_bool, True ], + 'notify_on_new_gmail_email': [ opt_bool, True ], 'usegpg': [ opt_bool, False ], 'use_gpg_agent': [ opt_bool, False ], 'change_roster_title': [ opt_bool, True, _('Add * and [n] in roster title?')], diff --git a/src/common/connection.py b/src/common/connection.py index fa693eb32..ec47355ef 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -316,6 +316,15 @@ class Connection: else: self.dispatch('VCARD', vcard) + def _gMailCB(self, con, gm): + """Called when we get notified of new mail messages in gmail account""" + if not gm.getTag('new-mail'): + return + if gm.getTag('new-mail').getNamespace() == common.xmpp.NS_GMAILNOTIFY: + jid = gajim.get_jid_from_account(self.name) + gajim.log.debug(('Notifying user of new gmail e-mail on %s.') % (jid)) + self.dispatch('GMAIL_NOTIFY', jid) + raise common.xmpp.NodeProcessed def _messageCB(self, con, msg): """Called when we receive a message""" @@ -871,6 +880,17 @@ class Connection: return file_props['receiver'] = self.get_full_jid(iq_obj) si = iq_obj.getTag('si') + file_tag = si.getTag('file') + range_tag = None + if file_tag: + range_tag = file_tag.getTag('range') + if range_tag: + offset = range_tag.getAttr('offset') + if offset: + file_props['offset'] = int(offset) + length = range_tag.getAttr('length') + if length: + file_props['length'] = int(length) feature = si.setTag('feature') if feature.getNamespace() != common.xmpp.NS_FEATURE: return @@ -1054,6 +1074,9 @@ class Connection: si.setNamespace(common.xmpp.NS_SI) file_tag = si.setTag('file') file_tag.setNamespace(common.xmpp.NS_FILE) + if file_props.has_key('offset') and file_props['offset']: + range_tag = file_tag.setTag('range') + range_tag.setAttr('offset', file_props['offset']) feature = si.setTag('feature') feature.setNamespace(common.xmpp.NS_FEATURE) _feature = common.xmpp.DataForm(typ='submit') @@ -1694,6 +1717,8 @@ class Connection: common.xmpp.NS_PRIVATE) con.RegisterHandler('iq', self._HttpAuthCB, 'get', common.xmpp.NS_HTTP_AUTH) + con.RegisterHandler('iq', self._gMailCB, 'set', + common.xmpp.NS_GMAILNOTIFY) con.RegisterHandler('iq', self._ErrorCB, 'error') con.RegisterHandler('iq', self._IqCB) con.RegisterHandler('iq', self._StanzaArrivedCB) diff --git a/src/common/socks5.py b/src/common/socks5.py index 38292c642..ff8e0a42c 100644 --- a/src/common/socks5.py +++ b/src/common/socks5.py @@ -366,6 +366,10 @@ class Socks5: if self.fd == None: try: self.fd = open(self.file_props['file-name'],'rb') + if self.file_props.has_key('offset') and self.file_props['offset']: + self.size = self.file_props['offset'] + self.fd.seek(self.size) + self.file_props['received-len'] = self.size except IOError, e: self.close_file() raise IOError, e @@ -383,11 +387,16 @@ class Socks5: if self.file_props.has_key('fd'): fd = self.file_props['fd'] else: - fd = open(self.file_props['file-name'],'wb') + offset = 0 + opt = 'wb' + if self.file_props.has_key('offset') and self.file_props['offset']: + offset = self.file_props['offset'] + opt = 'ab' + fd = open(self.file_props['file-name'], opt) self.file_props['fd'] = fd self.file_props['elapsed-time'] = 0 self.file_props['last-time'] = time.time() - self.file_props['received-len'] = 0 + self.file_props['received-len'] = offset return fd def rem_fd(self, fd): diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index bfe05b9d6..9d90fc6d9 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -63,6 +63,7 @@ NS_TIME ='jabber:iq:time' NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' NS_VACATION ='http://jabber.org/protocol/vacation' NS_VCARD ='vcard-temp' +NS_GMAILNOTIFY ='google:mail:notify' NS_VCARD_UPDATE =NS_VCARD+':x:update' NS_VERSION ='jabber:iq:version' NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027 diff --git a/src/config.py b/src/config.py index 97704445e..67d56538c 100644 --- a/src/config.py +++ b/src/config.py @@ -415,6 +415,10 @@ class PreferencesWindow: # send os info st = gajim.config.get('send_os_info') self.xml.get_widget('send_os_info_checkbutton').set_active(st) + + # Notify user of new gmail e-mail messages + st = gajim.config.get('notify_on_new_gmail_email') + self.xml.get_widget('notify_gmail_checkbutton').set_active(st) self.xml.signal_autoconnect(self) @@ -868,6 +872,9 @@ class PreferencesWindow: def on_send_os_info_checkbutton_toggled(self, widget): self.on_checkbutton_toggled(widget, 'send_os_info') + + def on_notify_gmail_checkbutton_toggled(self, widget): + self.on_checkbutton_toggled(widget, 'notify_on_new_gmail_email') def fill_msg_treeview(self): self.xml.get_widget('delete_msg_button').set_sensitive(False) diff --git a/src/dialogs.py b/src/dialogs.py index 6e0014c25..36b016379 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -562,7 +562,30 @@ class ConfirmationDialogCheck(ConfirmationDialog): def is_checked(self): ''' Get active state of the checkbutton ''' return self.checkbutton.get_active() - + +class FTOverwriteConfirmationDialog(ConfirmationDialog): + '''HIG compliant confirmation dialog to overwrite or resume a file transfert''' + def __init__(self, pritext, sectext='', propose_resume=True): + HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_CANCEL, + pritext, sectext) + + if propose_resume: + b = gtk.Button('', gtk.STOCK_REFRESH) + align = b.get_children()[0] + hbox = align.get_children()[0] + label = hbox.get_children()[1] + label.set_text('_Resume') + label.set_use_underline(True) + self.add_action_widget(b, 100) + + b = gtk.Button('', gtk.STOCK_SAVE_AS) + align = b.get_children()[0] + hbox = align.get_children()[0] + label = hbox.get_children()[1] + label.set_text('Re_place') + label.set_use_underline(True) + self.add_action_widget(b, 200) + class InputDialog: '''Class for Input dialog''' def __init__(self, title, label_str, input_str = None, is_modal = True, @@ -886,6 +909,12 @@ class PopupNotificationWindow: else: txt = '' event_description_label.set_markup('%s' % txt) + elif event_type == _('New E-mail'): + dodgerblue = gtk.gdk.color_parse('dodgerblue') + close_button.modify_bg(gtk.STATE_NORMAL, dodgerblue) + eventbox.modify_bg(gtk.STATE_NORMAL, dodgerblue) + txt = _('You have new E-mail on %s.') % (jid) + event_description_label.set_markup('%s' % txt) # position the window to bottom-right of screen window_width, self.window_height = self.window.get_size() gajim.interface.roster.popups_notification_height += self.window_height diff --git a/src/disco.py b/src/disco.py index 81823d254..e342e0895 100644 --- a/src/disco.py +++ b/src/disco.py @@ -1166,6 +1166,9 @@ class ToplevelAgentBrowser(AgentBrowser): model, iter = self.window.services_treeview.get_selection().get_selected() if not iter: return + if not model[iter][0]: + # We're on a category row + return if model[iter][4] != 0: # We don't have the info (yet) # It's either unknown or a transport, register button should be active diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py index 540c0f335..8742595b8 100644 --- a/src/filetransfers_window.py +++ b/src/filetransfers_window.py @@ -300,6 +300,24 @@ _('Connection with peer cannot be established.')) gajim.connections[account].send_file_request(file_props) return True + def confirm_overwrite_cb(self, dialog, file_props): + file_path = dialog.get_filename() + file_path = file_path.decode('utf-8') + if os.path.exists(file_path): + stat = os.stat(file_path) + dl_size = stat.st_size + file_size = file_props['size'] + dl_finished = dl_size >= file_size + dialog = dialogs.FTOverwriteConfirmationDialog( + _('This file already exists'), _('What do you want to do?'), + not dl_finished) + response = dialog.get_response() + if response == gtk.RESPONSE_CANCEL: + return gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN + elif response == 100: + file_props['offset'] = dl_size + return gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME + def show_file_request(self, account, contact, file_props): ''' show dialog asking for comfirmation and store location of new file requested by a contact''' @@ -326,6 +344,8 @@ _('Connection with peer cannot be established.')) gtk28 = False if gtk.gtk_version >= (2, 8, 0) and gtk.pygtk_version >= (2, 8, 0): dialog.props.do_overwrite_confirmation = True + dialog.connect('confirm-overwrite', self.confirm_overwrite_cb, + file_props) gtk28 = True if last_save_dir and os.path.isdir(last_save_dir): dialog.set_current_folder(last_save_dir) diff --git a/src/gajim.py b/src/gajim.py index adca48e09..c9420852f 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -420,7 +420,7 @@ class Interface: return if gajim.config.get('ignore_unknown_contacts') and \ - not gajim.contacts[account].has_key(jid): + not gajim.contacts.get_contact(account, jid): return # Handle chat states @@ -807,6 +807,10 @@ class Interface: if gajim.show_notification(account): notify.notify(_('File Transfer Error'), jid, account, 'file-send-error', file_props) + + def handle_event_gmail_notify(self, account, jid): + if gajim.config.get('notify_on_new_gmail_email'): + notify.notify(_('New E-mail'), jid, account) def add_event(self, account, jid, typ, args): '''add an event to the awaiting_events var''' @@ -1196,6 +1200,7 @@ class Interface: 'BOOKMARKS': self.handle_event_bookmarks, 'CON_TYPE': self.handle_event_con_type, 'FILE_REQUEST': self.handle_event_file_request, + 'GMAIL_NOTIFY': self.handle_event_gmail_notify, 'FILE_REQUEST_ERROR': self.handle_event_file_request_error, 'FILE_SEND_ERROR': self.handle_event_file_send_error, 'STANZA_ARRIVED': self.handle_event_stanza_arrived, diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 9217b34d2..9fa011b2c 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -668,6 +668,8 @@ class GroupchatControl(ChatControlBase): if not iter: iter = self.add_contact_to_roster(nick, show, role, affiliation, status, jid) + if statusCode == '201': # We just created the room + gajim.connections[self.account].request_gc_config(room_jid) else: actual_role = self.get_role(nick) if role != actual_role: diff --git a/src/groupchat_window.py b/src/groupchat_window.py index 63995279a..6dd36f42a 100644 --- a/src/groupchat_window.py +++ b/src/groupchat_window.py @@ -492,6 +492,9 @@ class GroupchatWindow(chat.Chat): if not iter: iter = self.add_contact_to_roster(room_jid, nick, show, role, affiliation, status, jid) + if statusCode == '201': # We just created the room + gajim.connections[self.account].request_gc_config(room_jid) + else: actual_role = self.get_role(room_jid, nick) if role != actual_role: diff --git a/src/gtkgui.glade b/src/gtkgui.glade index 0db888e16..a0ea85e56 100644 --- a/src/gtkgui.glade +++ b/src/gtkgui.glade @@ -6208,6 +6208,26 @@ Custom False + + + + True + True + Notify on new _Gmail e-mail (GoogleTalk users) + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + False + False + + diff --git a/src/notify.py b/src/notify.py index 3cb8dd037..24564c399 100644 --- a/src/notify.py +++ b/src/notify.py @@ -197,6 +197,9 @@ class DesktopNotification: img = 'ft_stopped.png' else: txt = '' + elif event_type == _('New Email'): + txt = _('You have new E-mail on %s.') % (jid) + ntype = 'gmail.notify' else: # defaul failsafe values img = 'chat_msg_recv.png' # img to display diff --git a/src/roster_window.py b/src/roster_window.py index 2f65bf045..3872b2644 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -1259,6 +1259,8 @@ class RosterWindow: gajim.connections[account].request_subscription(jid, txt) if group: group = [group] + else: + group = [] contact = gajim.contacts.get_contact_with_highest_priority(account, jid) if not contact: keyID = '' @@ -2476,10 +2478,30 @@ _('If "%s" accepts this request you will know his or her status.') % jid) if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2 \ and path_dest[1] == 0: # dropped before the first group return + iter_dest = model.get_iter(path_dest) + + if info == self.TARGET_TYPE_URI_LIST: + # User dropped a file on the roster + if len(path_dest) < 3: + return + account = model[iter_dest][C_ACCOUNT].decode('utf-8') + jid = model[iter_dest][C_JID].decode('utf-8') + type_dest = model[iter_dest][C_TYPE].decode('utf-8') + if type_dest != 'contact': + return + c = gajim.contacts.get_contact_with_highest_priority(account, jid) + + uri = data.strip() + uri_splitted = uri.split() # we may have more than one file dropped + for uri in uri_splitted: + path = helpers.get_file_path_from_dnd_dropped_uri(uri) + if os.path.isfile(path): # is it file? + gajim.interface.instances['file_transfers'].send_file(account, c, + path) + return if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2: # dropped before a group : we drop it in the previous group - path_dest = (path_dest[1], path_dest[1]-1) - iter_dest = model.get_iter(path_dest) + path_dest = (path_dest[0], path_dest[1]-1) iter_source = treeview.get_selection().get_selected()[1] path_source = model.get_path(iter_source) if len(path_dest) == 1: # dropped on an account @@ -2730,10 +2752,13 @@ _('If "%s" accepts this request you will know his or her status.') % jid) self.tree.set_expander_column(col) #signals + self.TARGET_TYPE_URI_LIST = 80 TARGETS = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)] - self.tree.enable_model_drag_source( gtk.gdk.BUTTON1_MASK, TARGETS, + TARGETS2 = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0), + ('text/uri-list', 0, self.TARGET_TYPE_URI_LIST)] + self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, TARGETS, gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_COPY) - self.tree.enable_model_drag_dest(TARGETS, gtk.gdk.ACTION_DEFAULT) + self.tree.enable_model_drag_dest(TARGETS2, gtk.gdk.ACTION_DEFAULT) self.tree.connect('drag_data_get', self.drag_data_get_data) self.tree.connect('drag_data_received', self.drag_data_received_data) self.xml.signal_autoconnect(self) diff --git a/src/tabbed_chat_window.py b/src/tabbed_chat_window.py index 90327b366..463ccb720 100644 --- a/src/tabbed_chat_window.py +++ b/src/tabbed_chat_window.py @@ -84,11 +84,12 @@ class TabbedChatWindow(chat.Chat): self.xml.signal_autoconnect(signal_dict) - if gajim.config.get('saveposition') and \ - not gtkgui_helpers.one_window_opened('chats'): - # get window position and size from config - gtkgui_helpers.move_window(self.window, gajim.config.get('chat-x-position'), - gajim.config.get('chat-y-position')) + if gajim.config.get('saveposition'): + if gtkgui_helpers.one_window_opened('chats'): + # get window position and size from config + gtkgui_helpers.move_window(self.window, gajim.config.get('chat-x-position'), + gajim.config.get('chat-y-position')) + # Even if one is already opened we can use the saved size gtkgui_helpers.resize_window(self.window, gajim.config.get('chat-width'), gajim.config.get('chat-height')) @@ -451,6 +452,11 @@ class TabbedChatWindow(chat.Chat): # send the message self.send_message(message) + def on_toggle_gpg_menuitem_activate(self, widget): + jid = self.get_active_jid() + tb = self.xmls[jid].get_widget('gpg_togglebutton') + tb.set_active(not tb.get_active()) + def remove_tab(self, jid): if time.time() - gajim.last_message_time[self.account][jid] < 2: dialog = dialogs.ConfirmationDialog(