summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/default_config.cfg22
-rw-r--r--doc/en/configure.txt27
-rw-r--r--doc/en/usage.txt12
-rw-r--r--src/bookmark.py2
-rw-r--r--src/config.py6
-rw-r--r--src/connection.py4
-rw-r--r--src/contact.py5
-rw-r--r--src/core.py79
-rw-r--r--src/plugin_manager.py4
-rw-r--r--src/tabs.py288
-rw-r--r--src/windows.py30
11 files changed, 377 insertions, 102 deletions
diff --git a/data/default_config.cfg b/data/default_config.cfg
index dc5cc4d6..2c5f0d81 100644
--- a/data/default_config.cfg
+++ b/data/default_config.cfg
@@ -199,6 +199,28 @@ plugins_autoload =
# with no activity, set to true. Else, set to false
show_inactive_tabs = true
+# If you want to show the tab names in the bottom tab bar, set this to true
+show_tab_names = false
+
+# If you want to disable the numbers in the bottom tab bar, set this to false
+# If show_tab_names and show_tab_numbers are both false, the numbers will be
+# shown
+show_tab_numbers = true
+
+# Use the contact name, the nick in the MUC instead of the full JID to
+# display the tab if set to true.
+use_tab_nicks = true
+
+# If set to false, poezio will only display the user part of the JID (before
+# the @) when displaying the MUC tab name.
+show_muc_jid = true
+
+# If this option is set to false, the roster will not show the contact JIDs
+# when that is possible.
+# e.g. instead of: toto (toto@example.org) (2)
+# poezio will only show: toto (2)
+show_roster_jids = true
+
# The terminal can beep on various event. Put the event you want in a list
# (separated by spaces).
# The events can be
diff --git a/doc/en/configure.txt b/doc/en/configure.txt
index b6fe41bf..36a49206 100644
--- a/doc/en/configure.txt
+++ b/doc/en/configure.txt
@@ -247,6 +247,33 @@ section of this documentation.
If you want to show all the tabs in the Tab bar, even those
with no activity, set to true. Else, set to false
+*show_tab_names*:: false
+
+ If you want to show the tab name in the bottom Tab bar, set this to true.
+
+*show_tab_numbers*:: true
+
+ If you want to disable the numbers in the bottom Tab bar, set this to false.
+ Note that if both show_tab_names and show_tab_numbers are set to false, the
+ numbers will still be displayed.
+
+*use_tab_nicks*:: true
+
+ The tabs have a name, and a nick, which is, for a contact, its name in the
+ roster, or for a private conversation, the nickname in the MUC. Set this to
+ true if you want to have them shown instead of the jid of the contact.
+
+*show_muc_jid*:: true
+
+ Set this to false if you want to display only the *user* part of the MUC
+ jid. E.g. if you have poezio@muc.poezio.eu, it will be displayed as
+ `poezio`. This will be used only if use_tab_nicks is set to true.
+
+*show_roster_jids*:: true
+
+ Set this to false if you want to hide the JIDs in the roster (and keep only
+ the contact names). If there is no contact name, the JID will still be
+ displayed.
*beep_on*:: highlight private
diff --git a/doc/en/usage.txt b/doc/en/usage.txt
index 46e88a7e..d843a173 100644
--- a/doc/en/usage.txt
+++ b/doc/en/usage.txt
@@ -151,6 +151,14 @@ The commands described in this page are shown like this:
You can get the same help as below with the _/help_ command.
+NOTE: Use command parameters like this:
+
+* Do not use quotes if they are unnecessary (words without special chars)
+* If the command takes several agrguments, you need to put quotes around
+ arguments containing special chars such as backslashes or quotes
+* If the command always takes only one argument, then do not use quotes even
+ for words containing special chars
+
Global commands
~~~~~~~~~~~~~~~
@@ -364,6 +372,10 @@ Roster tab commands
*/groupadd <jid> <group>*:: Add the given JID to the given group (if the group
does not exist, it will be created).
+*/groupmove <jid> <old_group> <new_group>*:: Move the given JID from one group
+ to another (the JID has to be in the first group, and the new group may not
+ exist).
+
*/groupremove <jid> <group>*:: Remove the given JID from the given group (if
the group is empty after that, it will get deleted).
diff --git a/src/bookmark.py b/src/bookmark.py
index 38979697..95f42dd8 100644
--- a/src/bookmark.py
+++ b/src/bookmark.py
@@ -124,7 +124,7 @@ def save_privatexml(xmpp):
""""Save the remote bookmarks with privatexml."""
xmpp.plugin['xep_0048'].set_bookmarks_old(stanza_storage('privatexml'))
-def save_remote(xmpp, method="privatexml"):
+def save_remote(xmpp, method=preferred):
"""Save the remote bookmarks."""
method = "privatexml" if method != 'pep' else 'pep'
diff --git a/src/config.py b/src/config.py
index b4b07491..e99572d7 100644
--- a/src/config.py
+++ b/src/config.py
@@ -50,6 +50,12 @@ class Config(RawConfigParser):
return default
return res
+ def getl(self, option, default, section=DEFSECTION):
+ """
+ get a value and return it lowercase
+ """
+ return self.get(option, default, section).lower()
+
def get_by_tabname(self, option, default, tabname, fallback=True):
"""
Try to get the value for the option. First we look in
diff --git a/src/connection.py b/src/connection.py
index e4e2cfb2..2c51a78c 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -62,7 +62,9 @@ class Connection(sleekxmpp.ClientXMPP):
'version':'0.7.5-dev'}
if config.get('send_os_info', 'true') == 'true':
info['os'] = common.get_os_info()
- self.register_plugin('xep_0092', pconfig=info)
+ else:
+ info = {'name': '', 'version': ''}
+ self.register_plugin('xep_0092', pconfig=info)
if config.get('send_time', 'true') == 'true':
self.register_plugin('xep_0202')
self.register_plugin('xep_0224')
diff --git a/src/contact.py b/src/contact.py
index 69dfb984..48f5d751 100644
--- a/src/contact.py
+++ b/src/contact.py
@@ -62,6 +62,11 @@ class Contact(object):
return self.__item['groups'] or ['none']
@property
+ def resources(self):
+ """Resources of the contact"""
+ return self._resources
+
+ @property
def bare_jid(self):
"""The bare_jid or the contact"""
return self.__item.jid
diff --git a/src/core.py b/src/core.py
index da9ff4da..f7447863 100644
--- a/src/core.py
+++ b/src/core.py
@@ -1359,14 +1359,14 @@ class Core(object):
/help <command_name>
"""
args = arg.split()
- if len(args) == 0:
+ if not args:
msg = _('Available commands are: ')
for command in self.commands:
msg += "%s " % command
for command in self.current_tab().commands:
msg += "%s " % command
msg += _("\nType /help <command_name> to know what each command does")
- if len(args) >= 1:
+ if args:
if args[0] in self.commands:
msg = self.commands[args[0]][1]
elif args[0] in self.current_tab().commands:
@@ -1377,7 +1377,7 @@ class Core(object):
def completion_help(self, the_input):
commands = list(self.commands.keys()) + list(self.current_tab().commands.keys())
- return the_input.auto_completion(commands, ' ')
+ return the_input.auto_completion(commands, ' ', quotify=False)
def command_status(self, arg):
"""
@@ -1521,14 +1521,14 @@ class Core(object):
"""
/message <jid> [message]
"""
- args = arg.split()
+ args = common.shell_split(arg)
if len(args) < 1:
self.command_help('message')
return
jid = args[0]
tab = self.open_conversation_window(jid, focus=True)
if len(args) > 1:
- tab.command_say(arg.strip()[len(jid)+1:])
+ tab.command_say(args[1])
def command_version(self, arg):
"""
@@ -1560,16 +1560,15 @@ class Core(object):
/list <server>
Opens a MucListTab containing the list of the room in the specified server
"""
- args = arg.split()
- if len(args) > 1:
- self.command_help('list')
- return
- elif len(args) == 0:
+ arg = arg.split()
+ if len(arg) > 1:
+ return self.command_help('list')
+ elif arg:
+ server = JID(arg[0]).server
+ else:
if not isinstance(self.current_tab(), tabs.MucTab):
return self.information('Please provide a server', 'Error')
server = JID(self.current_tab().get_name()).server
- else:
- server = arg.strip()
list_tab = tabs.MucListTab(server)
self.add_tab(list_tab, True)
self.xmpp.plugin['xep_0030'].get_items(jid=server, block=False, callback=list_tab.on_muc_list_item_received)
@@ -1577,7 +1576,7 @@ class Core(object):
def command_theme(self, arg):
"""/theme <theme name>"""
args = arg.split()
- if len(args) == 1:
+ if args:
self.command_set('theme %s' % (args[0],))
warning = theming.reload_theme()
if warning:
@@ -1600,20 +1599,20 @@ class Core(object):
theme_files = [name[:-3] for name in names if name.endswith('.py')]
if not 'default' in theme_files:
theme_files.append('default')
- return the_input.auto_completion(theme_files, '')
+ return the_input.auto_completion(theme_files, '', quotify=False)
def command_win(self, arg):
"""
/win <number>
"""
- args = arg.split()
- if len(args) != 1:
+ arg = arg.strip()
+ if not arg:
self.command_help('win')
return
try:
- nb = int(args[0])
+ nb = int(arg.split()[0])
except ValueError:
- nb = arg.strip()
+ nb = arg
if self.current_tab().nb == nb:
return
self.previous_tab_nb = self.current_tab().nb
@@ -1636,7 +1635,7 @@ class Core(object):
def completion_win(self, the_input):
l = [JID(tab.get_name()).user for tab in self.tabs]
l.remove('')
- return the_input.auto_completion(l, ' ')
+ return the_input.auto_completion(l, ' ', quotify=False)
def completion_join(self, the_input):
"""
@@ -1703,6 +1702,7 @@ class Core(object):
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
return the_input.auto_completion(jids_list, '')
muc_list = [tab.get_name() for tab in self.tabs if isinstance(tab, tabs.MucTab)]
+ muc_list.append('*')
return the_input.auto_completion(muc_list, '')
def completion_bookmark(self, the_input):
@@ -1737,6 +1737,7 @@ class Core(object):
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
return the_input.auto_completion(jids_list, '')
muc_list = [tab.get_name() for tab in self.tabs if isinstance(tab, tabs.MucTab)]
+ muc_list.append('*')
return the_input.auto_completion(muc_list, '')
def completion_version(self, the_input):
@@ -1744,7 +1745,7 @@ class Core(object):
n = len(the_input.get_text().split())
if n > 2 or (n == 2 and the_input.get_text().endswith(' ')):
return
- return the_input.auto_completion([jid for jid in roster.jids()], '')
+ return the_input.auto_completion([jid for jid in roster.jids()], '', quotify=False)
def completion_list(self, the_input):
muc_serv_list = []
@@ -1753,7 +1754,7 @@ class Core(object):
tab.get_name() not in muc_serv_list:
muc_serv_list.append(JID(tab.get_name()).server)
if muc_serv_list:
- return the_input.auto_completion(muc_serv_list, ' ')
+ return the_input.auto_completion(muc_serv_list, ' ', quotify=False)
def command_join(self, arg, histo_length=None):
"""
@@ -1853,15 +1854,28 @@ class Core(object):
roomname = tab.get_name()
if tab.joined:
nick = tab.own_nick
+ elif args[0] == '*':
+ for tab in self.tabs:
+ if isinstance(tab, tabs.MucTab):
+ b = bookmark.get_by_jid(tab.get_name())
+ if not b:
+ b = bookmark.Bookmark(tab.get_name(), autojoin=True, method="local")
+ bookmark.bookmarks.append(b)
+ else:
+ b.method = "local"
+ bookmark.save_local()
+ self.information('Bookmarks added and saved.', 'Info')
+ return
else:
info = JID(args[0])
if info.resource != '':
nick = info.resource
roomname = info.bare
- if roomname == '':
+ if not roomname:
if not isinstance(self.current_tab(), tabs.MucTab):
return
roomname = self.current_tab().get_name()
+
bm = bookmark.get_by_jid(roomname)
if not bm:
bm = bookmark.Bookmark(jid=roomname)
@@ -1881,6 +1895,7 @@ class Core(object):
"""
/bookmark [room][/nick] [autojoin] [password]
"""
+
if config.get('use_remote_bookmarks', 'true').lower() == 'false':
self.command_bookmark_local(arg)
return
@@ -1895,6 +1910,24 @@ class Core(object):
nick = tab.own_nick
autojoin = True
password = None
+ elif args[0] == '*':
+ if len(args) > 1:
+ autojoin = False if args[1].lower() == 'false' else True
+ else:
+ autojoin = True
+ for tab in self.tabs:
+ if isinstance(tab, tabs.MucTab):
+ b = bookmark.get_by_jid(tab.get_name())
+ if not b:
+ b = bookmark.Bookmark(tab.get_name(), autojoin=autojoin)
+ bookmark.bookmarks.append(b)
+ else:
+ b.method = "local"
+ if bookmark.save_remote(self.xmpp, self):
+ self.information("Bookmarks added.", "Info")
+ else:
+ self.information("Could not add the bookmarks.", "Info")
+ return
else:
info = JID(args[0])
if info.resource != '':
@@ -1923,7 +1956,7 @@ class Core(object):
bm.password = password
if autojoin:
bm.autojoin = autojoin
- if bookmark.save_remote(self.xmpp, self):
+ if bookmark.save_remote(self.xmpp):
self.information('Bookmark added.', 'Info')
self.information(_('Your remote bookmarks are now: %s') %
[b for b in bookmark.bookmarks if b.method in ('pep', 'privatexml')], 'Info')
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
index 98879841..40367052 100644
--- a/src/plugin_manager.py
+++ b/src/plugin_manager.py
@@ -213,10 +213,10 @@ class PluginManager(object):
self.core.information(_('Completion failed: %s' % e), 'Error')
return
plugins_files = [name[:-3] for name in names if name.endswith('.py')]
- return the_input.auto_completion(plugins_files, '')
+ return the_input.auto_completion(plugins_files, '', quotify=False)
def completion_unload(self, the_input):
"""
completion function that completes the name of the plugins that are loaded
"""
- return the_input.auto_completion(list(self.plugins.keys()), '')
+ return the_input.auto_completion(list(self.plugins.keys()), '', quotify=False)
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]()
diff --git a/src/windows.py b/src/windows.py
index 49e23a46..b0780cf5 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -319,15 +319,24 @@ class GlobalInfoBar(Win):
self._win.erase()
self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
sorted_tabs = sorted(self.core.tabs, key=comp)
+ show_names = config.getl('show_tab_names', 'false') == 'true'
+ show_nums = config.getl('show_tab_numbers', 'true') != 'false'
+ use_nicks = config.getl('use_tab_nicks', 'true') != 'false'
for tab in sorted_tabs:
color = tab.color
if config.get('show_inactive_tabs', 'true') == 'false' and\
color is get_theme().COLOR_TAB_NORMAL:
continue
try:
- self.addstr("%s" % str(tab.nb), to_curses_attr(color))
- if config.get('show_tab_names', 'false') == 'true':
- self.addstr(" %s" % str(tab.get_name()), to_curses_attr(color))
+ if show_nums or not show_names:
+ self.addstr("%s" % str(tab.nb), to_curses_attr(color))
+ if show_names:
+ self.addstr(' ', to_curses_attr(color))
+ if show_names:
+ if use_nicks:
+ self.addstr("%s" % str(tab.get_nick()), to_curses_attr(color))
+ else:
+ self.addstr("%s" % str(tab.get_name()), to_curses_attr(color))
self.addstr("|", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
except: # end of line
break
@@ -356,6 +365,7 @@ class VerticalGlobalInfoBar(Win):
sorted_tabs = [tab for tab in sorted_tabs if\
tab.vertical_color is not get_theme().COLOR_VERTICAL_TAB_NORMAL]
nb_tabs = len(sorted_tabs)
+ use_nicks = config.getl('use_tab_nicks', 'true') != 'false'
if nb_tabs >= height:
for y, tab in enumerate(sorted_tabs):
if tab.vertical_color == get_theme().COLOR_VERTICAL_TAB_CURRENT:
@@ -372,7 +382,10 @@ class VerticalGlobalInfoBar(Win):
color = tab.vertical_color
self.addstr(y if config.get('vertical_tab_list_sort', 'desc') != 'asc' else height - y - 1, 0, "%2d" % tab.nb, to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER))
self.addstr('.')
- self.addnstr("%s" % tab.get_name(), width - 4, to_curses_attr(color))
+ if use_nicks:
+ self.addnstr("%s" % tab.get_nick(), width - 4, to_curses_attr(color))
+ else:
+ self.addnstr("%s" % tab.get_name(), width - 4, to_curses_attr(color))
self._win.attron(to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR))
self._win.vline(0, width-1, curses.ACS_VLINE, height)
self._win.attroff(to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR))
@@ -1077,8 +1090,7 @@ class Input(Win):
completion_type = config.get('completion', 'normal')
if quotify:
for i, word in enumerate(word_list[:]):
- if ' ' in word:
- word_list[i] = '"' + word + '"'
+ word_list[i] = '"' + word + '"'
if completion_type == 'shell' and self.text != '':
self.shell_completion(word_list, add_after)
else:
@@ -1619,7 +1631,9 @@ class RosterWin(Win):
presence = resource.presence
nb = ' (%s)' % len(contact)
color = RosterWin.color_show[presence]()
- if contact.name:
+ if config.getl('show_roster_jids', 'true') == 'false' and contact.name:
+ display_name = '%s %s' % (contact.name, nb[1:])
+ elif contact.name:
display_name = '%s (%s)%s' % (contact.name,
contact.bare_jid, nb,)
else:
@@ -1681,6 +1695,8 @@ class ContactInfoWin(Win):
else:
self.addstr('Ask: %s' % (contact.ask,))
self.finish_line()
+ if resource:
+ self.addstr(2, 0, 'Status: %s' % (resource.status))
def draw_group_info(self, group):