diff options
Diffstat (limited to 'src/tabs.py')
-rw-r--r-- | src/tabs.py | 288 |
1 files changed, 220 insertions, 68 deletions
diff --git a/src/tabs.py b/src/tabs.py index fceacfe7..df5a9b90 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -44,7 +44,7 @@ from sleekxmpp.xmlstream import matcher from sleekxmpp.xmlstream.handler import Callback from config import config from roster import RosterGroup, roster -from contact import Contact +from contact import Contact, Resource from text_buffer import TextBuffer from user import User from os import getenv, path @@ -211,7 +211,7 @@ class Tab(object): # complete the command's name words = ['/%s'% (name) for name in self.core.commands] +\ ['/%s' % (name) for name in self.commands] - the_input.auto_completion(words, '') + the_input.auto_completion(words, '', quotify=False) # Do not try to cycle command completion if there was only # one possibily. The next tab will complete the argument. # Otherwise we would need to add a useless space before being @@ -269,6 +269,12 @@ class Tab(object): """ return self.__class__.__name__ + def get_nick(self): + """ + Get the nick of the tab (defaults to its name) + """ + return self.get_name() + def get_text_window(self): """ Returns the principal TextWin window, if there's one @@ -415,7 +421,7 @@ class ChatTab(Tab): for word in txt.split(): if len(word) >= 4 and word not in words: words.append(word) - self.input.auto_completion(words, ' ') + self.input.auto_completion(words, ' ', quotify=False) def on_enter(self): txt = self.input.key_enter() @@ -629,23 +635,23 @@ class MucTab(ChatTab): if user.nick != self.own_nick] contact_list = [jid for jid in roster.jids()] userlist.extend(contact_list) - return the_input.auto_completion(userlist, '') + return the_input.auto_completion(userlist, '', quotify=False) def completion_nick(self, the_input): """Completion for /nick""" nicks = [os.environ.get('USER'), config.get('default_nick', ''), self.core.get_bookmark_nickname(self.get_name())] while nicks.count(''): nicks.remove('') - return the_input.auto_completion(nicks, '') + return the_input.auto_completion(nicks, '', quotify=False) def completion_recolor(self, the_input): - return the_input.auto_completion(['random'], '') + return the_input.auto_completion(['random'], '', quotify=False) def completion_ignore(self, the_input): """Completion for /ignore""" userlist = [user.nick for user in self.users] userlist.remove(self.own_nick) - return the_input.auto_completion(userlist, '') + return the_input.auto_completion(userlist, '', quotify=False) def completion_role(self, the_input): """Completion for /role""" @@ -693,18 +699,20 @@ class MucTab(ChatTab): self.input.refresh() def command_info(self, arg): - args = common.shell_split(arg) - if len(args) != 1: - return self.core.information("Info command takes only 1 argument") - user = self.get_user_by_name(args[0]) + """ + /info <nick> + """ + if not arg: + return self.core.command_help('info') + user = self.get_user_by_name(arg) if not user: - return self.core.information("Unknown user: %s" % args[0]) - info = '%s%s: show: %s, affiliation: %s, role: %s%s' % (args[0], - ' (%s)' % user.jid if user.jid else '', - user.show or 'Available', - user.role or 'None', - user.affiliation or 'None', - '\n%s' % user.status if user.status else '') + return self.core.information("Unknown user: %s" % arg) + info = '%s%s: show: %s, affiliation: %s, role: %s%s' % (arg, + ' (%s)' % user.jid if user.jid else '', + user.show or 'Available', + user.role or 'None', + user.affiliation or 'None', + '\n%s' % user.status if user.status else '') self.core.information(info, 'Info') def command_configure(self, arg): @@ -738,9 +746,10 @@ class MucTab(ChatTab): def command_recolor(self, arg): """ + /recolor [random] Re-assign color to the participants of the room """ - args = common.shell_split(arg) + arg = arg.strip() compare_users = lambda x: x.last_talked users = list(self.users) sorted_users = sorted(users, key=compare_users, reverse=True) @@ -750,9 +759,8 @@ class MucTab(ChatTab): sorted_users.remove(user) user.color = get_theme().COLOR_OWN_NICK colors = list(get_theme().LIST_COLOR_NICKNAMES) - if len(args) >= 1: - if args[0] == 'random': - random.shuffle(colors) + if arg and arg == 'random': + random.shuffle(colors) for i, user in enumerate(sorted_users): user.color = colors[i % len(colors)] self.text_win.rebuild_everything(self._text_buffer) @@ -768,30 +776,29 @@ class MucTab(ChatTab): if not res: return self.core.information('Could not get the software version from %s' % (jid,), 'Warning') version = '%s is running %s version %s on %s' % (jid, - res.get('name') or _('an unknown software'), - res.get('version') or _('unknown'), - res.get('os') or _('on an unknown platform')) + res.get('name') or _('an unknown software'), + res.get('version') or _('unknown'), + res.get('os') or _('on an unknown platform')) self.core.information(version, 'Info') - args = common.shell_split(arg) - if len(args) < 1: - return - if args[0] in [user.nick for user in self.users]: - jid = self.name + '/' + args[0] + if not arg: + return self.core.command_help('version') + if arg in [user.nick for user in self.users]: + jid = JID(self.name) + jid.resource = arg else: - jid = args[0] + jid = JID(arg) self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback) def command_nick(self, arg): """ /nick <nickname> """ - args = common.shell_split(arg) - if len(args) != 1: - return - nick = args[0] + if not arg: + return self.core.command_help('nick') + nick = arg if not self.joined: - return + return self.core.information('/nick only works in joined rooms', 'Info') current_status = self.core.get_status() muc.change_nick(self.core.xmpp, self.name, nick, current_status.message, current_status.show) @@ -799,11 +806,7 @@ class MucTab(ChatTab): """ /part [msg] """ - args = arg.split() - if len(args): - arg = ' '.join(args) - else: - arg = None + arg = arg.strip() if self.joined: self.disconnect() muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, arg) @@ -823,7 +826,6 @@ class MucTab(ChatTab): self.command_part(arg) self.core.close_tab() - def command_query(self, arg): """ /query <nick> [message] @@ -911,7 +913,7 @@ class MucTab(ChatTab): /kick <nick> [reason] """ args = common.shell_split(arg) - if not len(args): + if not args: self.core.command_help('kick') else: if len(args) > 1: @@ -988,11 +990,10 @@ class MucTab(ChatTab): """ /ignore <nick> """ - args = common.shell_split(arg) - if len(args) != 1: + if not arg: self.core.command_help('ignore') return - nick = args[0] + nick = arg user = self.get_user_by_name(nick) if not user: self.core.information(_('%s is not in the room') % nick) @@ -1006,11 +1007,10 @@ class MucTab(ChatTab): """ /unignore <nick> """ - args = common.shell_split(arg) - if len(args) != 1: + if not arg: self.core.command_help('unignore') return - nick = args[0] + nick = arg user = self.get_user_by_name(nick) if not user: self.core.information(_('%s is not in the room') % nick) @@ -1021,7 +1021,7 @@ class MucTab(ChatTab): self.core.information(_('%s is now unignored') % nick) def completion_unignore(self, the_input): - return the_input.auto_completion([user.nick for user in self.ignores], ' ') + return the_input.auto_completion([user.nick for user in self.ignores], ' ', quotify=False) def resize(self): """ @@ -1092,6 +1092,11 @@ class MucTab(ChatTab): def get_name(self): return self.name + def get_nick(self): + if config.getl('show_muc_jid', 'true') == 'false': + return JID(self.name).user + return self.name + def get_text_window(self): return self.text_win @@ -1591,6 +1596,9 @@ class PrivateTab(ChatTab): def get_name(self): return self.name + def get_nick(self): + return JID(self.name).resource + def on_input(self, key, raw): if not raw and key in self.key_func: self.key_func[key]() @@ -1702,6 +1710,9 @@ class RosterInfoTab(Tab): self.key_func["M-[1;5B"] = self.move_cursor_to_next_group self.key_func["M-[1;5A"] = self.move_cursor_to_prev_group self.key_func["o"] = self.toggle_offline_show + self.key_func["v"] = self.get_contact_version + self.key_func["i"] = self.show_contact_info + self.key_func["n"] = self.change_contact_name self.key_func["s"] = self.start_search self.key_func["S"] = self.start_search_slow self.commands['deny'] = (self.command_deny, _("Usage: /deny [jid]\nDeny: Deny your presence to the provided JID (or the selected contact in your roster), who is asking you to be in his/here roster."), self.completion_deny) @@ -1709,6 +1720,7 @@ class RosterInfoTab(Tab): self.commands['add'] = (self.command_add, _("Usage: /add <jid>\nAdd: Add the specified JID to your roster, ask him to allow you to see his presence, and allow him to see your presence."), None) self.commands['name'] = (self.command_name, _("Usage: /name <jid> <name>\nSet the given JID's name."), self.completion_name) self.commands['groupadd'] = (self.command_groupadd, _("Usage: /groupadd <jid> <group>\nAdd the given JID to the given group."), self.completion_groupadd) + self.commands['groupmove'] = (self.command_groupmove, _("Usage: /groupchange <jid> <old group> <new group>\nMoves the given JID from the old group to the new group."), self.completion_groupmove) self.commands['groupremove'] = (self.command_groupremove, _("Usage: /groupremove <jid> <group>\nRemove the given JID from the given group."), self.completion_groupremove) self.commands['remove'] = (self.command_remove, _("Usage: /remove [jid]\nRemove: Remove the specified JID from your roster. This wil unsubscribe you from its presence, cancel its subscription to yours, and remove the item from your roster."), self.completion_remove) self.commands['export'] = (self.command_export, _("Usage: /export [/path/to/file]\nExport: Export your contacts into /path/to/file if specified, or $HOME/poezio_contacts if not."), self.completion_file) @@ -1769,12 +1781,12 @@ class RosterInfoTab(Tab): self.core.information_win.rebuild_everything(self.core.information_buffer) self.refresh() - def command_deny(self, args): + def command_deny(self, arg): """ + /deny [jid] Denies a JID from our roster """ - args = args.split() - if not args: + if not arg: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -1782,7 +1794,10 @@ class RosterInfoTab(Tab): self.core.information('No subscription to deny') return else: - jid = JID(args[0]).bare + jid = JID(arg).bare + if not jid in [jid for jid in roster.jids()]: + self.core.information('No subscription to deny') + return contact = roster[jid] if contact: @@ -1801,13 +1816,13 @@ class RosterInfoTab(Tab): return self.core.information('Already subscribed.', 'Roster') roster.add(jid) - def command_name(self, args): + def command_name(self, arg): """ Set a name for the specified JID in your roster """ - args = args.split(None, 1) - if len(args) < 1: - return + args = common.shell_split(arg) + if not args: + return self.core.command_help('name') jid = JID(args[0]).bare name = args[1] if len(args) == 2 else '' @@ -1852,11 +1867,57 @@ class RosterInfoTab(Tab): if self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription): roster.update_contact_groups(jid) + def command_groupmove(self, arg): + """ + Remove the specified JID from the first specified group and add it to the second one + """ + args = common.shell_split(arg) + if len(args) != 3: + return self.core.command_help('groupmove') + jid = JID(args[0]).bare + group_from = args[1] + group_to = args[2] + + contact = roster[jid.bare] + if not contact: + self.core.information(_('No such JID in roster'), 'Error') + return + + new_groups = set(contact.groups) + if 'none' in new_groups: + new_groups.remove('none') + + if group_to == 'none' or group_from == 'none': + self.core.information(_('"none" is not a group.'), 'Error') + return + + if group_from not in new_groups: + self.core.information(_('JID not in first group'), 'Error') + return + + if group_to in new_groups: + self.core.information(_('JID already in second group'), 'Error') + return + + if group_to == group_from: + self.core.information(_('The groups are the same.'), 'Error') + return + + new_groups.add(group_to) + if 'none' in new_groups: + new_groups.remove('none') + + new_groups.remove(group_from) + name = contact.name + subscription = contact.subscription + if self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription): + roster.edit_groups_of_contact(contact, new_groups) + def command_groupremove(self, args): """ - Remove the specified JID to the specified group + Remove the specified JID from the specified group """ - args = args.split(None, 1) + args = common.shell_split(args) if len(args) != 2: return jid = JID(args[0]).bare @@ -1956,7 +2017,7 @@ class RosterInfoTab(Tab): def completion_name(self, the_input): text = the_input.get_text() - n = len(text.split()) + n = len(common.shell_split(text)) if text.endswith(' '): n += 1 @@ -1967,7 +2028,7 @@ class RosterInfoTab(Tab): def completion_groupadd(self, the_input): text = the_input.get_text() - n = len(text.split()) + n = len(common.shell_split(text)) if text.endswith(' '): n += 1 @@ -1979,9 +2040,32 @@ class RosterInfoTab(Tab): return the_input.auto_completion(groups, '') return False + def completion_groupmove(self, the_input): + text = the_input.get_text() + args = common.shell_split(text) + n = len(args) + if text.endswith(' '): + n += 1 + + if n == 2: + jids = [jid for jid in roster.jids()] + return the_input.auto_completion(jids, '') + elif n == 3: + contact = roster[args[1]] + if not contact: + return False + groups = list(contact.groups) + if 'none' in groups: + groups.remove('none') + return the_input.auto_completion(groups, '') + elif n == 4: + groups = [group for group in roster.groups] + return the_input.auto_completion(groups, '') + return False + def completion_groupremove(self, the_input): text = the_input.get_text() - args = text.split() + args = common.shell_split(text) n = len(args) if text.endswith(' '): n += 1 @@ -2008,14 +2092,13 @@ class RosterInfoTab(Tab): """ jids = [str(contact.bare_jid) for contact in roster.contacts.values()\ if contact.pending_in] - return the_input.auto_completion(jids, '') + return the_input.auto_completion(jids, '', quotify=False) - def command_accept(self, args): + def command_accept(self, arg): """ Accept a JID from in roster. Authorize it AND subscribe to it """ - args = args.split() - if not args: + if not arg: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -2023,7 +2106,7 @@ class RosterInfoTab(Tab): self.core.information('No subscription to accept') return else: - jid = JID(args[0]).bare + jid = JID(arg).bare contact = roster[jid] if contact is None: return @@ -2159,6 +2242,67 @@ class RosterInfoTab(Tab): selected_row.toggle_folded() return True + def get_contact_version(self): + """ + Show the versions of the resource(s) currently selected + """ + selected_row = self.roster_win.get_selected_row() + if isinstance(selected_row, Contact): + for resource in selected_row.resources: + self.core.command_version(str(resource.jid)) + elif isinstance(selected_row, Resource): + self.core.command_version(str(selected_row.jid)) + else: + self.core.information('Nothing to get versions from', 'Info') + + def show_contact_info(self): + """ + Show the contact info (resource number, status, presence, etc) + when 'i' is pressed. + """ + selected_row = self.roster_win.get_selected_row() + if isinstance(selected_row, Contact): + cont = selected_row + res = selected_row.get_highest_priority_resource() + msg = 'Contact: %s (%s)\n%s connected resource%s\nCurrent status: %s' % ( + cont.bare_jid, + res.presence if res else 'unavailable', + len(cont), + '' if len(cont) == 1 else 's', + res.status if res else '',) + elif isinstance(selected_row, Resource): + res = selected_row + msg = 'Resource: %s (%s)\nCurrent status: %s' % ( + res.jid, + res.presence, + res.status,) + elif isinstance(selected_row, RosterGroup): + rg = selected_row + msg = 'Group: %s [%s/%s] contacts online' % ( + rg.name, + rg.get_nb_connected_contacts(), + len(rg),) + else: + msg = None + if msg: + self.core.information(msg, 'Info') + + def change_contact_name(self): + """ + Auto-fill a /name command when 'n' is pressed + """ + selected_row = self.roster_win.get_selected_row() + if isinstance(selected_row, Contact): + jid = selected_row.bare_jid + elif isinstance(selected_row, Resource): + jid = JID(selected_row.jid).bare + else: + return + self.on_slash() + self.input.text = '/name "%s" ' % jid + self.input.key_end() + self.input.refresh() + def start_search(self): """ Start the search. The input should appear with a short instruction @@ -2347,6 +2491,14 @@ class ConversationTab(ChatTab): def get_name(self): return self.name + def get_nick(self): + jid = JID(self.name) + contact = roster[jid.bare] + if contact: + return contact.name or jid.user + else: + return jid.user + def on_input(self, key, raw): if not raw and key in self.key_func: self.key_func[key]() |