summaryrefslogtreecommitdiff
path: root/poezio
diff options
context:
space:
mode:
authormathieui <mathieui@mathieui.net>2017-11-12 15:03:09 +0100
committermathieui <mathieui@mathieui.net>2017-11-12 15:03:09 +0100
commitd55cc5872503567775f0d7a7731d6f489bf2299b (patch)
tree725f9e7b8144d36054447b3c82edfb45bda8df1d /poezio
parent92496db823db34f7f7fb1ab31eaef093a707c3e8 (diff)
downloadpoezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.gz
poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.bz2
poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.tar.xz
poezio-d55cc5872503567775f0d7a7731d6f489bf2299b.zip
yapf -ir
Diffstat (limited to 'poezio')
-rw-r--r--poezio/__main__.py1
-rw-r--r--poezio/args.py40
-rw-r--r--poezio/asyncio.py5
-rw-r--r--poezio/bookmarks.py53
-rw-r--r--poezio/common.py38
-rw-r--r--poezio/config.py87
-rw-r--r--poezio/connection.py55
-rw-r--r--poezio/contact.py17
-rw-r--r--poezio/core/__init__.py1
-rw-r--r--poezio/core/commands.py202
-rw-r--r--poezio/core/completions.py184
-rw-r--r--poezio/core/core.py816
-rw-r--r--poezio/core/handlers.py659
-rw-r--r--poezio/core/structs.py14
-rwxr-xr-xpoezio/daemon.py10
-rw-r--r--poezio/decorators.py23
-rw-r--r--poezio/events.py5
-rw-r--r--poezio/fifo.py7
-rw-r--r--poezio/fixes.py9
-rwxr-xr-xpoezio/keyboard.py14
-rw-r--r--poezio/logger.py123
-rw-r--r--poezio/multiuserchat.py66
-rw-r--r--poezio/pep.py408
-rw-r--r--poezio/plugin.py52
-rw-r--r--poezio/plugin_manager.py84
-rw-r--r--poezio/poezio.py8
-rw-r--r--poezio/poezio_shlex.py24
-rw-r--r--poezio/poopt.py7
-rw-r--r--poezio/roster.py39
-rw-r--r--poezio/roster_sorting.py48
-rw-r--r--poezio/size_manager.py3
-rw-r--r--poezio/tabs/__init__.py12
-rw-r--r--poezio/tabs/adhoc_commands_list.py29
-rw-r--r--poezio/tabs/basetabs.py307
-rw-r--r--poezio/tabs/bookmarkstab.py45
-rw-r--r--poezio/tabs/confirmtab.py27
-rw-r--r--poezio/tabs/conversationtab.py197
-rw-r--r--poezio/tabs/data_forms.py6
-rw-r--r--poezio/tabs/listtab.py50
-rw-r--r--poezio/tabs/muclisttab.py25
-rw-r--r--poezio/tabs/muctab.py1064
-rw-r--r--poezio/tabs/privatetab.py199
-rw-r--r--poezio/tabs/rostertab.py524
-rw-r--r--poezio/tabs/xmltab.py146
-rw-r--r--poezio/text_buffer.py115
-rwxr-xr-xpoezio/theming.py273
-rw-r--r--poezio/timed_events.py5
-rw-r--r--poezio/user.py27
-rw-r--r--poezio/windows/__init__.py18
-rw-r--r--poezio/windows/base_wins.py17
-rw-r--r--poezio/windows/bookmark_forms.py125
-rw-r--r--poezio/windows/confirm.py3
-rw-r--r--poezio/windows/data_forms.py159
-rw-r--r--poezio/windows/funcs.py14
-rw-r--r--poezio/windows/image.py16
-rw-r--r--poezio/windows/info_bar.py35
-rw-r--r--poezio/windows/info_wins.py78
-rw-r--r--poezio/windows/input_placeholders.py5
-rw-r--r--poezio/windows/inputs.py111
-rw-r--r--poezio/windows/list.py42
-rw-r--r--poezio/windows/misc.py3
-rw-r--r--poezio/windows/muc.py35
-rw-r--r--poezio/windows/roster_win.py152
-rw-r--r--poezio/windows/text_win.py122
-rw-r--r--poezio/xhtml.py103
65 files changed, 4361 insertions, 2830 deletions
diff --git a/poezio/__main__.py b/poezio/__main__.py
index b8696bb5..b0ba98e4 100644
--- a/poezio/__main__.py
+++ b/poezio/__main__.py
@@ -8,5 +8,6 @@ def run():
sys.exit(1)
return 0
+
if __name__ == '__main__':
run()
diff --git a/poezio/args.py b/poezio/args.py
index 63e77927..71ae7488 100644
--- a/poezio/args.py
+++ b/poezio/args.py
@@ -6,23 +6,37 @@ There is a fallback to the deprecated optparse if argparse is not found
from os import path
from argparse import ArgumentParser, SUPPRESS
+
def parse_args(CONFIG_PATH=''):
"""
Parse the arguments from the command line
"""
parser = ArgumentParser('poezio')
- parser.add_argument("-c", "--check-config", dest="check_config",
- action='store_true',
- help='Check the config file')
- parser.add_argument("-d", "--debug", dest="debug",
- help="The file where debug will be written",
- metavar="DEBUG_FILE")
- parser.add_argument("-f", "--file", dest="filename",
- default=path.join(CONFIG_PATH, 'poezio.cfg'),
- help="The config file you want to use",
- metavar="CONFIG_FILE")
- parser.add_argument("-v", "--version", dest="version",
- help=SUPPRESS, metavar="VERSION",
- default="1.0-dev")
+ parser.add_argument(
+ "-c",
+ "--check-config",
+ dest="check_config",
+ action='store_true',
+ help='Check the config file')
+ parser.add_argument(
+ "-d",
+ "--debug",
+ dest="debug",
+ help="The file where debug will be written",
+ metavar="DEBUG_FILE")
+ parser.add_argument(
+ "-f",
+ "--file",
+ dest="filename",
+ default=path.join(CONFIG_PATH, 'poezio.cfg'),
+ help="The config file you want to use",
+ metavar="CONFIG_FILE")
+ parser.add_argument(
+ "-v",
+ "--version",
+ dest="version",
+ help=SUPPRESS,
+ metavar="VERSION",
+ default="1.0-dev")
options = parser.parse_args()
return options
diff --git a/poezio/asyncio.py b/poezio/asyncio.py
index 2b02a91f..d333ffa6 100644
--- a/poezio/asyncio.py
+++ b/poezio/asyncio.py
@@ -28,6 +28,7 @@ def monkey_patch_asyncio_slixmpp():
if self._idle:
handle = self._idle.popleft()
handle._run()
+
cls = asyncio.get_event_loop().__class__
cls._idle = collections.deque()
cls.idle_call = idle_call
@@ -35,8 +36,8 @@ def monkey_patch_asyncio_slixmpp():
cls._run_once = my_run_once
spawn_event = slixmpp.xmlstream.XMLStream._spawn_event
+
def patchy(self, xml):
self.loop.idle_call(functools.partial(spawn_event, self, xml))
- slixmpp.xmlstream.XMLStream._spawn_event = patchy
-
+ slixmpp.xmlstream.XMLStream._spawn_event = patchy
diff --git a/poezio/bookmarks.py b/poezio/bookmarks.py
index 81d006b7..e007ddd1 100644
--- a/poezio/bookmarks.py
+++ b/poezio/bookmarks.py
@@ -40,8 +40,13 @@ log = logging.getLogger(__name__)
class Bookmark(object):
-
- def __init__(self, jid, name=None, autojoin=False, nick=None, password=None, method='local'):
+ def __init__(self,
+ jid,
+ name=None,
+ autojoin=False,
+ nick=None,
+ password=None,
+ method='local'):
self.jid = jid
self.name = name or jid
self.autojoin = autojoin
@@ -61,9 +66,8 @@ class Bookmark(object):
self._method = value
def __repr__(self):
- return '<%s%s|%s>' % (self.jid,
- ('/'+self.nick) if self.nick else '',
- self.method)
+ return '<%s%s|%s>' % (self.jid, ('/' + self.nick)
+ if self.nick else '', self.method)
def stanza(self):
"""
@@ -98,7 +102,8 @@ class Bookmark(object):
"""
jid = el.get('jid')
name = el.get('name')
- autojoin = True if el.get('autojoin', 'false').lower() in ('true', '1') else False
+ autojoin = True if el.get('autojoin',
+ 'false').lower() in ('true', '1') else False
nick = None
for n in el.iter('nick'):
nick = n.text
@@ -121,8 +126,8 @@ class Bookmark(object):
name = el['name']
return Bookmark(jid, name, autojoin, nick, password, method='remote')
-class BookmarkList(object):
+class BookmarkList(object):
def __init__(self):
self.bookmarks = []
preferred = config.get('use_bookmarks_method').lower()
@@ -191,17 +196,21 @@ class BookmarkList(object):
method = 'xep_0049' if self.preferred == 'privatexml' else 'xep_0223'
if method:
- xmpp.plugin['xep_0048'].set_bookmarks(stanza_storage(self.bookmarks),
- method=method,
- callback=callback)
+ xmpp.plugin['xep_0048'].set_bookmarks(
+ stanza_storage(self.bookmarks),
+ method=method,
+ callback=callback)
+
def save_local(self):
"""Save the local bookmarks."""
- local = ''.join(bookmark.local() for bookmark in self if bookmark.method == 'local')
+ local = ''.join(bookmark.local() for bookmark in self
+ if bookmark.method == 'local')
config.set_and_save('rooms', local)
def save(self, xmpp, core=None, callback=None):
"""Save all the bookmarks."""
self.save_local()
+
def _cb(iq):
if callback:
callback(iq)
@@ -209,14 +218,17 @@ class BookmarkList(object):
core.information('Could not save remote bookmarks.', 'Error')
elif core:
core.information('Bookmarks saved', 'Info')
+
if config.get('use_remote_bookmarks'):
self.save_remote(xmpp, _cb)
def get_pep(self, xmpp, callback):
"""Add the remotely stored bookmarks via pep to the list."""
+
def _cb(iq):
if iq['type'] == 'result':
- for conf in iq['pubsub']['items']['item']['bookmarks']['conferences']:
+ for conf in iq['pubsub']['items']['item']['bookmarks'][
+ 'conferences']:
if isinstance(conf, URL):
continue
b = Bookmark.parse(conf)
@@ -230,6 +242,7 @@ class BookmarkList(object):
"""
Fetch the remote bookmarks stored via privatexml.
"""
+
def _cb(iq):
if iq['type'] == 'result':
for conf in iq['private']['bookmarks']['conferences']:
@@ -250,12 +263,15 @@ class BookmarkList(object):
if force and not any(self.available_storage.values()):
old_callback = callback
method = 'pep' if self.preferred == 'pep' else 'privatexml'
+
def new_callback(result):
if result['type'] != 'error':
self.available_storage[method] = True
old_callback(result)
else:
- information('No remote bookmark storage available', 'Warning')
+ information('No remote bookmark storage available',
+ 'Warning')
+
callback = new_callback
if self.preferred == 'pep':
@@ -277,10 +293,17 @@ class BookmarkList(object):
nick = jid.resource
else:
nick = None
- passwd = config.get_by_tabname('password', jid.bare, fallback=False) or None
- b = Bookmark(jid.bare, autojoin=True, nick=nick, password=passwd, method='local')
+ passwd = config.get_by_tabname(
+ 'password', jid.bare, fallback=False) or None
+ b = Bookmark(
+ jid.bare,
+ autojoin=True,
+ nick=nick,
+ password=passwd,
+ method='local')
self.append(b)
+
def stanza_storage(bookmarks):
"""Generate a <storage/> stanza with the conference elements."""
storage = Bookmarks()
diff --git a/poezio/common.py b/poezio/common.py
index 7168fe18..0c6a77a8 100644
--- a/poezio/common.py
+++ b/poezio/common.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Various useful functions.
"""
@@ -45,6 +44,7 @@ def get_base64_from_file(path):
mime_type = mimetypes.guess_type(path)[0]
return (encoded, mime_type, sha1)
+
def _get_output_of_command(command):
"""
Runs a command and returns its output.
@@ -54,10 +54,12 @@ def _get_output_of_command(command):
:rtype: :py:class:`str`
"""
try:
- return subprocess.check_output(command.split()).decode('utf-8').split('\n')
+ return subprocess.check_output(
+ command.split()).decode('utf-8').split('\n')
except subprocess.CalledProcessError:
return None
+
def _is_in_path(command, return_abs_path=False):
"""
Check if *command* is in the $PATH or not.
@@ -81,6 +83,7 @@ def _is_in_path(command, return_abs_path=False):
pass
return False
+
DISTRO_INFO = {
'Arch Linux': '/etc/arch-release',
'Aurox Linux': '/etc/aurox-release',
@@ -103,6 +106,7 @@ DISTRO_INFO = {
'Redhat Linux': '/etc/redhat-release'
}
+
def get_os_info():
"""
Returns a detailed and well formated string containing
@@ -116,10 +120,12 @@ def get_os_info():
full_path_to_executable = _is_in_path(executable, return_abs_path=True)
if full_path_to_executable:
command = executable + params
- process = subprocess.Popen([command], shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- close_fds=True)
+ process = subprocess.Popen(
+ [command],
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ close_fds=True)
process.wait()
output = process.stdout.readline().decode('utf-8').strip()
# some distros put n/a in places, so remove those
@@ -136,7 +142,7 @@ def get_os_info():
text = _get_output_of_command(path_to_file)[0]
else:
fdes = open(path_to_file, encoding='utf-8')
- text = fdes.readline().strip() # get only first line
+ text = fdes.readline().strip() # get only first line
fdes.close()
if path_to_file.endswith('version'):
# sourcemage_version and slackware-version files
@@ -158,11 +164,12 @@ def get_os_info():
# our last chance, ask uname and strip it
uname_output = _get_output_of_command('uname -sr')
if uname_output is not None:
- os_info = uname_output[0] # only first line
+ os_info = uname_output[0] # only first line
return os_info
os_info = 'N/A'
return os_info
+
def _datetime_tuple(timestamp):
"""
Convert a timestamp using strptime and the format: %Y%m%dT%H:%M:%S.
@@ -194,7 +201,7 @@ def _datetime_tuple(timestamp):
tz_msg = timedelta(seconds=tz_mod * tz_msg)
ret -= tz_msg
except ValueError:
- pass # ignore if we got a badly-formatted offset
+ pass # ignore if we got a badly-formatted offset
# convert UTC to local time, with DST etc.
if time.daylight and time.localtime().tm_isdst:
tz = timedelta(seconds=-time.altzone)
@@ -203,6 +210,7 @@ def _datetime_tuple(timestamp):
ret += tz
return ret
+
def get_utc_time(local_time=None):
"""
Get the current UTC time
@@ -225,6 +233,7 @@ def get_utc_time(local_time=None):
return utc_time
+
def get_local_time(utc_time):
"""
Get the local time from an UTC time
@@ -240,6 +249,7 @@ def get_local_time(utc_time):
return local_time
+
def find_delayed_tag(message):
"""
Check if a message is delayed or not.
@@ -266,6 +276,7 @@ def find_delayed_tag(message):
date = None
return (delayed, date)
+
def shell_split(st):
"""
Split a string correctly according to the quotes
@@ -288,6 +299,7 @@ def shell_split(st):
w = sh.get_token()
return ret
+
def find_argument(pos, text, quoted=True):
"""
Split an input into a list of arguments, return the number of the
@@ -308,6 +320,7 @@ def find_argument(pos, text, quoted=True):
else:
return _find_argument_unquoted(pos, text)
+
def _find_argument_quoted(pos, text):
"""
Get the number of the argument at position pos in
@@ -324,6 +337,7 @@ def _find_argument_quoted(pos, text):
return count + 1
+
def _find_argument_unquoted(pos, text):
"""
Get the number of the argument at position pos in
@@ -341,6 +355,7 @@ def _find_argument_unquoted(pos, text):
argnum = i
return argnum + 1
+
def parse_str_to_secs(duration=''):
"""
Parse a string of with a number of d, h, m, s.
@@ -368,6 +383,7 @@ def parse_str_to_secs(duration=''):
result += int(tmp)
return result
+
def parse_secs_to_str(duration=0):
"""
Do the reverse operation of :py:func:`parse_str_to_secs`.
@@ -397,6 +413,7 @@ def parse_secs_to_str(duration=0):
result = '0s'
return result
+
def format_tune_string(infos):
"""
Contruct a string from a dict created from an "User tune" event.
@@ -434,6 +451,7 @@ def format_tune_string(infos):
elems.append('[' + mins + ':' + secs + ']')
return ' '.join(elems)
+
def format_gaming_string(infos):
"""
Construct a string from a dict containing "user gaming" information.
@@ -452,6 +470,7 @@ def format_gaming_string(infos):
return '%s on %s' % (name, server_address)
return name
+
def safeJID(*args, **kwargs):
"""
Construct a :py:class:`slixmpp.JID` object from a string.
@@ -463,4 +482,3 @@ def safeJID(*args, **kwargs):
return JID(*args, **kwargs)
except InvalidJID:
return JID('')
-
diff --git a/poezio/config.py b/poezio/config.py
index bef1c1a6..24e771fd 100644
--- a/poezio/config.py
+++ b/poezio/config.py
@@ -144,14 +144,15 @@ DEFAULT_CONFIG = {
'folded_roster_groups': '',
'info_win_height': 2
},
- 'muc_colors': {
- }
+ 'muc_colors': {}
}
+
class Config(RawConfigParser):
"""
load/save the config to a file
"""
+
def __init__(self, file_name, default=None):
RawConfigParser.__init__(self, None)
# make the options case sensitive
@@ -198,8 +199,12 @@ class Config(RawConfigParser):
return default
return res
- def get_by_tabname(self, option, tabname,
- fallback=True, fallback_server=True, default=''):
+ def get_by_tabname(self,
+ option,
+ tabname,
+ fallback=True,
+ fallback_server=True,
+ default=''):
"""
Try to get the value for the option. First we look in
a section named `tabname`, if the option is not present
@@ -232,7 +237,6 @@ class Config(RawConfigParser):
return self.get(option, default)
return default
-
def __get(self, option, section=DEFSECTION, **kwargs):
"""
facility for RawConfigParser.get
@@ -331,11 +335,7 @@ class Config(RawConfigParser):
prefix, file = path.split(self.file_name)
filename = path.join(prefix, '.%s.tmp' % file)
fd = os.fdopen(
- os.open(
- filename,
- os.O_WRONLY | os.O_CREAT,
- 0o600),
- 'w')
+ os.open(filename, os.O_WRONLY | os.O_CREAT, 0o600), 'w')
for line in lines:
fd.write('%s\n' % line)
fd.close()
@@ -362,9 +362,10 @@ class Config(RawConfigParser):
with open(self.file_name, 'r', encoding='utf-8') as df:
lines_before = [line.strip() for line in df]
except OSError:
- log.error('Unable to read the config file %s',
- self.file_name,
- exc_info=True)
+ log.error(
+ 'Unable to read the config file %s',
+ self.file_name,
+ exc_info=True)
return tuple()
else:
lines_before = []
@@ -415,8 +416,7 @@ class Config(RawConfigParser):
else:
return ('Could not toggle option: %s.'
' Current value is %s.' %
- (option, current or "empty"),
- 'Warning')
+ (option, current or "empty"), 'Warning')
if self.has_section(section):
RawConfigParser.set(self, section, option, value)
else:
@@ -477,12 +477,13 @@ def find_line(lines, start, end, option):
"""
current = start
for line in lines[start:end]:
- if (line.startswith('%s ' % option) or
- line.startswith('%s=' % option)):
+ if (line.startswith('%s ' % option)
+ or line.startswith('%s=' % option)):
return current
current += 1
return -1
+
def file_ok(filepath):
"""
Returns True if the file exists and is readable and writeable,
@@ -492,6 +493,7 @@ def file_ok(filepath):
val &= os.access(filepath, os.R_OK | os.W_OK)
return bool(val)
+
def check_create_config_dir():
"""
create the configuration directory if it doesn't exist
@@ -507,6 +509,7 @@ def check_create_config_dir():
pass
return CONFIG_PATH
+
def check_create_cache_dir():
"""
create the cache directory if it doesn't exist
@@ -525,6 +528,7 @@ def check_create_cache_dir():
except OSError:
pass
+
def check_config():
"""
Check the config file and print results
@@ -533,7 +537,8 @@ def check_config():
for option in DEFAULT_CONFIG['Poezio']:
value = config.get(option)
if value != DEFAULT_CONFIG['Poezio'][option]:
- result['changed'].append((option, value, DEFAULT_CONFIG['Poezio'][option]))
+ result['changed'].append((option, value,
+ DEFAULT_CONFIG['Poezio'][option]))
else:
value = config.get(option, default='')
upper = value.upper()
@@ -544,15 +549,19 @@ def check_config():
result['changed'].sort(key=lambda x: x[0])
result['missing'].sort()
if result['changed']:
- print('\033[1mOptions changed from the default configuration:\033[0m\n')
+ print(
+ '\033[1mOptions changed from the default configuration:\033[0m\n')
for option, new_value, default in result['changed']:
- print(' \033[1m%s\033[0m = \033[33m%s\033[0m (default: \033[32m%s\033[0m)' % (option, new_value, default))
+ print(
+ ' \033[1m%s\033[0m = \033[33m%s\033[0m (default: \033[32m%s\033[0m)'
+ % (option, new_value, default))
if result['missing']:
print('\n\033[1mMissing options:\033[0m (the defaults are used)\n')
for option in result['missing']:
print(' \033[31m%s\033[0m' % option)
+
def run_cmdline_args(CONFIG_PATH):
"Parse the command line arguments"
global options
@@ -560,7 +569,8 @@ def run_cmdline_args(CONFIG_PATH):
# Copy a default file if none exists
if not path.isfile(options.filename):
- default = path.join(path.dirname(__file__), '../data/default_config.cfg')
+ default = path.join(
+ path.dirname(__file__), '../data/default_config.cfg')
other = pkg_resources.resource_filename('poezio', 'default_config.cfg')
if path.isfile(default):
copy2(default, options.filename)
@@ -577,6 +587,7 @@ def run_cmdline_args(CONFIG_PATH):
global firstrun
firstrun = True
+
def create_global_config():
"Create the global config object, or crash"
try:
@@ -589,6 +600,7 @@ def create_global_config():
traceback.print_exc(limit=0)
sys.exit(1)
+
def check_create_log_dir():
"Create the poezio logging directory if it doesn’t exist"
global LOG_DIR
@@ -610,29 +622,29 @@ def check_create_log_dir():
except:
pass
+
def setup_logging():
"Change the logging config according to the cmdline options and config"
if config.get('log_errors'):
LOGGING_CONFIG['root']['handlers'].append('error')
LOGGING_CONFIG['handlers']['error'] = {
- 'level': 'ERROR',
- 'class': 'logging.FileHandler',
- 'filename': path.join(LOG_DIR, 'errors.log'),
- 'formatter': 'simple',
- }
+ 'level': 'ERROR',
+ 'class': 'logging.FileHandler',
+ 'filename': path.join(LOG_DIR, 'errors.log'),
+ 'formatter': 'simple',
+ }
logging.disable(logging.WARNING)
if options.debug:
LOGGING_CONFIG['root']['handlers'].append('debug')
LOGGING_CONFIG['handlers']['debug'] = {
- 'level':'DEBUG',
- 'class':'logging.FileHandler',
- 'filename': options.debug,
- 'formatter': 'simple',
- }
+ 'level': 'DEBUG',
+ 'class': 'logging.FileHandler',
+ 'filename': options.debug,
+ 'formatter': 'simple',
+ }
logging.disable(logging.NOTSET)
-
if LOGGING_CONFIG['root']['handlers']:
logging.config.dictConfig(LOGGING_CONFIG)
else:
@@ -642,6 +654,7 @@ def setup_logging():
global log
log = logging.getLogger(__name__)
+
def post_logging_setup():
# common imports slixmpp, which creates then its loggers, so
# it needs to be after logger configuration
@@ -649,6 +662,7 @@ def post_logging_setup():
global safeJID
safeJID = JID
+
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': True,
@@ -657,12 +671,11 @@ LOGGING_CONFIG = {
'format': '%(asctime)s %(levelname)s:%(module)s:%(message)s'
}
},
- 'handlers': {
- },
+ 'handlers': {},
'root': {
- 'handlers': [],
- 'propagate': True,
- 'level': 'DEBUG',
+ 'handlers': [],
+ 'propagate': True,
+ 'level': 'DEBUG',
}
}
diff --git a/poezio/connection.py b/poezio/connection.py
index d00f714f..99a19c11 100644
--- a/poezio/connection.py
+++ b/poezio/connection.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Defines the Connection class
"""
@@ -12,7 +11,6 @@ Defines the Connection class
import logging
log = logging.getLogger(__name__)
-
import getpass
import subprocess
import sys
@@ -25,12 +23,14 @@ from poezio import fixes
from poezio.common import safeJID
from poezio.config import config, options
+
class Connection(slixmpp.ClientXMPP):
"""
Receives everything from Jabber and emits the
appropriate signals
"""
__init = False
+
def __init__(self):
keyfile = config.get('keyfile')
certfile = config.get('certfile')
@@ -43,27 +43,35 @@ class Connection(slixmpp.ClientXMPP):
jid = '%s' % config.get('jid')
password = config.get('password')
eval_password = config.get('eval_password')
- if not password and not eval_password and not (keyfile and certfile):
+ if not password and not eval_password and not (keyfile
+ and certfile):
password = getpass.getpass()
elif not password and not (keyfile and certfile):
- sys.stderr.write("No password or certificates provided, using the eval_password command.\n")
- process = subprocess.Popen(['sh', '-c', eval_password], stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, close_fds=True)
+ sys.stderr.write(
+ "No password or certificates provided, using the eval_password command.\n"
+ )
+ process = subprocess.Popen(
+ ['sh', '-c', eval_password],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ close_fds=True)
code = process.wait()
if code != 0:
- sys.stderr.write('The eval_password command (%s) returned a '
- 'nonzero status code: %s.\n' % (eval_password, code))
+ sys.stderr.write(
+ 'The eval_password command (%s) returned a '
+ 'nonzero status code: %s.\n' % (eval_password, code))
sys.stderr.write('Poezio will now exit\n')
sys.exit(code)
- password = process.stdout.readline().decode('utf-8').strip('\n')
- else: # anonymous auth
+ password = process.stdout.readline().decode('utf-8').strip(
+ '\n')
+ else: # anonymous auth
self.anon = True
jid = config.get('server')
password = None
jid = safeJID(jid)
# TODO: use the system language
- slixmpp.ClientXMPP.__init__(self, jid, password,
- lang=config.get('lang'))
+ slixmpp.ClientXMPP.__init__(
+ self, jid, password, lang=config.get('lang'))
force_encryption = config.get('force_encryption')
if force_encryption:
@@ -75,18 +83,19 @@ class Connection(slixmpp.ClientXMPP):
self.keyfile = config.get('keyfile')
self.certfile = config.get('certfile')
if keyfile and not certfile:
- log.error('keyfile is present in configuration file without certfile')
+ log.error(
+ 'keyfile is present in configuration file without certfile')
elif certfile and not keyfile:
- log.error('certfile is present in configuration file without keyfile')
+ log.error(
+ 'certfile is present in configuration file without keyfile')
self.core = None
self.auto_reconnect = config.get('auto_reconnect')
self.auto_authorize = None
# prosody defaults, lowest is AES128-SHA, it should be a minimum
# for anything that came out after 2002
- self.ciphers = config.get('ciphers',
- 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK'
- ':!SRP:!3DES:!aNULL')
+ self.ciphers = config.get('ciphers', 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK'
+ ':!SRP:!3DES:!aNULL')
self.ca_certs = config.get('ca_cert_path') or None
interval = config.get('whitespace_interval')
if int(interval) > 0:
@@ -116,7 +125,8 @@ class Connection(slixmpp.ClientXMPP):
XEP_0184._filter_add_receipt_request = fixes._filter_add_receipt_request
self.register_plugin('xep_0184')
self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts')
- self.plugin['xep_0184'].auto_request = config.get('request_message_receipts')
+ self.plugin['xep_0184'].auto_request = config.get(
+ 'request_message_receipts')
self.register_plugin('xep_0191')
if config.get('enable_smacks'):
@@ -139,16 +149,15 @@ class Connection(slixmpp.ClientXMPP):
self.register_plugin('xep_0196')
if config.get('send_poezio_info'):
- info = {'name':'poezio',
- 'version': options.version}
+ info = {'name': 'poezio', 'version': options.version}
if config.get('send_os_info'):
info['os'] = common.get_os_info()
self.plugin['xep_0030'].set_identities(
- identities={('client', 'console', None, 'Poezio')})
+ identities={('client', 'console', None, 'Poezio')})
else:
info = {'name': '', 'version': ''}
self.plugin['xep_0030'].set_identities(
- identities={('client', 'console', None, '')})
+ identities={('client', 'console', None, '')})
self.register_plugin('xep_0092', pconfig=info)
if config.get('send_time'):
self.register_plugin('xep_0202')
@@ -212,10 +221,12 @@ class Connection(slixmpp.ClientXMPP):
self.core.handler.outgoing_stanza(data)
slixmpp.ClientXMPP.send_raw(self, data)
+
class MatchAll(slixmpp.xmlstream.matcher.base.MatcherBase):
"""
Callback to retrieve all the stanzas for the XML tab
"""
+
def match(self, xml):
"match everything"
return True
diff --git a/poezio/contact.py b/poezio/contact.py
index 090fed92..4b233088 100644
--- a/poezio/contact.py
+++ b/poezio/contact.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Defines the Resource and Contact classes, which are used in
the roster.
@@ -16,16 +15,18 @@ log = logging.getLogger(__name__)
from poezio.common import safeJID
from collections import defaultdict
+
class Resource(object):
"""
Defines a roster item.
It's a precise resource.
"""
+
def __init__(self, jid, data):
"""
data: the dict to use as a source
"""
- self._jid = jid # Full jid
+ self._jid = jid # Full jid
self._data = data
@property
@@ -52,12 +53,14 @@ class Resource(object):
return False
return self.jid == value.jid and self._data == value._data
+
class Contact(object):
"""
This a way to gather multiple resources from the same bare JID.
This class contains zero or more Resource object and useful methods
to get the resource with the highest priority, etc
"""
+
def __init__(self, item):
"""
item: a slixmpp RosterItem pointing to that contact
@@ -118,17 +121,17 @@ class Contact(object):
@property
def resources(self):
"""List of the available resources as Resource objects"""
- return (Resource(
- '%s%s' % (self.bare_jid, ('/' + key) if key else ''),
- self.__item.resources[key]
- ) for key in self.__item.resources.keys())
+ return (Resource('%s%s' % (self.bare_jid, ('/' + key)
+ if key else ''), self.__item.resources[key])
+ for key in self.__item.resources.keys())
@property
def subscription(self):
return self.__item['subscription']
def __contains__(self, value):
- return value in self.__item.resources or safeJID(value).resource in self.__item.resources
+ return value in self.__item.resources or safeJID(
+ value).resource in self.__item.resources
def __len__(self):
"""Number of resources"""
diff --git a/poezio/core/__init__.py b/poezio/core/__init__.py
index 0c6d63d9..cfe4c179 100644
--- a/poezio/core/__init__.py
+++ b/poezio/core/__init__.py
@@ -5,4 +5,3 @@ __all__ = ['Core', 'Command', 'Status']
from poezio.core.core import Core
from poezio.core.structs import Command, Status
-
diff --git a/poezio/core/commands.py b/poezio/core/commands.py
index 5a28182b..ab0ced9a 100644
--- a/poezio/core/commands.py
+++ b/poezio/core/commands.py
@@ -46,10 +46,8 @@ class CommandCore:
buff = ['Global commands:']
for name, command in self.core.commands.items():
if isinstance(command, Command):
- acc.append(' \x19%s}%s\x19o - %s' % (
- color,
- name,
- command.short_desc))
+ acc.append(' \x19%s}%s\x19o - %s' % (color, name,
+ command.short_desc))
else:
acc.append(' \x19%s}%s\x19o' % (color, name))
acc = sorted(acc)
@@ -59,10 +57,8 @@ class CommandCore:
tab_commands = self.core.current_tab().commands
for name, command in tab_commands.items():
if isinstance(command, Command):
- acc.append(' \x19%s}%s\x19o - %s' % (
- color,
- name,
- command.short_desc))
+ acc.append(' \x19%s}%s\x19o - %s' % (color, name,
+ command.short_desc))
else:
acc.append(' \x19%s}%s\x19o' % (color, name))
acc = sorted(acc)
@@ -93,11 +89,13 @@ class CommandCore:
"""
/runkey <key>
"""
+
def replace_line_breaks(key):
"replace ^J with \n"
if key == '^J':
return '\n'
return key
+
if args is None:
return self.help('runkey')
char = args[0]
@@ -135,7 +133,8 @@ class CommandCore:
current.send_chat_state('inactive')
for tab in self.core.tabs:
if isinstance(tab, tabs.MucTab) and tab.joined:
- muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show, msg)
+ muc.change_show(self.core.xmpp, tab.name, tab.own_nick, show,
+ msg)
if hasattr(tab, 'directed_presence'):
del tab.directed_presence
self.core.set_status(show, msg)
@@ -156,12 +155,14 @@ class CommandCore:
if ptype == 'available':
ptype = None
try:
- pres = self.core.xmpp.make_presence(pto=jid, ptype=ptype, pstatus=status)
+ pres = self.core.xmpp.make_presence(
+ pto=jid, ptype=ptype, pstatus=status)
self.core.events.trigger('send_normal_presence', pres)
pres.send()
except (XMPPError, NotConnectedError):
self.core.information('Could not send directed presence', 'Error')
- log.debug('Could not send directed presence to %s', jid, exc_info=True)
+ log.debug(
+ 'Could not send directed presence to %s', jid, exc_info=True)
return
tab = self.core.get_tab_by_name(jid)
if tab:
@@ -184,7 +185,7 @@ class CommandCore:
"""/theme <theme name>"""
if args is None:
return self.help('theme')
- self.set('theme %s' % (args[0],))
+ self.set('theme %s' % (args[0], ))
@command_args_parser.quoted(1)
def win(self, args):
@@ -254,10 +255,12 @@ class CommandCore:
if not old_tab and value == tab.name:
old_tab = tab
if not old_tab:
- self.core.information("Tab %s does not exist" % args[0], "Error")
+ self.core.information("Tab %s does not exist" % args[0],
+ "Error")
return None
ref = old_tab.nb
return ref
+
old = get_nb_from_value(args[0])
new = get_nb_from_value(args[1])
if new is None or old is None:
@@ -281,19 +284,20 @@ class CommandCore:
jid = safeJID(args[0])
else:
if not isinstance(self.core.current_tab(), tabs.MucTab):
- return self.core.information('Please provide a server', 'Error')
+ return self.core.information('Please provide a server',
+ 'Error')
jid = safeJID(self.core.current_tab().name)
list_tab = tabs.MucListTab(self.core, jid)
self.core.add_tab(list_tab, True)
cb = list_tab.on_muc_list_item_received
- self.core.xmpp.plugin['xep_0030'].get_items(jid=jid,
- callback=cb)
+ self.core.xmpp.plugin['xep_0030'].get_items(jid=jid, callback=cb)
@command_args_parser.quoted(1)
def version(self, args):
"""
/version <jid>
"""
+
def callback(res):
"Callback for /version"
if not res:
@@ -301,10 +305,9 @@ class CommandCore:
' 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 'an unknown platform')
+ jid, res.get('name') or 'an unknown software',
+ res.get('version') or 'unknown',
+ res.get('os') or 'an unknown platform')
self.core.information(version, 'Info')
if args is None:
@@ -315,7 +318,8 @@ class CommandCore:
fixes.get_version(self.core.xmpp, jid, callback=callback)
elif jid in roster:
for resource in roster[jid].resources:
- fixes.get_version(self.core.xmpp, resource.jid, callback=callback)
+ fixes.get_version(
+ self.core.xmpp, resource.jid, callback=callback)
def _empty_join(self):
tab = self.core.current_tab()
@@ -372,7 +376,7 @@ class CommandCore:
else:
room, nick = self._parse_join_jid(args[0])
if not room and not nick:
- return # nothing was parsed
+ return # nothing was parsed
room = room.lower()
if nick == '':
@@ -467,11 +471,13 @@ class CommandCore:
bookmark.nick = nick
if password:
bookmark.password = password
+
def callback(iq):
if iq["type"] != "error":
self.core.information('Bookmark added.', 'Info')
else:
self.core.information("Could not add the bookmarks.", "Info")
+
self.core.bookmarks.save_local()
self.core.bookmarks.save_remote(self.core.xmpp, callback)
@@ -480,8 +486,7 @@ class CommandCore:
for tab in self.core.get_tabs(tabs.MucTab):
bookmark = self.core.bookmarks[tab.name]
if not bookmark:
- bookmark = Bookmark(tab.name, autojoin=True,
- method=method)
+ bookmark = Bookmark(tab.name, autojoin=True, method=method)
new_bookmarks.append(bookmark)
else:
bookmark.method = method
@@ -489,11 +494,14 @@ class CommandCore:
self.core.bookmarks.remove(bookmark)
new_bookmarks.extend(self.core.bookmarks.bookmarks)
self.core.bookmarks.set(new_bookmarks)
+
def _cb(iq):
if iq["type"] != "error":
self.core.information("Bookmarks saved.", "Info")
else:
- self.core.information("Could not save the remote bookmarks.", "Info")
+ self.core.information("Could not save the remote bookmarks.",
+ "Info")
+
self.core.bookmarks.save_local()
self.core.bookmarks.save_remote(self.core.xmpp, _cb)
@@ -520,7 +528,8 @@ class CommandCore:
if success:
self.core.information('Bookmark deleted', 'Info')
else:
- self.core.information('Error while deleting the bookmark', 'Error')
+ self.core.information('Error while deleting the bookmark',
+ 'Error')
if not args:
tab = self.core.current_tab()
@@ -546,15 +555,17 @@ class CommandCore:
lines = []
theme = get_theme()
for section_name, section in config_dict.items():
- lines.append('\x19%(section_col)s}[%(section)s]\x19o' %
- {
- 'section': section_name,
- 'section_col': dump_tuple(theme.COLOR_INFORMATION_TEXT),
- })
+ lines.append(
+ '\x19%(section_col)s}[%(section)s]\x19o' % {
+ 'section': section_name,
+ 'section_col': dump_tuple(
+ theme.COLOR_INFORMATION_TEXT),
+ })
for option_name, option_value in section.items():
- lines.append('%s\x19%s}=\x19o%s' % (option_name,
- dump_tuple(theme.COLOR_REVISIONS_MESSAGE),
- option_value))
+ lines.append('%s\x19%s}=\x19o%s' %
+ (option_name,
+ dump_tuple(theme.COLOR_REVISIONS_MESSAGE),
+ option_value))
info = ('Current options:\n%s' % '\n'.join(lines), 'Info')
elif len(args) == 1:
option = args[0]
@@ -573,7 +584,8 @@ class CommandCore:
file_name = os.path.join(file_name, plugin_name + '.cfg')
plugin_config = PluginConfig(file_name, plugin_name)
else:
- plugin_config = self.core.plugin_manager.plugins[plugin_name].config
+ plugin_config = self.core.plugin_manager.plugins[
+ plugin_name].config
value = plugin_config.get(option, default='', section=section)
info = ('%s=%s' % (option, value), 'Info')
else:
@@ -600,14 +612,15 @@ class CommandCore:
file_name = os.path.join(file_name, plugin_name + '.cfg')
plugin_config = PluginConfig(file_name, plugin_name)
else:
- plugin_config = self.core.plugin_manager.plugins[plugin_name].config
+ plugin_config = self.core.plugin_manager.plugins[
+ plugin_name].config
info = plugin_config.set_and_save(option, value, section)
else:
if args[0] == '.':
name = safeJID(self.core.current_tab().name).bare
if not name:
- self.core.information('Invalid tab to use the "." argument.',
- 'Error')
+ self.core.information(
+ 'Invalid tab to use the "." argument.', 'Error')
return
section = name
else:
@@ -679,35 +692,35 @@ class CommandCore:
"""
/last_activity <jid>
"""
+
def callback(iq):
"Callback for the last activity"
if iq['type'] != 'result':
if iq['error']['type'] == 'auth':
self.core.information('You are not allowed to see the '
- 'activity of this contact.',
- 'Error')
+ 'activity of this contact.', 'Error')
else:
- self.core.information('Error retrieving the activity', 'Error')
+ self.core.information('Error retrieving the activity',
+ 'Error')
return
seconds = iq['last_activity']['seconds']
status = iq['last_activity']['status']
from_ = iq['from']
if not safeJID(from_).user:
msg = 'The uptime of %s is %s.' % (
- from_,
- common.parse_secs_to_str(seconds))
+ from_, common.parse_secs_to_str(seconds))
else:
msg = 'The last activity of %s was %s ago%s' % (
- from_,
- common.parse_secs_to_str(seconds),
- (' and his/her last status was %s' % status) if status else '')
+ from_, common.parse_secs_to_str(seconds),
+ (' and his/her last status was %s' % status)
+ if status else '')
self.core.information(msg, 'Info')
if args is None:
return self.help('last_activity')
jid = safeJID(args[0])
- self.core.xmpp.plugin['xep_0012'].get_last_activity(jid,
- callback=callback)
+ self.core.xmpp.plugin['xep_0012'].get_last_activity(
+ jid, callback=callback)
@command_args_parser.quoted(0, 2)
def mood(self, args):
@@ -719,15 +732,14 @@ class CommandCore:
mood = args[0]
if mood not in pep.MOODS:
- return self.core.information('%s is not a correct value for a mood.'
- % mood,
- 'Error')
+ return self.core.information(
+ '%s is not a correct value for a mood.' % mood, 'Error')
if len(args) == 2:
text = args[1]
else:
text = None
- self.core.xmpp.plugin['xep_0107'].publish_mood(mood, text,
- callback=dumb_callback)
+ self.core.xmpp.plugin['xep_0107'].publish_mood(
+ mood, text, callback=dumb_callback)
@command_args_parser.quoted(0, 3)
def activity(self, args):
@@ -740,9 +752,8 @@ class CommandCore:
general = args[0]
if general not in pep.ACTIVITIES:
- return self.core.information('%s is not a correct value for an activity'
- % general,
- 'Error')
+ return self.core.information(
+ '%s is not a correct value for an activity' % general, 'Error')
specific = None
text = None
if length == 2:
@@ -755,10 +766,9 @@ class CommandCore:
text = args[2]
if specific and specific not in pep.ACTIVITIES[general]:
return self.core.information('%s is not a correct value '
- 'for an activity' % specific,
- 'Error')
- self.core.xmpp.plugin['xep_0108'].publish_activity(general, specific, text,
- callback=dumb_callback)
+ 'for an activity' % specific, 'Error')
+ self.core.xmpp.plugin['xep_0108'].publish_activity(
+ general, specific, text, callback=dumb_callback)
@command_args_parser.quoted(0, 2)
def gaming(self, args):
@@ -773,9 +783,8 @@ class CommandCore:
address = args[1]
else:
address = None
- return self.core.xmpp.plugin['xep_0196'].publish_gaming(name=name,
- server_address=address,
- callback=dumb_callback)
+ return self.core.xmpp.plugin['xep_0196'].publish_gaming(
+ name=name, server_address=address, callback=dumb_callback)
@command_args_parser.quoted(2, 1, [None])
def invite(self, args):
@@ -800,9 +809,9 @@ class CommandCore:
return
reason = args[1]
del self.core.pending_invites[jid.bare]
- self.core.xmpp.plugin['xep_0045'].decline_invite(jid.bare,
- self.core.pending_invites[jid.bare],
- reason)
+ self.core.xmpp.plugin['xep_0045'].decline_invite(
+ jid.bare, self.core.pending_invites[jid.bare], reason)
+
### Commands without a completion in this class ###
@@ -811,8 +820,8 @@ class CommandCore:
"""/invitations"""
build = ""
for invite in self.core.pending_invites:
- build += "%s by %s" % (invite,
- safeJID(self.core.pending_invites[invite]).bare)
+ build += "%s by %s" % (
+ invite, safeJID(self.core.pending_invites[invite]).bare)
if self.core.pending_invites:
build = "You are invited to the following rooms:\n" + build
else:
@@ -838,7 +847,8 @@ class CommandCore:
self.core.save_config()
self.core.plugin_manager.disable_plugins()
self.core.disconnect(msg)
- self.core.xmpp.add_event_handler("disconnected", self.core.exit, disposable=True)
+ self.core.xmpp.add_event_handler(
+ "disconnected", self.core.exit, disposable=True)
@command_args_parser.quoted(0, 1, [''])
def destroy_room(self, args):
@@ -849,7 +859,8 @@ class CommandCore:
if room:
muc.destroy_room(self.core.xmpp, room)
elif isinstance(self.core.current_tab(), tabs.MucTab) and not args[0]:
- muc.destroy_room(self.core.xmpp, self.core.current_tab().general_jid)
+ muc.destroy_room(self.core.xmpp,
+ self.core.current_tab().general_jid)
else:
self.core.information('Invalid JID: "%s"' % args[0], 'Error')
@@ -862,12 +873,15 @@ class CommandCore:
return self.help('bind')
if not config.silent_set(args[0], args[1], section='bindings'):
- self.core.information('Unable to write in the config file', 'Error')
+ self.core.information('Unable to write in the config file',
+ 'Error')
if args[1]:
- self.core.information('%s is now bound to %s' % (args[0], args[1]), 'Info')
+ self.core.information('%s is now bound to %s' % (args[0], args[1]),
+ 'Info')
else:
- self.core.information('%s is now reset to the default binding' % args[0], 'Info')
+ self.core.information(
+ '%s is now reset to the default binding' % args[0], 'Info')
@command_args_parser.raw
def rawxml(self, args):
@@ -881,7 +895,8 @@ class CommandCore:
stanza = args
try:
stanza = StanzaBase(self.core.xmpp, xml=ET.fromstring(stanza))
- if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in ('get', 'set'):
+ if stanza.xml.tag == 'iq' and stanza.xml.attrib.get('type') in (
+ 'get', 'set'):
iq_id = stanza.xml.attrib.get('id')
if not iq_id:
iq_id = self.core.xmpp.new_id()
@@ -893,18 +908,15 @@ class CommandCore:
self.core.xmpp.remove_handler('Iq %s' % iq_id)
self.core.xmpp.register_handler(
- Callback('Iq %s' % iq_id,
- StanzaPath('iq@id=%s' % iq_id),
- iqfunc
- )
- )
+ Callback('Iq %s' % iq_id,
+ StanzaPath('iq@id=%s' % iq_id), iqfunc))
stanza.send()
except:
self.core.information('Could not send custom stanza', 'Error')
- log.debug('/rawxml: Could not send custom stanza (%s)',
- repr(stanza),
- exc_info=True)
-
+ log.debug(
+ '/rawxml: Could not send custom stanza (%s)',
+ repr(stanza),
+ exc_info=True)
@command_args_parser.quoted(1, 256)
def load(self, args):
@@ -928,9 +940,8 @@ class CommandCore:
"""
/plugins
"""
- self.core.information("Plugins currently in use: %s" %
- repr(list(self.core.plugin_manager.plugins.keys())),
- 'Info')
+ self.core.information("Plugins currently in use: %s" % repr(
+ list(self.core.plugin_manager.plugins.keys())), 'Info')
@command_args_parser.quoted(1, 1)
def message(self, args):
@@ -942,7 +953,8 @@ class CommandCore:
jid = safeJID(args[0])
if not jid.user and not jid.domain and not jid.resource:
return self.core.information('Invalid JID.', 'Error')
- tab = self.core.get_conversation_by_jid(jid.full, False, fallback_barejid=False)
+ tab = self.core.get_conversation_by_jid(
+ jid.full, False, fallback_barejid=False)
muc = self.core.get_tab_by_name(jid.bare, typ=tabs.MucTab)
if not tab and not muc:
tab = self.core.open_conversation_window(jid.full, focus=True)
@@ -977,8 +989,8 @@ class CommandCore:
list_tab = tabs.AdhocCommandsListTab(self.core, jid)
self.core.add_tab(list_tab, True)
cb = list_tab.on_list_received
- self.core.xmpp.plugin['xep_0050'].get_commands(jid=jid, local=False,
- callback=cb)
+ self.core.xmpp.plugin['xep_0050'].get_commands(
+ jid=jid, local=False, callback=cb)
@command_args_parser.ignored
def self_(self):
@@ -990,15 +1002,11 @@ class CommandCore:
nick = self.core.own_nick
jid = self.core.xmpp.boundjid.full
info = ('Your JID is %s\nYour current status is "%s" (%s)'
- '\nYour default nickname is %s\nYou are running poezio %s' % (
- jid,
- message if message else '',
- show if show else 'available',
- nick,
- config_opts.version))
+ '\nYour default nickname is %s\nYou are running poezio %s' %
+ (jid, message if message else '', show
+ if show else 'available', nick, config_opts.version))
self.core.information(info, 'Info')
-
@command_args_parser.ignored
def reload(self):
"""
@@ -1006,6 +1014,6 @@ class CommandCore:
"""
self.core.reload_config()
+
def dumb_callback(*args, **kwargs):
"mock callback"
-
diff --git a/poezio/core/completions.py b/poezio/core/completions.py
index 634ab6b3..5e7e510f 100644
--- a/poezio/core/completions.py
+++ b/poezio/core/completions.py
@@ -17,13 +17,15 @@ from poezio.roster import roster
from poezio.core.structs import POSSIBLE_SHOW, Completion
+
class CompletionCore:
def __init__(self, core):
self.core = core
def help(self, the_input):
"""Completion for /help."""
- commands = sorted(self.core.commands.keys()) + sorted(self.core.current_tab().commands.keys())
+ commands = sorted(self.core.commands.keys()) + sorted(
+ self.core.current_tab().commands.keys())
return Completion(the_input.new_completion, commands, 1, quotify=False)
def status(self, the_input):
@@ -31,8 +33,11 @@ class CompletionCore:
Completion of /status
"""
if the_input.get_argument_position() == 1:
- return Completion(the_input.new_completion, [status for status in POSSIBLE_SHOW], 1, ' ', quotify=False)
-
+ return Completion(
+ the_input.new_completion, [status for status in POSSIBLE_SHOW],
+ 1,
+ ' ',
+ quotify=False)
def presence(self, the_input):
"""
@@ -40,29 +45,38 @@ class CompletionCore:
"""
arg = the_input.get_argument_position()
if arg == 1:
- return Completion(the_input.auto_completion, [jid for jid in roster.jids()], '', quotify=True)
+ return Completion(
+ the_input.auto_completion, [jid for jid in roster.jids()],
+ '',
+ quotify=True)
elif arg == 2:
- return Completion(the_input.auto_completion, [status for status in POSSIBLE_SHOW], '', quotify=True)
-
+ return Completion(
+ the_input.auto_completion,
+ [status for status in POSSIBLE_SHOW],
+ '',
+ quotify=True)
def theme(self, the_input):
""" Completion for /theme"""
themes_dir = config.get('themes_dir')
- themes_dir = (themes_dir or
- os.path.join(os.environ.get('XDG_DATA_HOME') or
- os.path.join(os.environ.get('HOME'), '.local', 'share'),
- 'poezio', 'themes'))
+ themes_dir = (themes_dir or os.path.join(
+ os.environ.get('XDG_DATA_HOME')
+ or os.path.join(os.environ.get('HOME'), '.local', 'share'),
+ 'poezio', 'themes'))
themes_dir = os.path.expanduser(themes_dir)
try:
names = os.listdir(themes_dir)
except OSError:
log.error('Completion for /theme failed', exc_info=True)
return False
- theme_files = [name[:-3] for name in names if name.endswith('.py') and name != '__init__.py']
+ theme_files = [
+ name[:-3] for name in names
+ if name.endswith('.py') and name != '__init__.py'
+ ]
if 'default' not in theme_files:
theme_files.append('default')
- return Completion(the_input.new_completion, theme_files, 1, '', quotify=False)
-
+ return Completion(
+ the_input.new_completion, theme_files, 1, '', quotify=False)
def win(self, the_input):
"""Completion for /win"""
@@ -72,7 +86,6 @@ class CompletionCore:
l = [i[1] for i in l]
return Completion(the_input.new_completion, l, 1, '', quotify=False)
-
def join(self, the_input):
"""
Completion for /join
@@ -97,7 +110,9 @@ class CompletionCore:
relevant_rooms = []
relevant_rooms.extend(sorted(self.core.pending_invites.keys()))
- bookmarks = [(str(elem.jid) if not elem.nick else '%s/%s' % (elem.jid, elem.nick)) for elem in self.core.bookmarks]
+ bookmarks = [(str(elem.jid)
+ if not elem.nick else '%s/%s' % (elem.jid, elem.nick))
+ for elem in self.core.bookmarks]
to_suggest = []
for bookmark in bookmarks:
tab = self.core.get_tab_by_name(bookmark, tabs.MucTab)
@@ -113,31 +128,39 @@ class CompletionCore:
serv_list = []
for tab in self.core.get_tabs(tabs.MucTab):
if tab.joined:
- serv_list.append('%s@%s' % (jid.user, safeJID(tab.name).host))
+ serv_list.append('%s@%s' % (jid.user,
+ safeJID(tab.name).host))
serv_list.extend(relevant_rooms)
- return Completion(the_input.new_completion, serv_list, 1, quotify=True)
+ return Completion(
+ the_input.new_completion, serv_list, 1, quotify=True)
elif args[1].startswith('/'):
# we completing only a resource
- return Completion(the_input.new_completion, ['/%s' % self.core.own_nick], 1, quotify=True)
+ return Completion(
+ the_input.new_completion, ['/%s' % self.core.own_nick],
+ 1,
+ quotify=True)
else:
- return Completion(the_input.new_completion, relevant_rooms, 1, quotify=True)
-
+ return Completion(
+ the_input.new_completion, relevant_rooms, 1, quotify=True)
def version(self, the_input):
"""Completion for /version"""
- comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), [])
- return Completion(the_input.new_completion, sorted(comp), 1, quotify=False)
-
+ comp = reduce(lambda x, y: x + [i.jid for i in y],
+ (roster[jid].resources for jid in roster.jids()
+ if len(roster[jid])), [])
+ return Completion(
+ the_input.new_completion, sorted(comp), 1, quotify=False)
def list(self, the_input):
"""Completion for /list"""
muc_serv_list = []
- for tab in self.core.get_tabs(tabs.MucTab): # TODO, also from an history
+ for tab in self.core.get_tabs(
+ tabs.MucTab): # TODO, also from an history
if tab.name not in muc_serv_list:
muc_serv_list.append(safeJID(tab.name).server)
if muc_serv_list:
- return Completion(the_input.new_completion, muc_serv_list, 1, quotify=False)
-
+ return Completion(
+ the_input.new_completion, muc_serv_list, 1, quotify=False)
def move_tab(self, the_input):
"""Completion for /move_tab"""
@@ -145,8 +168,8 @@ class CompletionCore:
if n == 1:
nodes = [tab.name for tab in self.core.tabs if tab]
nodes.remove('Roster')
- return Completion(the_input.new_completion, nodes, 1, ' ', quotify=True)
-
+ return Completion(
+ the_input.new_completion, nodes, 1, ' ', quotify=True)
def runkey(self, the_input):
"""
@@ -157,14 +180,14 @@ class CompletionCore:
list_.extend(self.core.current_tab().key_func.keys())
return Completion(the_input.new_completion, list_, 1, quotify=False)
-
def bookmark(self, the_input):
"""Completion for /bookmark"""
args = common.shell_split(the_input.text)
n = the_input.get_argument_position(quoted=True)
if n == 2:
- return Completion(the_input.new_completion, ['true', 'false'], 2, quotify=True)
+ return Completion(
+ the_input.new_completion, ['true', 'false'], 2, quotify=True)
if n >= 3:
return False
@@ -175,7 +198,8 @@ class CompletionCore:
if jid.server and (jid.resource or jid.full.endswith('/')):
tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab)
nicks = [tab.own_nick] if tab else []
- default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
+ default = os.environ.get('USER') if os.environ.get(
+ 'USER') else 'poezio'
nick = config.get('default_nick')
if not nick:
if default not in nicks:
@@ -184,29 +208,37 @@ class CompletionCore:
if nick not in nicks:
nicks.append(nick)
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
- return Completion(the_input.new_completion, jids_list, 1, quotify=True)
+ return Completion(
+ the_input.new_completion, jids_list, 1, quotify=True)
muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)]
muc_list.sort()
muc_list.append('*')
return Completion(the_input.new_completion, muc_list, 1, quotify=True)
-
def remove_bookmark(self, the_input):
"""Completion for /remove_bookmark"""
- return Completion(the_input.new_completion, [bm.jid for bm in self.core.bookmarks], 1, quotify=False)
-
+ return Completion(
+ the_input.new_completion, [bm.jid for bm in self.core.bookmarks],
+ 1,
+ quotify=False)
def decline(self, the_input):
"""Completion for /decline"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return Completion(the_input.auto_completion, sorted(self.core.pending_invites.keys()), 1, '', quotify=True)
-
+ return Completion(
+ the_input.auto_completion,
+ sorted(self.core.pending_invites.keys()),
+ 1,
+ '',
+ quotify=True)
def bind(self, the_input):
n = the_input.get_argument_position()
if n == 1:
- args = [key for key in self.core.key_func if not key.startswith('_')]
+ args = [
+ key for key in self.core.key_func if not key.startswith('_')
+ ]
elif n == 2:
args = [key for key in self.core.key_func]
else:
@@ -214,7 +246,6 @@ class CompletionCore:
return Completion(the_input.new_completion, args, n, '', quotify=False)
-
def message(self, the_input):
"""Completion for /message"""
n = the_input.get_argument_position(quoted=True)
@@ -231,14 +262,17 @@ class CompletionCore:
l.append(jid)
return Completion(the_input.new_completion, l, 1, '', quotify=True)
-
def invite(self, the_input):
"""Completion for /invite"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), [])
+ comp = reduce(lambda x, y: x + [i.jid for i in y],
+ (roster[jid].resources for jid in roster.jids()
+ if len(roster[jid])), [])
comp = sorted(comp)
- bares = sorted(roster[contact].bare_jid for contact in roster.jids() if len(roster[contact]))
+ bares = sorted(
+ roster[contact].bare_jid for contact in roster.jids()
+ if len(roster[contact]))
off = sorted(jid for jid in roster.jids() if jid not in bares)
comp = comp + bares + off
return Completion(the_input.new_completion, comp, n, quotify=True)
@@ -248,15 +282,19 @@ class CompletionCore:
if tab.joined:
rooms.append(tab.name)
rooms.sort()
- return Completion(the_input.new_completion, rooms, n, '', quotify=True)
-
+ return Completion(
+ the_input.new_completion, rooms, n, '', quotify=True)
def activity(self, the_input):
"""Completion for /activity"""
n = the_input.get_argument_position(quoted=True)
args = common.shell_split(the_input.text)
if n == 1:
- return Completion(the_input.new_completion, sorted(pep.ACTIVITIES.keys()), n, quotify=True)
+ return Completion(
+ the_input.new_completion,
+ sorted(pep.ACTIVITIES.keys()),
+ n,
+ quotify=True)
elif n == 2:
if args[1] in pep.ACTIVITIES:
l = list(pep.ACTIVITIES[args[1]])
@@ -264,13 +302,15 @@ class CompletionCore:
l.sort()
return Completion(the_input.new_completion, l, n, quotify=True)
-
def mood(self, the_input):
"""Completion for /mood"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return Completion(the_input.new_completion, sorted(pep.MOODS.keys()), 1, quotify=True)
-
+ return Completion(
+ the_input.new_completion,
+ sorted(pep.MOODS.keys()),
+ 1,
+ quotify=True)
def last_activity(self, the_input):
"""
@@ -279,9 +319,11 @@ class CompletionCore:
n = the_input.get_argument_position(quoted=False)
if n >= 2:
return False
- comp = reduce(lambda x, y: x + [i.jid for i in y], (roster[jid].resources for jid in roster.jids() if len(roster[jid])), [])
- return Completion(the_input.new_completion, sorted(comp), 1, '', quotify=False)
-
+ comp = reduce(lambda x, y: x + [i.jid for i in y],
+ (roster[jid].resources for jid in roster.jids()
+ if len(roster[jid])), [])
+ return Completion(
+ the_input.new_completion, sorted(comp), 1, '', quotify=False)
def server_cycle(self, the_input):
"""Completion for /server_cycle"""
@@ -291,7 +333,6 @@ class CompletionCore:
serv_list.add(serv)
return Completion(the_input.new_completion, sorted(serv_list), 1, ' ')
-
def set(self, the_input):
"""Completion for /set"""
args = common.shell_split(the_input.text)
@@ -302,9 +343,13 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return Completion(the_input.new_completion, [], n, quotify=True)
+ return Completion(
+ the_input.new_completion, [], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
- end_list = ['%s|%s' % (plugin_name, section) for section in plugin.config.sections()]
+ end_list = [
+ '%s|%s' % (plugin_name, section)
+ for section in plugin.config.sections()
+ ]
else:
end_list = set(config.options('Poezio'))
end_list.update(config.default.get('Poezio', {}))
@@ -314,11 +359,13 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return Completion(the_input.new_completion, [''], n, quotify=True)
+ return Completion(
+ the_input.new_completion, [''], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
end_list = set(plugin.config.options(section or plugin_name))
if plugin.config.default:
- end_list.update(plugin.config.default.get(section or plugin_name, {}))
+ end_list.update(
+ plugin.config.default.get(section or plugin_name, {}))
end_list = list(end_list)
end_list.sort()
elif not config.has_option('Poezio', args[1]):
@@ -333,9 +380,14 @@ class CompletionCore:
if '|' in args[1]:
plugin_name, section = args[1].split('|')[:2]
if plugin_name not in self.core.plugin_manager.plugins:
- return Completion(the_input.new_completion, [''], n, quotify=True)
+ return Completion(
+ the_input.new_completion, [''], n, quotify=True)
plugin = self.core.plugin_manager.plugins[plugin_name]
- end_list = [str(plugin.config.get(args[2], '', section or plugin_name)), '']
+ end_list = [
+ str(
+ plugin.config.get(args[2], '', section
+ or plugin_name)), ''
+ ]
else:
if not config.has_section(args[1]):
end_list = ['']
@@ -345,7 +397,6 @@ class CompletionCore:
return False
return Completion(the_input.new_completion, end_list, n, quotify=True)
-
def set_default(self, the_input):
""" Completion for /set_default
"""
@@ -357,11 +408,13 @@ class CompletionCore:
return Completion(self.set, the_input)
return False
-
def toggle(self, the_input):
"Completion for /toggle"
- return Completion(the_input.new_completion, config.options('Poezio'), 1, quotify=False)
-
+ return Completion(
+ the_input.new_completion,
+ config.options('Poezio'),
+ 1,
+ quotify=False)
def bookmark_local(self, the_input):
"""Completion for /bookmark_local"""
@@ -377,7 +430,8 @@ class CompletionCore:
if jid.server and (jid.resource or jid.full.endswith('/')):
tab = self.core.get_tab_by_name(jid.bare, tabs.MucTab)
nicks = [tab.own_nick] if tab else []
- default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
+ default = os.environ.get('USER') if os.environ.get(
+ 'USER') else 'poezio'
nick = config.get('default_nick')
if not nick:
if default not in nicks:
@@ -386,8 +440,8 @@ class CompletionCore:
if nick not in nicks:
nicks.append(nick)
jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks]
- return Completion(the_input.new_completion, jids_list, 1, quotify=True)
+ return Completion(
+ the_input.new_completion, jids_list, 1, quotify=True)
muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)]
muc_list.append('*')
return Completion(the_input.new_completion, muc_list, 1, quotify=True)
-
diff --git a/poezio/core/core.py b/poezio/core/core.py
index 33bbbc54..7678c747 100644
--- a/poezio/core/core.py
+++ b/poezio/core/core.py
@@ -66,8 +66,7 @@ class Core(object):
self.stdscr = None
status = config.get('status')
status = POSSIBLE_SHOW.get(status, None)
- self.status = Status(show=status,
- message=config.get('status_message'))
+ self.status = Status(show=status, message=config.get('status_message'))
self.running = True
self.xmpp = connection.Connection()
self.xmpp.core = self
@@ -81,7 +80,8 @@ class Core(object):
# that are displayed in almost all tabs, in an
# information window.
self.information_buffer = TextBuffer()
- self.information_win_size = config.get('info_win_height', section='var')
+ self.information_win_size = config.get(
+ 'info_win_height', section='var')
self.information_win = windows.TextWin(300)
self.information_buffer.add_window(self.information_win)
self.left_tab_win = None
@@ -164,7 +164,7 @@ class Core(object):
'M-D': self.scroll_info_up,
'M-C': self.scroll_info_down,
'M-k': self.escape_next_key,
- ######## actions mappings ##########
+ ######## actions mappings ##########
'_noop': lambda *args, **kwargs: None,
'_bookmark': self.command.bookmark,
'_bookmark_local': self.command.bookmark_local,
@@ -188,25 +188,30 @@ class Core(object):
'_show_plugins': self.command.plugins,
'_show_xmltab': self.command.xml_tab,
'_toggle_pane': self.toggle_left_pane,
- ###### status actions ######
+ ###### status actions ######
'_available': lambda: self.command.status('available'),
'_away': lambda: self.command.status('away'),
'_chat': lambda: self.command.status('chat'),
'_dnd': lambda: self.command.status('dnd'),
'_xa': lambda: self.command.status('xa'),
- ##### Custom actions ########
+ ##### Custom actions ########
'_exc_': self.try_execute,
}
self.key_func.update(key_func)
# Add handlers
self.xmpp.add_event_handler('connected', self.handler.on_connected)
- self.xmpp.add_event_handler('connection_failed', self.handler.on_failed_connection)
- self.xmpp.add_event_handler('disconnected', self.handler.on_disconnected)
- self.xmpp.add_event_handler('stream_error', self.handler.on_stream_error)
- self.xmpp.add_event_handler('failed_all_auth', self.handler.on_failed_all_auth)
+ self.xmpp.add_event_handler('connection_failed',
+ self.handler.on_failed_connection)
+ self.xmpp.add_event_handler('disconnected',
+ self.handler.on_disconnected)
+ self.xmpp.add_event_handler('stream_error',
+ self.handler.on_stream_error)
+ self.xmpp.add_event_handler('failed_all_auth',
+ self.handler.on_failed_all_auth)
self.xmpp.add_event_handler('no_auth', self.handler.on_no_auth)
- self.xmpp.add_event_handler("session_start", self.handler.on_session_start)
+ self.xmpp.add_event_handler("session_start",
+ self.handler.on_session_start)
self.xmpp.add_event_handler("session_start",
self.handler.on_session_start_features)
self.xmpp.add_event_handler("groupchat_presence",
@@ -215,8 +220,9 @@ class Core(object):
self.handler.on_groupchat_message)
self.xmpp.add_event_handler("groupchat_invite",
self.handler.on_groupchat_invitation)
- self.xmpp.add_event_handler("groupchat_direct_invite",
- self.handler.on_groupchat_direct_invitation)
+ self.xmpp.add_event_handler(
+ "groupchat_direct_invite",
+ self.handler.on_groupchat_direct_invitation)
self.xmpp.add_event_handler("groupchat_decline",
self.handler.on_groupchat_decline)
self.xmpp.add_event_handler("groupchat_config_status",
@@ -224,13 +230,17 @@ class Core(object):
self.xmpp.add_event_handler("groupchat_subject",
self.handler.on_groupchat_subject)
self.xmpp.add_event_handler("message", self.handler.on_message)
- self.xmpp.add_event_handler("message_error", self.handler.on_error_message)
- self.xmpp.add_event_handler("receipt_received", self.handler.on_receipt)
+ self.xmpp.add_event_handler("message_error",
+ self.handler.on_error_message)
+ self.xmpp.add_event_handler("receipt_received",
+ self.handler.on_receipt)
self.xmpp.add_event_handler("got_online", self.handler.on_got_online)
self.xmpp.add_event_handler("got_offline", self.handler.on_got_offline)
- self.xmpp.add_event_handler("roster_update", self.handler.on_roster_update)
+ self.xmpp.add_event_handler("roster_update",
+ self.handler.on_roster_update)
self.xmpp.add_event_handler("changed_status", self.handler.on_presence)
- self.xmpp.add_event_handler("presence_error", self.handler.on_presence_error)
+ self.xmpp.add_event_handler("presence_error",
+ self.handler.on_presence_error)
self.xmpp.add_event_handler("roster_subscription_request",
self.handler.on_subscription_request)
self.xmpp.add_event_handler("roster_subscription_authorized",
@@ -252,8 +262,10 @@ class Core(object):
self.handler.on_chatstate_inactive)
self.xmpp.add_event_handler("attention", self.handler.on_attention)
self.xmpp.add_event_handler("ssl_cert", self.handler.validate_ssl)
- self.xmpp.add_event_handler("ssl_invalid_chain", self.handler.ssl_invalid_chain)
- self.xmpp.add_event_handler('carbon_received', self.handler.on_carbon_received)
+ self.xmpp.add_event_handler("ssl_invalid_chain",
+ self.handler.ssl_invalid_chain)
+ self.xmpp.add_event_handler('carbon_received',
+ self.handler.on_carbon_received)
self.xmpp.add_event_handler('carbon_sent', self.handler.on_carbon_sent)
self.xmpp.add_event_handler('http_confirm', self.handler.http_confirm)
@@ -315,14 +327,11 @@ class Core(object):
self.xmpp.set_keepalive_values)
self.add_configuration_handler("connection_check_interval",
self.xmpp.set_keepalive_values)
- self.add_configuration_handler("themes_dir",
- theming.update_themes_dir)
- self.add_configuration_handler("theme",
- self.on_theme_config_change)
+ self.add_configuration_handler("themes_dir", theming.update_themes_dir)
+ self.add_configuration_handler("theme", self.on_theme_config_change)
self.add_configuration_handler("use_bookmarks_method",
self.on_bookmarks_method_config_change)
- self.add_configuration_handler("password",
- self.on_password_change)
+ self.add_configuration_handler("password", self.on_password_change)
self.add_configuration_handler("enable_vertical_tab_list",
self.on_vertical_tab_list_config_change)
self.add_configuration_handler("vertical_tab_list_size",
@@ -394,14 +403,15 @@ class Core(object):
"""
Called when the request_message_receipts option changes
"""
- self.xmpp.plugin['xep_0184'].auto_request = config.get(option,
- default=True)
+ self.xmpp.plugin['xep_0184'].auto_request = config.get(
+ option, default=True)
def on_ack_receipts_config_change(self, option, value):
"""
Called when the ack_message_receipts option changes
"""
- self.xmpp.plugin['xep_0184'].auto_ack = config.get(option, default=True)
+ self.xmpp.plugin['xep_0184'].auto_ack = config.get(
+ option, default=True)
def on_plugins_dir_config_change(self, option, value):
"""
@@ -438,7 +448,6 @@ class Core(object):
"""
self.xmpp.password = value
-
def on_nick_determinism_changed(self, option, value):
"""If we change the value to true, we call /recolor on all the MucTabs, to
make the current nick colors reflect their deterministic value.
@@ -501,10 +510,10 @@ class Core(object):
"""
sig = args[0]
signals = {
- 1: 'SIGHUP',
- 13: 'SIGPIPE',
- 15: 'SIGTERM',
- }
+ 1: 'SIGHUP',
+ 13: 'SIGPIPE',
+ 15: 'SIGTERM',
+ }
log.error("%s received. Exiting…", signals[sig])
if config.get('enable_user_mood'):
@@ -547,13 +556,12 @@ class Core(object):
'The online help is here http://doc.poez.io/\n'
'No room is joined by default, but you can join poezio’s'
' room (with /join poezio@muc.poez.io), where you can'
- ' ask for help or tell us how great it is.',
- 'Help')
+ ' ask for help or tell us how great it is.', 'Help')
self.refresh_window()
self.xmpp.plugin['xep_0012'].begin_idle(jid=self.xmpp.boundjid)
def exit(self, event=None):
- log.debug("exit(%s)", event)
+ log.debug("exit(%s)", event)
asyncio.get_event_loop().stop()
def on_exception(self, typ, value, trace):
@@ -592,11 +600,13 @@ class Core(object):
"""
main loop waiting for the user to press a key
"""
+
def replace_line_breaks(key):
"replace ^J with \n"
if key == '^J':
return '\n'
return key
+
def separate_chars_from_bindings(char_list):
"""
returns a list of lists. For example if you give
@@ -638,7 +648,7 @@ class Core(object):
log.debug("Input is readable.")
big_char_list = [replace_key_with_bound(key)\
for key in self.read_keyboard()]
- log.debug("Got from keyboard: %s", (big_char_list,))
+ log.debug("Got from keyboard: %s", (big_char_list, ))
# whether to refresh after ALL keys have been handled
for char_list in separate_chars_from_bindings(big_char_list):
@@ -651,7 +661,8 @@ class Core(object):
except ValueError:
pass
else:
- if self.current_tab().nb == nb and config.get('go_to_previous_tab_on_alt_number'):
+ if self.current_tab().nb == nb and config.get(
+ 'go_to_previous_tab_on_alt_number'):
self.go_to_previous_tab()
else:
self.command.win('%d' % nb)
@@ -673,12 +684,10 @@ class Core(object):
"""
ok = roster.save_to_config_file()
ok = ok and config.silent_set('info_win_height',
- self.information_win_size,
- 'var')
+ self.information_win_size, 'var')
if not ok:
self.information('Unable to save runtime preferences'
- ' in the config file',
- 'Error')
+ ' in the config file', 'Error')
def on_roster_enter_key(self, roster_row):
"""
@@ -690,9 +699,8 @@ class Core(object):
else:
self.focus_tab_named(roster_row.bare_jid)
if isinstance(roster_row, Resource):
- if not self.get_conversation_by_jid(roster_row.jid,
- False,
- fallback_barejid=False):
+ if not self.get_conversation_by_jid(
+ roster_row.jid, False, fallback_barejid=False):
self.open_conversation_window(roster_row.jid)
else:
self.focus_tab_named(roster_row.jid)
@@ -716,7 +724,6 @@ class Core(object):
"""
self.do_command(text, True)
-
##################### Anything related to command execution ###################
def execute(self, line):
@@ -727,15 +734,14 @@ class Core(object):
return
if line.startswith('/'):
command = line.strip().split()[0][1:]
- arg = line[2+len(command):] # jump the '/' and the ' '
+ arg = line[2 + len(command):] # jump the '/' and the ' '
# example. on "/link 0 open", command = "link" and arg = "0 open"
if command in self.commands:
func = self.commands[command].func
func(arg)
return
else:
- self.information("Unknown command (%s)" % (command),
- 'Error')
+ self.information("Unknown command (%s)" % (command), 'Error')
def exec_command(self, command):
"""
@@ -770,16 +776,15 @@ class Core(object):
fifo_path = config.get('remote_fifo_path')
if not self.remote_fifo:
try:
- self.remote_fifo = Fifo(os.path.join(fifo_path,
- 'poezio.fifo'),
- 'w')
+ self.remote_fifo = Fifo(
+ os.path.join(fifo_path, 'poezio.fifo'), 'w')
except (OSError, IOError) as exc:
- log.error('Could not open the fifo for writing (%s)',
- os.path.join(fifo_path, './', 'poezio.fifo'),
- exc_info=True)
+ log.error(
+ 'Could not open the fifo for writing (%s)',
+ os.path.join(fifo_path, './', 'poezio.fifo'),
+ exc_info=True)
self.information('Could not open the fifo '
- 'file for writing: %s' % exc,
- 'Error')
+ 'file for writing: %s' % exc, 'Error')
return
args = (pipes.quote(arg.replace('\n', ' ')) for arg in command)
@@ -787,10 +792,11 @@ class Core(object):
try:
self.remote_fifo.write(command_str)
except (IOError) as exc:
- log.error('Could not write in the fifo (%s): %s',
- os.path.join(fifo_path, './', 'poezio.fifo'),
- repr(command),
- exc_info=True)
+ log.error(
+ 'Could not write in the fifo (%s): %s',
+ os.path.join(fifo_path, './', 'poezio.fifo'),
+ repr(command),
+ exc_info=True)
self.information('Could not execute %s: %s' % (command, exc),
'Error')
self.remote_fifo = None
@@ -799,12 +805,12 @@ class Core(object):
try:
executor.start()
except ValueError as exc:
- log.error('Could not execute command (%s)',
- repr(command),
- exc_info=True)
+ log.error(
+ 'Could not execute command (%s)',
+ repr(command),
+ exc_info=True)
self.information('%s' % exc, 'Error')
-
def do_command(self, key, raw):
"""
Execute the action associated with a key
@@ -824,7 +830,6 @@ class Core(object):
else:
self.current_tab().on_input(key, raw)
-
def try_execute(self, line):
"""
Try to execute a command in the current tab
@@ -835,7 +840,6 @@ class Core(object):
except:
log.error('Execute failed (%s)', line, exc_info=True)
-
########################## TImed Events #######################################
def remove_timed_event(self, event):
@@ -844,9 +848,8 @@ class Core(object):
def add_timed_event(self, event):
"""Add a new timed event"""
- event.handler = asyncio.get_event_loop().call_later(event.delay,
- event.callback,
- *event.args)
+ event.handler = asyncio.get_event_loop().call_later(
+ event.delay, event.callback, *event.args)
####################### XMPP-related actions ##################################
@@ -894,7 +897,10 @@ class Core(object):
if reconnect:
# Add a one-time event to reconnect as soon as we are
# effectively disconnected
- self.xmpp.add_event_handler('disconnected', lambda event: self.xmpp.connect(), disposable=True)
+ self.xmpp.add_event_handler(
+ 'disconnected',
+ lambda event: self.xmpp.connect(),
+ disposable=True)
def send_message(self, msg):
"""
@@ -913,20 +919,19 @@ class Core(object):
or a mediated one if it does not.
TODO: allow passwords
"""
+
def callback(iq):
if not iq:
return
if 'jabber:x:conference' in iq['disco_info'].get_features():
self.xmpp.plugin['xep_0249'].send_invitation(
- jid,
- room,
- reason=reason)
- else: # fallback
- self.xmpp.plugin['xep_0045'].invite(room, jid,
- reason=reason or '')
+ jid, room, reason=reason)
+ else: # fallback
+ self.xmpp.plugin['xep_0045'].invite(
+ room, jid, reason=reason or '')
- self.xmpp.plugin['xep_0030'].get_info(jid=jid, timeout=5,
- callback=callback)
+ self.xmpp.plugin['xep_0030'].get_info(
+ jid=jid, timeout=5, callback=callback)
def get_error_message(self, stanza, deprecated=False):
"""
@@ -951,16 +956,22 @@ class Core(object):
body = condition or 'Unknown error'
if code:
message = '%(from)s: %(code)s - %(msg)s: %(body)s' % {
- 'from': sender, 'msg': msg, 'body': body, 'code': code}
+ 'from': sender,
+ 'msg': msg,
+ 'body': body,
+ 'code': code
+ }
else:
message = '%(from)s: %(msg)s: %(body)s' % {
- 'from': sender, 'msg': msg, 'body': body}
+ 'from': sender,
+ 'msg': msg,
+ 'body': body
+ }
return message
-
####################### Tab logic-related things ##############################
- ### Tab getters ###
+### Tab getters ###
def get_tabs(self, cls=None):
"Get all the tabs of a type"
@@ -1002,8 +1013,8 @@ class Core(object):
# We create a dynamic conversation with the bare Jid if
# nothing was found (and we lock it to the resource
# later)
- conversation = self.open_conversation_window(jid.bare,
- False)
+ conversation = self.open_conversation_window(
+ jid.bare, False)
else:
conversation = None
return conversation
@@ -1062,7 +1073,8 @@ class Core(object):
if not target:
if new_pos < len(self.tabs):
old_tab = self.tabs[old_pos]
- self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab(self)
+ self.tabs[new_pos], self.tabs[old_pos] = old_tab, tabs.GapTab(
+ self)
else:
self.tabs.append(self.tabs[old_pos])
self.tabs[old_pos] = tabs.GapTab(self)
@@ -1139,6 +1151,7 @@ class Core(object):
Read 2 more chars and go to the tab
with the given number
"""
+
def read_next_digit(digit):
try:
int(digit)
@@ -1156,6 +1169,7 @@ class Core(object):
else:
# We need to read more digits
keyboard.continuation_keys_callback = read_next_digit
+
keyboard.continuation_keys_callback = read_next_digit
def go_to_roster(self):
@@ -1164,7 +1178,7 @@ class Core(object):
def go_to_previous_tab(self):
"Go to the previous tab"
- self.command.win('%s' % (self.previous_tab_nb,))
+ self.command.win('%s' % (self.previous_tab_nb, ))
def go_to_important_room(self):
"""
@@ -1183,15 +1197,14 @@ class Core(object):
else:
tab_refs[tab.state].append(tab)
# sort the state by priority and remove those with negative priority
- states = sorted(tab_refs.keys(),
- key=(lambda x: priority.get(x, 0)),
- reverse=True)
+ states = sorted(
+ tab_refs.keys(), key=(lambda x: priority.get(x, 0)), reverse=True)
states = [state for state in states if priority.get(state, -1) >= 0]
for state in states:
for tab in tab_refs[state]:
- if (tab.nb < self.current_tab_nb and
- tab_refs[state][-1].nb > self.current_tab_nb):
+ if (tab.nb < self.current_tab_nb
+ and tab_refs[state][-1].nb > self.current_tab_nb):
continue
self.command.win('%s' % tab.nb)
return
@@ -1202,7 +1215,7 @@ class Core(object):
for tab in self.tabs:
if tab.name == tab_name:
if (type_ and (isinstance(tab, type_))) or not type_:
- self.command.win('%s' % (tab.nb,))
+ self.command.win('%s' % (tab.nb, ))
return True
return False
@@ -1249,7 +1262,7 @@ class Core(object):
"""
Open a Private conversation in a MUC and focus if needed.
"""
- complete_jid = room_name+'/'+user_nick
+ complete_jid = room_name + '/' + user_nick
# if the room exists, focus it and return
for tab in self.get_tabs(tabs.PrivateTab):
if tab.name == complete_jid:
@@ -1301,12 +1314,14 @@ class Core(object):
if tab:
tab.rename_user(old_nick, user)
- def on_user_left_private_conversation(self, room_name, user, status_message):
+ def on_user_left_private_conversation(self, room_name, user,
+ status_message):
"""
The user left the MUC: add a message in the associated
private conversation
"""
- tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick), tabs.PrivateTab)
+ tab = self.get_tab_by_name('%s/%s' % (room_name, user.nick),
+ tabs.PrivateTab)
if tab:
tab.user_left(status_message, user)
@@ -1315,7 +1330,8 @@ class Core(object):
The user joined a MUC: add a message in the associated
private conversation
"""
- tab = self.get_tab_by_name('%s/%s' % (room_name, nick), tabs.PrivateTab)
+ tab = self.get_tab_by_name('%s/%s' % (room_name, nick),
+ tabs.PrivateTab)
if tab:
tab.user_rejoined(nick)
@@ -1352,10 +1368,10 @@ class Core(object):
if tab is None:
tab = self.current_tab()
if isinstance(tab, tabs.RosterInfoTab):
- return # The tab 0 should NEVER be closed
+ return # The tab 0 should NEVER be closed
tab.on_close()
- del tab.key_func # Remove self references
- del tab.commands # and make the object collectable
+ del tab.key_func # Remove self references
+ del tab.commands # and make the object collectable
nb = tab.nb
if was_current:
if self.previous_tab_nb != nb:
@@ -1365,7 +1381,7 @@ class Core(object):
if nb >= len(self.tabs) - 1:
self.tabs.remove(tab)
nb -= 1
- while not self.tabs[nb]: # remove the trailing gaps
+ while not self.tabs[nb]: # remove the trailing gaps
self.tabs.pop()
nb -= 1
else:
@@ -1397,7 +1413,6 @@ class Core(object):
if self.current_tab() is tab:
self.refresh_window()
-
####################### Curses and ui-related stuff ###########################
def doupdate(self):
@@ -1412,18 +1427,21 @@ class Core(object):
"""
filter_types = config.get('information_buffer_type_filter').split(':')
if typ.lower() in filter_types:
- log.debug('Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration', typ, msg)
+ log.debug(
+ 'Did not show the message:\n\t%s> %s \n\tdue to information_popup_type_filter configuration',
+ typ, msg)
return False
filter_messages = config.get('filter_info_messages').split(':')
for words in filter_messages:
if words and words in msg:
- log.debug('Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration', typ, msg)
+ log.debug(
+ 'Did not show the message:\n\t%s> %s \n\tdue to filter_info_messages configuration',
+ typ, msg)
return False
colors = get_theme().INFO_COLORS
color = colors.get(typ.lower(), colors.get('default', None))
- nb_lines = self.information_buffer.add_message(msg,
- nickname=typ,
- nick_color=color)
+ nb_lines = self.information_buffer.add_message(
+ msg, nickname=typ, nick_color=color)
popup_on = config.get('information_buffer_popup_on').split()
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
@@ -1449,8 +1467,8 @@ class Core(object):
curses.start_color()
curses.use_default_colors()
theming.reload_theme()
- curses.ungetch(" ") # H4X: without this, the screen is
- stdscr.getkey() # erased on the first "getkey()"
+ curses.ungetch(" ") # H4X: without this, the screen is
+ stdscr.getkey() # erased on the first "getkey()"
def reset_curses(self):
"""
@@ -1604,9 +1622,8 @@ class Core(object):
if time <= 0 or size <= 0:
return
result = self.grow_information_win(size)
- timed_event = timed_events.DelayedEvent(time,
- self.shrink_information_win,
- result)
+ timed_event = timed_events.DelayedEvent(
+ time, self.shrink_information_win, result)
self.add_timed_event(timed_event)
self.refresh_window()
@@ -1627,12 +1644,10 @@ class Core(object):
self.information_win_size = tabs.Tab.height - 6
if tabs.Tab.height < 6:
self.information_win_size = 0
- height = (tabs.Tab.height - 1 - self.information_win_size
- - tabs.Tab.tab_win_height())
- self.information_win.resize(self.information_win_size,
- tabs.Tab.width,
- height,
- 0)
+ height = (tabs.Tab.height - 1 - self.information_win_size -
+ tabs.Tab.tab_win_height())
+ self.information_win.resize(self.information_win_size, tabs.Tab.width,
+ height, 0)
def resize_global_info_bar(self):
"""
@@ -1645,16 +1660,15 @@ class Core(object):
return
try:
height, _ = self.stdscr.getmaxyx()
- truncated_win = self.stdscr.subwin(height,
- config.get('vertical_tab_list_size'),
- 0, 0)
+ truncated_win = self.stdscr.subwin(
+ height, config.get('vertical_tab_list_size'), 0, 0)
except:
log.error('Curses error on infobar resize', exc_info=True)
return
- self.left_tab_win = windows.VerticalGlobalInfoBar(self, truncated_win)
+ self.left_tab_win = windows.VerticalGlobalInfoBar(
+ self, truncated_win)
elif not self.size.core_degrade_y:
- self.tab_win.resize(1, tabs.Tab.width,
- tabs.Tab.height - 2, 0)
+ self.tab_win.resize(1, tabs.Tab.width, tabs.Tab.height - 2, 0)
self.left_tab_win = None
def add_message_to_text_buffer(self, buff, txt, nickname=None):
@@ -1663,7 +1677,8 @@ class Core(object):
(in the Info tab of the info window in the RosterTab)
"""
if not buff:
- self.information('Trying to add a message in no room: %s' % txt, 'Error')
+ self.information('Trying to add a message in no room: %s' % txt,
+ 'Error')
return
buff.add_message(txt, nickname=nickname)
@@ -1683,11 +1698,11 @@ class Core(object):
# the screen that they can occupy, and we draw the tab list on the
# remaining space, on the left
height, width = self.stdscr.getmaxyx()
- if (config.get('enable_vertical_tab_list') and
- not self.size.core_degrade_x):
+ if (config.get('enable_vertical_tab_list')
+ and not self.size.core_degrade_x):
try:
scr = self.stdscr.subwin(0,
- config.get('vertical_tab_list_size'))
+ config.get('vertical_tab_list_size'))
except:
log.error('Curses error on resize', exc_info=True)
return
@@ -1739,227 +1754,300 @@ class Core(object):
"""
Register the commands when poezio starts
"""
- self.register_command('help', self.command.help,
- usage='[command]',
- shortdesc='\\_o< KOIN KOIN KOIN',
- completion=self.completion.help)
- self.register_command('join', self.command.join,
- usage="[room_name][@server][/nick] [password]",
- desc="Join the specified room. You can specify a nickname "
- "after a slash (/). If no nickname is specified, you will"
- " use the default_nick in the configuration file. You can"
- " omit the room name: you will then join the room you\'re"
- " looking at (useful if you were kicked). You can also "
- "provide a room_name without specifying a server, the "
- "server of the room you're currently in will be used. You"
- " can also provide a password to join the room.\nExamples"
- ":\n/join room@server.tld\n/join room@server.tld/John\n"
- "/join room2\n/join /me_again\n/join\n/join room@server"
- ".tld/my_nick password\n/join / password",
- shortdesc='Join a room',
- completion=self.completion.join)
- self.register_command('exit', self.command.quit,
- desc='Just disconnect from the server and exit poezio.',
- shortdesc='Exit poezio.')
- self.register_command('quit', self.command.quit,
- desc='Just disconnect from the server and exit poezio.',
- shortdesc='Exit poezio.')
- self.register_command('next', self.rotate_rooms_right,
- shortdesc='Go to the next room.')
- self.register_command('prev', self.rotate_rooms_left,
- shortdesc='Go to the previous room.')
- self.register_command('win', self.command.win,
- usage='<number or name>',
- shortdesc='Go to the specified room',
- completion=self.completion.win)
+ self.register_command(
+ 'help',
+ self.command.help,
+ usage='[command]',
+ shortdesc='\\_o< KOIN KOIN KOIN',
+ completion=self.completion.help)
+ self.register_command(
+ 'join',
+ self.command.join,
+ usage="[room_name][@server][/nick] [password]",
+ desc="Join the specified room. You can specify a nickname "
+ "after a slash (/). If no nickname is specified, you will"
+ " use the default_nick in the configuration file. You can"
+ " omit the room name: you will then join the room you\'re"
+ " looking at (useful if you were kicked). You can also "
+ "provide a room_name without specifying a server, the "
+ "server of the room you're currently in will be used. You"
+ " can also provide a password to join the room.\nExamples"
+ ":\n/join room@server.tld\n/join room@server.tld/John\n"
+ "/join room2\n/join /me_again\n/join\n/join room@server"
+ ".tld/my_nick password\n/join / password",
+ shortdesc='Join a room',
+ completion=self.completion.join)
+ self.register_command(
+ 'exit',
+ self.command.quit,
+ desc='Just disconnect from the server and exit poezio.',
+ shortdesc='Exit poezio.')
+ self.register_command(
+ 'quit',
+ self.command.quit,
+ desc='Just disconnect from the server and exit poezio.',
+ shortdesc='Exit poezio.')
+ self.register_command(
+ 'next', self.rotate_rooms_right, shortdesc='Go to the next room.')
+ self.register_command(
+ 'prev',
+ self.rotate_rooms_left,
+ shortdesc='Go to the previous room.')
+ self.register_command(
+ 'win',
+ self.command.win,
+ usage='<number or name>',
+ shortdesc='Go to the specified room',
+ completion=self.completion.win)
self.commands['w'] = self.commands['win']
- self.register_command('move_tab', self.command.move_tab,
- usage='<source> <destination>',
- desc="Insert the <source> tab at the position of "
- "<destination>. This will make the following tabs shift in"
- " some cases (refer to the documentation). A tab can be "
- "designated by its number or by the beginning of its "
- "address. You can use \".\" as a shortcut for the current "
- "tab.",
- shortdesc='Move a tab.',
- completion=self.completion.move_tab)
- self.register_command('destroy_room', self.command.destroy_room,
- usage='[room JID]',
- desc='Try to destroy the room [room JID], or the current'
- ' tab if it is a multi-user chat and [room JID] is '
- 'not given.',
- shortdesc='Destroy a room.',
- completion=None)
- self.register_command('show', self.command.status,
- usage='<availability> [status message]',
- desc="Sets your availability and (optionally) your status "
- "message. The <availability> argument is one of \"available"
- ", chat, away, afk, dnd, busy, xa\" and the optional "
- "[status message] argument will be your status message.",
- shortdesc='Change your availability.',
- completion=self.completion.status)
+ self.register_command(
+ 'move_tab',
+ self.command.move_tab,
+ usage='<source> <destination>',
+ desc="Insert the <source> tab at the position of "
+ "<destination>. This will make the following tabs shift in"
+ " some cases (refer to the documentation). A tab can be "
+ "designated by its number or by the beginning of its "
+ "address. You can use \".\" as a shortcut for the current "
+ "tab.",
+ shortdesc='Move a tab.',
+ completion=self.completion.move_tab)
+ self.register_command(
+ 'destroy_room',
+ self.command.destroy_room,
+ usage='[room JID]',
+ desc='Try to destroy the room [room JID], or the current'
+ ' tab if it is a multi-user chat and [room JID] is '
+ 'not given.',
+ shortdesc='Destroy a room.',
+ completion=None)
+ self.register_command(
+ 'show',
+ self.command.status,
+ usage='<availability> [status message]',
+ desc="Sets your availability and (optionally) your status "
+ "message. The <availability> argument is one of \"available"
+ ", chat, away, afk, dnd, busy, xa\" and the optional "
+ "[status message] argument will be your status message.",
+ shortdesc='Change your availability.',
+ completion=self.completion.status)
self.commands['status'] = self.commands['show']
- self.register_command('bookmark_local', self.command.bookmark_local,
- usage="[roomname][/nick] [password]",
- desc="Bookmark Local: Bookmark locally the specified room "
- "(you will then auto-join it on each poezio start). This"
- " commands uses almost the same syntaxe as /join. Type "
- "/help join for syntax examples. Note that when typing "
- "\"/bookmark\" on its own, the room will be bookmarked "
- "with the nickname you\'re currently using in this room "
- "(instead of default_nick)",
- shortdesc='Bookmark a room locally.',
- completion=self.completion.bookmark_local)
- self.register_command('bookmark', self.command.bookmark,
- usage="[roomname][/nick] [autojoin] [password]",
- desc="Bookmark: Bookmark online the specified room (you "
- "will then auto-join it on each poezio start if autojoin"
- " is specified and is 'true'). This commands uses almost"
- " the same syntax as /join. Type /help join for syntax "
- "examples. Note that when typing \"/bookmark\" alone, the"
- " room will be bookmarked with the nickname you\'re "
- "currently using in this room (instead of default_nick).",
- shortdesc="Bookmark a room online.",
- completion=self.completion.bookmark)
- self.register_command('set', self.command.set,
- usage="[plugin|][section] <option> [value]",
- desc="Set the value of an option in your configuration file."
- " You can, for example, change your default nickname by "
- "doing `/set default_nick toto` or your resource with `/set"
- " resource blabla`. You can also set options in specific "
- "sections with `/set bindings M-i ^i` or in specific plugin"
- " with `/set mpd_client| host 127.0.0.1`. `toggle` can be "
- "used as a special value to toggle a boolean option.",
- shortdesc="Set the value of an option",
- completion=self.completion.set)
- self.register_command('set_default', self.command.set_default,
- usage="[section] <option>",
- desc="Set the default value of an option. For example, "
- "`/set_default resource` will reset the resource "
- "option. You can also reset options in specific "
- "sections by doing `/set_default section option`.",
- shortdesc="Set the default value of an option",
- completion=self.completion.set_default)
- self.register_command('toggle', self.command.toggle,
- usage='<option>',
- desc='Shortcut for /set <option> toggle',
- shortdesc='Toggle an option',
- completion=self.completion.toggle)
- self.register_command('theme', self.command.theme,
- usage='[theme name]',
- desc="Reload the theme defined in the config file. If theme"
- "_name is provided, set that theme before reloading it.",
- shortdesc='Load a theme',
- completion=self.completion.theme)
- self.register_command('list', self.command.list,
- usage='[server]',
- desc="Get the list of public rooms"
- " on the specified server.",
- shortdesc='List the rooms.',
- completion=self.completion.list)
- self.register_command('message', self.command.message,
- usage='<jid> [optional message]',
- desc="Open a conversation with the specified JID (even if it"
- " is not in our roster), and send a message to it, if the "
- "message is specified.",
- shortdesc='Send a message',
- completion=self.completion.message)
- self.register_command('version', self.command.version,
- usage='<jid>',
- desc="Get the software version of the given JID (usually its"
- " XMPP client and Operating System).",
- shortdesc='Get the software version of a JID.',
- completion=self.completion.version)
- self.register_command('server_cycle', self.command.server_cycle,
- usage='[domain] [message]',
- desc='Disconnect and reconnect in all the rooms in domain.',
- shortdesc='Cycle a range of rooms',
- completion=self.completion.server_cycle)
- self.register_command('bind', self.command.bind,
- usage='<key> <equ>',
- desc="Bind a key to another key or to a “command”. For "
- "example \"/bind ^H KEY_UP\" makes Control + h do the"
- " same same as the Up key.",
- completion=self.completion.bind,
- shortdesc='Bind a key to another key.')
- self.register_command('load', self.command.load,
- usage='<plugin> [<otherplugin> …]',
- shortdesc='Load the specified plugin(s)',
- completion=self.plugin_manager.completion_load)
- self.register_command('unload', self.command.unload,
- usage='<plugin> [<otherplugin> …]',
- shortdesc='Unload the specified plugin(s)',
- completion=self.plugin_manager.completion_unload)
- self.register_command('plugins', self.command.plugins,
- shortdesc='Show the plugins in use.')
- self.register_command('presence', self.command.presence,
- usage='<JID> [type] [status]',
- desc="Send a directed presence to <JID> and using"
- " [type] and [status] if provided.",
- shortdesc='Send a directed presence.',
- completion=self.completion.presence)
- self.register_command('rawxml', self.command.rawxml,
- usage='<xml>',
- shortdesc='Send a custom xml stanza.')
- self.register_command('invite', self.command.invite,
- usage='<jid> <room> [reason]',
- desc='Invite jid in room with reason.',
- shortdesc='Invite someone in a room.',
- completion=self.completion.invite)
- self.register_command('invitations', self.command.invitations,
- shortdesc='Show the pending invitations.')
- self.register_command('bookmarks', self.command.bookmarks,
- shortdesc='Show the current bookmarks.')
- self.register_command('remove_bookmark', self.command.remove_bookmark,
- usage='[jid]',
- desc="Remove the specified bookmark, or the "
- "bookmark on the current tab, if any.",
- shortdesc='Remove a bookmark',
- completion=self.completion.remove_bookmark)
- self.register_command('xml_tab', self.command.xml_tab,
- shortdesc='Open an XML tab.')
- self.register_command('runkey', self.command.runkey,
- usage='<key>',
- shortdesc='Execute the action defined for <key>.',
- completion=self.completion.runkey)
- self.register_command('self', self.command.self_,
- shortdesc='Remind you of who you are.')
- self.register_command('last_activity', self.command.last_activity,
- usage='<jid>',
- desc='Informs you of the last activity of a JID.',
- shortdesc='Get the activity of someone.',
- completion=self.completion.last_activity)
- self.register_command('ad-hoc', self.command.adhoc,
- usage='<jid>',
- shortdesc='List available ad-hoc commands on the given jid')
- self.register_command('reload', self.command.reload,
- shortdesc='Reload the config. You can achieve the same by '
- 'sending SIGUSR1 to poezio.')
+ self.register_command(
+ 'bookmark_local',
+ self.command.bookmark_local,
+ usage="[roomname][/nick] [password]",
+ desc="Bookmark Local: Bookmark locally the specified room "
+ "(you will then auto-join it on each poezio start). This"
+ " commands uses almost the same syntaxe as /join. Type "
+ "/help join for syntax examples. Note that when typing "
+ "\"/bookmark\" on its own, the room will be bookmarked "
+ "with the nickname you\'re currently using in this room "
+ "(instead of default_nick)",
+ shortdesc='Bookmark a room locally.',
+ completion=self.completion.bookmark_local)
+ self.register_command(
+ 'bookmark',
+ self.command.bookmark,
+ usage="[roomname][/nick] [autojoin] [password]",
+ desc="Bookmark: Bookmark online the specified room (you "
+ "will then auto-join it on each poezio start if autojoin"
+ " is specified and is 'true'). This commands uses almost"
+ " the same syntax as /join. Type /help join for syntax "
+ "examples. Note that when typing \"/bookmark\" alone, the"
+ " room will be bookmarked with the nickname you\'re "
+ "currently using in this room (instead of default_nick).",
+ shortdesc="Bookmark a room online.",
+ completion=self.completion.bookmark)
+ self.register_command(
+ 'set',
+ self.command.set,
+ usage="[plugin|][section] <option> [value]",
+ desc="Set the value of an option in your configuration file."
+ " You can, for example, change your default nickname by "
+ "doing `/set default_nick toto` or your resource with `/set"
+ " resource blabla`. You can also set options in specific "
+ "sections with `/set bindings M-i ^i` or in specific plugin"
+ " with `/set mpd_client| host 127.0.0.1`. `toggle` can be "
+ "used as a special value to toggle a boolean option.",
+ shortdesc="Set the value of an option",
+ completion=self.completion.set)
+ self.register_command(
+ 'set_default',
+ self.command.set_default,
+ usage="[section] <option>",
+ desc="Set the default value of an option. For example, "
+ "`/set_default resource` will reset the resource "
+ "option. You can also reset options in specific "
+ "sections by doing `/set_default section option`.",
+ shortdesc="Set the default value of an option",
+ completion=self.completion.set_default)
+ self.register_command(
+ 'toggle',
+ self.command.toggle,
+ usage='<option>',
+ desc='Shortcut for /set <option> toggle',
+ shortdesc='Toggle an option',
+ completion=self.completion.toggle)
+ self.register_command(
+ 'theme',
+ self.command.theme,
+ usage='[theme name]',
+ desc="Reload the theme defined in the config file. If theme"
+ "_name is provided, set that theme before reloading it.",
+ shortdesc='Load a theme',
+ completion=self.completion.theme)
+ self.register_command(
+ 'list',
+ self.command.list,
+ usage='[server]',
+ desc="Get the list of public rooms"
+ " on the specified server.",
+ shortdesc='List the rooms.',
+ completion=self.completion.list)
+ self.register_command(
+ 'message',
+ self.command.message,
+ usage='<jid> [optional message]',
+ desc="Open a conversation with the specified JID (even if it"
+ " is not in our roster), and send a message to it, if the "
+ "message is specified.",
+ shortdesc='Send a message',
+ completion=self.completion.message)
+ self.register_command(
+ 'version',
+ self.command.version,
+ usage='<jid>',
+ desc="Get the software version of the given JID (usually its"
+ " XMPP client and Operating System).",
+ shortdesc='Get the software version of a JID.',
+ completion=self.completion.version)
+ self.register_command(
+ 'server_cycle',
+ self.command.server_cycle,
+ usage='[domain] [message]',
+ desc='Disconnect and reconnect in all the rooms in domain.',
+ shortdesc='Cycle a range of rooms',
+ completion=self.completion.server_cycle)
+ self.register_command(
+ 'bind',
+ self.command.bind,
+ usage='<key> <equ>',
+ desc="Bind a key to another key or to a “command”. For "
+ "example \"/bind ^H KEY_UP\" makes Control + h do the"
+ " same same as the Up key.",
+ completion=self.completion.bind,
+ shortdesc='Bind a key to another key.')
+ self.register_command(
+ 'load',
+ self.command.load,
+ usage='<plugin> [<otherplugin> …]',
+ shortdesc='Load the specified plugin(s)',
+ completion=self.plugin_manager.completion_load)
+ self.register_command(
+ 'unload',
+ self.command.unload,
+ usage='<plugin> [<otherplugin> …]',
+ shortdesc='Unload the specified plugin(s)',
+ completion=self.plugin_manager.completion_unload)
+ self.register_command(
+ 'plugins',
+ self.command.plugins,
+ shortdesc='Show the plugins in use.')
+ self.register_command(
+ 'presence',
+ self.command.presence,
+ usage='<JID> [type] [status]',
+ desc="Send a directed presence to <JID> and using"
+ " [type] and [status] if provided.",
+ shortdesc='Send a directed presence.',
+ completion=self.completion.presence)
+ self.register_command(
+ 'rawxml',
+ self.command.rawxml,
+ usage='<xml>',
+ shortdesc='Send a custom xml stanza.')
+ self.register_command(
+ 'invite',
+ self.command.invite,
+ usage='<jid> <room> [reason]',
+ desc='Invite jid in room with reason.',
+ shortdesc='Invite someone in a room.',
+ completion=self.completion.invite)
+ self.register_command(
+ 'invitations',
+ self.command.invitations,
+ shortdesc='Show the pending invitations.')
+ self.register_command(
+ 'bookmarks',
+ self.command.bookmarks,
+ shortdesc='Show the current bookmarks.')
+ self.register_command(
+ 'remove_bookmark',
+ self.command.remove_bookmark,
+ usage='[jid]',
+ desc="Remove the specified bookmark, or the "
+ "bookmark on the current tab, if any.",
+ shortdesc='Remove a bookmark',
+ completion=self.completion.remove_bookmark)
+ self.register_command(
+ 'xml_tab', self.command.xml_tab, shortdesc='Open an XML tab.')
+ self.register_command(
+ 'runkey',
+ self.command.runkey,
+ usage='<key>',
+ shortdesc='Execute the action defined for <key>.',
+ completion=self.completion.runkey)
+ self.register_command(
+ 'self', self.command.self_, shortdesc='Remind you of who you are.')
+ self.register_command(
+ 'last_activity',
+ self.command.last_activity,
+ usage='<jid>',
+ desc='Informs you of the last activity of a JID.',
+ shortdesc='Get the activity of someone.',
+ completion=self.completion.last_activity)
+ self.register_command(
+ 'ad-hoc',
+ self.command.adhoc,
+ usage='<jid>',
+ shortdesc='List available ad-hoc commands on the given jid')
+ self.register_command(
+ 'reload',
+ self.command.reload,
+ shortdesc='Reload the config. You can achieve the same by '
+ 'sending SIGUSR1 to poezio.')
if config.get('enable_user_activity'):
- self.register_command('activity', self.command.activity,
- usage='[<general> [specific] [text]]',
- desc='Send your current activity to your contacts '
- '(use the completion). Nothing means '
- '"stop broadcasting an activity".',
- shortdesc='Send your activity.',
- completion=self.completion.activity)
+ self.register_command(
+ 'activity',
+ self.command.activity,
+ usage='[<general> [specific] [text]]',
+ desc='Send your current activity to your contacts '
+ '(use the completion). Nothing means '
+ '"stop broadcasting an activity".',
+ shortdesc='Send your activity.',
+ completion=self.completion.activity)
if config.get('enable_user_mood'):
- self.register_command('mood', self.command.mood,
- usage='[<mood> [text]]',
- desc='Send your current mood to your contacts '
- '(use the completion). Nothing means '
- '"stop broadcasting a mood".',
- shortdesc='Send your mood.',
- completion=self.completion.mood)
+ self.register_command(
+ 'mood',
+ self.command.mood,
+ usage='[<mood> [text]]',
+ desc='Send your current mood to your contacts '
+ '(use the completion). Nothing means '
+ '"stop broadcasting a mood".',
+ shortdesc='Send your mood.',
+ completion=self.completion.mood)
if config.get('enable_user_gaming'):
- self.register_command('gaming', self.command.gaming,
- usage='[<game name> [server address]]',
- desc='Send your current gaming activity to '
- 'your contacts. Nothing means "stop '
- 'broadcasting a gaming activity".',
- shortdesc='Send your gaming activity.',
- completion=None)
+ self.register_command(
+ 'gaming',
+ self.command.gaming,
+ usage='[<game name> [server address]]',
+ desc='Send your current gaming activity to '
+ 'your contacts. Nothing means "stop '
+ 'broadcasting a gaming activity".',
+ shortdesc='Send your gaming activity.',
+ completion=None)
+
####################### Random things to move #################################
@@ -1971,22 +2059,26 @@ class Core(object):
tab = self.get_tab_by_name(bm.jid, tabs.MucTab)
nick = bm.nick if bm.nick else self.own_nick
if not tab:
- self.open_new_room(bm.jid, nick, focus=False,
- password=bm.password)
+ self.open_new_room(
+ bm.jid, nick, focus=False, password=bm.password)
self.initial_joins.append(bm.jid)
# do not join rooms that do not have autojoin
# but display them anyway
if bm.autojoin:
- muc.join_groupchat(self, bm.jid, nick,
- passwd=bm.password,
- status=self.status.message,
- show=self.status.show)
+ muc.join_groupchat(
+ self,
+ bm.jid,
+ nick,
+ passwd=bm.password,
+ status=self.status.message,
+ show=self.status.show)
def check_bookmark_storage(self, features):
private = 'jabber:iq:private' in features
pep_ = 'http://jabber.org/protocol/pubsub#publish' in features
self.bookmarks.available_storage['private'] = private
self.bookmarks.available_storage['pep'] = pep_
+
def _join_remote_only(iq):
if iq['type'] == 'error':
type_ = iq['error']['type']
@@ -1998,8 +2090,10 @@ class Core(object):
return
remote_bookmarks = self.bookmarks.remote()
self.join_initial_rooms(remote_bookmarks)
+
if not self.xmpp.anon and config.get('use_remote_bookmarks'):
- self.bookmarks.get_remote(self.xmpp, self.information, _join_remote_only)
+ self.bookmarks.get_remote(self.xmpp, self.information,
+ _join_remote_only)
def room_error(self, error, room_name):
"""
@@ -2009,32 +2103,42 @@ class Core(object):
if not tab:
return
error_message = self.get_error_message(error)
- tab.add_message(error_message, highlight=True, nickname='Error',
- nick_color=get_theme().COLOR_ERROR_MSG, typ=2)
+ tab.add_message(
+ error_message,
+ highlight=True,
+ nickname='Error',
+ nick_color=get_theme().COLOR_ERROR_MSG,
+ typ=2)
code = error['error']['code']
if code == '401':
msg = 'To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'
tab.add_message(msg, typ=2)
if code == '409':
if config.get('alternative_nickname') != '':
- self.command.join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname')))
+ self.command.join(
+ '%s/%s' %
+ (tab.name,
+ tab.own_nick + config.get('alternative_nickname')))
else:
if not tab.joined:
- tab.add_message('You can join the room with an other nick, by typing "/join /other_nick"', typ=2)
+ tab.add_message(
+ 'You can join the room with an other nick, by typing "/join /other_nick"',
+ typ=2)
self.refresh_window()
-
class KeyDict(dict):
"""
A dict, with a wrapper for get() that will return a custom value
if the key starts with _exc_
"""
+
def get(self, k, d=None):
if isinstance(k, str) and k.startswith('_exc_') and len(k) > 5:
return lambda: dict.get(self, '_exc_')(k[5:])
return dict.get(self, k, d)
+
def replace_key_with_bound(key):
"""
Replace an inputted key with the one defined as its replacement
@@ -2044,5 +2148,3 @@ def replace_key_with_bound(key):
if not bind:
bind = key
return bind
-
-
diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py
index e6d54558..ac6ae573 100644
--- a/poezio/core/handlers.py
+++ b/poezio/core/handlers.py
@@ -80,6 +80,7 @@ class HandlerCore:
"""
Enable carbons & blocking on session start if wanted and possible
"""
+
def callback(iq):
if not iq:
return
@@ -87,21 +88,23 @@ class HandlerCore:
rostertab = self.core.get_tab_by_name('Roster', tabs.RosterInfoTab)
rostertab.check_blocking(features)
rostertab.check_saslexternal(features)
- if (config.get('enable_carbons') and
- 'urn:xmpp:carbons:2' in features):
+ if (config.get('enable_carbons')
+ and 'urn:xmpp:carbons:2' in features):
self.core.xmpp.plugin['xep_0280'].enable()
self.core.check_bookmark_storage(features)
- self.core.xmpp.plugin['xep_0030'].get_info(jid=self.core.xmpp.boundjid.domain,
- callback=callback)
+ self.core.xmpp.plugin['xep_0030'].get_info(
+ jid=self.core.xmpp.boundjid.domain, callback=callback)
def on_carbon_received(self, message):
"""
Carbon <received/> received
"""
+
def ignore_message(recv):
log.debug('%s has category conference, ignoring carbon',
recv['from'].server)
+
def receive_message(recv):
recv['to'] = self.core.xmpp.boundjid.full
if recv['receipt']:
@@ -109,12 +112,14 @@ class HandlerCore:
self.on_normal_message(recv)
recv = message['carbon_received']
- if (recv['from'].bare not in roster or
- roster[recv['from'].bare].subscription == 'none'):
- fixes.has_identity(self.core.xmpp, recv['from'].server,
- identity='conference',
- on_true=functools.partial(ignore_message, recv),
- on_false=functools.partial(receive_message, recv))
+ if (recv['from'].bare not in roster
+ or roster[recv['from'].bare].subscription == 'none'):
+ fixes.has_identity(
+ self.core.xmpp,
+ recv['from'].server,
+ identity='conference',
+ on_true=functools.partial(ignore_message, recv),
+ on_false=functools.partial(receive_message, recv))
return
else:
receive_message(recv)
@@ -123,20 +128,24 @@ class HandlerCore:
"""
Carbon <sent/> received
"""
+
def ignore_message(sent):
log.debug('%s has category conference, ignoring carbon',
sent['to'].server)
+
def send_message(sent):
sent['from'] = self.core.xmpp.boundjid.full
self.on_normal_message(sent)
sent = message['carbon_sent']
- if (sent['to'].bare not in roster or
- roster[sent['to'].bare].subscription == 'none'):
- fixes.has_identity(self.core.xmpp, sent['to'].server,
- identity='conference',
- on_true=functools.partial(ignore_message, sent),
- on_false=functools.partial(send_message, sent))
+ if (sent['to'].bare not in roster
+ or roster[sent['to'].bare].subscription == 'none'):
+ fixes.has_identity(
+ self.core.xmpp,
+ sent['to'].server,
+ identity='conference',
+ on_true=functools.partial(ignore_message, sent),
+ on_false=functools.partial(send_message, sent))
else:
send_message(sent)
@@ -150,7 +159,11 @@ class HandlerCore:
if jid.bare in self.core.pending_invites:
return
# there are 2 'x' tags in the messages, making message['x'] useless
- invite = StanzaBase(self.core.xmpp, xml=message.xml.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite'))
+ invite = StanzaBase(
+ self.core.xmpp,
+ xml=message.xml.find(
+ '{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite'
+ ))
inviter = invite['from']
reason = invite['reason']
password = invite['password']
@@ -204,7 +217,9 @@ class HandlerCore:
When receiving private message from a muc OR a normal message
(from one of our contacts)
"""
- if message.xml.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite') is not None:
+ if message.xml.find(
+ '{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}invite'
+ ) is not None:
return
if message['type'] == 'groupchat':
return
@@ -228,7 +243,8 @@ class HandlerCore:
self.core.room_error(message, jid_from.bare)
else:
text = self.core.get_error_message(message)
- p_tab = self.core.get_tab_by_name(jid_from.full, tabs.PrivateTab)
+ p_tab = self.core.get_tab_by_name(jid_from.full,
+ tabs.PrivateTab)
if p_tab:
p_tab.add_error(text)
else:
@@ -240,12 +256,11 @@ class HandlerCore:
self.core.information(error_msg, 'Error')
return
error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK),
- error_msg)
+ error_msg)
if not tab.nack_message('\n' + error, message['id'], message['to']):
tab.add_message(error, typ=0)
self.core.refresh_window()
-
def on_normal_message(self, message):
"""
When receiving "normal" messages (not a private message from a
@@ -254,14 +269,18 @@ class HandlerCore:
if message['type'] == 'error':
return
elif message['type'] == 'headline' and message['body']:
- return self.core.information('%s says: %s' % (message['from'], message['body']), 'Headline')
+ return self.core.information(
+ '%s says: %s' % (message['from'], message['body']), 'Headline')
- use_xhtml = config.get_by_tabname('enable_xhtml_im', message['from'].bare)
+ use_xhtml = config.get_by_tabname('enable_xhtml_im',
+ message['from'].bare)
tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
extract_images = config.get('extract_inline_images')
- body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
- tmp_dir=tmp_dir,
- extract_images=extract_images)
+ body = xhtml.get_body_from_message_stanza(
+ message,
+ use_xhtml=use_xhtml,
+ tmp_dir=tmp_dir,
+ extract_images=extract_images)
if not body:
if not self.core.xmpp.plugin['xep_0380'].has_eme(message):
return
@@ -279,7 +298,8 @@ class HandlerCore:
remote_nick = roster[conv_jid.bare].name
# check for a received nick
if not remote_nick and config.get('enable_user_nick'):
- if message.xml.find('{http://jabber.org/protocol/nick}nick') is not None:
+ if message.xml.find(
+ '{http://jabber.org/protocol/nick}nick') is not None:
remote_nick = message['nick']['nick']
if not remote_nick:
remote_nick = conv_jid.user
@@ -298,7 +318,8 @@ class HandlerCore:
return
conversation = self.core.get_conversation_by_jid(conv_jid, create=True)
- if isinstance(conversation, tabs.DynamicConversationTab) and conv_jid.resource:
+ if isinstance(conversation,
+ tabs.DynamicConversationTab) and conv_jid.resource:
conversation.lock(conv_jid.resource)
if not own and not conversation.nick:
@@ -312,9 +333,11 @@ class HandlerCore:
self.core.events.trigger('conversation_msg', message, conversation)
if not message['body']:
return
- body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
- tmp_dir=tmp_dir,
- extract_images=extract_images)
+ body = xhtml.get_body_from_message_stanza(
+ message,
+ use_xhtml=use_xhtml,
+ tmp_dir=tmp_dir,
+ extract_images=extract_images)
delayed, date = common.find_delayed_tag(message)
def try_modify():
@@ -324,21 +347,27 @@ class HandlerCore:
if replaced_id and config.get_by_tabname('group_corrections',
conv_jid.bare):
try:
- conversation.modify_message(body, replaced_id, message['id'], jid=jid,
- nickname=remote_nick)
+ conversation.modify_message(
+ body,
+ replaced_id,
+ message['id'],
+ jid=jid,
+ nickname=remote_nick)
return True
except CorrectionError:
log.debug('Unable to correct a message', exc_info=True)
return False
if not try_modify():
- conversation.add_message(body, date,
- nickname=remote_nick,
- nick_color=color,
- history=delayed,
- identifier=message['id'],
- jid=jid,
- typ=1)
+ conversation.add_message(
+ body,
+ date,
+ nickname=remote_nick,
+ nick_color=color,
+ history=delayed,
+ identifier=message['id'],
+ jid=jid,
+ typ=1)
if conversation.remote_wants_chatstates is None and not delayed:
if message['chat_state']:
@@ -366,7 +395,8 @@ class HandlerCore:
return
log.debug('Received 0084 avatar update from %s', jid)
try:
- metadata = msg['pubsub_event']['items']['item']['avatar_metadata']['items']
+ metadata = msg['pubsub_event']['items']['item']['avatar_metadata'][
+ 'items']
except Exception:
log.debug('Failed getting metadata from 0084:', exc_info=True)
return
@@ -387,12 +417,16 @@ class HandlerCore:
# If we didn’t have any, query the data instead.
if not info['url']:
try:
- result = yield from self.core.xmpp['xep_0084'].retrieve_avatar(jid,
- avatar_hash,
- timeout=60)
- contact.avatar = result['pubsub']['items']['item']['avatar_data']['value']
+ result = yield from self.core.xmpp[
+ 'xep_0084'].retrieve_avatar(
+ jid, avatar_hash, timeout=60)
+ contact.avatar = result['pubsub']['items']['item'][
+ 'avatar_data']['value']
except Exception:
- log.debug('Failed retrieving 0084 data from %s:', jid, exc_info=True)
+ log.debug(
+ 'Failed retrieving 0084 data from %s:',
+ jid,
+ exc_info=True)
continue
log.debug('Received %s avatar: %s', jid, info['type'])
@@ -427,9 +461,8 @@ class HandlerCore:
# If we didn’t have any, query the vCard instead.
try:
- result = yield from self.core.xmpp['xep_0054'].get_vcard(jid,
- cached=True,
- timeout=60)
+ result = yield from self.core.xmpp['xep_0054'].get_vcard(
+ jid, cached=True, timeout=60)
avatar = result['vcard_temp']['PHOTO']
contact.avatar = avatar['BINVAL']
except Exception:
@@ -473,25 +506,32 @@ class HandlerCore:
item = item['gaming']
# only name and server_address are used for now
contact.gaming = {
- 'character_name': item['character_name'],
- 'character_profile': item['character_profile'],
- 'name': item['name'],
- 'level': item['level'],
- 'uri': item['uri'],
- 'server_name': item['server_name'],
- 'server_address': item['server_address'],
- }
+ 'character_name': item['character_name'],
+ 'character_profile': item['character_profile'],
+ 'name': item['name'],
+ 'level': item['level'],
+ 'uri': item['uri'],
+ 'server_name': item['server_name'],
+ 'server_address': item['server_address'],
+ }
else:
contact.gaming = {}
if contact.gaming:
- logger.log_roster_change(contact.bare_jid, 'is playing %s' % (common.format_gaming_string(contact.gaming)))
+ logger.log_roster_change(
+ contact.bare_jid, 'is playing %s' %
+ (common.format_gaming_string(contact.gaming)))
- if old_gaming != contact.gaming and config.get_by_tabname('display_gaming_notifications', contact.bare_jid):
+ if old_gaming != contact.gaming and config.get_by_tabname(
+ 'display_gaming_notifications', contact.bare_jid):
if contact.gaming:
- self.core.information('%s is playing %s' % (contact.bare_jid, common.format_gaming_string(contact.gaming)), 'Gaming')
+ self.core.information(
+ '%s is playing %s' %
+ (contact.bare_jid,
+ common.format_gaming_string(contact.gaming)), 'Gaming')
else:
- self.core.information(contact.bare_jid + ' stopped playing.', 'Gaming')
+ self.core.information(contact.bare_jid + ' stopped playing.',
+ 'Gaming')
def on_mood_event(self, message):
"""
@@ -518,13 +558,18 @@ class HandlerCore:
contact.mood = ''
if contact.mood:
- logger.log_roster_change(contact.bare_jid, 'has now the mood: %s' % contact.mood)
+ logger.log_roster_change(contact.bare_jid,
+ 'has now the mood: %s' % contact.mood)
- if old_mood != contact.mood and config.get_by_tabname('display_mood_notifications', contact.bare_jid):
+ if old_mood != contact.mood and config.get_by_tabname(
+ 'display_mood_notifications', contact.bare_jid):
if contact.mood:
- self.core.information('Mood from '+ contact.bare_jid + ': ' + contact.mood, 'Mood')
+ self.core.information(
+ 'Mood from ' + contact.bare_jid + ': ' + contact.mood,
+ 'Mood')
else:
- self.core.information(contact.bare_jid + ' stopped having his/her mood.', 'Mood')
+ self.core.information(
+ contact.bare_jid + ' stopped having his/her mood.', 'Mood')
def on_activity_event(self, message):
"""
@@ -537,7 +582,8 @@ class HandlerCore:
roster.modified()
item = message['pubsub_event']['items']['item']
old_activity = contact.activity
- if item.xml.find('{http://jabber.org/protocol/activity}activity') is not None:
+ if item.xml.find(
+ '{http://jabber.org/protocol/activity}activity') is not None:
try:
activity = item['activity']['value']
except ValueError:
@@ -557,13 +603,18 @@ class HandlerCore:
contact.activity = ''
if contact.activity:
- logger.log_roster_change(contact.bare_jid, 'has now the activity %s' % contact.activity)
+ logger.log_roster_change(
+ contact.bare_jid, 'has now the activity %s' % contact.activity)
- if old_activity != contact.activity and config.get_by_tabname('display_activity_notifications', contact.bare_jid):
+ if old_activity != contact.activity and config.get_by_tabname(
+ 'display_activity_notifications', contact.bare_jid):
if contact.activity:
- self.core.information('Activity from '+ contact.bare_jid + ': ' + contact.activity, 'Activity')
+ self.core.information('Activity from ' + contact.bare_jid +
+ ': ' + contact.activity, 'Activity')
else:
- self.core.information(contact.bare_jid + ' stopped doing his/her activity.', 'Activity')
+ self.core.information(
+ contact.bare_jid + ' stopped doing his/her activity.',
+ 'Activity')
def on_tune_event(self, message):
"""
@@ -579,27 +630,31 @@ class HandlerCore:
if item.xml.find('{http://jabber.org/protocol/tune}tune') is not None:
item = item['tune']
contact.tune = {
- 'artist': item['artist'],
- 'length': item['length'],
- 'rating': item['rating'],
- 'source': item['source'],
- 'title': item['title'],
- 'track': item['track'],
- 'uri': item['uri']
- }
+ 'artist': item['artist'],
+ 'length': item['length'],
+ 'rating': item['rating'],
+ 'source': item['source'],
+ 'title': item['title'],
+ 'track': item['track'],
+ 'uri': item['uri']
+ }
else:
contact.tune = {}
if contact.tune:
- logger.log_roster_change(message['from'].bare, 'is now listening to %s' % common.format_tune_string(contact.tune))
+ logger.log_roster_change(message['from'].bare,
+ 'is now listening to %s' %
+ common.format_tune_string(contact.tune))
- if old_tune != contact.tune and config.get_by_tabname('display_tune_notifications', contact.bare_jid):
+ if old_tune != contact.tune and config.get_by_tabname(
+ 'display_tune_notifications', contact.bare_jid):
if contact.tune:
self.core.information(
- 'Tune from '+ message['from'].bare + ': ' + common.format_tune_string(contact.tune),
- 'Tune')
+ 'Tune from ' + message['from'].bare + ': ' +
+ common.format_tune_string(contact.tune), 'Tune')
else:
- self.core.information(contact.bare_jid + ' stopped listening to music.', 'Tune')
+ self.core.information(
+ contact.bare_jid + ' stopped listening to music.', 'Tune')
def on_groupchat_message(self, message):
"""
@@ -609,14 +664,16 @@ class HandlerCore:
return
room_from = message['from'].bare
- if message['type'] == 'error': # Check if it's an error
+ if message['type'] == 'error': # Check if it's an error
self.core.room_error(message, room_from)
return
tab = self.core.get_tab_by_name(room_from, tabs.MucTab)
if not tab:
- self.core.information("message received for a non-existing room: %s" % (room_from))
- muc.leave_groupchat(self.core.xmpp, room_from, self.core.own_nick, msg='')
+ self.core.information(
+ "message received for a non-existing room: %s" % (room_from))
+ muc.leave_groupchat(
+ self.core.xmpp, room_from, self.core.own_nick, msg='')
return
nick_from = message['mucnick']
@@ -628,9 +685,11 @@ class HandlerCore:
use_xhtml = config.get_by_tabname('enable_xhtml_im', room_from)
tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
extract_images = config.get('extract_inline_images')
- body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
- tmp_dir=tmp_dir,
- extract_images=extract_images)
+ body = xhtml.get_body_from_message_stanza(
+ message,
+ use_xhtml=use_xhtml,
+ tmp_dir=tmp_dir,
+ extract_images=extract_images)
if not body:
return
@@ -639,18 +698,29 @@ class HandlerCore:
replaced = False
if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None:
replaced_id = message['replace']['id']
- if replaced_id is not '' and config.get_by_tabname('group_corrections',
- message['from'].bare):
+ if replaced_id is not '' and config.get_by_tabname(
+ 'group_corrections', message['from'].bare):
try:
delayed_date = date or datetime.now()
- if tab.modify_message(body, replaced_id, message['id'],
+ if tab.modify_message(
+ body,
+ replaced_id,
+ message['id'],
time=delayed_date,
- nickname=nick_from, user=user):
+ nickname=nick_from,
+ user=user):
self.core.events.trigger('highlight', message, tab)
replaced = True
except CorrectionError:
log.debug('Unable to correct a message', exc_info=True)
- if not replaced and tab.add_message(body, date, nick_from, history=delayed, identifier=message['id'], jid=message['from'], typ=1):
+ if not replaced and tab.add_message(
+ body,
+ date,
+ nick_from,
+ history=delayed,
+ identifier=message['id'],
+ jid=message['from'],
+ typ=1):
self.core.events.trigger('highlight', message, tab)
if message['from'].resource == tab.own_nick:
@@ -693,45 +763,61 @@ class HandlerCore:
use_xhtml = config.get_by_tabname('enable_xhtml_im', jid.bare)
tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
extract_images = config.get('extract_inline_images')
- body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
- tmp_dir=tmp_dir,
- extract_images=extract_images)
- tab = self.core.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation
+ body = xhtml.get_body_from_message_stanza(
+ message,
+ use_xhtml=use_xhtml,
+ tmp_dir=tmp_dir,
+ extract_images=extract_images)
+ tab = self.core.get_tab_by_name(
+ jid.full,
+ tabs.PrivateTab) # get the tab with the private conversation
ignore = config.get_by_tabname('ignore_private', room_from)
- if not tab: # It's the first message we receive: create the tab
+ if not tab: # It's the first message we receive: create the tab
if body and not ignore:
- tab = self.core.open_private_window(room_from, nick_from, False)
+ tab = self.core.open_private_window(room_from, nick_from,
+ False)
if ignore:
self.core.events.trigger('ignored_private', message, tab)
msg = config.get_by_tabname('private_auto_response', room_from)
if msg and body:
- self.core.xmpp.send_message(mto=jid.full, mbody=msg, mtype='chat')
+ self.core.xmpp.send_message(
+ mto=jid.full, mbody=msg, mtype='chat')
return
tab.last_remote_message = datetime.now()
self.core.events.trigger('private_msg', message, tab)
- body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
- tmp_dir=tmp_dir,
- extract_images=extract_images)
+ body = xhtml.get_body_from_message_stanza(
+ message,
+ use_xhtml=use_xhtml,
+ tmp_dir=tmp_dir,
+ extract_images=extract_images)
if not body or not tab:
return
replaced = False
user = tab.parent_muc.get_user_by_name(nick_from)
if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None:
replaced_id = message['replace']['id']
- if replaced_id is not '' and config.get_by_tabname('group_corrections',
- room_from):
+ if replaced_id is not '' and config.get_by_tabname(
+ 'group_corrections', room_from):
try:
- tab.modify_message(body, replaced_id, message['id'], user=user, jid=message['from'],
- nickname=nick_from)
+ tab.modify_message(
+ body,
+ replaced_id,
+ message['id'],
+ user=user,
+ jid=message['from'],
+ nickname=nick_from)
replaced = True
except CorrectionError:
log.debug('Unable to correct a message', exc_info=True)
if not replaced:
- tab.add_message(body, time=None, nickname=nick_from,
- forced_user=user,
- identifier=message['id'],
- jid=message['from'],
- typ=1)
+ tab.add_message(
+ body,
+ time=None,
+ nickname=nick_from,
+ forced_user=user,
+ identifier=message['id'],
+ jid=message['from'],
+ typ=1)
if tab.remote_wants_chatstates is None:
if message['chat_state']:
@@ -767,7 +853,8 @@ class HandlerCore:
def _on_chatstate(self, message, state):
if message['type'] == 'chat':
if not self._on_chatstate_normal_conversation(message, state):
- tab = self.core.get_tab_by_name(message['from'].full, tabs.PrivateTab)
+ tab = self.core.get_tab_by_name(message['from'].full,
+ tabs.PrivateTab)
if not tab:
return
self._on_chatstate_private_conversation(message, state)
@@ -862,10 +949,10 @@ class HandlerCore:
contact = roster.get_and_set(jid)
roster.update_contact_groups(contact)
contact.pending_in = True
- self.core.information('%s wants to subscribe to your presence, use '
- '/accept <jid> or /deny <jid> in the roster '
- 'tab to accept or reject the query.' % jid,
- 'Roster')
+ self.core.information(
+ '%s wants to subscribe to your presence, use '
+ '/accept <jid> or /deny <jid> in the roster '
+ 'tab to accept or reject the query.' % jid, 'Roster')
self.core.get_tab_by_number(0).state = 'highlight'
roster.modified()
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
@@ -876,7 +963,8 @@ class HandlerCore:
jid = presence['from'].bare
contact = roster[jid]
if contact.subscription not in ('both', 'from'):
- self.core.information('%s accepted your contact proposal' % jid, 'Roster')
+ self.core.information('%s accepted your contact proposal' % jid,
+ 'Roster')
if contact.pending_out:
contact.pending_out = False
@@ -892,7 +980,8 @@ class HandlerCore:
if not contact:
return
roster.modified()
- self.core.information('%s does not want to receive your status anymore.' % jid, 'Roster')
+ self.core.information(
+ '%s does not want to receive your status anymore.' % jid, 'Roster')
self.core.get_tab_by_number(0).state = 'highlight'
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
self.core.refresh_window()
@@ -905,10 +994,13 @@ class HandlerCore:
return
roster.modified()
if contact.pending_out:
- self.core.information('%s rejected your contact proposal' % jid, 'Roster')
+ self.core.information('%s rejected your contact proposal' % jid,
+ 'Roster')
contact.pending_out = False
else:
- self.core.information('%s does not want you to receive his/her/its status anymore.'%jid, 'Roster')
+ self.core.information(
+ '%s does not want you to receive his/her/its status anymore.' %
+ jid, 'Roster')
self.core.get_tab_by_number(0).state = 'highlight'
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
self.core.refresh_window()
@@ -916,7 +1008,8 @@ class HandlerCore:
### Presence-related handlers ###
def on_presence(self, presence):
- if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None:
+ if presence.match('presence/muc') or presence.xml.find(
+ '{http://jabber.org/protocol/muc#user}x') is not None:
return
jid = presence['from']
contact = roster[jid.bare]
@@ -930,10 +1023,12 @@ class HandlerCore:
return
roster.modified()
contact.error = None
- self.core.events.trigger('normal_presence', presence, contact[jid.full])
+ self.core.events.trigger('normal_presence', presence,
+ contact[jid.full])
tab = self.core.get_conversation_by_jid(jid, create=False)
if tab:
- tab.update_status(Status(show=presence['show'], message=presence['status']))
+ tab.update_status(
+ Status(show=presence['show'], message=presence['status']))
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
self.core.refresh_window()
elif self.core.current_tab() == tab:
@@ -956,7 +1051,8 @@ class HandlerCore:
"""
A JID got offline
"""
- if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None:
+ if presence.match('presence/muc') or presence.xml.find(
+ '{http://jabber.org/protocol/muc#user}x') is not None:
return
jid = presence['from']
if not logger.log_roster_change(jid.bare, 'got offline'):
@@ -970,9 +1066,12 @@ class HandlerCore:
if contact.name:
name = contact.name
if jid.resource:
- self.core.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % name)
- self.core.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x191}offline' % name)
- self.core.information('\x193}%s \x195}is \x191}offline' % name, 'Roster')
+ self.core.add_information_message_to_conversation_tab(
+ jid.full, '\x195}%s is \x191}offline' % name)
+ self.core.add_information_message_to_conversation_tab(
+ jid.bare, '\x195}%s is \x191}offline' % name)
+ self.core.information('\x193}%s \x195}is \x191}offline' % name,
+ 'Roster')
roster.modified()
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
self.core.refresh_window()
@@ -981,7 +1080,8 @@ class HandlerCore:
"""
A JID got online
"""
- if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x') is not None:
+ if presence.match('presence/muc') or presence.xml.find(
+ '{http://jabber.org/protocol/muc#user}x') is not None:
return
jid = presence['from']
contact = roster[jid.bare]
@@ -996,17 +1096,22 @@ class HandlerCore:
'priority': presence.get_priority() or 0,
'status': presence['status'],
'show': presence['show'],
- })
+ })
self.core.events.trigger('normal_presence', presence, resource)
name = contact.name if contact.name else jid.bare
- self.core.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x194}online' % name)
+ self.core.add_information_message_to_conversation_tab(
+ jid.full, '\x195}%s is \x194}online' % name)
if time.time() - self.core.connection_time > 10:
# We do not display messages if we recently logged in
if presence['status']:
- self.core.information("\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" % (name, presence['status']), "Roster")
+ self.core.information(
+ "\x193}%s \x195}is \x194}online\x195} (\x19o%s\x195})" %
+ (name, presence['status']), "Roster")
else:
- self.core.information("\x193}%s \x195}is \x194}online\x195}" % name, "Roster")
- self.core.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x194}online' % name)
+ self.core.information(
+ "\x193}%s \x195}is \x194}online\x195}" % name, "Roster")
+ self.core.add_information_message_to_conversation_tab(
+ jid.bare, '\x195}%s is \x194}online' % name)
if isinstance(self.core.current_tab(), tabs.RosterInfoTab):
self.core.refresh_window()
@@ -1022,14 +1127,14 @@ class HandlerCore:
self.core.events.trigger('muc_presence', presence, tab)
tab.handle_presence(presence)
-
### Connection-related handlers ###
def on_failed_connection(self, error):
"""
We cannot contact the remote server
"""
- self.core.information("Connection to remote server failed: %s" % (error,), 'Error')
+ self.core.information("Connection to remote server failed: %s" %
+ (error, ), 'Error')
@asyncio.coroutine
def on_disconnected(self, event):
@@ -1046,10 +1151,12 @@ class HandlerCore:
tab.disconnect()
msg_typ = 'Error' if not self.core.legitimate_disconnect else 'Info'
self.core.information("Disconnected from server.", msg_typ)
- if self.core.legitimate_disconnect or not config.get('auto_reconnect', True):
+ if self.core.legitimate_disconnect or not config.get(
+ 'auto_reconnect', True):
return
- if (self.core.last_stream_error and
- self.core.last_stream_error[1]['condition'] in ('conflict', 'host-unknown')):
+ if (self.core.last_stream_error
+ and self.core.last_stream_error[1]['condition'] in (
+ 'conflict', 'host-unknown')):
return
yield from asyncio.sleep(1)
self.core.information("Auto-reconnecting.", 'Info')
@@ -1076,8 +1183,8 @@ class HandlerCore:
"""
Authentication failed (no mech)
"""
- self.core.information("Authentication failed, no login method available.",
- 'Error')
+ self.core.information(
+ "Authentication failed, no login method available.", 'Error')
self.core.legitimate_disconnect = True
def on_connected(self, event):
@@ -1092,10 +1199,11 @@ class HandlerCore:
Called when we are connected and authenticated
"""
self.core.connection_time = time.time()
- if not self.core.plugins_autoloaded: # Do not reload plugins on reconnection
+ if not self.core.plugins_autoloaded: # Do not reload plugins on reconnection
self.core.autoload_plugins()
self.core.information("Authentication success.", 'Info')
- self.core.information("Your JID is %s" % self.core.xmpp.boundjid.full, 'Info')
+ self.core.information("Your JID is %s" % self.core.xmpp.boundjid.full,
+ 'Info')
if not self.core.xmpp.anon:
# request the roster
self.core.xmpp.get_roster()
@@ -1112,7 +1220,8 @@ class HandlerCore:
self.core.join_initial_rooms(self.core.bookmarks)
if config.get('enable_user_nick'):
- self.core.xmpp.plugin['xep_0172'].publish_nick(nick=self.core.own_nick, callback=dumb_callback)
+ self.core.xmpp.plugin['xep_0172'].publish_nick(
+ nick=self.core.own_nick, callback=dumb_callback)
asyncio.ensure_future(self.core.xmpp.plugin['xep_0115'].update_caps())
# Start the ping's plugin regular event
self.core.xmpp.set_keepalive_values()
@@ -1126,9 +1235,14 @@ class HandlerCore:
"""
room_from = message['from']
tab = self.core.get_tab_by_name(room_from, tabs.MucTab)
- status_codes = {s.attrib['code'] for s in message.xml.findall('{%s}x/{%s}status' % (tabs.NS_MUC_USER, tabs.NS_MUC_USER))}
+ status_codes = {
+ s.attrib['code']
+ for s in message.xml.findall('{%s}x/{%s}status' % (
+ tabs.NS_MUC_USER, tabs.NS_MUC_USER))
+ }
if '101' in status_codes:
- self.core.information('Your affiliation in the room %s changed' % room_from, 'Info')
+ self.core.information(
+ 'Your affiliation in the room %s changed' % room_from, 'Info')
elif tab and status_codes:
show_unavailable = '102' in status_codes
hide_unavailable = '103' in status_codes
@@ -1141,38 +1255,70 @@ class HandlerCore:
modif = False
if show_unavailable or hide_unavailable or non_priv or logging_off\
or non_anon or semi_anon or full_anon:
- tab.add_message('\x19%(info_col)s}Info: A configuration change not privacy-related occured.' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: A configuration change not privacy-related occured.'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
modif = True
if show_unavailable:
- tab.add_message('\x19%(info_col)s}Info: The unavailable members are now shown.' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: The unavailable members are now shown.'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
elif hide_unavailable:
- tab.add_message('\x19%(info_col)s}Info: The unavailable members are now hidden.' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: The unavailable members are now hidden.'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
if non_anon:
- tab.add_message('\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
elif semi_anon:
- tab.add_message('\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
elif full_anon:
- tab.add_message('\x19%(info_col)s}Info: The room is now fully anonymous.' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: The room is now fully anonymous.' %
+ {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
if logging_on:
- tab.add_message('\x191}Warning: \x19%(info_col)s}This room is publicly logged' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x191}Warning: \x19%(info_col)s}This room is publicly logged'
+ % {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
elif logging_off:
- tab.add_message('\x19%(info_col)s}Info: This room is not logged anymore.' %
- {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ tab.add_message(
+ '\x19%(info_col)s}Info: This room is not logged anymore.' %
+ {
+ 'info_col': dump_tuple(
+ get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
if modif:
self.core.refresh_window()
@@ -1203,22 +1349,30 @@ class HandlerCore:
after = ''
if user:
user_col = dump_tuple(user.color)
- user_string = '\x19%s}%s\x19%s}%s' % (user_col, nick_from, fmt['info_col'], after)
+ user_string = '\x19%s}%s\x19%s}%s' % (user_col, nick_from,
+ fmt['info_col'],
+ after)
else:
- user_string = '\x19%s}%s%s' % (fmt['info_col'], nick_from, after)
+ user_string = '\x19%s}%s%s' % (fmt['info_col'], nick_from,
+ after)
fmt['user'] = user_string
if nick_from:
- tab.add_message("%(user)s set the subject to: \x19%(text_col)s}%(subject)s" % fmt,
- time=None,
- typ=2)
+ tab.add_message(
+ "%(user)s set the subject to: \x19%(text_col)s}%(subject)s"
+ % fmt,
+ time=None,
+ typ=2)
else:
- tab.add_message("\x19%(info_col)s}The subject is: \x19%(text_col)s}%(subject)s" % fmt,
- time=None,
- typ=2)
+ tab.add_message(
+ "\x19%(info_col)s}The subject is: \x19%(text_col)s}%(subject)s"
+ % fmt,
+ time=None,
+ typ=2)
tab.topic = subject
tab.topic_from = nick_from
- if self.core.get_tab_by_name(room_from, tabs.MucTab) is self.core.current_tab():
+ if self.core.get_tab_by_name(room_from,
+ tabs.MucTab) is self.core.current_tab():
self.core.refresh_window()
def on_receipt(self, message):
@@ -1231,7 +1385,8 @@ class HandlerCore:
return
conversation = self.core.get_tab_by_name(jid.full, tabs.OneToOneTab)
- conversation = conversation or self.core.get_tab_by_name(jid.bare, tabs.OneToOneTab)
+ conversation = conversation or self.core.get_tab_by_name(
+ jid.bare, tabs.OneToOneTab)
if not conversation:
log.error("Received ack from non-existing chat tab: %s", jid)
return
@@ -1272,15 +1427,21 @@ class HandlerCore:
if self.core.xml_tab:
if PYGMENTS:
xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER)
- poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip()
+ poezio_colored = xhtml.xhtml_to_poezio_colors(
+ xhtml_text, force=True).rstrip('\x19o').strip()
else:
poezio_colored = '%s' % stanza
- self.core.add_message_to_text_buffer(self.core.xml_buffer, poezio_colored,
- nickname=get_theme().CHAR_XML_OUT)
+ self.core.add_message_to_text_buffer(
+ self.core.xml_buffer,
+ poezio_colored,
+ nickname=get_theme().CHAR_XML_OUT)
try:
- if self.core.xml_tab.match_stanza(ElementBase(ET.fromstring(stanza))):
- self.core.add_message_to_text_buffer(self.core.xml_tab.filtered_buffer, poezio_colored,
- nickname=get_theme().CHAR_XML_OUT)
+ if self.core.xml_tab.match_stanza(
+ ElementBase(ET.fromstring(stanza))):
+ self.core.add_message_to_text_buffer(
+ self.core.xml_tab.filtered_buffer,
+ poezio_colored,
+ nickname=get_theme().CHAR_XML_OUT)
except:
log.debug('', exc_info=True)
@@ -1295,15 +1456,20 @@ class HandlerCore:
if self.core.xml_tab:
if PYGMENTS:
xhtml_text = highlight('%s' % stanza, LEXER, FORMATTER)
- poezio_colored = xhtml.xhtml_to_poezio_colors(xhtml_text, force=True).rstrip('\x19o').strip()
+ poezio_colored = xhtml.xhtml_to_poezio_colors(
+ xhtml_text, force=True).rstrip('\x19o').strip()
else:
poezio_colored = '%s' % stanza
- self.core.add_message_to_text_buffer(self.core.xml_buffer, poezio_colored,
- nickname=get_theme().CHAR_XML_IN)
+ self.core.add_message_to_text_buffer(
+ self.core.xml_buffer,
+ poezio_colored,
+ nickname=get_theme().CHAR_XML_IN)
try:
if self.core.xml_tab.match_stanza(stanza):
- self.core.add_message_to_text_buffer(self.core.xml_tab.filtered_buffer, poezio_colored,
- nickname=get_theme().CHAR_XML_IN)
+ self.core.add_message_to_text_buffer(
+ self.core.xml_tab.filtered_buffer,
+ poezio_colored,
+ nickname=get_theme().CHAR_XML_IN)
except:
log.debug('', exc_info=True)
if isinstance(self.core.current_tab(), tabs.XMLTab):
@@ -1311,33 +1477,34 @@ class HandlerCore:
self.core.doupdate()
def ssl_invalid_chain(self, tb):
- self.core.information('The certificate sent by the server is invalid.', 'Error')
+ self.core.information('The certificate sent by the server is invalid.',
+ 'Error')
self.core.disconnect()
def _ssl_pop_tab(self, old_cert, new_cert):
def cb(result):
if result:
self.core.information(
- 'New certificate accepted:\nnew: %s\nold: %s' % (
- old_cert, new_cert),
- 'Info')
+ 'New certificate accepted:\nnew: %s\nold: %s' %
+ (old_cert, new_cert), 'Info')
log.debug('Setting certificate to %s', new_cert)
if not config.silent_set('certificate', new_cert):
- self.core.information(
- 'Unable to write in the config file',
- 'Error')
+ self.core.information('Unable to write in the config file',
+ 'Error')
else:
- self.core.information('You refused to validate the certificate.'
- ' You are now disconnected.', 'Info')
+ self.core.information(
+ 'You refused to validate the certificate.'
+ ' You are now disconnected.', 'Info')
self.core.disconnect()
- confirm_tab = tabs.ConfirmTab(self.core,
- 'Certificate check required',
- CERT_WARNING_TEXT % (self.core.xmpp.boundjid.domain, old_cert, new_cert),
- 'You need to accept or reject the certificate',
- cb,
- critical=True)
-
+ confirm_tab = tabs.ConfirmTab(
+ self.core,
+ 'Certificate check required',
+ CERT_WARNING_TEXT % (self.core.xmpp.boundjid.domain, old_cert,
+ new_cert),
+ 'You need to accept or reject the certificate',
+ cb,
+ critical=True)
self.core.add_tab(confirm_tab, True)
self.core.doupdate()
@@ -1356,21 +1523,28 @@ class HandlerCore:
cert = config.get('certificate')
# update the cert representation when it uses the old one
if cert and ':' not in cert:
- cert = ':'.join(i + j for i, j in zip(cert[::2], cert[1::2])).upper()
+ cert = ':'.join(
+ i + j for i, j in zip(cert[::2], cert[1::2])).upper()
config.set_and_save('certificate', cert)
der = ssl.PEM_cert_to_DER_cert(pem)
- asn1 = pyasn1.codec.der.decoder.decode(der, asn1Spec=pyasn1_modules.rfc2459.Certificate())[0]
- spki = asn1.getComponentByName("tbsCertificate").getComponentByName("subjectPublicKeyInfo")
- spki_digest = sha256(pyasn1.codec.der.encoder.encode(spki)).hexdigest().upper()
- spki_found_cert = ':'.join(i + j for i, j in zip(spki_digest[::2], spki_digest[1::2]))
+ asn1 = pyasn1.codec.der.decoder.decode(
+ der, asn1Spec=pyasn1_modules.rfc2459.Certificate())[0]
+ spki = asn1.getComponentByName("tbsCertificate").getComponentByName(
+ "subjectPublicKeyInfo")
+ spki_digest = sha256(
+ pyasn1.codec.der.encoder.encode(spki)).hexdigest().upper()
+ spki_found_cert = ':'.join(
+ i + j for i, j in zip(spki_digest[::2], spki_digest[1::2]))
sha2_digest = sha512(der).hexdigest().upper()
- sha2_found_cert = ':'.join(i + j for i, j in zip(sha2_digest[::2], sha2_digest[1::2]))
+ sha2_found_cert = ':'.join(
+ i + j for i, j in zip(sha2_digest[::2], sha2_digest[1::2]))
if cert:
if sha2_found_cert == cert:
- log.debug('Current hash is cert hash, moving to SPKI hash (%s)',
- spki_found_cert)
+ log.debug(
+ 'Current hash is cert hash, moving to SPKI hash (%s)',
+ spki_found_cert)
config.set_and_save('certificate', spki_found_cert)
return
elif spki_found_cert == cert:
@@ -1380,10 +1554,12 @@ class HandlerCore:
else:
log.debug('First time. Setting certificate to %s', spki_found_cert)
if not config.silent_set('certificate', spki_found_cert):
- self.core.information('Unable to write in the config file', 'Error')
+ self.core.information('Unable to write in the config file',
+ 'Error')
def http_confirm(self, stanza):
confirm = stanza['confirm']
+
def cb(result):
if result:
reply = stanza.reply()
@@ -1396,13 +1572,15 @@ class HandlerCore:
reply.append(stanza['confirm'])
reply.send()
- c_id, c_url, c_method = confirm['id'], confirm['url'], confirm['method']
- confirm_tab = tabs.ConfirmTab(self.core,
- 'HTTP Verification',
- HTTP_VERIF_TEXT % (c_method, c_url, c_id, stanza['from'].full),
- 'An HTTP verification was requested',
- cb,
- critical=False)
+ c_id, c_url, c_method = confirm['id'], confirm['url'], confirm[
+ 'method']
+ confirm_tab = tabs.ConfirmTab(
+ self.core,
+ 'HTTP Verification',
+ HTTP_VERIF_TEXT % (c_method, c_url, c_id, stanza['from'].full),
+ 'An HTTP verification was requested',
+ cb,
+ critical=False)
self.core.add_tab(confirm_tab, False)
self.core.refresh_window()
self.core.doupdate()
@@ -1411,19 +1589,24 @@ class HandlerCore:
def next_adhoc_step(self, iq, adhoc_session):
status = iq['command']['status']
- xform = iq.xml.find('{http://jabber.org/protocol/commands}command/{jabber:x:data}x')
+ xform = iq.xml.find(
+ '{http://jabber.org/protocol/commands}command/{jabber:x:data}x')
if xform is not None:
form = self.core.xmpp.plugin['xep_0004'].build_form(xform)
else:
form = None
if status == 'error':
- return self.core.information("An error occured while executing the command")
+ return self.core.information(
+ "An error occured while executing the command")
if status == 'executing':
if not form:
- self.core.information("Adhoc command step does not contain a data-form. Aborting the execution.", "Error")
- return self.core.xmpp.plugin['xep_0050'].cancel_command(adhoc_session)
+ self.core.information(
+ "Adhoc command step does not contain a data-form. Aborting the execution.",
+ "Error")
+ return self.core.xmpp.plugin['xep_0050'].cancel_command(
+ adhoc_session)
on_validate = self._validate_adhoc_step
on_cancel = self._cancel_adhoc_command
if status == 'completed':
@@ -1435,18 +1618,20 @@ class HandlerCore:
if form:
for note in iq['command']['notes']:
form.add_field(type='fixed', label=note[1])
- self.core.open_new_form(form, on_cancel, on_validate,
- session=adhoc_session)
- else: # otherwise, just display an information
- # message
+ self.core.open_new_form(
+ form, on_cancel, on_validate, session=adhoc_session)
+ else: # otherwise, just display an information
+ # message
notes = '\n'.join([note[1] for note in iq['command']['notes']])
- self.core.information("Adhoc command %s: %s" % (status, notes), "Info")
+ self.core.information("Adhoc command %s: %s" % (status, notes),
+ "Info")
def adhoc_error(self, iq, adhoc_session):
self.core.xmpp.plugin['xep_0050'].terminate_command(adhoc_session)
error_message = self.core.get_error_message(iq)
- self.core.information("An error occured while executing the command: %s" % (error_message),
- 'Error')
+ self.core.information(
+ "An error occured while executing the command: %s" %
+ (error_message), 'Error')
def _cancel_adhoc_command(self, form, session):
self.core.xmpp.plugin['xep_0050'].cancel_command(session)
@@ -1461,6 +1646,7 @@ class HandlerCore:
self.core.xmpp.plugin['xep_0050'].terminate_command(session)
self.core.close_tab()
+
def _composing_tab_state(tab, state):
"""
Set a tab state to or from the "composing" state
@@ -1473,7 +1659,7 @@ def _composing_tab_state(tab, state):
elif isinstance(tab, tabs.ConversationTab):
values = ('true', 'direct', 'conversation')
else:
- return # should not happen
+ return # should not happen
show = config.get('show_composing_tabs')
show = show in values
@@ -1486,4 +1672,3 @@ def _composing_tab_state(tab, state):
tab.state = 'composing'
elif tab.state == 'composing' and state != 'composing':
tab.restore_state()
-
diff --git a/poezio/core/structs.py b/poezio/core/structs.py
index 7d568f04..72c9628a 100644
--- a/poezio/core/structs.py
+++ b/poezio/core/structs.py
@@ -2,8 +2,10 @@
Module defining structures useful to the core class and related methods
"""
-__all__ = ['ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW',
- 'Status', 'Command', 'Completion']
+__all__ = [
+ 'ERROR_AND_STATUS_CODES', 'DEPRECATED_ERRORS', 'POSSIBLE_SHOW', 'Status',
+ 'Command', 'Completion'
+]
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
@@ -15,7 +17,7 @@ ERROR_AND_STATUS_CODES = {
'407': 'You are not in the member list',
'409': 'This nickname is already in use or has been reserved',
'503': 'The maximum number of users has been reached',
- }
+}
# http://xmpp.org/extensions/xep-0086.html
DEPRECATED_ERRORS = {
@@ -48,14 +50,18 @@ POSSIBLE_SHOW = {
'xa': 'xa'
}
+
class Status:
__slots__ = ('show', 'message')
+
def __init__(self, show, message):
self.show = show
self.message = message
+
class Command:
__slots__ = ('func', 'desc', 'comp', 'short_desc', 'usage')
+
def __init__(self, func, desc, comp, short_desc, usage):
self.func = func
self.desc = desc
@@ -63,11 +69,13 @@ class Command:
self.short_desc = short_desc
self.usage = usage
+
class Completion:
"""
A completion result essentially currying the input completion call.
"""
__slots__ = ['func', 'args', 'kwargs', 'comp_list']
+
def __init__(self, func, comp_list, *args, **kwargs):
self.func = func
self.comp_list = comp_list
diff --git a/poezio/daemon.py b/poezio/daemon.py
index 6de0ec3a..c8225a07 100755
--- a/poezio/daemon.py
+++ b/poezio/daemon.py
@@ -5,7 +5,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
This file is a standalone program that reads commands on
stdin and executes them (each line should be a command).
@@ -29,6 +28,7 @@ from subprocess import DEVNULL
log = logging.getLogger(__name__)
+
class Executor(threading.Thread):
"""
Just a class to execute commands in a thread. This way, the execution
@@ -37,6 +37,7 @@ class Executor(threading.Thread):
WARNING: Be careful to properly escape what is untrusted by using
shlex.quote for example.
"""
+
def __init__(self, command, remote=False):
threading.Thread.__init__(self)
self.command = command
@@ -58,7 +59,10 @@ class Executor(threading.Thread):
try:
stdout = open(self.filename, self.redirection_mode)
except OSError:
- log.error('Could not open redirection file: %s', self.filename, exc_info=True)
+ log.error(
+ 'Could not open redirection file: %s',
+ self.filename,
+ exc_info=True)
return
try:
subprocess.call(self.command, stdout=stdout, stderr=DEVNULL)
@@ -69,6 +73,7 @@ class Executor(threading.Thread):
else:
log.error('Could not execute %s:', self.command, exc_info=True)
+
def main():
while True:
line = sys.stdin.readline()
@@ -78,5 +83,6 @@ def main():
e = Executor(command, remote=True)
e.start()
+
if __name__ == '__main__':
main()
diff --git a/poezio/decorators.py b/poezio/decorators.py
index e824a8f1..808c3ce6 100644
--- a/poezio/decorators.py
+++ b/poezio/decorators.py
@@ -4,6 +4,7 @@ Module containing various decorators
from poezio import common
+
class RefreshWrapper(object):
def __init__(self):
self.core = None
@@ -13,49 +14,60 @@ class RefreshWrapper(object):
Decorator to refresh the UI if the wrapped function
returns True
"""
+
def wrap(*args, **kwargs):
ret = func(*args, **kwargs)
if self.core and ret:
self.core.refresh_window()
return ret
+
return wrap
def always(self, func):
"""
Decorator that refreshs the UI no matter what after the function
"""
+
def wrap(*args, **kwargs):
ret = func(*args, **kwargs)
if self.core:
self.core.refresh_window()
return ret
+
return wrap
def update(self, func):
"""
Decorator that only updates the screen
"""
+
def wrap(*args, **kwargs):
ret = func(*args, **kwargs)
if self.core:
self.core.doupdate()
return ret
+
return wrap
+
refresh_wrapper = RefreshWrapper()
+
class CommandArgParser(object):
"""Modify the string argument of the function into a list of strings
containing the right number of extracted arguments, or None if we don’t
have enough.
"""
+
@staticmethod
def raw(func):
"""Just call the function with a single string, which is the original string
untouched
"""
+
def wrap(self, args, *a, **kw):
return func(self, args, *a, **kw)
+
return wrap
@staticmethod
@@ -63,14 +75,17 @@ class CommandArgParser(object):
"""
Call the function without any argument
"""
+
def wrap(self, args=None, *a, **kw):
return func(self, *a, **kw)
+
return wrap
@staticmethod
- def quoted(mandatory, optional=0, defaults=None,
+ def quoted(mandatory,
+ optional=0,
+ defaults=None,
ignore_trailing_arguments=False):
-
"""The function receives a list with a number of arguments that is between
the numbers `mandatory` and `optional`.
@@ -115,6 +130,7 @@ class CommandArgParser(object):
"""
if defaults is None:
defaults = []
+
def first(func):
def second(self, args, *a, **kw):
default_args = defaults
@@ -138,7 +154,10 @@ class CommandArgParser(object):
if args and res and not ignore_trailing_arguments:
res[-1] += " " + " ".join(args)
return func(self, res, *a, **kw)
+
return second
+
return first
+
command_args_parser = CommandArgParser()
diff --git a/poezio/events.py b/poezio/events.py
index 97d77626..15a308b4 100644
--- a/poezio/events.py
+++ b/poezio/events.py
@@ -3,13 +3,13 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Defines the EventHandler class.
The list of available events is here:
http://poezio.eu/doc/en/plugins.html#_poezio_events
"""
+
class EventHandler(object):
"""
A class keeping a list of possible events that are triggered
@@ -17,6 +17,7 @@ class EventHandler(object):
associated with an event name, and whenever that event is triggered,
the callback is called.
"""
+
def __init__(self):
self.events = {
'highlight': [],
@@ -43,7 +44,7 @@ class EventHandler(object):
'send_normal_presence': [],
'ignored_private': [],
'tab_change': [],
- }
+ }
def add_event_handler(self, name, callback, position=0):
"""
diff --git a/poezio/fifo.py b/poezio/fifo.py
index 863ef228..f2615847 100644
--- a/poezio/fifo.py
+++ b/poezio/fifo.py
@@ -11,6 +11,7 @@ log = logging.getLogger(__name__)
import os
import threading
+
class OpenTrick(threading.Thread):
"""
A threaded trick to make the open for writing succeed.
@@ -22,6 +23,7 @@ class OpenTrick(threading.Thread):
(we never read anything from it, obviously)
"""
+
def __init__(self, path):
threading.Thread.__init__(self)
self.path = path
@@ -37,6 +39,7 @@ class Fifo(object):
Mode is either 'r' or 'w', just like the mode for the open()
function.
"""
+
def __init__(self, path, mode):
self.trick = None
if not os.path.exists(path):
@@ -67,5 +70,5 @@ class Fifo(object):
if self.trick:
self.trick.fd.close()
except:
- log.error('Unable to close descriptors for the fifo',
- exc_info=True)
+ log.error(
+ 'Unable to close descriptors for the fifo', exc_info=True)
diff --git a/poezio/fixes.py b/poezio/fixes.py
index 1eadac12..77688bf8 100644
--- a/poezio/fixes.py
+++ b/poezio/fixes.py
@@ -12,6 +12,7 @@ import logging
log = logging.getLogger(__name__)
+
def has_identity(xmpp, jid, identity, on_true=None, on_false=None):
def _cb(iq):
ident = lambda x: x[0]
@@ -20,8 +21,10 @@ def has_identity(xmpp, jid, identity, on_true=None, on_false=None):
on_true()
if not res and on_false is not None:
on_false()
+
xmpp.plugin['xep_0030'].get_info(jid=jid, callback=_cb)
+
def get_version(xmpp, jid, callback=None, **kwargs):
def handle_result(res):
if res and res['type'] != 'error':
@@ -31,6 +34,7 @@ def get_version(xmpp, jid, callback=None, **kwargs):
if callback:
callback(ret)
return ret
+
iq = xmpp.make_iq_get(ito=jid)
iq['query'] = 'jabber:iq:version'
result = iq.send(callback=handle_result if callback else None)
@@ -42,7 +46,8 @@ def get_room_form(xmpp, room, callback):
def _cb(result):
if result["type"] == "error":
return callback(None)
- xform = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
+ xform = result.xml.find(
+ '{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
if xform is None:
return callback(None)
form = xmpp.plugin['xep_0004'].build_form(xform)
@@ -53,6 +58,7 @@ def get_room_form(xmpp, room, callback):
iq.append(query)
iq.send(callback=_cb)
+
def _filter_add_receipt_request(self, stanza):
"""
Auto add receipt requests to outgoing messages, if:
@@ -94,4 +100,3 @@ def _filter_add_receipt_request(self, stanza):
stanza['request_receipt'] = True
return stanza
-
diff --git a/poezio/keyboard.py b/poezio/keyboard.py
index ccf9e752..fe0dde08 100755
--- a/poezio/keyboard.py
+++ b/poezio/keyboard.py
@@ -5,7 +5,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Functions to interact with the keyboard
Mainly, read keys entered and return a string (most
@@ -27,6 +26,7 @@ log = logging.getLogger(__name__)
# processing of keys)
continuation_keys_callback = None
+
def get_next_byte(s):
"""
Read the next byte of the utf-8 char
@@ -40,7 +40,8 @@ def get_next_byte(s):
return (None, None)
if len(c) >= 4:
return (None, c)
- return (ord(c), c.encode('latin-1')) # returns a number and a bytes object
+ return (ord(c), c.encode('latin-1')) # returns a number and a bytes object
+
def get_char_list(s):
ret_list = []
@@ -50,7 +51,7 @@ def get_char_list(s):
except curses.error:
# No input, this means a timeout occurs.
return ret_list
- except ValueError: # invalid input
+ except ValueError: # invalid input
log.debug('Invalid character entered.')
return ret_list
# Set to non-blocking. We try to read more bytes. If there are no
@@ -68,10 +69,11 @@ def get_char_list(s):
part = s.get_wch()
if part == '[':
# CTRL+arrow and meta+arrow keys have a long format
- part += s.get_wch() + s.get_wch() + s.get_wch() + s.get_wch()
+ part += s.get_wch() + s.get_wch() + s.get_wch(
+ ) + s.get_wch()
except curses.error:
pass
- except ValueError: # invalid input
+ except ValueError: # invalid input
log.debug('Invalid character entered.')
else:
key = 'M-%s' % part
@@ -93,6 +95,7 @@ def get_char_list(s):
key = '^M'
ret_list.append(key)
+
class Keyboard(object):
def __init__(self):
self.escape = False
@@ -132,6 +135,7 @@ class Keyboard(object):
self.escape = False
return ret_list
+
if __name__ == '__main__':
import sys
keyboard = Keyboard()
diff --git a/poezio/logger.py b/poezio/logger.py
index e4ce4c5a..7302df2a 100644
--- a/poezio/logger.py
+++ b/poezio/logger.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
The logger module that handles logging of the poezio
conversations and roster changes
@@ -34,23 +33,30 @@ INFO_LOG_RE = re.compile(r'^MI (\d{4})(\d{2})(\d{2})T'
r'(\d{2}):(\d{2}):(\d{2})Z '
r'(\d+) (.*)$')
+
class LogItem:
- def __init__(self, year, month, day, hour, minute, second, nb_lines, message):
- self.time = datetime(int(year), int(month), int(day), int(hour),
- int(minute), int(second))
+ def __init__(self, year, month, day, hour, minute, second, nb_lines,
+ message):
+ self.time = datetime(
+ int(year),
+ int(month), int(day), int(hour), int(minute), int(second))
self.nb_lines = int(nb_lines)
self.text = message
+
class LogInfo(LogItem):
def __init__(self, *args):
LogItem.__init__(self, *args)
+
class LogMessage(LogItem):
- def __init__(self, year, month, day, hour, minute, seconds, nb_lines, nick, message):
+ def __init__(self, year, month, day, hour, minute, seconds, nb_lines, nick,
+ message):
LogItem.__init__(self, year, month, day, hour, minute, seconds,
nb_lines, message)
self.nick = nick
+
def parse_log_line(msg):
match = re.match(MESSAGE_LOG_RE, msg)
if match:
@@ -61,11 +67,13 @@ def parse_log_line(msg):
log.debug('Error while parsing "%s"', msg)
return None
+
class Logger(object):
"""
Appends things to files. Error/information/warning logs
and also log the conversations to logfiles
"""
+
def __init__(self):
self._roster_logfile = None
# a dict of 'groupchatname': file-object (opened)
@@ -76,7 +84,7 @@ class Logger(object):
if opened_file:
try:
opened_file.close()
- except: # Can't close? too bad
+ except: # Can't close? too bad
pass
def close(self, jid):
@@ -106,7 +114,7 @@ class Logger(object):
try:
makedirs(log_dir)
except OSError as e:
- if e.errno != 17: # file exists
+ if e.errno != 17: # file exists
log.error('Unable to create the log dir', exc_info=True)
except:
log.error('Unable to create the log dir', exc_info=True)
@@ -118,9 +126,10 @@ class Logger(object):
self._fds[room] = fd
return fd
except IOError:
- log.error('Unable to open the log file (%s)',
- os.path.join(log_dir, room),
- exc_info=True)
+ log.error(
+ 'Unable to open the log file (%s)',
+ os.path.join(log_dir, room),
+ exc_info=True)
def get_logs(self, jid, nb=10):
"""
@@ -143,14 +152,16 @@ class Logger(object):
try:
fd = open(os.path.join(log_dir, jid), 'rb')
except FileNotFoundError:
- log.info('Non-existing log file (%s)',
- os.path.join(log_dir, jid),
- exc_info=True)
+ log.info(
+ 'Non-existing log file (%s)',
+ os.path.join(log_dir, jid),
+ exc_info=True)
return
except OSError:
- log.error('Unable to open the log file (%s)',
- os.path.join(log_dir, jid),
- exc_info=True)
+ log.error(
+ 'Unable to open the log file (%s)',
+ os.path.join(log_dir, jid),
+ exc_info=True)
return
if not fd:
return
@@ -161,22 +172,23 @@ class Logger(object):
with fd:
try:
m = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
- except Exception: # file probably empty
- log.error('Unable to mmap the log file for (%s)',
- os.path.join(log_dir, jid),
- exc_info=True)
+ except Exception: # file probably empty
+ log.error(
+ 'Unable to mmap the log file for (%s)',
+ os.path.join(log_dir, jid),
+ exc_info=True)
return
- pos = m.rfind(b"\nM") # start of messages begin with MI or MR,
- # after a \n
+ pos = m.rfind(b"\nM") # start of messages begin with MI or MR,
+ # after a \n
# number of message found so far
count = 0
- while pos != -1 and count < nb-1:
+ while pos != -1 and count < nb - 1:
count += 1
pos = m.rfind(b"\nM", 0, pos)
- if pos == -1: # If we don't have enough lines in the file
- pos = 1 # 1, because we do -1 just on the next line
- # to get 0 (start of the file)
- lines = m[pos-1:].decode(errors='replace').splitlines()
+ if pos == -1: # If we don't have enough lines in the file
+ pos = 1 # 1, because we do -1 just on the next line
+ # to get 0 (start of the file)
+ lines = m[pos - 1:].decode(errors='replace').splitlines()
messages = []
color = '\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG)
@@ -184,7 +196,7 @@ class Logger(object):
# now convert that data into actual Message objects
idx = 0
while idx < len(lines):
- if lines[idx].startswith(' '): # should not happen ; skip
+ if lines[idx].startswith(' '): # should not happen ; skip
idx += 1
log.debug('fail?')
continue
@@ -193,9 +205,11 @@ class Logger(object):
if not isinstance(log_item, LogItem):
log.debug('wrong log format? %s', log_item)
continue
- message = {'lines': [],
- 'history': True,
- 'time': common.get_local_time(log_item.time)}
+ message = {
+ 'lines': [],
+ 'history': True,
+ 'time': common.get_local_time(log_item.time)
+ }
size = log_item.nb_lines
if isinstance(log_item, LogInfo):
message['lines'].append(color + log_item.text)
@@ -237,7 +251,8 @@ class Logger(object):
if date is None:
str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
else:
- str_time = common.get_utc_time(date).strftime('%Y%m%dT%H:%M:%SZ')
+ str_time = common.get_utc_time(date).strftime(
+ '%Y%m%dT%H:%M:%SZ')
if typ == 1:
prefix = 'MR'
else:
@@ -248,23 +263,27 @@ class Logger(object):
if nick:
nick = '<' + nick + '>'
- fd.write('%s %s %s %s %s\n' % (prefix, str_time, nb_lines, nick, first_line))
+ fd.write('%s %s %s %s %s\n' % (prefix, str_time, nb_lines,
+ nick, first_line))
else:
- fd.write('%s %s %s %s\n' % (prefix, str_time, nb_lines, first_line))
+ fd.write('%s %s %s %s\n' % (prefix, str_time, nb_lines,
+ first_line))
for line in lines:
fd.write(' %s\n' % line)
except:
- log.error('Unable to write in the log file (%s)',
- os.path.join(log_dir, jid),
- exc_info=True)
+ log.error(
+ 'Unable to write in the log file (%s)',
+ os.path.join(log_dir, jid),
+ exc_info=True)
return False
else:
try:
- fd.flush() # TODO do something better here?
+ fd.flush() # TODO do something better here?
except OSError:
- log.error('Unable to flush the log file (%s)',
- os.path.join(log_dir, jid),
- exc_info=True)
+ log.error(
+ 'Unable to flush the log file (%s)',
+ os.path.join(log_dir, jid),
+ exc_info=True)
return False
return True
@@ -277,11 +296,13 @@ class Logger(object):
self._check_and_create_log_dir('', open_fd=False)
if not self._roster_logfile:
try:
- self._roster_logfile = open(os.path.join(log_dir, 'roster.log'), 'a')
+ self._roster_logfile = open(
+ os.path.join(log_dir, 'roster.log'), 'a')
except IOError:
- log.error('Unable to create the log file (%s)',
- os.path.join(log_dir, 'roster.log'),
- exc_info=True)
+ log.error(
+ 'Unable to create the log file (%s)',
+ os.path.join(log_dir, 'roster.log'),
+ exc_info=True)
return False
try:
str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
@@ -289,20 +310,24 @@ class Logger(object):
lines = message.split('\n')
first_line = lines.pop(0)
nb_lines = str(len(lines)).zfill(3)
- self._roster_logfile.write('MI %s %s %s %s\n' % (str_time, nb_lines, jid, first_line))
+ self._roster_logfile.write('MI %s %s %s %s\n' %
+ (str_time, nb_lines, jid, first_line))
for line in lines:
self._roster_logfile.write(' %s\n' % line)
self._roster_logfile.flush()
except:
- log.error('Unable to write in the log file (%s)',
- os.path.join(log_dir, 'roster.log'),
- exc_info=True)
+ log.error(
+ 'Unable to write in the log file (%s)',
+ os.path.join(log_dir, 'roster.log'),
+ exc_info=True)
return False
return True
+
def create_logger():
"Create the global logger object"
global logger
logger = Logger()
+
logger = None
diff --git a/poezio/multiuserchat.py b/poezio/multiuserchat.py
index 23e0f7eb..64b84069 100644
--- a/poezio/multiuserchat.py
+++ b/poezio/multiuserchat.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Implementation of the XEP-0045: Multi-User Chat.
Add some facilities that are not available on the XEP_0045
@@ -41,15 +40,17 @@ def destroy_room(xmpp, room, reason='', altroom=''):
destroy.append(xreason)
query.append(destroy)
iq.append(query)
+
def callback(iq):
if not iq or iq['type'] == 'error':
- xmpp.core.information('Unable to destroy room %s' % room,
- 'Info')
+ xmpp.core.information('Unable to destroy room %s' % room, 'Info')
else:
xmpp.core.information('Room %s destroyed' % room, 'Info')
+
iq.send(callback=callback)
return True
+
def send_private_message(xmpp, jid, line):
"""
Send a private message
@@ -57,6 +58,7 @@ def send_private_message(xmpp, jid, line):
jid = safeJID(jid)
xmpp.send_message(mto=jid, mbody=line, mtype='chat')
+
def send_groupchat_message(xmpp, jid, line):
"""
Send a message to the groupchat
@@ -64,18 +66,20 @@ def send_groupchat_message(xmpp, jid, line):
jid = safeJID(jid)
xmpp.send_message(mto=jid, mbody=line, mtype='groupchat')
+
def change_show(xmpp, jid, own_nick, show, status):
"""
Change our 'Show'
"""
jid = safeJID(jid)
pres = xmpp.make_presence(pto='%s/%s' % (jid, own_nick))
- if show: # if show is None, don't put a <show /> tag. It means "available"
+ if show: # if show is None, don't put a <show /> tag. It means "available"
pres['type'] = show
if status:
pres['status'] = status
pres.send()
+
def change_subject(xmpp, jid, subject):
"""
Change the room subject
@@ -86,18 +90,28 @@ def change_subject(xmpp, jid, subject):
msg['subject'] = subject
msg.send()
+
def change_nick(core, jid, nick, status=None, show=None):
"""
Change our own nick in a room
"""
xmpp = core.xmpp
- presence = xmpp.make_presence(pshow=show, pstatus=status, pto=safeJID('%s/%s' % (jid, nick)))
+ presence = xmpp.make_presence(
+ pshow=show, pstatus=status, pto=safeJID('%s/%s' % (jid, nick)))
core.events.trigger('changing_nick', presence)
presence.send()
-def join_groupchat(core, jid, nick, passwd='', status=None, show=None, seconds=None):
+
+def join_groupchat(core,
+ jid,
+ nick,
+ passwd='',
+ status=None,
+ show=None,
+ seconds=None):
xmpp = core.xmpp
- stanza = xmpp.make_presence(pto='%s/%s' % (jid, nick), pstatus=status, pshow=show)
+ stanza = xmpp.make_presence(
+ pto='%s/%s' % (jid, nick), pstatus=status, pshow=show)
x = ET.Element('{http://jabber.org/protocol/muc}x')
if passwd:
passelement = ET.Element('password')
@@ -114,6 +128,7 @@ def join_groupchat(core, jid, nick, passwd='', status=None, show=None, seconds=N
xmpp.plugin['xep_0045'].rooms[jid] = {}
xmpp.plugin['xep_0045'].our_nicks[jid] = to.resource
+
def leave_groupchat(xmpp, jid, own_nick, msg):
"""
Leave the groupchat
@@ -122,8 +137,11 @@ def leave_groupchat(xmpp, jid, own_nick, msg):
try:
xmpp.plugin['xep_0045'].leave_muc(jid, own_nick, msg)
except KeyError:
- log.debug("muc.leave_groupchat: could not leave the room %s",
- jid, exc_info=True)
+ log.debug(
+ "muc.leave_groupchat: could not leave the room %s",
+ jid,
+ exc_info=True)
+
def set_user_role(xmpp, jid, nick, reason, role, callback=None):
"""
@@ -133,7 +151,7 @@ def set_user_role(xmpp, jid, nick, reason, role, callback=None):
jid = safeJID(jid)
iq = xmpp.make_iq_set()
query = ET.Element('{%s}query' % NS_MUC_ADMIN)
- item = ET.Element('{%s}item' % NS_MUC_ADMIN, {'nick':nick, 'role':role})
+ item = ET.Element('{%s}item' % NS_MUC_ADMIN, {'nick': nick, 'role': role})
if reason:
reason_el = ET.Element('{%s}reason' % NS_MUC_ADMIN)
reason_el.text = reason
@@ -148,19 +166,33 @@ def set_user_role(xmpp, jid, nick, reason, role, callback=None):
except (IqError, IqTimeout) as e:
return e.iq
-def set_user_affiliation(xmpp, muc_jid, affiliation, nick=None, jid=None, reason=None, callback=None):
+
+def set_user_affiliation(xmpp,
+ muc_jid,
+ affiliation,
+ nick=None,
+ jid=None,
+ reason=None,
+ callback=None):
"""
(try to) Set the affiliation of a MUC user
"""
muc_jid = safeJID(muc_jid)
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
if nick:
- item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation':affiliation, 'nick':nick})
+ item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {
+ 'affiliation': affiliation,
+ 'nick': nick
+ })
else:
- item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation':affiliation, 'jid':str(jid)})
+ item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {
+ 'affiliation': affiliation,
+ 'jid': str(jid)
+ })
if reason:
- reason_item = ET.Element('{http://jabber.org/protocol/muc#admin}reason')
+ reason_item = ET.Element(
+ '{http://jabber.org/protocol/muc#admin}reason')
reason_item.text = reason
item.append(reason_item)
@@ -170,11 +202,13 @@ def set_user_affiliation(xmpp, muc_jid, affiliation, nick=None, jid=None, reason
if callback:
return iq.send(callback=callback)
try:
- return xmpp.plugin['xep_0045'].set_affiliation(str(muc_jid), str(jid) if jid else None, nick, affiliation)
+ return xmpp.plugin['xep_0045'].set_affiliation(
+ str(muc_jid), str(jid) if jid else None, nick, affiliation)
except:
log.debug('Error setting the affiliation: %s', exc_info=True)
return False
+
def cancel_config(xmpp, room):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = ET.Element('{jabber:x:data}x', type='cancel')
@@ -183,6 +217,7 @@ def cancel_config(xmpp, room):
iq['to'] = room
iq.send()
+
def configure_room(xmpp, room, form):
if form is None:
return
@@ -193,4 +228,3 @@ def configure_room(xmpp, room, form):
query.append(form.xml)
iq.append(query)
iq.send()
-
diff --git a/poezio/pep.py b/poezio/pep.py
index 0f7a1ced..a211b09b 100644
--- a/poezio/pep.py
+++ b/poezio/pep.py
@@ -4,218 +4,202 @@ extracted directly from the XEP
"""
MOODS = {
- 'afraid': 'Afraid',
- 'amazed': 'Amazed',
- 'angry': 'Angry',
- 'amorous': 'Amorous',
- 'annoyed': 'Annoyed',
- 'anxious': 'Anxious',
- 'aroused': 'Aroused',
- 'ashamed': 'Ashamed',
- 'bored': 'Bored',
- 'brave': 'Brave',
- 'calm': 'Calm',
- 'cautious': 'Cautious',
- 'cold': 'Cold',
- 'confident': 'Confident',
- 'confused': 'Confused',
- 'contemplative': 'Contemplative',
- 'contented': 'Contented',
- 'cranky': 'Cranky',
- 'crazy': 'Crazy',
- 'creative': 'Creative',
- 'curious': 'Curious',
- 'dejected': 'Dejected',
- 'depressed': 'Depressed',
- 'disappointed': 'Disappointed',
- 'disgusted': 'Disgusted',
- 'dismayed': 'Dismayed',
- 'distracted': 'Distracted',
- 'embarrassed': 'Embarrassed',
- 'envious': 'Envious',
- 'excited': 'Excited',
- 'flirtatious': 'Flirtatious',
- 'frustrated': 'Frustrated',
- 'grumpy': 'Grumpy',
- 'guilty': 'Guilty',
- 'happy': 'Happy',
- 'hopeful': 'Hopeful',
- 'hot': 'Hot',
- 'humbled': 'Humbled',
- 'humiliated': 'Humiliated',
- 'hungry': 'Hungry',
- 'hurt': 'Hurt',
- 'impressed': 'Impressed',
- 'in_awe': 'In awe',
- 'in_love': 'In love',
- 'indignant': 'Indignant',
- 'interested': 'Interested',
- 'intoxicated': 'Intoxicated',
- 'invincible': 'Invincible',
- 'jealous': 'Jealous',
- 'lonely': 'Lonely',
- 'lucky': 'Lucky',
- 'mean': 'Mean',
- 'moody': 'Moody',
- 'nervous': 'Nervous',
- 'neutral': 'Neutral',
- 'offended': 'Offended',
- 'outraged': 'Outraged',
- 'playful': 'Playful',
- 'proud': 'Proud',
- 'relaxed': 'Relaxed',
- 'relieved': 'Relieved',
- 'remorseful': 'Remorseful',
- 'restless': 'Restless',
- 'sad': 'Sad',
- 'sarcastic': 'Sarcastic',
- 'serious': 'Serious',
- 'shocked': 'Shocked',
- 'shy': 'Shy',
- 'sick': 'Sick',
- 'sleepy': 'Sleepy',
- 'spontaneous': 'Spontaneous',
- 'stressed': 'Stressed',
- 'strong': 'Strong',
- 'surprised': 'Surprised',
- 'thankful': 'Thankful',
- 'thirsty': 'Thirsty',
- 'tired': 'Tired',
- 'undefined': 'Undefined',
- 'weak': 'Weak',
- 'worried': 'Worried'
+ 'afraid': 'Afraid',
+ 'amazed': 'Amazed',
+ 'angry': 'Angry',
+ 'amorous': 'Amorous',
+ 'annoyed': 'Annoyed',
+ 'anxious': 'Anxious',
+ 'aroused': 'Aroused',
+ 'ashamed': 'Ashamed',
+ 'bored': 'Bored',
+ 'brave': 'Brave',
+ 'calm': 'Calm',
+ 'cautious': 'Cautious',
+ 'cold': 'Cold',
+ 'confident': 'Confident',
+ 'confused': 'Confused',
+ 'contemplative': 'Contemplative',
+ 'contented': 'Contented',
+ 'cranky': 'Cranky',
+ 'crazy': 'Crazy',
+ 'creative': 'Creative',
+ 'curious': 'Curious',
+ 'dejected': 'Dejected',
+ 'depressed': 'Depressed',
+ 'disappointed': 'Disappointed',
+ 'disgusted': 'Disgusted',
+ 'dismayed': 'Dismayed',
+ 'distracted': 'Distracted',
+ 'embarrassed': 'Embarrassed',
+ 'envious': 'Envious',
+ 'excited': 'Excited',
+ 'flirtatious': 'Flirtatious',
+ 'frustrated': 'Frustrated',
+ 'grumpy': 'Grumpy',
+ 'guilty': 'Guilty',
+ 'happy': 'Happy',
+ 'hopeful': 'Hopeful',
+ 'hot': 'Hot',
+ 'humbled': 'Humbled',
+ 'humiliated': 'Humiliated',
+ 'hungry': 'Hungry',
+ 'hurt': 'Hurt',
+ 'impressed': 'Impressed',
+ 'in_awe': 'In awe',
+ 'in_love': 'In love',
+ 'indignant': 'Indignant',
+ 'interested': 'Interested',
+ 'intoxicated': 'Intoxicated',
+ 'invincible': 'Invincible',
+ 'jealous': 'Jealous',
+ 'lonely': 'Lonely',
+ 'lucky': 'Lucky',
+ 'mean': 'Mean',
+ 'moody': 'Moody',
+ 'nervous': 'Nervous',
+ 'neutral': 'Neutral',
+ 'offended': 'Offended',
+ 'outraged': 'Outraged',
+ 'playful': 'Playful',
+ 'proud': 'Proud',
+ 'relaxed': 'Relaxed',
+ 'relieved': 'Relieved',
+ 'remorseful': 'Remorseful',
+ 'restless': 'Restless',
+ 'sad': 'Sad',
+ 'sarcastic': 'Sarcastic',
+ 'serious': 'Serious',
+ 'shocked': 'Shocked',
+ 'shy': 'Shy',
+ 'sick': 'Sick',
+ 'sleepy': 'Sleepy',
+ 'spontaneous': 'Spontaneous',
+ 'stressed': 'Stressed',
+ 'strong': 'Strong',
+ 'surprised': 'Surprised',
+ 'thankful': 'Thankful',
+ 'thirsty': 'Thirsty',
+ 'tired': 'Tired',
+ 'undefined': 'Undefined',
+ 'weak': 'Weak',
+ 'worried': 'Worried'
}
-
-
-
ACTIVITIES = {
- 'doing_chores': {
- 'category': 'Doing_chores',
-
- 'buying_groceries': 'Buying groceries',
- 'cleaning': 'Cleaning',
- 'cooking': 'Cooking',
- 'doing_maintenance': 'Doing maintenance',
- 'doing_the_dishes': 'Doing the dishes',
- 'doing_the_laundry': 'Doing the laundry',
- 'gardening': 'Gardening',
- 'running_an_errand': 'Running an errand',
- 'walking_the_dog': 'Walking the dog',
- 'other': 'Other',
- },
- 'drinking': {
- 'category': 'Drinking',
-
- 'having_a_beer': 'Having a beer',
- 'having_coffee': 'Having coffee',
- 'having_tea': 'Having tea',
- 'other': 'Other',
- },
- 'eating': {
- 'category':'Eating',
-
- 'having_breakfast': 'Having breakfast',
- 'having_a_snack': 'Having a snack',
- 'having_dinner': 'Having dinner',
- 'having_lunch': 'Having lunch',
- 'other': 'Other',
- },
- 'exercising': {
- 'category': 'Exercising',
-
- 'cycling': 'Cycling',
- 'dancing': 'Dancing',
- 'hiking': 'Hiking',
- 'jogging': 'Jogging',
- 'playing_sports': 'Playing sports',
- 'running': 'Running',
- 'skiing': 'Skiing',
- 'swimming': 'Swimming',
- 'working_out': 'Working out',
- 'other': 'Other',
- },
- 'grooming': {
- 'category': 'Grooming',
-
- 'at_the_spa': 'At the spa',
- 'brushing_teeth': 'Brushing teeth',
- 'getting_a_haircut': 'Getting a haircut',
- 'shaving': 'Shaving',
- 'taking_a_bath': 'Taking a bath',
- 'taking_a_shower': 'Taking a shower',
- 'other': 'Other',
- },
- 'having_appointment': {
- 'category': 'Having appointment',
-
- 'other': 'Other',
- },
- 'inactive': {
- 'category': 'Inactive',
-
- 'day_off': 'Day_off',
- 'hanging_out': 'Hanging out',
- 'hiding': 'Hiding',
- 'on_vacation': 'On vacation',
- 'praying': 'Praying',
- 'scheduled_holiday': 'Scheduled holiday',
- 'sleeping': 'Sleeping',
- 'thinking': 'Thinking',
- 'other': 'Other',
- },
- 'relaxing': {
- 'category': 'Relaxing',
-
- 'fishing': 'Fishing',
- 'gaming': 'Gaming',
- 'going_out': 'Going out',
- 'partying': 'Partying',
- 'reading': 'Reading',
- 'rehearsing': 'Rehearsing',
- 'shopping': 'Shopping',
- 'smoking': 'Smoking',
- 'socializing': 'Socializing',
- 'sunbathing': 'Sunbathing',
- 'watching_a_movie': 'Watching a movie',
- 'watching_tv': 'Watching tv',
- 'other': 'Other',
- },
- 'talking': {
- 'category': 'Talking',
-
- 'in_real_life': 'In real life',
- 'on_the_phone': 'On the phone',
- 'on_video_phone': 'On video phone',
- 'other': 'Other',
- },
- 'traveling': {
- 'category': 'Traveling',
-
- 'commuting': 'Commuting',
- 'driving': 'Driving',
- 'in_a_car': 'In a car',
- 'on_a_bus': 'On a bus',
- 'on_a_plane': 'On a plane',
- 'on_a_train': 'On a train',
- 'on_a_trip': 'On a trip',
- 'walking': 'Walking',
- 'cycling': 'Cycling',
- 'other': 'Other',
- },
- 'undefined': {
- 'category': 'Undefined',
-
- 'other': 'Other',
- },
- 'working': {
- 'category': 'Working',
-
- 'coding': 'Coding',
- 'in_a_meeting': 'In a meeting',
- 'writing': 'Writing',
- 'studying': 'Studying',
- 'other': 'Other',
- }
- }
-
+ 'doing_chores': {
+ 'category': 'Doing_chores',
+ 'buying_groceries': 'Buying groceries',
+ 'cleaning': 'Cleaning',
+ 'cooking': 'Cooking',
+ 'doing_maintenance': 'Doing maintenance',
+ 'doing_the_dishes': 'Doing the dishes',
+ 'doing_the_laundry': 'Doing the laundry',
+ 'gardening': 'Gardening',
+ 'running_an_errand': 'Running an errand',
+ 'walking_the_dog': 'Walking the dog',
+ 'other': 'Other',
+ },
+ 'drinking': {
+ 'category': 'Drinking',
+ 'having_a_beer': 'Having a beer',
+ 'having_coffee': 'Having coffee',
+ 'having_tea': 'Having tea',
+ 'other': 'Other',
+ },
+ 'eating': {
+ 'category': 'Eating',
+ 'having_breakfast': 'Having breakfast',
+ 'having_a_snack': 'Having a snack',
+ 'having_dinner': 'Having dinner',
+ 'having_lunch': 'Having lunch',
+ 'other': 'Other',
+ },
+ 'exercising': {
+ 'category': 'Exercising',
+ 'cycling': 'Cycling',
+ 'dancing': 'Dancing',
+ 'hiking': 'Hiking',
+ 'jogging': 'Jogging',
+ 'playing_sports': 'Playing sports',
+ 'running': 'Running',
+ 'skiing': 'Skiing',
+ 'swimming': 'Swimming',
+ 'working_out': 'Working out',
+ 'other': 'Other',
+ },
+ 'grooming': {
+ 'category': 'Grooming',
+ 'at_the_spa': 'At the spa',
+ 'brushing_teeth': 'Brushing teeth',
+ 'getting_a_haircut': 'Getting a haircut',
+ 'shaving': 'Shaving',
+ 'taking_a_bath': 'Taking a bath',
+ 'taking_a_shower': 'Taking a shower',
+ 'other': 'Other',
+ },
+ 'having_appointment': {
+ 'category': 'Having appointment',
+ 'other': 'Other',
+ },
+ 'inactive': {
+ 'category': 'Inactive',
+ 'day_off': 'Day_off',
+ 'hanging_out': 'Hanging out',
+ 'hiding': 'Hiding',
+ 'on_vacation': 'On vacation',
+ 'praying': 'Praying',
+ 'scheduled_holiday': 'Scheduled holiday',
+ 'sleeping': 'Sleeping',
+ 'thinking': 'Thinking',
+ 'other': 'Other',
+ },
+ 'relaxing': {
+ 'category': 'Relaxing',
+ 'fishing': 'Fishing',
+ 'gaming': 'Gaming',
+ 'going_out': 'Going out',
+ 'partying': 'Partying',
+ 'reading': 'Reading',
+ 'rehearsing': 'Rehearsing',
+ 'shopping': 'Shopping',
+ 'smoking': 'Smoking',
+ 'socializing': 'Socializing',
+ 'sunbathing': 'Sunbathing',
+ 'watching_a_movie': 'Watching a movie',
+ 'watching_tv': 'Watching tv',
+ 'other': 'Other',
+ },
+ 'talking': {
+ 'category': 'Talking',
+ 'in_real_life': 'In real life',
+ 'on_the_phone': 'On the phone',
+ 'on_video_phone': 'On video phone',
+ 'other': 'Other',
+ },
+ 'traveling': {
+ 'category': 'Traveling',
+ 'commuting': 'Commuting',
+ 'driving': 'Driving',
+ 'in_a_car': 'In a car',
+ 'on_a_bus': 'On a bus',
+ 'on_a_plane': 'On a plane',
+ 'on_a_train': 'On a train',
+ 'on_a_trip': 'On a trip',
+ 'walking': 'Walking',
+ 'cycling': 'Cycling',
+ 'other': 'Other',
+ },
+ 'undefined': {
+ 'category': 'Undefined',
+ 'other': 'Other',
+ },
+ 'working': {
+ 'category': 'Working',
+ 'coding': 'Coding',
+ 'in_a_meeting': 'In a meeting',
+ 'writing': 'Writing',
+ 'studying': 'Studying',
+ 'other': 'Other',
+ }
+}
diff --git a/poezio/plugin.py b/poezio/plugin.py
index c8bffc95..359f1148 100644
--- a/poezio/plugin.py
+++ b/poezio/plugin.py
@@ -13,12 +13,14 @@ import traceback
import logging
log = logging.getLogger(__name__)
+
class PluginConfig(config.Config):
"""
Plugin configuration object.
They are accessible inside the plugin with self.config
and behave like the core Config object.
"""
+
def __init__(self, filename, module_name, default=None):
config.Config.__init__(self, filename, default=default)
self.module_name = module_name
@@ -81,8 +83,10 @@ class SafetyMetaclass(type):
raise
elif SafetyMetaclass.core:
log.error('Error in a plugin', exc_info=True)
- SafetyMetaclass.core.information(traceback.format_exc(), 'Error')
+ SafetyMetaclass.core.information(traceback.format_exc(),
+ 'Error')
return None
+
return helper
def __new__(meta, name, bases, class_dict):
@@ -92,10 +96,12 @@ class SafetyMetaclass(type):
class_dict[k] = SafetyMetaclass.safe_func(v)
return type.__new__(meta, name, bases, class_dict)
+
class PluginWrap(object):
"""
A wrapper to implicitly pass the module name to PluginAPI
"""
+
def __init__(self, api, module):
self.api = api
self.module = module
@@ -105,6 +111,7 @@ class PluginWrap(object):
module = object.__getattribute__(self, 'module')
return partial(getattr(api, name), module)
+
class PluginAPI(object):
"""
The public API exposed to the plugins.
@@ -368,6 +375,7 @@ class PluginAPI(object):
"""
self.core.xmpp.del_event_handler(event_name, handler)
+
class BasePlugin(object, metaclass=SafetyMetaclass):
"""
Class that all plugins derive from.
@@ -379,10 +387,10 @@ class BasePlugin(object, metaclass=SafetyMetaclass):
self.core = core
# More hack; luckily we'll never have more than one core object
SafetyMetaclass.core = core
- conf = os.path.join(plugins_conf_dir, self.__module__+'.cfg')
+ conf = os.path.join(plugins_conf_dir, self.__module__ + '.cfg')
try:
- self.config = PluginConfig(conf, self.__module__,
- default=self.default_config)
+ self.config = PluginConfig(
+ conf, self.__module__, default=self.default_config)
except Exception:
log.debug('Error while creating the plugin config', exc_info=True)
self.config = PluginConfig(conf, self.__module__)
@@ -419,13 +427,24 @@ class BasePlugin(object, metaclass=SafetyMetaclass):
def unload(self):
self.cleanup()
- def add_command(self, name, handler, help, completion=None, short='', usage=''):
+ def add_command(self,
+ name,
+ handler,
+ help,
+ completion=None,
+ short='',
+ usage=''):
"""
Add a global command.
You cannot overwrite the existing commands.
"""
- return self.api.add_command(name, handler, help,
- completion=completion, short=short, usage=usage)
+ return self.api.add_command(
+ name,
+ handler,
+ help,
+ completion=completion,
+ short=short,
+ usage=usage)
def del_command(self, name):
"""
@@ -458,12 +477,25 @@ class BasePlugin(object, metaclass=SafetyMetaclass):
"""
return self.api.del_tab_key(tab_type, key)
- def add_tab_command(self, tab_type, name, handler, help, completion=None, short='', usage=''):
+ def add_tab_command(self,
+ tab_type,
+ name,
+ handler,
+ help,
+ completion=None,
+ short='',
+ usage=''):
"""
Add a command only for a type of tab.
"""
- return self.api.add_tab_command(tab_type, name, handler, help,
- completion=completion, short=short, usage=usage)
+ return self.api.add_tab_command(
+ tab_type,
+ name,
+ handler,
+ help,
+ completion=completion,
+ short=short,
+ usage=usage)
def del_tab_command(self, tab_type, name):
"""
diff --git a/poezio/plugin_manager.py b/poezio/plugin_manager.py
index 8499cf51..cdc5f0d0 100644
--- a/poezio/plugin_manager.py
+++ b/poezio/plugin_manager.py
@@ -16,12 +16,14 @@ from poezio.config import config
log = logging.getLogger(__name__)
+
class PluginManager(object):
"""
Plugin Manager
Contains all the references to the plugins
And keeps track of everything the plugin has done through the API.
"""
+
def __init__(self, core):
self.core = core
# module name -> module object
@@ -65,7 +67,8 @@ class PluginManager(object):
module = None
loader = self.finder.find_module(name, self.load_path)
if not loader:
- self.core.information('Could not find plugin: %s' % name, 'Error')
+ self.core.information('Could not find plugin: %s' % name,
+ 'Error')
return
module = loader.load_module()
except Exception as e:
@@ -90,8 +93,7 @@ class PluginManager(object):
log.error('Error while loading the plugin %s', name, exc_info=True)
if notify:
self.core.information('Unable to load the plugin %s: %s' %
- (name, e),
- 'Error')
+ (name, e), 'Error')
self.unload(name, notify=False)
else:
if notify:
@@ -106,8 +108,8 @@ class PluginManager(object):
del self.core.key_func[key]
for tab in list(self.tab_commands[name].keys()):
for command in self.tab_commands[name][tab][:]:
- self.del_tab_command(name, getattr(tabs, tab),
- command[0])
+ self.del_tab_command(name,
+ getattr(tabs, tab), command[0])
del self.tab_commands[name][tab]
for tab in list(self.tab_keys[name].keys()):
for key in self.tab_keys[name][tab][:]:
@@ -128,16 +130,21 @@ class PluginManager(object):
except Exception as e:
log.debug("Could not unload plugin %s", name, exc_info=True)
self.core.information("Could not unload plugin %s: %s" %
- (name, e),
- 'Error')
+ (name, e), 'Error')
- def add_command(self, module_name, name, handler, help,
- completion=None, short='', usage=''):
+ def add_command(self,
+ module_name,
+ name,
+ handler,
+ help,
+ completion=None,
+ short='',
+ usage=''):
"""
Add a global command.
"""
if name in self.core.commands:
- raise Exception("Command '%s' already exists" % (name,))
+ raise Exception("Command '%s' already exists" % (name, ))
commands = self.commands[module_name]
commands[name] = Command(handler, help, completion, short, usage)
@@ -152,8 +159,15 @@ class PluginManager(object):
if name in self.core.commands:
del self.core.commands[name]
- def add_tab_command(self, module_name, tab_type, name, handler, help,
- completion=None, short='', usage=''):
+ def add_tab_command(self,
+ module_name,
+ tab_type,
+ name,
+ handler,
+ help,
+ completion=None,
+ short='',
+ usage=''):
"""
Add a command only for a type of Tab.
"""
@@ -164,8 +178,8 @@ class PluginManager(object):
if t not in commands:
commands[t] = []
commands[t].append((name, handler, help, completion))
- tab_type.plugin_commands[name] = Command(handler, help,
- completion, short, usage)
+ tab_type.plugin_commands[name] = Command(handler, help, completion,
+ short, usage)
for tab in self.core.tabs:
if isinstance(tab, tab_type):
tab.update_commands()
@@ -224,7 +238,7 @@ class PluginManager(object):
already exists.
"""
if key in self.core.key_func:
- raise Exception("Key '%s' already exists" % (key,))
+ raise Exception("Key '%s' already exists" % (key, ))
keys = self.keys[module_name]
keys[key] = handler
self.core.key_func[key] = handler
@@ -273,20 +287,31 @@ class PluginManager(object):
names |= add
except OSError:
pass
- plugins_files = [name[:-3] for name in names if name.endswith('.py')
- and name != '__init__.py' and not name.startswith('.')]
+ plugins_files = [
+ name[:-3] for name in names
+ if name.endswith('.py') and name != '__init__.py'
+ and not name.startswith('.')
+ ]
plugins_files.sort()
position = the_input.get_argument_position(quoted=False)
- return Completion(the_input.new_completion, plugins_files, position, '',
- quotify=False)
+ return Completion(
+ the_input.new_completion,
+ plugins_files,
+ position,
+ '',
+ quotify=False)
def completion_unload(self, the_input):
"""
completion function that completes the name of loaded plugins
"""
position = the_input.get_argument_position(quoted=False)
- return Completion(the_input.new_completion, sorted(self.plugins.keys()), position,
- '', quotify=False)
+ return Completion(
+ the_input.new_completion,
+ sorted(self.plugins.keys()),
+ position,
+ '',
+ quotify=False)
def on_plugins_dir_change(self, new_value):
self.plugins_dir = new_value
@@ -319,8 +344,10 @@ class PluginManager(object):
try:
os.makedirs(self.plugins_conf_dir)
except OSError:
- log.error('Unable to create the plugin conf dir: %s',
- self.plugins_conf_dir, exc_info=True)
+ log.error(
+ 'Unable to create the plugin conf dir: %s',
+ self.plugins_conf_dir,
+ exc_info=True)
return False
return True
@@ -346,8 +373,10 @@ class PluginManager(object):
try:
os.makedirs(self.plugins_dir, exist_ok=True)
except OSError:
- log.error('Unable to create the plugins dir: %s',
- self.plugins_dir, exc_info=True)
+ log.error(
+ 'Unable to create the plugins dir: %s',
+ self.plugins_dir,
+ exc_info=True)
return False
return True
@@ -358,8 +387,8 @@ class PluginManager(object):
self.load_path = []
- default_plugin_path = path.join(path.dirname(path.dirname(__file__)),
- 'plugins')
+ default_plugin_path = path.join(
+ path.dirname(path.dirname(__file__)), 'plugins')
if os.access(default_plugin_path, os.R_OK | os.X_OK):
self.load_path.insert(0, default_plugin_path)
@@ -374,4 +403,3 @@ class PluginManager(object):
else:
if poezio_plugins.__path__:
self.load_path.append(list(poezio_plugins.__path__)[0])
-
diff --git a/poezio/poezio.py b/poezio/poezio.py
index f841b672..51e10d1b 100644
--- a/poezio/poezio.py
+++ b/poezio/poezio.py
@@ -4,8 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
-
"""
Starting point of poezio. Launches both the Connection and Gui
"""
@@ -16,6 +14,7 @@ import signal
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
def test_curses():
"""
Check if the system ncurses linked with python has unicode capabilities.
@@ -74,9 +73,9 @@ def main():
from poezio import core
- signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore ctrl-c
+ signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore ctrl-c
cocore = core.Core()
- signal.signal(signal.SIGUSR1, cocore.sigusr_handler) # reload the config
+ signal.signal(signal.SIGUSR1, cocore.sigusr_handler) # reload the config
signal.signal(signal.SIGHUP, cocore.exit_from_signal)
signal.signal(signal.SIGTERM, cocore.exit_from_signal)
if options.debug:
@@ -84,6 +83,7 @@ def main():
cocore.start()
from slixmpp.exceptions import IqError, IqTimeout
+
def swallow_iqerrors(loop, context):
"""Do not log unhandled iq errors and timeouts"""
if not isinstance(context['exception'], (IqError, IqTimeout)):
diff --git a/poezio/poezio_shlex.py b/poezio/poezio_shlex.py
index 7072d10d..ba8d6d56 100644
--- a/poezio/poezio_shlex.py
+++ b/poezio/poezio_shlex.py
@@ -20,10 +20,12 @@ from io import StringIO
__all__ = ["shlex", "split", "quote"]
+
class shlex(object):
"""
A custom version of the shlex in the stdlib to yield more information
"""
+
def __init__(self, instream=None, infile=None, posix=True):
if isinstance(instream, str):
instream = StringIO(instream)
@@ -73,9 +75,9 @@ class shlex(object):
self.lineno = 1
if self.debug:
if newfile is not None:
- print('shlex: pushing to file %s' % (self.infile,))
+ print('shlex: pushing to file %s' % (self.infile, ))
else:
- print('shlex: pushing to stream %s' % (self.instream,))
+ print('shlex: pushing to stream %s' % (self.instream, ))
def pop_source(self):
"Pop the input source stack."
@@ -111,7 +113,7 @@ class shlex(object):
print("shlex: in state", repr(self.state), \
"I see character:", repr(nextchar))
if self.state == '\0':
- self.token = '' # past end of file
+ self.token = '' # past end of file
token_end = self.instream.tell()
break
elif self.state == ' ':
@@ -124,7 +126,7 @@ class shlex(object):
print("shlex: I see whitespace in whitespace state")
if self.token or (self.posix and quoted):
token_end = self.instream.tell() - 1
- break # emit current token
+ break # emit current token
else:
continue
elif nextchar in self.wordchars:
@@ -143,12 +145,12 @@ class shlex(object):
self.token = nextchar
if self.token or (self.posix and quoted):
token_end = self.instream.tell() - 1
- break # emit current token
+ break # emit current token
else:
continue
elif self.state == self.quotes:
quoted = True
- if not nextchar: # end of file
+ if not nextchar: # end of file
if self.debug >= 2:
print("shlex: I see EOF in quotes state")
# XXX what error should be raised here?
@@ -169,7 +171,7 @@ class shlex(object):
else:
self.token = self.token + nextchar
elif self.state == self.escape:
- if not nextchar: # end of file
+ if not nextchar: # end of file
if self.debug >= 2:
print("shlex: I see EOF in escape state")
# XXX what error should be raised here?
@@ -182,7 +184,7 @@ class shlex(object):
self.state = escapedstate
elif self.state == 'a':
if not nextchar:
- self.state = '\0' # end of file
+ self.state = '\0' # end of file
token_end = self.instream.tell()
break
elif nextchar in self.whitespace:
@@ -191,7 +193,7 @@ class shlex(object):
self.state = ' '
if self.token or (self.posix and quoted):
token_end = self.instream.tell() - 1
- break # emit current token
+ break # emit current token
else:
continue
elif nextchar in self.wordchars or nextchar == self.quotes \
@@ -204,7 +206,7 @@ class shlex(object):
self.state = ' '
if self.token:
token_end = self.instream.tell()
- break # emit current token
+ break # emit current token
else:
continue
result = self.token
@@ -244,6 +246,7 @@ class shlex(object):
raise StopIteration
return token
+
def split(s, comments=False, posix=True):
lex = shlex(s, posix=posix)
lex.whitespace_split = True
@@ -254,6 +257,7 @@ def split(s, comments=False, posix=True):
_find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search
+
def quote(s):
"""Return a shell-escaped version of the string *s*."""
if not s:
diff --git a/poezio/poopt.py b/poezio/poopt.py
index 51474222..b9243686 100644
--- a/poezio/poopt.py
+++ b/poezio/poopt.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
'''This is a template module just for instruction. And poopt.'''
from typing import List, Tuple
@@ -24,6 +23,7 @@ libc = ffi.dlopen(None)
# ctypedef Py_UCS4 wchar_t
# int wcwidth(wchar_t c)
+
# Just checking if the return value is -1. In some (all?) implementations,
# wcwidth("😆") returns -1 while it should return 1. In these cases, we
# return 1 instead because this is by far the most probable real value.
@@ -40,6 +40,7 @@ def xwcwidth(c: str) -> int:
return 1
return res
+
# cut_text: takes a string and returns a tuple of int.
#
# Each two int tuple is a line, represented by the ending position it
@@ -141,9 +142,10 @@ def cut_text(string: str, width: int) -> List[Tuple[int, int]]:
# char's columns to the line's columns
columns += cols
# We are at the end of the string, append the last line, not finished
- retlist.append((start_pos, spos+1))
+ retlist.append((start_pos, spos + 1))
return retlist
+
# wcswidth: An emulation of the POSIX wcswidth(3) function using xwcwidth.
def wcswidth(string: str) -> int:
'''wcswidth(s)
@@ -155,6 +157,7 @@ def wcswidth(string: str) -> int:
columns += xwcwidth(wc)
return columns
+
# cut_by_columns: takes a python string and a number of columns, returns a
# python string truncated to take at most that many columns
# For example cut_by_columns(n, "エメルカ") will return:
diff --git a/poezio/roster.py b/poezio/roster.py
index 96dfd396..9516d27d 100644
--- a/poezio/roster.py
+++ b/poezio/roster.py
@@ -4,8 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
-
"""
Defines the Roster and RosterGroup classes
"""
@@ -21,6 +19,7 @@ from datetime import datetime
from poezio.common import safeJID
from slixmpp.exceptions import IqError, IqTimeout
+
class Roster(object):
"""
The proxy class to get the roster from slixmpp.
@@ -28,6 +27,7 @@ class Roster(object):
"""
DEFAULT_FILTER = (lambda x, y: None, None)
+
def __init__(self):
"""
node: the RosterSingle from slixmpp
@@ -37,8 +37,8 @@ class Roster(object):
# A tuple(function, *args) function to filter contacts
# on search, for example
self.contact_filter = self.DEFAULT_FILTER
- self.folded_groups = set(config.get('folded_roster_groups',
- section='var').split(':'))
+ self.folded_groups = set(
+ config.get('folded_roster_groups', section='var').split(':'))
self.groups = {}
self.contacts = {}
self.length = 0
@@ -121,9 +121,8 @@ class Roster(object):
def get_groups(self, sort=''):
"""Return a list of the RosterGroups"""
group_list = sorted(
- (group for group in self.groups.values() if group),
- key=lambda x: x.name.lower() if x.name else ''
- )
+ (group for group in self.groups.values() if group),
+ key=lambda x: x.name.lower() if x.name else '')
for sorting in sort.split(':'):
if sorting == 'reverse':
@@ -137,7 +136,8 @@ class Roster(object):
"""Return a group or create it if not present"""
if name in self.groups:
return self.groups[name]
- self.groups[name] = RosterGroup(name, folded=name in self.folded_groups)
+ self.groups[name] = RosterGroup(
+ name, folded=name in self.folded_groups)
def add(self, jid):
"""Subscribe to a jid"""
@@ -216,7 +216,8 @@ class Roster(object):
for group in contact.groups:
if group not in self.groups:
- self.groups[group] = RosterGroup(group, folded=group in self.folded_groups)
+ self.groups[group] = RosterGroup(
+ group, folded=group in self.folded_groups)
self.groups[group].add(contact)
def __len__(self):
@@ -230,10 +231,10 @@ class Roster(object):
def __repr__(self):
ret = '== Roster:\nContacts:\n'
for contact in self.contacts.values():
- ret += '%s\n' % (contact,)
+ ret += '%s\n' % (contact, )
ret += 'Groups\n'
for group in self.groups:
- ret += '%s\n' % (group,)
+ ret += '%s\n' % (group, )
return ret + '\n'
def export(self, path):
@@ -242,7 +243,10 @@ class Roster(object):
return False
try:
f = open(path, 'w+', encoding='utf-8')
- f.writelines([str(i) + "\n" for i in self.contacts if self[i] and (self[i].subscription == "both" or self[i].ask)])
+ f.writelines([
+ str(i) + "\n" for i in self.contacts
+ if self[i] and (self[i].subscription == "both" or self[i].ask)
+ ])
f.close()
return True
except OSError:
@@ -263,12 +267,13 @@ class RosterGroup(object):
It can be Friends/Family etc, but also can be
Online/Offline or whatever
"""
+
def __init__(self, name, contacts=None, folded=False):
if not contacts:
contacts = []
self.contacts = set(contacts)
self.name = name if name is not None else ''
- self.folded = folded # if the group content is to be shown
+ self.folded = folded # if the group content is to be shown
def __iter__(self):
"""Iterate over the contacts"""
@@ -301,9 +306,9 @@ class RosterGroup(object):
if contact_filter is Roster.DEFAULT_FILTER or contact_filter is None:
contact_list = self.contacts.copy()
else:
- contact_list = [contact
- for contact in self.contacts.copy()
- if contact_filter[0](contact, contact_filter[1])
+ contact_list = [
+ contact for contact in self.contacts.copy()
+ if contact_filter[0](contact, contact_filter[1])
]
contact_list = sorted(contact_list, key=SORTING_METHODS['name'])
@@ -329,10 +334,12 @@ class RosterGroup(object):
"""Return the number of connected contacts"""
return len([1 for contact in self.contacts if len(contact)])
+
def create_roster():
"Create the global roster object"
global roster
roster = Roster()
+
# Shared roster object
roster = None
diff --git a/poezio/roster_sorting.py b/poezio/roster_sorting.py
index c57f0dce..9f156e0f 100644
--- a/poezio/roster_sorting.py
+++ b/poezio/roster_sorting.py
@@ -5,17 +5,21 @@ Defines the roster sorting methods used in roster.py
########################### Contacts sorting ############################
-PRESENCE_PRIORITY = {'unavailable': 5,
- 'xa': 4,
- 'away': 3,
- 'dnd': 2,
- '': 1,
- 'available': 1}
+PRESENCE_PRIORITY = {
+ 'unavailable': 5,
+ 'xa': 4,
+ 'away': 3,
+ 'dnd': 2,
+ '': 1,
+ 'available': 1
+}
+
def sort_jid(contact):
"""Sort by contact JID"""
return contact.bare_jid
+
def sort_show(contact):
"""Sort by show (from high availability to low)"""
res = contact.get_highest_priority_resource()
@@ -26,23 +30,28 @@ def sort_show(contact):
return 0
return PRESENCE_PRIORITY[show]
+
def sort_resource_nb(contact):
"""Sort by number of connected resources"""
- return - len(contact)
+ return -len(contact)
+
def sort_name(contact):
"""Sort by name (case insensitive)"""
return contact.name.lower() or contact.bare_jid
+
def sort_sname(contact):
"""Sort by name (case sensitive)"""
return contact.name or contact.bare_jid
+
def sort_online(contact):
"""Sort by connected/disconnected"""
result = sort_show(contact)
return 0 if result < 5 else 1
+
SORTING_METHODS = {
'jid': sort_jid,
'sname': sort_sname,
@@ -52,39 +61,44 @@ SORTING_METHODS = {
'online': sort_online,
}
-
######################## Roster Groups sorting ##########################
+
def sort_group_name(group):
"""Sort by name (case insensitive)"""
return group.name.lower()
+
def sort_group_sname(group):
"""Sort by name (case-sensitive)"""
return group.name
+
def sort_group_folded(group):
"""Sort by folded/unfolded"""
return group.folded
+
def sort_group_connected(group):
"""Sort by number of connected contacts"""
- return - group.get_nb_connected_contacts()
+ return -group.get_nb_connected_contacts()
+
def sort_group_size(group):
"""Sort by group size"""
- return - len(group)
+ return -len(group)
+
def sort_group_none(group):
"""Put the none group at the end, if any"""
return 0 if group.name != 'none' else 1
+
GROUP_SORTING_METHODS = {
- 'name': sort_group_name,
- 'fold': sort_group_folded,
- 'connected': sort_group_connected,
- 'size': sort_group_size,
- 'none': sort_group_none,
- 'sname': sort_group_sname,
+ 'name': sort_group_name,
+ 'fold': sort_group_folded,
+ 'connected': sort_group_connected,
+ 'size': sort_group_size,
+ 'none': sort_group_none,
+ 'sname': sort_group_sname,
}
-
diff --git a/poezio/size_manager.py b/poezio/size_manager.py
index 4dfa6f76..d5c7e719 100644
--- a/poezio/size_manager.py
+++ b/poezio/size_manager.py
@@ -12,8 +12,8 @@ THRESHOLD_HEIGHT_DEGRADE = 10
FULL_WIDTH_DEGRADE = 66
FULL_HEIGHT_DEGRADE = 10
-class SizeManager(object):
+class SizeManager(object):
def __init__(self, core):
self._core = core
@@ -36,4 +36,3 @@ class SizeManager(object):
def core_degrade_y(self):
y, x = self._core.stdscr.getmaxyx()
return y < FULL_HEIGHT_DEGRADE
-
diff --git a/poezio/tabs/__init__.py b/poezio/tabs/__init__.py
index 01f65aa3..81e9f115 100644
--- a/poezio/tabs/__init__.py
+++ b/poezio/tabs/__init__.py
@@ -13,8 +13,10 @@ from poezio.tabs.adhoc_commands_list import AdhocCommandsListTab
from poezio.tabs.data_forms import DataFormsTab
from poezio.tabs.bookmarkstab import BookmarksTab
-__all__ = ['Tab', 'ChatTab', 'GapTab', 'OneToOneTab', 'STATE_PRIORITY',
- 'SHOW_NAME', 'RosterInfoTab', 'MucTab', 'NS_MUC_USER', 'PrivateTab',
- 'ConfirmTab', 'ConversationTab', 'StaticConversationTab',
- 'DynamicConversationTab', 'XMLTab', 'ListTab', 'MucListTab',
- 'AdhocCommandsListTab', 'DataFormsTab', 'BookmarksTab']
+__all__ = [
+ 'Tab', 'ChatTab', 'GapTab', 'OneToOneTab', 'STATE_PRIORITY', 'SHOW_NAME',
+ 'RosterInfoTab', 'MucTab', 'NS_MUC_USER', 'PrivateTab', 'ConfirmTab',
+ 'ConversationTab', 'StaticConversationTab', 'DynamicConversationTab',
+ 'XMLTab', 'ListTab', 'MucListTab', 'AdhocCommandsListTab', 'DataFormsTab',
+ 'BookmarksTab'
+]
diff --git a/poezio/tabs/adhoc_commands_list.py b/poezio/tabs/adhoc_commands_list.py
index 6db654c9..a1b186be 100644
--- a/poezio/tabs/adhoc_commands_list.py
+++ b/poezio/tabs/adhoc_commands_list.py
@@ -11,42 +11,51 @@ from poezio.tabs import ListTab
from slixmpp.plugins.xep_0030.stanza.items import DiscoItem
+
class AdhocCommandsListTab(ListTab):
plugin_commands = {}
plugin_keys = {}
def __init__(self, core, jid):
- ListTab.__init__(self, core, jid.full,
- "“Enter”: execute selected command.",
- 'Ad-hoc commands of JID %s (Loading)' % jid,
- (('Node', 0), ('Description', 1)))
+ ListTab.__init__(
+ self, core, jid.full, "“Enter”: execute selected command.",
+ 'Ad-hoc commands of JID %s (Loading)' % jid, (('Node', 0),
+ ('Description', 1)))
self.key_func['^M'] = self.execute_selected_command
def execute_selected_command(self):
if not self.listview or not self.listview.get_selected_row():
return
node, name, jid = self.listview.get_selected_row()
- session = {'next': self.core.handler.next_adhoc_step,
- 'error': self.core.handler.adhoc_error}
+ session = {
+ 'next': self.core.handler.next_adhoc_step,
+ 'error': self.core.handler.adhoc_error
+ }
self.core.xmpp.plugin['xep_0050'].start_command(jid, node, session)
def get_columns_sizes(self):
- return {'Node': int(self.width * 3 / 8),
- 'Description': int(self.width * 5 / 8)}
+ return {
+ 'Node': int(self.width * 3 / 8),
+ 'Description': int(self.width * 5 / 8)
+ }
def on_list_received(self, iq):
"""
Fill the listview with the value from the received iq
"""
if iq['type'] == 'error':
- self.set_error(iq['error']['type'], iq['error']['code'], iq['error']['text'])
+ self.set_error(iq['error']['type'], iq['error']['code'],
+ iq['error']['text'])
return
+
def get_items():
substanza = iq['disco_items']
for item in substanza['substanzas']:
if isinstance(item, DiscoItem):
yield item
- items = [(item['node'], item['name'] or '', item['jid']) for item in get_items()]
+
+ items = [(item['node'], item['name'] or '', item['jid'])
+ for item in get_items()]
self.listview.set_lines(items)
self.info_header.message = 'Ad-hoc commands of JID %s' % self.name
if self.core.current_tab() is self:
diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py
index 0570be27..29404036 100644
--- a/poezio/tabs/basetabs.py
+++ b/poezio/tabs/basetabs.py
@@ -35,60 +35,61 @@ from poezio.decorators import command_args_parser
# getters for tab colors (lambdas, so that they are dynamic)
STATE_COLORS = {
- 'disconnected': lambda: get_theme().COLOR_TAB_DISCONNECTED,
- 'scrolled': lambda: get_theme().COLOR_TAB_SCROLLED,
- 'nonempty': lambda: get_theme().COLOR_TAB_NONEMPTY,
- 'joined': lambda: get_theme().COLOR_TAB_JOINED,
- 'message': lambda: get_theme().COLOR_TAB_NEW_MESSAGE,
- 'composing': lambda: get_theme().COLOR_TAB_COMPOSING,
- 'highlight': lambda: get_theme().COLOR_TAB_HIGHLIGHT,
- 'private': lambda: get_theme().COLOR_TAB_PRIVATE,
- 'normal': lambda: get_theme().COLOR_TAB_NORMAL,
- 'current': lambda: get_theme().COLOR_TAB_CURRENT,
- 'attention': lambda: get_theme().COLOR_TAB_ATTENTION,
- }
+ 'disconnected': lambda: get_theme().COLOR_TAB_DISCONNECTED,
+ 'scrolled': lambda: get_theme().COLOR_TAB_SCROLLED,
+ 'nonempty': lambda: get_theme().COLOR_TAB_NONEMPTY,
+ 'joined': lambda: get_theme().COLOR_TAB_JOINED,
+ 'message': lambda: get_theme().COLOR_TAB_NEW_MESSAGE,
+ 'composing': lambda: get_theme().COLOR_TAB_COMPOSING,
+ 'highlight': lambda: get_theme().COLOR_TAB_HIGHLIGHT,
+ 'private': lambda: get_theme().COLOR_TAB_PRIVATE,
+ 'normal': lambda: get_theme().COLOR_TAB_NORMAL,
+ 'current': lambda: get_theme().COLOR_TAB_CURRENT,
+ 'attention': lambda: get_theme().COLOR_TAB_ATTENTION,
+}
VERTICAL_STATE_COLORS = {
- 'disconnected': lambda: get_theme().COLOR_VERTICAL_TAB_DISCONNECTED,
- 'scrolled': lambda: get_theme().COLOR_VERTICAL_TAB_SCROLLED,
- 'nonempty': lambda: get_theme().COLOR_VERTICAL_TAB_NONEMPTY,
- 'joined': lambda: get_theme().COLOR_VERTICAL_TAB_JOINED,
- 'message': lambda: get_theme().COLOR_VERTICAL_TAB_NEW_MESSAGE,
- 'composing': lambda: get_theme().COLOR_VERTICAL_TAB_COMPOSING,
- 'highlight': lambda: get_theme().COLOR_VERTICAL_TAB_HIGHLIGHT,
- 'private': lambda: get_theme().COLOR_VERTICAL_TAB_PRIVATE,
- 'normal': lambda: get_theme().COLOR_VERTICAL_TAB_NORMAL,
- 'current': lambda: get_theme().COLOR_VERTICAL_TAB_CURRENT,
- 'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION,
- }
-
+ 'disconnected': lambda: get_theme().COLOR_VERTICAL_TAB_DISCONNECTED,
+ 'scrolled': lambda: get_theme().COLOR_VERTICAL_TAB_SCROLLED,
+ 'nonempty': lambda: get_theme().COLOR_VERTICAL_TAB_NONEMPTY,
+ 'joined': lambda: get_theme().COLOR_VERTICAL_TAB_JOINED,
+ 'message': lambda: get_theme().COLOR_VERTICAL_TAB_NEW_MESSAGE,
+ 'composing': lambda: get_theme().COLOR_VERTICAL_TAB_COMPOSING,
+ 'highlight': lambda: get_theme().COLOR_VERTICAL_TAB_HIGHLIGHT,
+ 'private': lambda: get_theme().COLOR_VERTICAL_TAB_PRIVATE,
+ 'normal': lambda: get_theme().COLOR_VERTICAL_TAB_NORMAL,
+ 'current': lambda: get_theme().COLOR_VERTICAL_TAB_CURRENT,
+ 'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION,
+}
# priority of the different tab states when using Alt+e
# higher means more priority, < 0 means not selectable
STATE_PRIORITY = {
- 'normal': -1,
- 'current': -1,
- 'disconnected': 0,
- 'nonempty': 0.1,
- 'scrolled': 0.5,
- 'joined': 0.8,
- 'composing': 0.9,
- 'message': 1,
- 'highlight': 2,
- 'private': 2,
- 'attention': 3
- }
+ 'normal': -1,
+ 'current': -1,
+ 'disconnected': 0,
+ 'nonempty': 0.1,
+ 'scrolled': 0.5,
+ 'joined': 0.8,
+ 'composing': 0.9,
+ 'message': 1,
+ 'highlight': 2,
+ 'private': 2,
+ 'attention': 3
+}
SHOW_NAME = {
- 'dnd': 'busy',
- 'away': 'away',
- 'xa': 'not available',
- 'chat': 'chatty',
- '': 'available'
- }
+ 'dnd': 'busy',
+ 'away': 'away',
+ 'xa': 'not available',
+ 'chat': 'chatty',
+ '': 'available'
+}
+
class Tab(object):
plugin_commands = {}
plugin_keys = {}
+
def __init__(self, core):
self.core = core
if not hasattr(self, 'name'):
@@ -99,10 +100,9 @@ class Tab(object):
self._prev_state = None
self.need_resize = False
- self.key_func = {} # each tab should add their keys in there
- # and use them in on_input
- self.commands = {} # and their own commands
-
+ self.key_func = {} # each tab should add their keys in there
+ # and use them in on_input
+ self.commands = {} # and their own commands
@property
def size(self):
@@ -148,9 +148,13 @@ class Tab(object):
elif STATE_PRIORITY[value] < STATE_PRIORITY[self._state] and \
value not in ('current', 'disconnected') and \
not (self._state == 'scrolled' and value == 'disconnected'):
- log.debug("Did not set state because of lower priority, asked: %s, kept: %s", value, self._state)
- elif self._state == 'disconnected' and value not in ('joined', 'current'):
- log.debug('Did not set state because disconnected tabs remain visible')
+ log.debug(
+ "Did not set state because of lower priority, asked: %s, kept: %s",
+ value, self._state)
+ elif self._state == 'disconnected' and value not in ('joined',
+ 'current'):
+ log.debug(
+ 'Did not set state because disconnected tabs remain visible')
else:
self._state = value
if self._state == 'current':
@@ -194,11 +198,22 @@ class Tab(object):
shortdesc = command.get('shortdesc', '')
completion = command.get('completion')
usage = command.get('usage', '')
- self.register_command(name, func, desc=desc, shortdesc=shortdesc,
- completion=completion, usage=usage)
-
-
- def register_command(self, name, func, *, desc='', shortdesc='', completion=None, usage=''):
+ self.register_command(
+ name,
+ func,
+ desc=desc,
+ shortdesc=shortdesc,
+ completion=completion,
+ usage=usage)
+
+ def register_command(self,
+ name,
+ func,
+ *,
+ desc='',
+ shortdesc='',
+ completion=None,
+ usage=''):
"""
Add a command
"""
@@ -234,7 +249,8 @@ class Tab(object):
whitespace = the_input.text.find(' ')
if whitespace == -1:
whitespace = len(the_input.text)
- the_input.text = the_input.text[:whitespace-1] + the_input.text[whitespace:]
+ the_input.text = the_input.text[:whitespace -
+ 1] + the_input.text[whitespace:]
the_input.new_completion(words, 0)
hit_copy = set(the_input.hit_list)
if len(hit_copy) == 1:
@@ -249,10 +265,10 @@ class Tab(object):
command = self.commands[command_name]
elif command_name in self.core.commands:
command = self.core.commands[command_name]
- else: # Unknown command, cannot complete
+ else: # Unknown command, cannot complete
return False
if command.comp is None:
- return False # There's no completion function
+ return False # There's no completion function
else:
comp = command.comp(the_input)
if comp:
@@ -269,11 +285,11 @@ class Tab(object):
if txt.startswith('/') and not txt.startswith('//') and\
not txt.startswith('/me '):
command = txt.strip().split()[0][1:]
- arg = txt[2+len(command):] # jump the '/' and the ' '
+ arg = txt[2 + len(command):] # jump the '/' and the ' '
func = None
- if command in self.commands: # check tab-specific commands
+ if command in self.commands: # check tab-specific commands
func = self.commands[command].func
- elif command in self.core.commands: # check global commands
+ elif command in self.core.commands: # check global commands
func = self.core.commands[command].func
else:
low = command.lower()
@@ -286,9 +302,8 @@ class Tab(object):
error_handled = self.missing_command_callback(low)
if not error_handled:
self.core.information("Unknown command (%s)" %
- (command),
- 'Error')
- if command in ('correct', 'say'): # hack
+ (command), 'Error')
+ if command in ('correct', 'say'): # hack
arg = xhtml.convert_simple_to_full_colors(arg)
else:
arg = xhtml.clean_text_simple(arg)
@@ -422,8 +437,8 @@ class Tab(object):
def __del__(self):
log.debug('------ Closing tab %s', self.__class__.__name__)
-class GapTab(Tab):
+class GapTab(Tab):
def __bool__(self):
return False
@@ -435,7 +450,9 @@ class GapTab(Tab):
return ''
def refresh(self):
- log.debug('WARNING: refresh() called on a gap tab, this should not happen')
+ log.debug(
+ 'WARNING: refresh() called on a gap tab, this should not happen')
+
class ChatTab(Tab):
"""
@@ -447,6 +464,7 @@ class ChatTab(Tab):
plugin_commands = {}
plugin_keys = {}
message_type = 'chat'
+
def __init__(self, core, jid=''):
Tab.__init__(self, core)
self.name = jid
@@ -454,7 +472,7 @@ class ChatTab(Tab):
self._remote_wants_chatstates = False
self.directed_presence = None
self._text_buffer = TextBuffer()
- self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive"
+ self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive"
# We keep a reference of the event that will set our chatstate to "paused", so that
# we can delete it or change it if we need to
self.timed_event_paused = None
@@ -464,18 +482,24 @@ class ChatTab(Tab):
self.key_func['M-h'] = self.scroll_separator
self.key_func['M-/'] = self.last_words_completion
self.key_func['^M'] = self.on_enter
- self.register_command('say', self.command_say,
- usage='<message>',
- shortdesc='Send the message.')
- self.register_command('xhtml', self.command_xhtml,
- usage='<custom xhtml>',
- shortdesc='Send custom XHTML.')
- self.register_command('clear', self.command_clear,
- shortdesc='Clear the current buffer.')
- self.register_command('correct', self.command_correct,
- desc='Fix the last message with whatever you want.',
- shortdesc='Correct the last message.',
- completion=self.completion_correct)
+ self.register_command(
+ 'say',
+ self.command_say,
+ usage='<message>',
+ shortdesc='Send the message.')
+ self.register_command(
+ 'xhtml',
+ self.command_xhtml,
+ usage='<custom xhtml>',
+ shortdesc='Send custom XHTML.')
+ self.register_command(
+ 'clear', self.command_clear, shortdesc='Clear the current buffer.')
+ self.register_command(
+ 'correct',
+ self.command_correct,
+ desc='Fix the last message with whatever you want.',
+ shortdesc='Correct the last message.',
+ completion=self.completion_correct)
self.chat_state = None
self.update_commands()
self.update_keys()
@@ -508,22 +532,39 @@ class ChatTab(Tab):
if not logger.log_message(name, nickname, txt, date=time, typ=typ):
self.core.information('Unable to write in the log file', 'Error')
- def add_message(self, txt, time=None, nickname=None, forced_user=None,
- nick_color=None, identifier=None, jid=None, history=None,
- typ=1, highlight=False):
+ def add_message(self,
+ txt,
+ time=None,
+ nickname=None,
+ forced_user=None,
+ nick_color=None,
+ identifier=None,
+ jid=None,
+ history=None,
+ typ=1,
+ highlight=False):
self.log_message(txt, nickname, time=time, typ=typ)
- self._text_buffer.add_message(txt, time=time,
- nickname=nickname,
- highlight=highlight,
- nick_color=nick_color,
- history=history,
- user=forced_user,
- identifier=identifier,
- jid=jid)
-
- def modify_message(self, txt, old_id, new_id, user=None, jid=None, nickname=None):
+ self._text_buffer.add_message(
+ txt,
+ time=time,
+ nickname=nickname,
+ highlight=highlight,
+ nick_color=nick_color,
+ history=history,
+ user=forced_user,
+ identifier=identifier,
+ jid=jid)
+
+ def modify_message(self,
+ txt,
+ old_id,
+ new_id,
+ user=None,
+ jid=None,
+ nickname=None):
self.log_message(txt, nickname, typ=1)
- message = self._text_buffer.modify_message(txt, old_id, new_id, time=time, user=user, jid=jid)
+ message = self._text_buffer.modify_message(
+ txt, old_id, new_id, time=time, user=user, jid=jid)
if message:
self.text_win.modify_message(old_id, message)
self.core.refresh_window()
@@ -535,7 +576,7 @@ class ChatTab(Tab):
Complete the input with words recently said
"""
# build the list of the recent words
- char_we_dont_want = string.punctuation+' ’„“”…«»'
+ char_we_dont_want = string.punctuation + ' ’„“”…«»'
words = list()
for msg in self._text_buffer.messages[:-40:-1]:
if not msg:
@@ -571,7 +612,8 @@ class ChatTab(Tab):
if not arg:
return
try:
- body = xhtml.clean_text(xhtml.xhtml_to_poezio_colors(arg, force=True))
+ body = xhtml.clean_text(
+ xhtml.xhtml_to_poezio_colors(arg, force=True))
ET.fromstring(arg)
except:
self.core.information('Could not send custom xhtml', 'Error')
@@ -604,7 +646,8 @@ class ChatTab(Tab):
Send an empty chatstate message
"""
if self.check_send_chat_state():
- if state in ('active', 'inactive', 'gone') and self.inactive and not always_send:
+ if state in ('active', 'inactive',
+ 'gone') and self.inactive and not always_send:
return
if (config.get_by_tabname('send_chat_states', self.general_jid)
and self.remote_wants_chatstates is not False):
@@ -642,7 +685,8 @@ class ChatTab(Tab):
# First, cancel the delay if it already exists, before rescheduling
# it at a new date
self.cancel_paused_delay()
- new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused')
+ new_event = timed_events.DelayedEvent(4, self.send_chat_state,
+ 'paused')
self.core.add_timed_event(new_event)
self.timed_event_paused = new_event
@@ -671,7 +715,10 @@ class ChatTab(Tab):
def completion_correct(self, the_input):
if self.last_sent_message and the_input.get_argument_position() == 1:
- return Completion(the_input.auto_completion, [self.last_sent_message['body']], '', quotify=False)
+ return Completion(
+ the_input.auto_completion, [self.last_sent_message['body']],
+ '',
+ quotify=False)
@property
def inactive(self):
@@ -703,23 +750,23 @@ class ChatTab(Tab):
return self.text_win.scroll_down(1)
def on_scroll_up(self):
- return self.text_win.scroll_up(self.text_win.height-1)
+ return self.text_win.scroll_up(self.text_win.height - 1)
def on_scroll_down(self):
- return self.text_win.scroll_down(self.text_win.height-1)
+ return self.text_win.scroll_down(self.text_win.height - 1)
def on_half_scroll_up(self):
- return self.text_win.scroll_up((self.text_win.height-1) // 2)
+ return self.text_win.scroll_up((self.text_win.height - 1) // 2)
def on_half_scroll_down(self):
- return self.text_win.scroll_down((self.text_win.height-1) // 2)
+ return self.text_win.scroll_down((self.text_win.height - 1) // 2)
@refresh_wrapper.always
def scroll_separator(self):
self.text_win.scroll_to_separator()
-class OneToOneTab(ChatTab):
+class OneToOneTab(ChatTab):
def __init__(self, core, jid=''):
ChatTab.__init__(self, core, jid)
@@ -735,18 +782,18 @@ class OneToOneTab(ChatTab):
self.remote_supports_attention = True
self.remote_supports_receipts = True
self.check_features()
- self.register_command('unquery', self.command_unquery,
- shortdesc='Close the tab.')
- self.register_command('close', self.command_unquery,
- shortdesc='Close the tab.')
+ self.register_command(
+ 'unquery', self.command_unquery, shortdesc='Close the tab.')
+ self.register_command(
+ 'close', self.command_unquery, shortdesc='Close the tab.')
def remote_user_color(self):
return dump_tuple(get_theme().COLOR_REMOTE_USER)
def update_status(self, status):
old_status = self.__status
- if not (old_status.show != status.show or
- old_status.message != status.message):
+ if not (old_status.show != status.show
+ or old_status.message != status.message):
return
self.__status = status
hide_status_change = config.get_by_tabname('hide_status_change',
@@ -820,16 +867,20 @@ class OneToOneTab(ChatTab):
message['chat_sate'] = 'active'
message.send()
body = xhtml.xhtml_to_poezio_colors(xhtml_data, force=True)
- self._text_buffer.add_message(body, nickname=self.core.own_nick,
- identifier=message['id'],)
+ self._text_buffer.add_message(
+ body,
+ nickname=self.core.own_nick,
+ identifier=message['id'],
+ )
self.refresh()
def check_features(self):
"check the features supported by the other party"
if safeJID(self.get_dest_jid()).resource:
self.core.xmpp.plugin['xep_0030'].get_info(
- jid=self.get_dest_jid(), timeout=5,
- callback=self.features_checked)
+ jid=self.get_dest_jid(),
+ timeout=5,
+ callback=self.features_checked)
@command_args_parser.raw
def command_attention(self, message):
@@ -872,12 +923,14 @@ class OneToOneTab(ChatTab):
"Check for the 'attention' features"
if 'urn:xmpp:attention:0' in features:
self.remote_supports_attention = True
- self.register_command('attention', self.command_attention,
- usage='[message]',
- shortdesc='Request the attention.',
- desc='Attention: Request the attention of '
- 'the contact. Can also send a message'
- ' along with the attention.')
+ self.register_command(
+ 'attention',
+ self.command_attention,
+ usage='[message]',
+ shortdesc='Request the attention.',
+ desc='Attention: Request the attention of '
+ 'the contact. Can also send a message'
+ ' along with the attention.')
else:
self.remote_supports_attention = False
return self.remote_supports_attention
@@ -888,10 +941,12 @@ class OneToOneTab(ChatTab):
if 'correct' in self.commands:
del self.commands['correct']
elif 'correct' not in self.commands:
- self.register_command('correct', self.command_correct,
- desc='Fix the last message with whatever you want.',
- shortdesc='Correct the last message.',
- completion=self.completion_correct)
+ self.register_command(
+ 'correct',
+ self.command_correct,
+ desc='Fix the last message with whatever you want.',
+ shortdesc='Correct the last message.',
+ completion=self.completion_correct)
return 'correct' in self.commands
def _feature_receipts(self, features):
@@ -905,8 +960,7 @@ class OneToOneTab(ChatTab):
def features_checked(self, iq):
"Features check callback"
features = iq['disco_info'].get_features() or []
- before = ('correct' in self.commands,
- self.remote_supports_attention,
+ before = ('correct' in self.commands, self.remote_supports_attention,
self.remote_supports_receipts)
correct = self._feature_correct(features)
attention = self._feature_attention(features)
@@ -918,7 +972,7 @@ class OneToOneTab(ChatTab):
self.__initial_disco = True
if not (correct or attention or receipts):
- return # don’t display anything
+ return # don’t display anything
ok = get_theme().CHAR_OK
nope = get_theme().CHAR_EMPTY
@@ -933,4 +987,3 @@ class OneToOneTab(ChatTab):
msg = msg % (color, correct, attention, receipts)
self.add_message(msg, typ=0)
self.core.refresh_window()
-
diff --git a/poezio/tabs/bookmarkstab.py b/poezio/tabs/bookmarkstab.py
index 452edd21..498d2f00 100644
--- a/poezio/tabs/bookmarkstab.py
+++ b/poezio/tabs/bookmarkstab.py
@@ -17,19 +17,17 @@ class BookmarksTab(Tab):
a 4 widgets to set the jid/password/autojoin/storage method
"""
plugin_commands = {}
+
def __init__(self, core, bookmarks: BookmarkList):
Tab.__init__(self, core)
self.name = "Bookmarks"
self.bookmarks = bookmarks
self.new_bookmarks = []
self.removed_bookmarks = []
- self.header_win = windows.ColumnHeaderWin(('room@server/nickname',
- 'password',
- 'autojoin',
- 'storage'))
- self.bookmarks_win = windows.BookmarksWin(self.bookmarks,
- self.height-4,
- self.width, 1, 0)
+ self.header_win = windows.ColumnHeaderWin(
+ ('room@server/nickname', 'password', 'autojoin', 'storage'))
+ self.bookmarks_win = windows.BookmarksWin(
+ self.bookmarks, self.height - 4, self.width, 1, 0)
self.help_win = windows.HelpText('Ctrl+Y: save, Ctrl+G: cancel, '
'↑↓: change lines, tab: change '
'column, M-a: add bookmark, C-k'
@@ -46,7 +44,8 @@ class BookmarksTab(Tab):
self.update_commands()
def add_bookmark(self):
- new_bookmark = Bookmark(safeJID('room@example.tld/nick'), method='local')
+ new_bookmark = Bookmark(
+ safeJID('room@example.tld/nick'), method='local')
self.new_bookmarks.append(new_bookmark)
self.bookmarks_win.add_bookmark(new_bookmark)
@@ -70,14 +69,16 @@ class BookmarksTab(Tab):
def on_save(self):
self.bookmarks_win.save()
if find_duplicates(self.new_bookmarks):
- self.core.information('Duplicate bookmarks in list (saving aborted)', 'Error')
+ self.core.information(
+ 'Duplicate bookmarks in list (saving aborted)', 'Error')
return
for bm in self.new_bookmarks:
if safeJID(bm.jid):
if not self.bookmarks[bm.jid]:
self.bookmarks.append(bm)
else:
- self.core.information('Invalid JID for bookmark: %s/%s' % (bm.jid, bm.nick), 'Error')
+ self.core.information('Invalid JID for bookmark: %s/%s' %
+ (bm.jid, bm.nick), 'Error')
return
for bm in self.removed_bookmarks:
@@ -89,6 +90,7 @@ class BookmarksTab(Tab):
self.core.information('Bookmarks saved.', 'Info')
else:
self.core.information('Remote bookmarks not saved.', 'Error')
+
self.bookmarks.save(self.core.xmpp, callback=send_cb)
self.core.close_tab(self)
return True
@@ -105,19 +107,23 @@ class BookmarksTab(Tab):
def resize(self):
self.need_resize = False
self.header_win.resize_columns({
- 'room@server/nickname': self.width//3,
- 'password': self.width//3,
- 'autojoin': self.width//6,
- 'storage': self.width//6
- })
+ 'room@server/nickname':
+ self.width // 3,
+ 'password':
+ self.width // 3,
+ 'autojoin':
+ self.width // 6,
+ 'storage':
+ self.width // 6
+ })
info_height = self.core.information_win_size
tab_height = Tab.tab_win_height()
self.header_win.resize(1, self.width, 0, 0)
self.bookmarks_win.resize(self.height - 3 - tab_height - info_height,
- self.width, 1, 0)
+ self.width, 1, 0)
self.help_win.resize(1, self.width, self.height - 1, 0)
self.info_header.resize(1, self.width,
- self.height - 2 - tab_height - info_height, 0)
+ self.height - 2 - tab_height - info_height, 0)
def on_info_win_size_changed(self):
if self.core.information_win_size >= self.height - 3:
@@ -125,9 +131,9 @@ class BookmarksTab(Tab):
info_height = self.core.information_win_size
tab_height = Tab.tab_win_height()
self.bookmarks_win.resize(self.height - 3 - tab_height - info_height,
- self.width, 1, 0)
+ self.width, 1, 0)
self.info_header.resize(1, self.width,
- self.height - 2 - tab_height - info_height, 0)
+ self.height - 2 - tab_height - info_height, 0)
def refresh(self):
if self.need_resize:
@@ -147,4 +153,3 @@ def find_duplicates(bm_list):
return True
jids.add(bookmark.jid)
return False
-
diff --git a/poezio/tabs/confirmtab.py b/poezio/tabs/confirmtab.py
index 39f630cb..28c26122 100644
--- a/poezio/tabs/confirmtab.py
+++ b/poezio/tabs/confirmtab.py
@@ -13,7 +13,13 @@ class ConfirmTab(Tab):
plugin_commands = {}
plugin_keys = {}
- def __init__(self, core, name, text, short_message, callback, critical=False):
+ def __init__(self,
+ core,
+ name,
+ text,
+ short_message,
+ callback,
+ critical=False):
"""Parameters:
name: The name of the tab
text: the text shown in the tab
@@ -26,7 +32,8 @@ class ConfirmTab(Tab):
Tab.__init__(self, core)
self.state = 'highlight'
self.name = name
- self.default_help_message = windows.HelpText("Choose with arrow keys and press enter")
+ self.default_help_message = windows.HelpText(
+ "Choose with arrow keys and press enter")
self.input = self.default_help_message
self.infowin_top = windows.ConfirmStatusWin(short_message, critical)
self.infowin_bottom = windows.ConfirmStatusWin(short_message, critical)
@@ -76,10 +83,12 @@ class ConfirmTab(Tab):
tab_win_height = Tab.tab_win_height()
self.infowin_top.resize(1, self.width, 0, 0)
- self.infowin_bottom.resize(1, self.width, self.height - 2 - info_win_height - tab_win_height, 0)
+ self.infowin_bottom.resize(
+ 1, self.width, self.height - 2 - info_win_height - tab_win_height,
+ 0)
self.dialog.resize(self.height - 3 - info_win_height - tab_win_height,
self.width, 1, 0)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
def close(self, arg=None):
self.done = True
@@ -95,8 +104,10 @@ class ConfirmTab(Tab):
return self.key_func[key]()
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
- self.dialog.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0)
- self.infowin_bottom.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0)
-
+ self.dialog.resize(self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(), self.width, 1, 0)
+ self.infowin_bottom.resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
diff --git a/poezio/tabs/conversationtab.py b/poezio/tabs/conversationtab.py
index cc6d716d..e8fcd720 100644
--- a/poezio/tabs/conversationtab.py
+++ b/poezio/tabs/conversationtab.py
@@ -30,6 +30,7 @@ from poezio.text_buffer import CorrectionError
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
+
class ConversationTab(OneToOneTab):
"""
The tab containg a normal conversation (not from a MUC)
@@ -39,12 +40,13 @@ class ConversationTab(OneToOneTab):
plugin_keys = {}
additional_information = {}
message_type = 'chat'
+
def __init__(self, core, jid):
OneToOneTab.__init__(self, core, jid)
self.nick = None
self.nick_sent = False
self.state = 'normal'
- self.name = jid # a conversation tab is linked to one specific full jid OR bare jid
+ self.name = jid # a conversation tab is linked to one specific full jid OR bare jid
self.text_win = windows.TextWin()
self._text_buffer.add_window(self.text_win)
self.upper_bar = windows.ConversationStatusMessageWin()
@@ -52,21 +54,30 @@ class ConversationTab(OneToOneTab):
# keys
self.key_func['^I'] = self.completion
# commands
- self.register_command('version', self.command_version,
- desc='Get the software version of the current interlocutor (usually its XMPP client and Operating System).',
- shortdesc='Get the software version of the user.')
- self.register_command('info', self.command_info,
- shortdesc='Get the status of the contact.')
- self.register_command('last_activity', self.command_last_activity,
- usage='[jid]',
- desc='Get the last activity of the given or the current contact.',
- shortdesc='Get the activity.',
- completion=self.core.completion.last_activity)
- self.register_command('add', self.command_add,
- desc='Add the current JID to your roster, ask them to'
- ' allow you to see his presence, and allow them to'
- ' see your presence.',
- shortdesc='Add a user to your roster.')
+ self.register_command(
+ 'version',
+ self.command_version,
+ desc=
+ 'Get the software version of the current interlocutor (usually its XMPP client and Operating System).',
+ shortdesc='Get the software version of the user.')
+ self.register_command(
+ 'info',
+ self.command_info,
+ shortdesc='Get the status of the contact.')
+ self.register_command(
+ 'last_activity',
+ self.command_last_activity,
+ usage='[jid]',
+ desc='Get the last activity of the given or the current contact.',
+ shortdesc='Get the activity.',
+ completion=self.core.completion.last_activity)
+ self.register_command(
+ 'add',
+ self.command_add,
+ desc='Add the current JID to your roster, ask them to'
+ ' allow you to see his presence, and allow them to'
+ ' see your presence.',
+ shortdesc='Add a user to your roster.')
self.update_commands()
self.update_keys()
@@ -114,8 +125,12 @@ class ConversationTab(OneToOneTab):
msg['replace']['id'] = self.last_sent_message['id']
if config.get_by_tabname('group_corrections', self.name):
try:
- self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], jid=self.core.xmpp.boundjid,
- nickname=self.core.own_nick)
+ self.modify_message(
+ msg['body'],
+ self.last_sent_message['id'],
+ msg['id'],
+ jid=self.core.xmpp.boundjid,
+ nickname=self.core.own_nick)
replaced = True
except CorrectionError:
log.error('Unable to correct a message', exc_info=True)
@@ -125,8 +140,8 @@ class ConversationTab(OneToOneTab):
msg.enable('html')
msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
msg['body'] = xhtml.clean_text(msg['body'])
- if (config.get_by_tabname('send_chat_states', self.general_jid) and
- self.remote_wants_chatstates is not False):
+ if (config.get_by_tabname('send_chat_states', self.general_jid)
+ and self.remote_wants_chatstates is not False):
needed = 'inactive' if self.inactive else 'active'
msg['chat_state'] = needed
if attention and self.remote_supports_attention:
@@ -138,12 +153,13 @@ class ConversationTab(OneToOneTab):
self.input.refresh()
return
if not replaced:
- self.add_message(msg['body'],
- nickname=self.core.own_nick,
- nick_color=get_theme().COLOR_OWN_NICK,
- identifier=msg['id'],
- jid=self.core.xmpp.boundjid,
- typ=1)
+ self.add_message(
+ msg['body'],
+ nickname=self.core.own_nick,
+ nick_color=get_theme().COLOR_OWN_NICK,
+ identifier=msg['id'],
+ jid=self.core.xmpp.boundjid,
+ typ=1)
self.last_sent_message = msg
if self.remote_supports_receipts:
@@ -164,9 +180,12 @@ class ConversationTab(OneToOneTab):
def callback(iq):
if iq['type'] != 'result':
if iq['error']['type'] == 'auth':
- self.core.information('You are not allowed to see the activity of this contact.', 'Error')
+ self.core.information(
+ 'You are not allowed to see the activity of this contact.',
+ 'Error')
else:
- self.core.information('Error retrieving the activity', 'Error')
+ self.core.information('Error retrieving the activity',
+ 'Error')
return
seconds = iq['last_activity']['seconds']
status = iq['last_activity']['status']
@@ -174,19 +193,21 @@ class ConversationTab(OneToOneTab):
msg = '\x19%s}The last activity of %s was %s ago%s'
if not safeJID(from_).user:
msg = '\x19%s}The uptime of %s is %s.' % (
- dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
- from_,
- common.parse_secs_to_str(seconds))
+ dump_tuple(get_theme().COLOR_INFORMATION_TEXT), from_,
+ common.parse_secs_to_str(seconds))
else:
msg = '\x19%s}The last activity of %s was %s ago%s' % (
dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
from_,
common.parse_secs_to_str(seconds),
- (' and his/her last status was %s' % status) if status else '',)
+ (' and his/her last status was %s' % status)
+ if status else '',
+ )
self.add_message(msg)
self.core.refresh_window()
- self.core.xmpp.plugin['xep_0012'].get_last_activity(self.get_dest_jid(), callback=callback)
+ self.core.xmpp.plugin['xep_0012'].get_last_activity(
+ self.get_dest_jid(), callback=callback)
@refresh_wrapper.conditional
@command_args_parser.ignored
@@ -201,12 +222,20 @@ class ConversationTab(OneToOneTab):
else:
resource = None
if resource:
- status = ('Status: %s' % resource.status) if resource.status else ''
- self._text_buffer.add_message("\x19%(info_col)s}Show: %(show)s, %(status)s\x19o" % {
- 'show': resource.presence or 'available', 'status': status, 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)})
+ status = (
+ 'Status: %s' % resource.status) if resource.status else ''
+ self._text_buffer.add_message(
+ "\x19%(info_col)s}Show: %(show)s, %(status)s\x19o" % {
+ 'show': resource.presence or 'available',
+ 'status': status,
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ })
return True
else:
- self._text_buffer.add_message("\x19%(info_col)s}No information available\x19o" % {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)})
+ self._text_buffer.add_message(
+ "\x19%(info_col)s}No information available\x19o" % {
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ })
return True
@command_args_parser.quoted(0, 1)
@@ -214,14 +243,18 @@ class ConversationTab(OneToOneTab):
"""
/version [jid]
"""
+
def callback(res):
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 'an unknown platform')
+ 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 'an unknown platform')
self.core.information(version, 'Info')
+
if args:
return self.core.command.version(args[0])
jid = safeJID(self.name)
@@ -229,8 +262,7 @@ class ConversationTab(OneToOneTab):
if jid in roster:
resource = roster[jid].get_highest_priority_resource()
jid = resource.jid if resource else jid
- fixes.get_version(self.core.xmpp, jid,
- callback=callback)
+ fixes.get_version(self.core.xmpp, jid, callback=callback)
@command_args_parser.ignored
def command_add(self):
@@ -258,16 +290,15 @@ class ConversationTab(OneToOneTab):
tab_win_height = Tab.tab_win_height()
bar_height = 1
- self.text_win.resize(self.height - 2 - bar_height - info_win_height
- - tab_win_height,
- self.width, bar_height, 0)
+ self.text_win.resize(
+ self.height - 2 - bar_height - info_win_height - tab_win_height,
+ self.width, bar_height, 0)
self.text_win.rebuild_everything(self._text_buffer)
if display_bar:
self.upper_bar.resize(1, self.width, 0, 0)
- self.get_info_header().resize(1, self.width,
- self.height - 2 - info_win_height
- - tab_win_height,
- 0)
+ self.get_info_header().resize(
+ 1, self.width, self.height - 2 - info_win_height - tab_win_height,
+ 0)
self.input.resize(1, self.width, self.height - 1, 0)
def refresh(self):
@@ -279,8 +310,11 @@ class ConversationTab(OneToOneTab):
self.text_win.refresh()
if display_bar:
- self.upper_bar.refresh(self.get_dest_jid(), roster[self.get_dest_jid()])
- self.get_info_header().refresh(self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win, self.chatstate, ConversationTab.additional_information)
+ self.upper_bar.refresh(self.get_dest_jid(),
+ roster[self.get_dest_jid()])
+ self.get_info_header().refresh(
+ self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win,
+ self.chatstate, ConversationTab.additional_information)
if display_info_win:
self.info_win.refresh()
@@ -288,8 +322,9 @@ class ConversationTab(OneToOneTab):
self.input.refresh()
def refresh_info_header(self):
- self.get_info_header().refresh(self.get_dest_jid(), roster[self.get_dest_jid()],
- self.text_win, self.chatstate, ConversationTab.additional_information)
+ self.get_info_header().refresh(
+ self.get_dest_jid(), roster[self.get_dest_jid()], self.text_win,
+ self.chatstate, ConversationTab.additional_information)
self.input.refresh()
def get_nick(self):
@@ -307,7 +342,9 @@ class ConversationTab(OneToOneTab):
self.key_func[key]()
return False
self.input.do_command(key, raw=raw)
- empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
+ empty_after = self.input.get_text() == '' or (
+ self.input.get_text().startswith('/')
+ and not self.input.get_text().startswith('//'))
self.send_composing_chat_state(empty_after)
return False
@@ -347,15 +384,18 @@ class ConversationTab(OneToOneTab):
curses.curs_set(1)
if (config.get_by_tabname('send_chat_states', self.general_jid)
and (not self.input.get_text()
- or not self.input.get_text().startswith('//'))):
+ or not self.input.get_text().startswith('//'))):
if resource:
self.send_chat_state('active')
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
- self.text_win.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0)
- self.get_info_header().resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0)
+ self.text_win.resize(self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(), self.width, 1, 0)
+ self.get_info_header().resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
def get_text_window(self):
return self.text_win
@@ -375,12 +415,14 @@ class ConversationTab(OneToOneTab):
res.append((0, contact.name))
return res
+
class DynamicConversationTab(ConversationTab):
"""
A conversation tab associated with one bare JID that can be “locked” to
a full jid, and unlocked, as described in the XEP-0296.
Only one DynamicConversationTab can be opened for a given jid.
"""
+
def __init__(self, core, jid, resource=None):
self.locked_resource = None
self.name = safeJID(jid).bare
@@ -388,8 +430,10 @@ class DynamicConversationTab(ConversationTab):
self.lock(resource)
ConversationTab.__init__(self, core, jid)
self.info_header = windows.DynamicConversationInfoWin()
- self.register_command('unlock', self.unlock_command,
- shortdesc='Unlock the conversation from a particular resource.')
+ self.register_command(
+ 'unlock',
+ self.unlock_command,
+ shortdesc='Unlock the conversation from a particular resource.')
self.resize()
def get_info_header(self):
@@ -399,7 +443,7 @@ class DynamicConversationTab(ConversationTab):
"""
Lock the tab to the resource.
"""
- assert(resource)
+ assert (resource)
if resource != self.locked_resource:
self.locked_resource = resource
info = '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
@@ -407,10 +451,11 @@ class DynamicConversationTab(ConversationTab):
message = ('%(info)sConversation locked to '
'%(jid_c)s%(jid)s/%(resource)s%(info)s.') % {
- 'info': info,
- 'jid_c': jid_c,
- 'jid': self.name,
- 'resource': resource}
+ 'info': info,
+ 'jid_c': jid_c,
+ 'jid': self.name,
+ 'resource': resource
+ }
self.add_message(message, typ=0)
self.check_features()
@@ -432,9 +477,10 @@ class DynamicConversationTab(ConversationTab):
if from_:
message = ('%(info)sConversation unlocked (received activity'
' from %(jid_c)s%(jid)s%(info)s).') % {
- 'info': info,
- 'jid_c': jid_c,
- 'jid': from_}
+ 'info': info,
+ 'jid_c': jid_c,
+ 'jid': from_
+ }
self.add_message(message, typ=0)
else:
message = '%sConversation unlocked.' % info
@@ -466,8 +512,8 @@ class DynamicConversationTab(ConversationTab):
else:
displayed_jid = self.name
self.get_info_header().refresh(displayed_jid, roster[self.name],
- self.text_win, self.chatstate,
- ConversationTab.additional_information)
+ self.text_win, self.chatstate,
+ ConversationTab.additional_information)
if display_info_win:
self.info_win.refresh()
@@ -483,16 +529,19 @@ class DynamicConversationTab(ConversationTab):
else:
displayed_jid = self.name
self.get_info_header().refresh(displayed_jid, roster[self.name],
- self.text_win, self.chatstate, ConversationTab.additional_information)
+ self.text_win, self.chatstate,
+ ConversationTab.additional_information)
self.input.refresh()
+
class StaticConversationTab(ConversationTab):
"""
A conversation tab associated with one Full JID. It cannot be locked to
an different resource or unlocked.
"""
+
def __init__(self, core, jid):
- assert(safeJID(jid).resource)
+ assert (safeJID(jid).resource)
ConversationTab.__init__(self, core, jid)
self.info_header = windows.ConversationInfoWin()
self.resize()
diff --git a/poezio/tabs/data_forms.py b/poezio/tabs/data_forms.py
index c73f4922..d216d4ca 100644
--- a/poezio/tabs/data_forms.py
+++ b/poezio/tabs/data_forms.py
@@ -8,12 +8,14 @@ log = logging.getLogger(__name__)
from poezio import windows
from poezio.tabs import Tab
+
class DataFormsTab(Tab):
"""
A tab contaning various window type, displaying
a form that the user needs to fill.
"""
plugin_commands = {}
+
def __init__(self, core, form, on_cancel, on_send, kwargs):
Tab.__init__(self, core)
self._form = form
@@ -24,7 +26,8 @@ class DataFormsTab(Tab):
for field in self._form:
self.fields.append(field)
self.topic_win = windows.Topic()
- self.form_win = windows.FormWin(form, self.height-4, self.width, 1, 0)
+ self.form_win = windows.FormWin(form, self.height - 4, self.width, 1,
+ 0)
self.help_win = windows.HelpText("Ctrl+Y: send form, Ctrl+G: cancel")
self.help_win_dyn = windows.HelpText()
self.key_func['KEY_UP'] = self.form_win.go_to_previous_input
@@ -72,4 +75,3 @@ class DataFormsTab(Tab):
self.help_win.refresh()
self.help_win_dyn.refresh(self.form_win.get_help_message())
self.form_win.refresh()
-
diff --git a/poezio/tabs/listtab.py b/poezio/tabs/listtab.py
index ed40241b..84dcc38b 100644
--- a/poezio/tabs/listtab.py
+++ b/poezio/tabs/listtab.py
@@ -49,8 +49,7 @@ class ListTab(Tab):
self.key_func['KEY_LEFT'] = self.list_header.sel_column_left
self.key_func['KEY_RIGHT'] = self.list_header.sel_column_right
self.key_func[' '] = self.sort_by
- self.register_command('close', self.close,
- shortdesc='Close this tab.')
+ self.register_command('close', self.close, shortdesc='Close this tab.')
self.resize()
self.update_keys()
self.update_commands()
@@ -64,7 +63,6 @@ class ListTab(Tab):
"""
raise NotImplementedError
-
def refresh(self):
if self.need_resize:
self.resize()
@@ -90,26 +88,27 @@ class ListTab(Tab):
info_win_height = self.core.information_win_size
tab_win_height = Tab.tab_win_height()
- self.info_header.resize(1, self.width,
- self.height - 2 - info_win_height
- - tab_win_height,
- 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - info_win_height - tab_win_height,
+ 0)
column_size = self.get_columns_sizes()
self.list_header.resize_columns(column_size)
self.list_header.resize(1, self.width, 0, 0)
self.listview.resize_columns(column_size)
- self.listview.resize(self.height - 3 - info_win_height - tab_win_height,
- self.width, 1, 0)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.listview.resize(
+ self.height - 3 - info_win_height - tab_win_height, self.width, 1,
+ 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
def on_slash(self):
"""
'/' is pressed, activate the input
"""
curses.curs_set(1)
- self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command)
- self.input.resize(1, self.width, self.height-1, 0)
- self.input.do_command("/") # we add the slash
+ self.input = windows.CommandInput("", self.reset_help_message,
+ self.execute_slash_command)
+ self.input.resize(1, self.width, self.height - 1, 0)
+ self.input.do_command("/") # we add the slash
def close(self, arg=None):
self.core.close_tab(self)
@@ -118,7 +117,11 @@ class ListTab(Tab):
"""
If there's an error (retrieving the values etc)
"""
- self._error_message = 'Error: %(code)s - %(msg)s: %(body)s' % {'msg':msg, 'body':body, 'code':code}
+ self._error_message = 'Error: %(code)s - %(msg)s: %(body)s' % {
+ 'msg': msg,
+ 'body': body,
+ 'code': code
+ }
self.info_header.message = self._error_message
self.info_header.refresh()
curses.doupdate()
@@ -126,14 +129,12 @@ class ListTab(Tab):
def sort_by(self):
if self.list_header.get_order():
self.listview.sort_by_column(
- col_name=self.list_header.get_sel_column(),
- asc=False)
+ col_name=self.list_header.get_sel_column(), asc=False)
self.list_header.set_order(False)
self.list_header.refresh()
else:
self.listview.sort_by_column(
- col_name=self.list_header.get_sel_column(),
- asc=True)
+ col_name=self.list_header.get_sel_column(), asc=True)
self.list_header.set_order(True)
self.list_header.refresh()
self.core.doupdate()
@@ -144,7 +145,7 @@ class ListTab(Tab):
return True
curses.curs_set(0)
self.input = self.default_help_message
- self.input.resize(1, self.width, self.height-1, 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
return True
def execute_slash_command(self, txt):
@@ -167,10 +168,13 @@ class ListTab(Tab):
return self.key_func[key]()
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
- self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0)
- self.listview.resize(self.height-3-self.core.information_win_size - Tab.tab_win_height(), self.width, 1, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
+ self.listview.resize(self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(), self.width, 1, 0)
def on_lose_focus(self):
self.state = 'normal'
@@ -197,5 +201,3 @@ class ListTab(Tab):
def matching_names(self):
return [(2, self.name)]
-
-
diff --git a/poezio/tabs/muclisttab.py b/poezio/tabs/muclisttab.py
index f14f4172..005f3fe1 100644
--- a/poezio/tabs/muclisttab.py
+++ b/poezio/tabs/muclisttab.py
@@ -11,6 +11,7 @@ from poezio.tabs import ListTab
from slixmpp.plugins.xep_0030.stanza.items import DiscoItem
+
class MucListTab(ListTab):
"""
A tab listing rooms from a specific server, displaying various information,
@@ -20,8 +21,7 @@ class MucListTab(ListTab):
plugin_keys = {}
def __init__(self, core, server):
- ListTab.__init__(self, core, server.full,
- "“j”: join room.",
+ ListTab.__init__(self, core, server.full, "“j”: join room.",
'Chatroom list on server %s (Loading)' % server,
(('node-part', 0), ('name', 2), ('users', 3)))
self.key_func['j'] = self.join_selected
@@ -29,10 +29,12 @@ class MucListTab(ListTab):
self.key_func['^M'] = self.join_selected
def get_columns_sizes(self):
- return {'node-part': int(self.width* 2 / 8),
- 'name': int(self.width * 5 / 8),
- 'users': self.width - int(self.width * 2 / 8)
- - int(self.width * 5 / 8)}
+ return {
+ 'node-part': int(self.width * 2 / 8),
+ 'name': int(self.width * 5 / 8),
+ 'users':
+ self.width - int(self.width * 2 / 8) - int(self.width * 5 / 8)
+ }
def join_selected_no_focus(self):
return
@@ -43,16 +45,18 @@ class MucListTab(ListTab):
Used with command_list
"""
if iq['type'] == 'error':
- self.set_error(iq['error']['type'], iq['error']['code'], iq['error']['text'])
+ self.set_error(iq['error']['type'], iq['error']['code'],
+ iq['error']['text'])
return
+
def get_items():
substanza = iq['disco_items']
for item in substanza['substanzas']:
if isinstance(item, DiscoItem):
yield (item['jid'], item['node'], item['name'])
- items = [(item[0].split('@')[0],
- item[0],
- item[2] or '', '') for item in get_items()]
+
+ items = [(item[0].split('@')[0], item[0], item[2] or '', '')
+ for item in get_items()]
self.listview.set_lines(items)
self.info_header.message = 'Chatroom list on server %s' % self.name
if self.core.current_tab() is self:
@@ -67,4 +71,3 @@ class MucListTab(ListTab):
if not row:
return
self.core.command.join(row[1])
-
diff --git a/poezio/tabs/muctab.py b/poezio/tabs/muctab.py
index 4438e015..94b521ba 100644
--- a/poezio/tabs/muctab.py
+++ b/poezio/tabs/muctab.py
@@ -34,7 +34,6 @@ from poezio.theming import get_theme, dump_tuple
from poezio.user import User
from poezio.core.structs import Completion, Status
-
NS_MUC_USER = 'http://jabber.org/protocol/muc#user'
STATUS_XPATH = '{%s}x/{%s}status' % (NS_MUC_USER, NS_MUC_USER)
@@ -129,11 +128,14 @@ class MucTab(ChatTab):
seconds = delta.seconds + delta.days * 24 * 3600
else:
seconds = 0
- muc.join_groupchat(self.core, self.name, self.own_nick,
- self.password,
- status=status.message,
- show=status.show,
- seconds=seconds)
+ muc.join_groupchat(
+ self.core,
+ self.name,
+ self.own_nick,
+ self.password,
+ status=status.message,
+ show=status.show,
+ seconds=seconds)
def leave_room(self, message):
if self.joined:
@@ -152,8 +154,10 @@ class MucTab(ChatTab):
'You (\x19%(color)s}%(nick)s\x19%(info_col)s})'
' left the room'
' (\x19o%(reason)s\x19%(info_col)s})') % {
- 'info_col': info_col, 'reason': message,
- 'spec': char_quit, 'color': color,
+ 'info_col': info_col,
+ 'reason': message,
+ 'spec': char_quit,
+ 'color': color,
'color_spec': spec_col,
'nick': self.own_nick,
}
@@ -162,60 +166,77 @@ class MucTab(ChatTab):
'You (\x19%(color)s}%(nick)s\x19%(info_col)s})'
' left the room') % {
'info_col': info_col,
- 'spec': char_quit, 'color': color,
+ 'spec': char_quit,
+ 'color': color,
'color_spec': spec_col,
'nick': self.own_nick,
}
self.add_message(msg, typ=2)
self.disconnect()
- muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, message)
+ muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick,
+ message)
self.core.disable_private_tabs(self.name, reason=msg)
else:
- muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, message)
+ muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick,
+ message)
def change_affiliation(self, nick_or_jid, affiliation, reason=''):
"""
Change the affiliation of a nick or JID
"""
+
def callback(iq):
if iq['type'] == 'error':
- self.core.information("Could not set affiliation '%s' for '%s'." % (
- affiliation, nick_or_jid), "Warning")
+ self.core.information(
+ "Could not set affiliation '%s' for '%s'." %
+ (affiliation, nick_or_jid), "Warning")
+
if not self.joined:
return
valid_affiliations = ('outcast', 'none', 'member', 'admin', 'owner')
if affiliation not in valid_affiliations:
- return self.core.information('The affiliation must be one of ' + ', '.join(valid_affiliations),
+ return self.core.information('The affiliation must be one of ' +
+ ', '.join(valid_affiliations),
'Error')
if nick_or_jid in [user.nick for user in self.users]:
- res = muc.set_user_affiliation(self.core.xmpp, self.name,
- affiliation, nick=nick_or_jid,
- callback=callback, reason=reason)
+ res = muc.set_user_affiliation(
+ self.core.xmpp,
+ self.name,
+ affiliation,
+ nick=nick_or_jid,
+ callback=callback,
+ reason=reason)
else:
- res = muc.set_user_affiliation(self.core.xmpp, self.name,
- affiliation, jid=safeJID(nick_or_jid),
- callback=callback, reason=reason)
+ res = muc.set_user_affiliation(
+ self.core.xmpp,
+ self.name,
+ affiliation,
+ jid=safeJID(nick_or_jid),
+ callback=callback,
+ reason=reason)
def change_role(self, nick, role, reason=''):
"""
Change the role of a nick
"""
+
def callback(iq):
if iq['type'] == 'error':
- self.core.information("Could not set role '%s' for '%s'." % (
- role, nick), "Warning")
+ self.core.information("Could not set role '%s' for '%s'." %
+ (role, nick), "Warning")
+
valid_roles = ('none', 'visitor', 'participant', 'moderator')
if not self.joined or role not in valid_roles:
- return self.core.information('The role must be one of ' + ', '.join(valid_roles),
- 'Error')
+ return self.core.information(
+ 'The role must be one of ' + ', '.join(valid_roles), 'Error')
if not safeJID(self.name + '/' + nick):
return self.core.information('Invalid nick', 'Info')
- muc.set_user_role(self.core.xmpp, self.name, nick, reason, role,
- callback=callback)
+ muc.set_user_role(
+ self.core.xmpp, self.name, nick, reason, role, callback=callback)
@refresh_wrapper.conditional
def print_info(self, nick):
@@ -228,26 +249,24 @@ class MucTab(ChatTab):
inf = '\x19' + dump_tuple(theme.COLOR_INFORMATION_TEXT) + '}'
if user.jid:
user_jid = '%s (\x19%s}%s\x19o%s)' % (
- inf,
- dump_tuple(theme.COLOR_MUC_JID),
- user.jid,
- inf)
+ inf, dump_tuple(theme.COLOR_MUC_JID), user.jid, inf)
else:
user_jid = ''
info = ('\x19%(user_col)s}%(nick)s\x19o%(jid)s%(info)s: show: '
'\x19%(show_col)s}%(show)s\x19o%(info)s, affiliation: '
'\x19%(role_col)s}%(affiliation)s\x19o%(info)s, role: '
'\x19%(role_col)s}%(role)s\x19o%(status)s') % {
- 'user_col': dump_tuple(user.color),
- 'nick': nick,
- 'jid': user_jid,
- 'info': inf,
- 'show_col': dump_tuple(theme.color_show(user.show)),
- 'show': user.show or 'Available',
- 'role_col': dump_tuple(theme.color_role(user.role)),
- 'affiliation': user.affiliation or 'None',
- 'role': user.role or 'None',
- 'status': '\n%s' % user.status if user.status else ''}
+ 'user_col': dump_tuple(user.color),
+ 'nick': nick,
+ 'jid': user_jid,
+ 'info': inf,
+ 'show_col': dump_tuple(theme.color_show(user.show)),
+ 'show': user.show or 'Available',
+ 'role_col': dump_tuple(theme.color_role(user.role)),
+ 'affiliation': user.affiliation or 'None',
+ 'role': user.role or 'None',
+ 'status': '\n%s' % user.status if user.status else ''
+ }
self.add_message(info, typ=0)
return True
@@ -266,21 +285,24 @@ class MucTab(ChatTab):
user = self.get_user_by_name(self.topic_from)
if user:
user_text = dump_tuple(user.color)
- user_string = '\x19%s}(set by \x19%s}%s\x19%s})' % (
- info_text, user_text, user.nick, info_text)
+ user_string = '\x19%s}(set by \x19%s}%s\x19%s})' % (info_text,
+ user_text,
+ user.nick,
+ info_text)
else:
user_string = self.topic_from
else:
user_string = ''
self._text_buffer.add_message(
- "\x19%s}The subject of the room is: \x19%s}%s %s" %
- (info_text, norm_text, self.topic, user_string))
+ "\x19%s}The subject of the room is: \x19%s}%s %s" %
+ (info_text, norm_text, self.topic, user_string))
@refresh_wrapper.always
def recolor(self, random_colors=False):
"""Recolor the current MUC users"""
- deterministic = config.get_by_tabname('deterministic_nick_colors', self.name)
+ deterministic = config.get_by_tabname('deterministic_nick_colors',
+ self.name)
if deterministic:
for user in self.users:
if user is self.own_user:
@@ -321,14 +343,16 @@ class MucTab(ChatTab):
return False
if color == 'unset':
if config.remove_and_save(nick, 'muc_colors'):
- self.core.information('Color for nick %s unset' % (nick), 'Info')
+ self.core.information('Color for nick %s unset' % (nick),
+ 'Info')
else:
if color == 'random':
color = random.choice(list(xhtml.colors))
if user:
user.change_color(color)
config.set_and_save(nick, color, 'muc_colors')
- nick_color_aliases = config.get_by_tabname('nick_color_aliases', self.name)
+ nick_color_aliases = config.get_by_tabname('nick_color_aliases',
+ self.name)
if nick_color_aliases:
# if any user in the room has a nick which is an alias of the
# nick, update its color
@@ -347,9 +371,9 @@ class MucTab(ChatTab):
return False
self.input.do_command(key, raw=raw)
empty_after = self.input.get_text() == ''
- empty_after = empty_after or (self.input.get_text().startswith('/')
- and not
- self.input.get_text().startswith('//'))
+ empty_after = empty_after or (
+ self.input.get_text().startswith('/')
+ and not self.input.get_text().startswith('//'))
self.send_composing_chat_state(empty_after)
return False
@@ -381,7 +405,8 @@ class MucTab(ChatTab):
and not config.get('show_useless_separator')):
self.text_win.remove_line_separator()
curses.curs_set(1)
- if self.joined and config.get_by_tabname('send_chat_states',
+ if self.joined and config.get_by_tabname(
+ 'send_chat_states',
self.general_jid) and not self.input.get_text():
self.send_chat_state('active')
@@ -416,7 +441,8 @@ class MucTab(ChatTab):
"""
Batch-process all the initial presences
"""
- deterministic = config.get_by_tabname('deterministic_nick_colors', self.name)
+ deterministic = config.get_by_tabname('deterministic_nick_colors',
+ self.name)
for stanza in self.presence_buffer:
try:
@@ -437,10 +463,11 @@ class MucTab(ChatTab):
"""
Presence received while we are not in the room (before code=110)
"""
- from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence)
+ from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(
+ presence)
user_color = self.search_for_color(from_nick)
- new_user = User(from_nick, affiliation, show,
- status, role, jid, deterministic, user_color)
+ new_user = User(from_nick, affiliation, show, status, role, jid,
+ deterministic, user_color)
self.users.append(new_user)
self.core.events.trigger('muc_join', presence, self)
if own:
@@ -475,44 +502,43 @@ class MucTab(ChatTab):
info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
warn_col = dump_tuple(get_theme().COLOR_WARNING_TEXT)
spec_col = dump_tuple(get_theme().COLOR_JOIN_CHAR)
- enable_message = (
- '\x19%(color_spec)s}%(spec)s\x19%(info_col)s} You '
- '(\x19%(nick_col)s}%(nick)s\x19%(info_col)s}) joined'
- ' the room') % {
- 'nick': from_nick,
- 'spec': get_theme().CHAR_JOIN,
- 'color_spec': spec_col,
- 'nick_col': color,
- 'info_col': info_col,
- }
+ enable_message = ('\x19%(color_spec)s}%(spec)s\x19%(info_col)s} You '
+ '(\x19%(nick_col)s}%(nick)s\x19%(info_col)s}) joined'
+ ' the room') % {
+ 'nick': from_nick,
+ 'spec': get_theme().CHAR_JOIN,
+ 'color_spec': spec_col,
+ 'nick_col': color,
+ 'info_col': info_col,
+ }
self.add_message(enable_message, typ=2)
self.core.enable_private_tabs(self.name, enable_message)
if '201' in status_codes:
self.add_message(
- '\x19%(info_col)s}Info: The room '
- 'has been created' %
- {'info_col': info_col},
+ '\x19%(info_col)s}Info: The room '
+ 'has been created' % {'info_col': info_col},
typ=0)
if '170' in status_codes:
self.add_message(
- '\x19%(warn_col)s}Warning:\x19%(info_col)s}'
- ' This room is publicly logged' %
- {'info_col': info_col,
- 'warn_col': warn_col},
+ '\x19%(warn_col)s}Warning:\x19%(info_col)s}'
+ ' This room is publicly logged' %
+ {'info_col': info_col,
+ 'warn_col': warn_col},
typ=0)
if '100' in status_codes:
self.add_message(
- '\x19%(warn_col)s}Warning:\x19%(info_col)s}'
- ' This room is not anonymous.' %
- {'info_col': info_col,
- 'warn_col': warn_col},
+ '\x19%(warn_col)s}Warning:\x19%(info_col)s}'
+ ' This room is not anonymous.' %
+ {'info_col': info_col,
+ 'warn_col': warn_col},
typ=0)
def handle_presence_joined(self, presence, status_codes):
"""
Handle new presences when we are already in the room
"""
- from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence)
+ from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(
+ presence)
change_nick = '303' in status_codes
kick = '307' in status_codes and typ == 'unavailable'
ban = '301' in status_codes and typ == 'unavailable'
@@ -523,8 +549,8 @@ class MucTab(ChatTab):
if not user and typ != "unavailable":
user_color = self.search_for_color(from_nick)
self.core.events.trigger('muc_join', presence, self)
- self.on_user_join(from_nick, affiliation, show, status, role,
- jid, user_color)
+ self.on_user_join(from_nick, affiliation, show, status, role, jid,
+ user_color)
elif user is None:
log.error('BUG: User %s in %s is None', from_nick, self.name)
return
@@ -533,13 +559,13 @@ class MucTab(ChatTab):
self.on_user_nick_change(presence, user, from_nick, from_room)
elif ban:
self.core.events.trigger('muc_ban', presence, self)
- self.core.on_user_left_private_conversation(from_room,
- user, status)
+ self.core.on_user_left_private_conversation(
+ from_room, user, status)
self.on_user_banned(presence, user, from_nick)
elif kick:
self.core.events.trigger('muc_kick', presence, self)
- self.core.on_user_left_private_conversation(from_room,
- user, status)
+ self.core.on_user_left_private_conversation(
+ from_room, user, status)
self.on_user_kicked(presence, user, from_nick)
elif shutdown:
self.core.events.trigger('muc_shutdown', presence, self)
@@ -549,38 +575,40 @@ class MucTab(ChatTab):
self.on_non_member_kicked()
# user quit
elif typ == 'unavailable':
- self.on_user_leave_groupchat(user, jid, status,
- from_nick, from_room)
+ self.on_user_leave_groupchat(user, jid, status, from_nick,
+ from_room)
# status change
else:
- self.on_user_change_status(user, from_nick, from_room,
- affiliation, role, show, status)
+ self.on_user_change_status(user, from_nick, from_room, affiliation,
+ role, show, status)
def on_non_member_kicked(self):
"""We have been kicked because the MUC is members-only"""
self.add_message(
- '\x19%(info_col)s}You have been kicked because you '
- 'are not a member and the room is now members-only.' % {
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
+ '\x19%(info_col)s}You have been kicked because you '
+ 'are not a member and the room is now members-only.' %
+ {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
typ=2)
self.disconnect()
def on_muc_shutdown(self):
"""We have been kicked because the MUC service is shutting down"""
self.add_message(
- '\x19%(info_col)s}You have been kicked because the'
- ' MUC service is shutting down.' % {
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
+ '\x19%(info_col)s}You have been kicked because the'
+ ' MUC service is shutting down.' %
+ {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
typ=2)
self.disconnect()
- def on_user_join(self, from_nick, affiliation, show, status, role, jid, color):
+ def on_user_join(self, from_nick, affiliation, show, status, role, jid,
+ color):
"""
When a new user joins the groupchat
"""
- deterministic = config.get_by_tabname('deterministic_nick_colors', self.name)
- user = User(from_nick, affiliation,
- show, status, role, jid, deterministic, color)
+ deterministic = config.get_by_tabname('deterministic_nick_colors',
+ self.name)
+ user = User(from_nick, affiliation, show, status, role, jid,
+ deterministic, color)
bisect.insort_left(self.users, user)
hide_exit_join = config.get_by_tabname('hide_exit_join',
self.general_jid)
@@ -596,27 +624,30 @@ class MucTab(ChatTab):
if not jid.full:
msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}%(nick)s'
'\x19%(info_col)s} joined the room') % {
- 'nick': from_nick, 'spec': char_join,
- 'color': color,
- 'info_col': info_col,
- 'color_spec': spec_col,
- }
+ 'nick': from_nick,
+ 'spec': char_join,
+ 'color': color,
+ 'info_col': info_col,
+ 'color_spec': spec_col,
+ }
else:
msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}%(nick)s'
'\x19%(info_col)s} (\x19%(jid_color)s}%(jid)s\x19'
'%(info_col)s}) joined the room') % {
- 'spec': char_join, 'nick': from_nick,
- 'color':color, 'jid':jid.full,
- 'info_col': info_col,
- 'jid_color': dump_tuple(get_theme().COLOR_MUC_JID),
- 'color_spec': spec_col,
- }
+ 'spec': char_join,
+ 'nick': from_nick,
+ 'color': color,
+ 'jid': jid.full,
+ 'info_col': info_col,
+ 'jid_color': dump_tuple(get_theme().COLOR_MUC_JID),
+ 'color_spec': spec_col,
+ }
self.add_message(msg, typ=2)
self.core.on_user_rejoined_private_conversation(self.name, from_nick)
def on_user_nick_change(self, presence, user, from_nick, from_room):
- new_nick = presence.xml.find('{%s}x/{%s}item' % (NS_MUC_USER, NS_MUC_USER)
- ).attrib['nick']
+ new_nick = presence.xml.find('{%s}x/{%s}item' %
+ (NS_MUC_USER, NS_MUC_USER)).attrib['nick']
if user.nick == self.own_nick:
self.own_nick = new_nick
# also change our nick in all private discussions of this room
@@ -624,8 +655,8 @@ class MucTab(ChatTab):
else:
color = config.get_by_tabname(new_nick, 'muc_colors')
if color != '':
- deterministic = config.get_by_tabname('deterministic_nick_colors',
- self.name)
+ deterministic = config.get_by_tabname(
+ 'deterministic_nick_colors', self.name)
user.change_color(color, deterministic)
user.change_nick(new_nick)
self.users.remove(user)
@@ -637,11 +668,15 @@ class MucTab(ChatTab):
else:
color = 3
info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
- self.add_message('\x19%(color)s}%(old)s\x19%(info_col)s} is'
- ' now known as \x19%(color)s}%(new)s' % {
- 'old':from_nick, 'new':new_nick,
- 'color':color, 'info_col': info_col},
- typ=2)
+ self.add_message(
+ '\x19%(color)s}%(old)s\x19%(info_col)s} is'
+ ' now known as \x19%(color)s}%(new)s' % {
+ 'old': from_nick,
+ 'new': new_nick,
+ 'color': color,
+ 'info_col': info_col
+ },
+ typ=2)
# rename the private tabs if needed
self.core.rename_private_tabs(self.name, from_nick, user)
@@ -662,16 +697,20 @@ class MucTab(ChatTab):
info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
char_kick = get_theme().CHAR_KICK
- if from_nick == self.own_nick: # we are banned
+ if from_nick == self.own_nick: # we are banned
if by:
kick_msg = ('\x191}%(spec)s \x193}You\x19%(info_col)s}'
' have been banned by \x194}%(by)s') % {
- 'spec': char_kick, 'by': by,
- 'info_col': info_col}
+ 'spec': char_kick,
+ 'by': by,
+ 'info_col': info_col
+ }
else:
kick_msg = ('\x191}%(spec)s \x193}You\x19'
'%(info_col)s} have been banned.') % {
- 'spec': char_kick, 'info_col': info_col}
+ 'spec': char_kick,
+ 'info_col': info_col
+ }
self.core.disable_private_tabs(self.name, reason=kick_msg)
self.disconnect()
self.refresh_tab_win()
@@ -684,11 +723,9 @@ class MucTab(ChatTab):
if delay <= 0:
muc.join_groupchat(self.core, self.name, self.own_nick)
else:
- self.core.add_timed_event(timed_events.DelayedEvent(
- delay,
- muc.join_groupchat,
- self.core,
- self.name,
+ self.core.add_timed_event(
+ timed_events.DelayedEvent(delay, muc.join_groupchat,
+ self.core, self.name,
self.own_nick))
else:
@@ -702,18 +739,26 @@ class MucTab(ChatTab):
kick_msg = ('\x191}%(spec)s \x19%(color)s}'
'%(nick)s\x19%(info_col)s} '
'has been banned by \x194}%(by)s') % {
- 'spec': char_kick, 'nick': from_nick,
- 'color': color, 'by': by,
- 'info_col': info_col}
+ 'spec': char_kick,
+ 'nick': from_nick,
+ 'color': color,
+ 'by': by,
+ 'info_col': info_col
+ }
else:
kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s'
'\x19%(info_col)s} has been banned') % {
- 'spec': char_kick, 'nick': from_nick,
- 'color': color, 'info_col': info_col}
+ 'spec': char_kick,
+ 'nick': from_nick,
+ 'color': color,
+ 'info_col': info_col
+ }
if reason is not None and reason.text:
kick_msg += ('\x19%(info_col)s} Reason: \x196}'
'%(reason)s\x19%(info_col)s}') % {
- 'reason': reason.text, 'info_col': info_col}
+ 'reason': reason.text,
+ 'info_col': info_col
+ }
self.add_message(kick_msg, typ=2)
def on_user_kicked(self, presence, user, from_nick):
@@ -730,18 +775,21 @@ class MucTab(ChatTab):
char_kick = get_theme().CHAR_KICK
if actor_elem is not None:
by = actor_elem.get('nick') or actor_elem.get('jid')
- if from_nick == self.own_nick: # we are kicked
+ if from_nick == self.own_nick: # we are kicked
if by:
kick_msg = ('\x191}%(spec)s \x193}You\x19'
'%(info_col)s} have been kicked'
' by \x193}%(by)s') % {
- 'spec': char_kick, 'by': by,
- 'info_col': info_col}
+ 'spec': char_kick,
+ 'by': by,
+ 'info_col': info_col
+ }
else:
kick_msg = ('\x191}%(spec)s \x193}You\x19%(info_col)s}'
' have been kicked.') % {
- 'spec': char_kick,
- 'info_col': info_col}
+ 'spec': char_kick,
+ 'info_col': info_col
+ }
self.core.disable_private_tabs(self.name, reason=kick_msg)
self.disconnect()
self.refresh_tab_win()
@@ -755,12 +803,10 @@ class MucTab(ChatTab):
if delay <= 0:
muc.join_groupchat(self.core, self.name, self.own_nick)
else:
- self.core.add_timed_event(timed_events.DelayedEvent(
- delay,
- muc.join_groupchat,
- self.core,
- self.name,
- self.own_nick))
+ self.core.add_timed_event(
+ timed_events.DelayedEvent(delay, muc.join_groupchat,
+ self.core, self.name,
+ self.own_nick))
else:
if config.get_by_tabname('display_user_color_in_join_part',
self.general_jid):
@@ -771,17 +817,26 @@ class MucTab(ChatTab):
kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s'
'\x19%(info_col)s} has been kicked by '
'\x193}%(by)s') % {
- 'spec': char_kick, 'nick':from_nick,
- 'color':color, 'by':by, 'info_col': info_col}
+ 'spec': char_kick,
+ 'nick': from_nick,
+ 'color': color,
+ 'by': by,
+ 'info_col': info_col
+ }
else:
kick_msg = ('\x191}%(spec)s \x19%(color)s}%(nick)s'
'\x19%(info_col)s} has been kicked') % {
- 'spec': char_kick, 'nick': from_nick,
- 'color':color, 'info_col': info_col}
+ 'spec': char_kick,
+ 'nick': from_nick,
+ 'color': color,
+ 'info_col': info_col
+ }
if reason is not None and reason.text:
kick_msg += ('\x19%(info_col)s} Reason: \x196}'
'%(reason)s') % {
- 'reason': reason.text, 'info_col': info_col}
+ 'reason': reason.text,
+ 'info_col': info_col
+ }
self.add_message(kick_msg, typ=2)
def on_user_leave_groupchat(self, user, jid, status, from_nick, from_room):
@@ -812,35 +867,39 @@ class MucTab(ChatTab):
leave_msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}'
'%(nick)s\x19%(info_col)s} has left the '
'room') % {
- 'nick':from_nick, 'color':color,
- 'spec':get_theme().CHAR_QUIT,
- 'info_col': info_col,
- 'color_spec': spec_col}
+ 'nick': from_nick,
+ 'color': color,
+ 'spec': get_theme().CHAR_QUIT,
+ 'info_col': info_col,
+ 'color_spec': spec_col
+ }
else:
jid_col = dump_tuple(get_theme().COLOR_MUC_JID)
leave_msg = ('\x19%(color_spec)s}%(spec)s \x19%(color)s}'
'%(nick)s\x19%(info_col)s} (\x19%(jid_col)s}'
'%(jid)s\x19%(info_col)s}) has left the '
'room') % {
- 'spec':get_theme().CHAR_QUIT,
- 'nick':from_nick, 'color':color,
- 'jid':jid.full, 'info_col': info_col,
- 'color_spec': spec_col,
- 'jid_col': jid_col}
+ 'spec': get_theme().CHAR_QUIT,
+ 'nick': from_nick,
+ 'color': color,
+ 'jid': jid.full,
+ 'info_col': info_col,
+ 'color_spec': spec_col,
+ 'jid_col': jid_col
+ }
if status:
leave_msg += ' (\x19o%s\x19%s})' % (status, info_col)
self.add_message(leave_msg, typ=2)
- self.core.on_user_left_private_conversation(from_room, user,
- status)
+ self.core.on_user_left_private_conversation(from_room, user, status)
- def on_user_change_status(
- self, user, from_nick, from_room, affiliation, role, show, status):
+ def on_user_change_status(self, user, from_nick, from_room, affiliation,
+ role, show, status):
"""
When an user changes her status
"""
# build the message
- display_message = False # flag to know if something significant enough
- # to be displayed has changed
+ display_message = False # flag to know if something significant enough
+ # to be displayed has changed
if config.get_by_tabname('display_user_color_in_join_part',
self.general_jid):
color = dump_tuple(user.color)
@@ -848,12 +907,15 @@ class MucTab(ChatTab):
color = 3
if from_nick == self.own_nick:
msg = '\x19%(color)s}You\x19%(info_col)s} changed: ' % {
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
- 'color': color}
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
+ 'color': color
+ }
else:
msg = '\x19%(color)s}%(nick)s\x19%(info_col)s} changed: ' % {
- 'nick': from_nick, 'color': color,
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}
+ 'nick': from_nick,
+ 'color': color,
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ }
if affiliation != user.affiliation:
msg += 'affiliation: %s, ' % affiliation
display_message = True
@@ -873,7 +935,7 @@ class MucTab(ChatTab):
display_message = True
if not display_message:
return
- msg = msg[:-2] # remove the last ", "
+ msg = msg[:-2] # remove the last ", "
hide_status_change = config.get_by_tabname('hide_status_change',
self.general_jid)
if hide_status_change < -1:
@@ -891,8 +953,8 @@ class MucTab(ChatTab):
role != user.role):
# display the message in the room
self._text_buffer.add_message(msg)
- self.core.on_user_changed_status_in_private('%s/%s' %
- (from_room, from_nick),
+ self.core.on_user_changed_status_in_private('%s/%s' % (from_room,
+ from_nick),
Status(show, status))
self.users.remove(user)
# finally, effectively change the user status
@@ -922,7 +984,7 @@ class MucTab(ChatTab):
Log the messages in the archives, if it needs
to be
"""
- if time is None and self.joined: # don't log the history messages
+ if time is None and self.joined: # don't log the history messages
if not logger.log_message(self.name, nickname, txt, typ=typ):
self.core.information('Unable to write in the log file',
'Error')
@@ -965,32 +1027,44 @@ class MucTab(ChatTab):
args['user'] = kwargs['forced_user']
if (not time and nickname and nickname != self.own_nick
- and self.state != 'current'):
- if (self.state != 'highlight' and
- config.get_by_tabname('notify_messages', self.name)):
+ and self.state != 'current'):
+ if (self.state != 'highlight'
+ and config.get_by_tabname('notify_messages', self.name)):
self.state = 'message'
if time and not txt.startswith('/me'):
txt = '\x19%(info_col)s}%(txt)s' % {
- 'txt': txt,
- 'info_col': dump_tuple(get_theme().COLOR_LOG_MSG)}
+ 'txt': txt,
+ 'info_col': dump_tuple(get_theme().COLOR_LOG_MSG)
+ }
elif not nickname:
txt = '\x19%(info_col)s}%(txt)s' % {
- 'txt': txt,
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)}
- elif not kwargs.get('highlight'): # TODO
+ 'txt': txt,
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ }
+ elif not kwargs.get('highlight'): # TODO
args['highlight'] = self.do_highlight(txt, time, nickname)
time = time or datetime.now()
self._text_buffer.add_message(txt, time, nickname, **args)
return args.get('highlight', False)
- def modify_message(self, txt, old_id, new_id,
- time=None, nickname=None, user=None, jid=None):
+ def modify_message(self,
+ txt,
+ old_id,
+ new_id,
+ time=None,
+ nickname=None,
+ user=None,
+ jid=None):
self.log_message(txt, nickname, time=time, typ=1)
highlight = self.do_highlight(txt, time, nickname, corrected=True)
- message = self._text_buffer.modify_message(txt, old_id, new_id,
- highlight=highlight,
- time=time, user=user,
- jid=jid)
+ message = self._text_buffer.modify_message(
+ txt,
+ old_id,
+ new_id,
+ highlight=highlight,
+ time=time,
+ user=user,
+ jid=jid)
if message:
self.text_win.modify_message(old_id, message)
return highlight
@@ -1000,11 +1074,13 @@ class MucTab(ChatTab):
return [(1, safeJID(self.name).user), (3, self.name)]
def enable_self_ping_event(self):
- delay = config.get_by_tabname("self_ping_delay", self.general_jid, default=0)
- if delay <= 0: # use 0 or some negative value to disable it
+ delay = config.get_by_tabname(
+ "self_ping_delay", self.general_jid, default=0)
+ if delay <= 0: # use 0 or some negative value to disable it
return
self.disable_self_ping_event()
- self.self_ping_event = timed_events.DelayedEvent(delay, self.send_self_ping)
+ self.self_ping_event = timed_events.DelayedEvent(
+ delay, self.send_self_ping)
self.core.add_timed_event(self.self_ping_event)
def disable_self_ping_event(self):
@@ -1014,16 +1090,17 @@ class MucTab(ChatTab):
def send_self_ping(self):
to = self.name + "/" + self.own_nick
- self.core.xmpp.plugin['xep_0199'].send_ping(jid=to,
- callback=self.on_self_ping_result,
- timeout_callback=self.on_self_ping_failed,
- timeout=60)
+ self.core.xmpp.plugin['xep_0199'].send_ping(
+ jid=to,
+ callback=self.on_self_ping_result,
+ timeout_callback=self.on_self_ping_failed,
+ timeout=60)
def on_self_ping_result(self, iq):
if iq["type"] == "error" and iq["error"]["condition"] != "feature-not-implemented":
self.command_cycle(iq["error"]["text"] or "not in this room")
self.core.refresh_window()
- else: # Re-send a self-ping in a few seconds
+ else: # Re-send a self-ping in a few seconds
self.enable_self_ping_event()
def search_for_color(self, nick):
@@ -1035,7 +1112,8 @@ class MucTab(ChatTab):
color = config.get_by_tabname(nick, 'muc_colors')
if color != '':
return color
- nick_color_aliases = config.get_by_tabname('nick_color_aliases', self.name)
+ nick_color_aliases = config.get_by_tabname('nick_color_aliases',
+ self.name)
if nick_color_aliases:
nick_alias = re.sub('^_*(.*?)_*$', '\\1', nick)
color = config.get_by_tabname(nick_alias, 'muc_colors')
@@ -1088,26 +1166,23 @@ class MucTab(ChatTab):
tab_win_height = Tab.tab_win_height()
info_win_height = self.core.information_win_size
-
- self.user_win.resize(self.height - 3 - info_win_height
- - tab_win_height,
- self.width - (self.width // 10) * 9 - 1,
- 1,
- (self.width // 10) * 9 + 1)
- self.v_separator.resize(self.height - 3 - info_win_height - tab_win_height,
- 1, 1, 9 * (self.width // 10))
+ self.user_win.resize(
+ self.height - 3 - info_win_height - tab_win_height, self.width -
+ (self.width // 10) * 9 - 1, 1, (self.width // 10) * 9 + 1)
+ self.v_separator.resize(
+ self.height - 3 - info_win_height - tab_win_height, 1, 1,
+ 9 * (self.width // 10))
self.topic_win.resize(1, self.width, 0, 0)
- self.text_win.resize(self.height - 3 - info_win_height
- - tab_win_height,
- text_width, 1, 0)
+ self.text_win.resize(
+ self.height - 3 - info_win_height - tab_win_height, text_width, 1,
+ 0)
self.text_win.rebuild_everything(self._text_buffer)
- self.info_header.resize(1, self.width,
- self.height - 2 - info_win_height
- - tab_win_height,
- 0)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - info_win_height - tab_win_height,
+ 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
def refresh(self):
if self.need_resize:
@@ -1131,40 +1206,40 @@ class MucTab(ChatTab):
self.input.refresh()
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
if config.get("hide_user_list"):
text_width = self.width
else:
- text_width = (self.width//10)*9
- self.user_win.resize(self.height - 3 - self.core.information_win_size
- - Tab.tab_win_height(),
- self.width - (self.width // 10) * 9 - 1,
- 1,
+ text_width = (self.width // 10) * 9
+ self.user_win.resize(self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(),
+ self.width - (self.width // 10) * 9 - 1, 1,
(self.width // 10) * 9 + 1)
- self.v_separator.resize(self.height - 3 - self.core.information_win_size - Tab.tab_win_height(),
- 1, 1, 9 * (self.width // 10))
- self.text_win.resize(self.height - 3 - self.core.information_win_size
- - Tab.tab_win_height(),
- text_width, 1, 0)
- self.info_header.resize(1, self.width,
- self.height-2-self.core.information_win_size
- - Tab.tab_win_height(),
- 0)
+ self.v_separator.resize(
+ self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(), 1, 1, 9 * (self.width // 10))
+ self.text_win.resize(self.height - 3 - self.core.information_win_size -
+ Tab.tab_win_height(), text_width, 1, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
+
def do_highlight(self, txt, time, nickname, corrected=False):
"""
Set the tab color and returns the nick color
"""
highlighted = False
- if (not time or corrected) and nickname and nickname != self.own_nick and self.joined:
+ if (not time or corrected
+ ) and nickname and nickname != self.own_nick and self.joined:
if re.search(r'\b' + self.own_nick.lower() + r'\b', txt.lower()):
if self.state != 'current':
self.state = 'highlight'
highlighted = True
else:
- highlight_words = config.get_by_tabname('highlight_on',
- self.general_jid)
+ highlight_words = config.get_by_tabname(
+ 'highlight_on', self.general_jid)
highlight_words = highlight_words.split(':')
for word in highlight_words:
if word and word.lower() in txt.lower():
@@ -1205,11 +1280,11 @@ class MucTab(ChatTab):
"""
/configure
"""
+
def on_form_received(form):
if not form:
self.core.information(
- 'Could not retrieve the configuration form',
- 'Error')
+ 'Could not retrieve the configuration form', 'Error')
return
self.core.open_new_form(form, self.cancel_config, self.send_config)
@@ -1254,17 +1329,18 @@ class MucTab(ChatTab):
"""
/version <jid or nick>
"""
+
def callback(res):
if not res:
return self.core.information('Could not get the software '
- 'version from %s' % (jid,),
+ '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 'an unknown platform')
+ jid, res.get('name') or 'an unknown software',
+ res.get('version') or 'unknown',
+ res.get('os') or 'an unknown platform')
self.core.information(version, 'Info')
+
if args is None:
return self.core.command.help('version')
nick = args[0]
@@ -1273,8 +1349,7 @@ class MucTab(ChatTab):
jid = safeJID(jid + '/' + nick)
else:
jid = safeJID(nick)
- fixes.get_version(self.core.xmpp, jid,
- callback=callback)
+ fixes.get_version(self.core.xmpp, jid, callback=callback)
@command_args_parser.quoted(1)
def command_nick(self, args):
@@ -1290,8 +1365,7 @@ class MucTab(ChatTab):
current_status = self.core.get_status()
if not safeJID(self.name + '/' + nick):
return self.core.information('Invalid nick', 'Info')
- muc.change_nick(self.core, self.name, nick,
- current_status.message,
+ muc.change_nick(self.core, self.name, nick, current_status.message,
current_status.show)
@command_args_parser.quoted(0, 1, [''])
@@ -1323,7 +1397,7 @@ class MucTab(ChatTab):
/query <nick> [message]
"""
if args is None:
- return self.core.command.help('query')
+ return self.core.command.help('query')
nick = args[0]
r = None
for user in self.users:
@@ -1332,7 +1406,7 @@ class MucTab(ChatTab):
if r and len(args) == 2:
msg = args[1]
self.core.current_tab().command_say(
- xhtml.convert_simple_to_full_colors(msg))
+ xhtml.convert_simple_to_full_colors(msg))
if not r:
self.core.information("Cannot find user: %s" % nick, 'Error')
@@ -1355,11 +1429,11 @@ class MucTab(ChatTab):
return
aff = {
- 'owner': get_theme().CHAR_AFFILIATION_OWNER,
- 'admin': get_theme().CHAR_AFFILIATION_ADMIN,
- 'member': get_theme().CHAR_AFFILIATION_MEMBER,
- 'none': get_theme().CHAR_AFFILIATION_NONE,
- }
+ 'owner': get_theme().CHAR_AFFILIATION_OWNER,
+ 'admin': get_theme().CHAR_AFFILIATION_ADMIN,
+ 'member': get_theme().CHAR_AFFILIATION_MEMBER,
+ 'none': get_theme().CHAR_AFFILIATION_NONE,
+ }
colors = {}
colors["visitor"] = dump_tuple(get_theme().COLOR_USER_VISITOR)
@@ -1372,8 +1446,9 @@ class MucTab(ChatTab):
affiliation = aff.get(user.affiliation,
get_theme().CHAR_AFFILIATION_NONE)
color = colors.get(user.role, color_other)
- buff.append('\x19%s}%s\x19o\x19%s}%s\x19o' % (
- color, affiliation, dump_tuple(user.color), user.nick))
+ buff.append('\x19%s}%s\x19o\x19%s}%s\x19o' %
+ (color, affiliation, dump_tuple(user.color),
+ user.nick))
buff.append('\n')
message = ' '.join(buff)
@@ -1414,6 +1489,7 @@ class MucTab(ChatTab):
Changes the role of an user
roles can be: none, visitor, participant, moderator
"""
+
def callback(iq):
if iq['type'] == 'error':
self.core.room_error(iq, self.name)
@@ -1431,6 +1507,7 @@ class MucTab(ChatTab):
Changes the affiliation of an user
affiliations can be: outcast, none, member, admin, owner
"""
+
def callback(iq):
if iq['type'] == 'error':
self.core.room_error(iq, self.name)
@@ -1542,9 +1619,8 @@ class MucTab(ChatTab):
after = config.get('after_completion') + ' '
input_pos = self.input.pos
if ' ' not in self.input.get_text()[:input_pos] or (
- self.input.last_completion and
- self.input.get_text()[:input_pos] ==
- self.input.last_completion + after):
+ self.input.last_completion and self.input.get_text()
+ [:input_pos] == self.input.last_completion + after):
add_after = after
else:
if not config.get('add_space_after_completion'):
@@ -1553,9 +1629,9 @@ class MucTab(ChatTab):
add_after = ' '
self.input.auto_completion(word_list, add_after, quotify=False)
empty_after = self.input.get_text() == ''
- empty_after = empty_after or (self.input.get_text().startswith('/')
- and not
- self.input.get_text().startswith('//'))
+ empty_after = empty_after or (
+ self.input.get_text().startswith('/')
+ and not self.input.get_text().startswith('//'))
self.send_composing_chat_state(empty_after)
def completion_version(self, the_input):
@@ -1584,15 +1660,18 @@ class MucTab(ChatTab):
def completion_nick(self, the_input):
"""Completion for /nick"""
- nicks = [os.environ.get('USER'),
- config.get('default_nick'),
- self.core.get_bookmark_nickname(self.name)]
+ nicks = [
+ os.environ.get('USER'),
+ config.get('default_nick'),
+ self.core.get_bookmark_nickname(self.name)
+ ]
nicks = [i for i in nicks if i]
return Completion(the_input.auto_completion, nicks, '', quotify=False)
def completion_recolor(self, the_input):
if the_input.get_argument_position() == 1:
- return Completion(the_input.new_completion, ['random'], 1, '', quotify=False)
+ return Completion(
+ the_input.new_completion, ['random'], 1, '', quotify=False)
return True
def completion_color(self, the_input):
@@ -1602,13 +1681,15 @@ class MucTab(ChatTab):
userlist = [user.nick for user in self.users]
if self.own_nick in userlist:
userlist.remove(self.own_nick)
- return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
+ return Completion(
+ the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
colors = [i for i in xhtml.colors if i]
colors.sort()
colors.append('unset')
colors.append('random')
- return Completion(the_input.new_completion, colors, 2, '', quotify=False)
+ return Completion(
+ the_input.new_completion, colors, 2, '', quotify=False)
def completion_ignore(self, the_input):
"""Completion for /ignore"""
@@ -1625,11 +1706,12 @@ class MucTab(ChatTab):
userlist = [user.nick for user in self.users]
if self.own_nick in userlist:
userlist.remove(self.own_nick)
- return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
+ return Completion(
+ the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
possible_roles = ['none', 'visitor', 'participant', 'moderator']
- return Completion(the_input.new_completion, possible_roles, 2, '',
- quotify=True)
+ return Completion(
+ the_input.new_completion, possible_roles, 2, '', quotify=True)
def completion_affiliation(self, the_input):
"""Completion for /affiliation"""
@@ -1642,22 +1724,30 @@ class MucTab(ChatTab):
if self.core.xmpp.boundjid.bare in jidlist:
jidlist.remove(self.core.xmpp.boundjid.bare)
userlist.extend(jidlist)
- return Completion(the_input.new_completion, userlist, 1, '', quotify=True)
+ return Completion(
+ the_input.new_completion, userlist, 1, '', quotify=True)
elif n == 2:
- possible_affiliations = ['none', 'member', 'admin',
- 'owner', 'outcast']
- return Completion(the_input.new_completion, possible_affiliations, 2, '',
- quotify=True)
+ possible_affiliations = [
+ 'none', 'member', 'admin', 'owner', 'outcast'
+ ]
+ return Completion(
+ the_input.new_completion,
+ possible_affiliations,
+ 2,
+ '',
+ quotify=True)
def completion_invite(self, the_input):
"""Completion for /invite"""
n = the_input.get_argument_position(quoted=True)
if n == 1:
- return Completion(the_input.new_completion, roster.jids(), 1, quotify=True)
+ return Completion(
+ the_input.new_completion, roster.jids(), 1, quotify=True)
def completion_topic(self, the_input):
if the_input.get_argument_position() == 1:
- return Completion(the_input.auto_completion, [self.topic], '', quotify=False)
+ return Completion(
+ the_input.auto_completion, [self.topic], '', quotify=False)
def completion_quoted(self, the_input):
"""Nick completion, but with quotes"""
@@ -1668,13 +1758,15 @@ class MucTab(ChatTab):
if user.nick != self.own_nick:
word_list.append(user.nick)
- return Completion(the_input.new_completion, word_list, 1, quotify=True)
+ return Completion(
+ the_input.new_completion, word_list, 1, quotify=True)
def completion_unignore(self, the_input):
if the_input.get_argument_position() == 1:
users = [user.nick for user in self.ignores]
return Completion(the_input.auto_completion, users, quotify=False)
+
########################## REGISTER STUFF ##############################
def register_keys(self):
@@ -1687,183 +1779,261 @@ class MucTab(ChatTab):
def register_commands(self):
"Register tab-specific commands"
- self.register_commands_batch([
- {
- 'name': 'ignore',
- 'func': self.command_ignore,
- 'usage': '<nickname>',
- 'desc': 'Ignore a specified nickname.',
- 'shortdesc': 'Ignore someone',
- 'completion': self.completion_unignore
- },
- {
- 'name': 'unignore',
- 'func': self.command_unignore,
- 'usage': '<nickname>',
- 'desc': 'Remove the specified nickname from the ignore list.',
- 'shortdesc': 'Unignore someone.',
- 'completion': self.completion_unignore
- },
- {
- 'name': 'kick',
- 'func': self.command_kick,
- 'usage': '<nick> [reason]',
- 'desc': ('Kick the user with the specified nickname.'
- ' You also can give an optional reason.'),
- 'shortdesc': 'Kick someone.',
- 'completion': self.completion_quoted
- },
- {
- 'name': 'ban',
- 'func': self.command_ban,
- 'usage': '<nick> [reason]',
- 'desc': ('Ban the user with the specified nickname.'
- ' You also can give an optional reason.'),
- 'shortdesc': 'Ban someone',
- 'completion': self.completion_quoted
- },
- {
- 'name': 'role',
- 'func': self.command_role,
- 'usage': '<nick> <role> [reason]',
- 'desc': ('Set the role of an user. Roles can be:'
- ' none, visitor, participant, moderator.'
- ' You also can give an optional reason.'),
- 'shortdesc': 'Set the role of an user.',
- 'completion': self.completion_role
- },
- {
- 'name': 'affiliation',
- 'func': self.command_affiliation,
- 'usage': '<nick or jid> <affiliation>',
- 'desc': ('Set the affiliation of an user. Affiliations can be:'
- ' outcast, none, member, admin, owner.'),
- 'shortdesc': 'Set the affiliation of an user.',
- 'completion': self.completion_affiliation
- },
- {
- 'name': 'topic',
- 'func': self.command_topic,
- 'usage': '<subject>',
- 'desc': 'Change the subject of the room.',
- 'shortdesc': 'Change the subject.',
- 'completion': self.completion_topic
- },
- {
- 'name': 'subject',
- 'func': self.command_topic,
- 'usage': '<subject>',
- 'desc': 'Change the subject of the room.',
- 'shortdesc': 'Change the subject.',
- 'completion': self.completion_topic
- },
- {
- 'name': 'query',
- 'func': self.command_query,
- 'usage': '<nick> [message]',
- 'desc': ('Open a private conversation with <nick>. This nick'
- ' has to be present in the room you\'re currently in.'
- ' If you specified a message after the nickname, it '
- 'will immediately be sent to this user.'),
- 'shortdesc': 'Query a user.',
- 'completion': self.completion_quoted
- },
- {
- 'name': 'part',
- 'func': self.command_part,
- 'usage': '[message]',
- 'desc': ('Disconnect from a room. You can'
- ' specify an optional message.'),
- 'shortdesc': 'Leave the room.'
- },
- {
- 'name': 'close',
- 'func': self.command_close,
- 'usage': '[message]',
- 'desc': ('Disconnect from a room and close the tab.'
- ' You can specify an optional message if '
- 'you are still connected.'),
- 'shortdesc': 'Close the tab.'
- },
- {
- 'name': 'nick',
- 'func': self.command_nick,
- 'usage': '<nickname>',
- 'desc': 'Change your nickname in the current room.',
- 'shortdesc': 'Change your nickname.',
- 'completion': self.completion_nick
- },
- {
- 'name':'recolor',
- 'func': self.command_recolor,
- 'usage': '[random]',
- 'desc': ('Re-assign a color to all participants of the'
- ' current room, based on the last time they talked.'
- ' Use this if the participants currently talking '
- 'have too many identical colors. Use /recolor random'
- ' for a non-deterministic result.'),
- 'shortdesc': 'Change the nicks colors.',
- 'completion': self.completion_recolor
- },
- {
- 'name': 'color',
- 'func': self.command_color,
- 'usage': '<nick> <color>',
- 'desc': ('Fix a color for a nick. Use "unset" instead of a '
- 'color to remove the attribution'),
- 'shortdesc': 'Fix a color for a nick.',
- 'completion': self.completion_recolor
- },
- {
- 'name': 'cycle',
- 'func': self.command_cycle,
- 'usage': '[message]',
- 'desc': 'Leave the current room and rejoin it immediately.',
- 'shortdesc': 'Leave and re-join the room.'
- },
- {
- 'name': 'info',
- 'func': self.command_info,
- 'usage': '<nickname>',
- 'desc': ('Display some information about the user '
- 'in the MUC: its/his/her role, affiliation,'
- ' status and status message.'),
- 'shortdesc': 'Show an user\'s infos.',
- 'completion': self.completion_info
- },
- {
- 'name': 'configure',
- 'func': self.command_configure,
- 'desc': 'Configure the current room, through a form.',
- 'shortdesc': 'Configure the room.'
- },
- {
- 'name': 'version',
- 'func': self.command_version,
- 'usage': '<jid or nick>',
- 'desc': ('Get the software version of the given JID'
- ' or nick in room (usually its XMPP client'
- ' and Operating System).'),
- 'shortdesc': 'Get the software version of a jid.',
- 'completion': self.completion_version
- },
- {
- 'name': 'names',
- 'func': self.command_names,
- 'desc': 'Get the users in the room with their roles.',
- 'shortdesc': 'List the users.'
- },
- {
- 'name': 'invite',
- 'func': self.command_invite,
- 'desc': 'Invite a contact to this room',
- 'usage': '<jid> [reason]',
- 'shortdesc': 'Invite a contact to this room',
- 'completion': self.completion_invite
- }
- ])
+ self.register_commands_batch([{
+ 'name': 'ignore',
+ 'func': self.command_ignore,
+ 'usage': '<nickname>',
+ 'desc': 'Ignore a specified nickname.',
+ 'shortdesc': 'Ignore someone',
+ 'completion': self.completion_unignore
+ }, {
+ 'name':
+ 'unignore',
+ 'func':
+ self.command_unignore,
+ 'usage':
+ '<nickname>',
+ 'desc':
+ 'Remove the specified nickname from the ignore list.',
+ 'shortdesc':
+ 'Unignore someone.',
+ 'completion':
+ self.completion_unignore
+ }, {
+ 'name':
+ 'kick',
+ 'func':
+ self.command_kick,
+ 'usage':
+ '<nick> [reason]',
+ 'desc': ('Kick the user with the specified nickname.'
+ ' You also can give an optional reason.'),
+ 'shortdesc':
+ 'Kick someone.',
+ 'completion':
+ self.completion_quoted
+ }, {
+ 'name':
+ 'ban',
+ 'func':
+ self.command_ban,
+ 'usage':
+ '<nick> [reason]',
+ 'desc': ('Ban the user with the specified nickname.'
+ ' You also can give an optional reason.'),
+ 'shortdesc':
+ 'Ban someone',
+ 'completion':
+ self.completion_quoted
+ }, {
+ 'name':
+ 'role',
+ 'func':
+ self.command_role,
+ 'usage':
+ '<nick> <role> [reason]',
+ 'desc': ('Set the role of an user. Roles can be:'
+ ' none, visitor, participant, moderator.'
+ ' You also can give an optional reason.'),
+ 'shortdesc':
+ 'Set the role of an user.',
+ 'completion':
+ self.completion_role
+ }, {
+ 'name':
+ 'affiliation',
+ 'func':
+ self.command_affiliation,
+ 'usage':
+ '<nick or jid> <affiliation>',
+ 'desc': ('Set the affiliation of an user. Affiliations can be:'
+ ' outcast, none, member, admin, owner.'),
+ 'shortdesc':
+ 'Set the affiliation of an user.',
+ 'completion':
+ self.completion_affiliation
+ }, {
+ 'name':
+ 'topic',
+ 'func':
+ self.command_topic,
+ 'usage':
+ '<subject>',
+ 'desc':
+ 'Change the subject of the room.',
+ 'shortdesc':
+ 'Change the subject.',
+ 'completion':
+ self.completion_topic
+ }, {
+ 'name':
+ 'subject',
+ 'func':
+ self.command_topic,
+ 'usage':
+ '<subject>',
+ 'desc':
+ 'Change the subject of the room.',
+ 'shortdesc':
+ 'Change the subject.',
+ 'completion':
+ self.completion_topic
+ }, {
+ 'name':
+ 'query',
+ 'func':
+ self.command_query,
+ 'usage':
+ '<nick> [message]',
+ 'desc': ('Open a private conversation with <nick>. This nick'
+ ' has to be present in the room you\'re currently in.'
+ ' If you specified a message after the nickname, it '
+ 'will immediately be sent to this user.'),
+ 'shortdesc':
+ 'Query a user.',
+ 'completion':
+ self.completion_quoted
+ }, {
+ 'name':
+ 'part',
+ 'func':
+ self.command_part,
+ 'usage':
+ '[message]',
+ 'desc': ('Disconnect from a room. You can'
+ ' specify an optional message.'),
+ 'shortdesc':
+ 'Leave the room.'
+ }, {
+ 'name':
+ 'close',
+ 'func':
+ self.command_close,
+ 'usage':
+ '[message]',
+ 'desc': ('Disconnect from a room and close the tab.'
+ ' You can specify an optional message if '
+ 'you are still connected.'),
+ 'shortdesc':
+ 'Close the tab.'
+ }, {
+ 'name':
+ 'nick',
+ 'func':
+ self.command_nick,
+ 'usage':
+ '<nickname>',
+ 'desc':
+ 'Change your nickname in the current room.',
+ 'shortdesc':
+ 'Change your nickname.',
+ 'completion':
+ self.completion_nick
+ }, {
+ 'name':
+ 'recolor',
+ 'func':
+ self.command_recolor,
+ 'usage':
+ '[random]',
+ 'desc': ('Re-assign a color to all participants of the'
+ ' current room, based on the last time they talked.'
+ ' Use this if the participants currently talking '
+ 'have too many identical colors. Use /recolor random'
+ ' for a non-deterministic result.'),
+ 'shortdesc':
+ 'Change the nicks colors.',
+ 'completion':
+ self.completion_recolor
+ }, {
+ 'name':
+ 'color',
+ 'func':
+ self.command_color,
+ 'usage':
+ '<nick> <color>',
+ 'desc': ('Fix a color for a nick. Use "unset" instead of a '
+ 'color to remove the attribution'),
+ 'shortdesc':
+ 'Fix a color for a nick.',
+ 'completion':
+ self.completion_recolor
+ }, {
+ 'name':
+ 'cycle',
+ 'func':
+ self.command_cycle,
+ 'usage':
+ '[message]',
+ 'desc':
+ 'Leave the current room and rejoin it immediately.',
+ 'shortdesc':
+ 'Leave and re-join the room.'
+ }, {
+ 'name':
+ 'info',
+ 'func':
+ self.command_info,
+ 'usage':
+ '<nickname>',
+ 'desc': ('Display some information about the user '
+ 'in the MUC: its/his/her role, affiliation,'
+ ' status and status message.'),
+ 'shortdesc':
+ 'Show an user\'s infos.',
+ 'completion':
+ self.completion_info
+ }, {
+ 'name':
+ 'configure',
+ 'func':
+ self.command_configure,
+ 'desc':
+ 'Configure the current room, through a form.',
+ 'shortdesc':
+ 'Configure the room.'
+ }, {
+ 'name':
+ 'version',
+ 'func':
+ self.command_version,
+ 'usage':
+ '<jid or nick>',
+ 'desc': ('Get the software version of the given JID'
+ ' or nick in room (usually its XMPP client'
+ ' and Operating System).'),
+ 'shortdesc':
+ 'Get the software version of a jid.',
+ 'completion':
+ self.completion_version
+ }, {
+ 'name':
+ 'names',
+ 'func':
+ self.command_names,
+ 'desc':
+ 'Get the users in the room with their roles.',
+ 'shortdesc':
+ 'List the users.'
+ }, {
+ 'name':
+ 'invite',
+ 'func':
+ self.command_invite,
+ 'desc':
+ 'Invite a contact to this room',
+ 'usage':
+ '<jid> [reason]',
+ 'shortdesc':
+ 'Invite a contact to this room',
+ 'completion':
+ self.completion_invite
+ }])
+
+
+class PresenceError(Exception):
+ pass
-class PresenceError(Exception): pass
def dissect_presence(presence):
"""
diff --git a/poezio/tabs/privatetab.py b/poezio/tabs/privatetab.py
index ec3ed74a..735522e1 100644
--- a/poezio/tabs/privatetab.py
+++ b/poezio/tabs/privatetab.py
@@ -27,6 +27,7 @@ from poezio.logger import logger
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
+
class PrivateTab(OneToOneTab):
"""
The tab containg a private conversation (someone from a MUC)
@@ -35,6 +36,7 @@ class PrivateTab(OneToOneTab):
plugin_commands = {}
additional_information = {}
plugin_keys = {}
+
def __init__(self, core, name, nick):
OneToOneTab.__init__(self, core, name)
self.own_nick = nick
@@ -46,12 +48,18 @@ class PrivateTab(OneToOneTab):
# keys
self.key_func['^I'] = self.completion
# commands
- self.register_command('info', self.command_info,
- desc='Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.',
- shortdesc='Info about the user.')
- self.register_command('version', self.command_version,
- desc='Get the software version of the current interlocutor (usually its XMPP client and Operating System).',
- shortdesc='Get the software version of a jid.')
+ self.register_command(
+ 'info',
+ self.command_info,
+ desc=
+ 'Display some information about the user in the MUC: its/his/her role, affiliation, status and status message.',
+ shortdesc='Info about the user.')
+ self.register_command(
+ 'version',
+ self.command_version,
+ desc=
+ 'Get the software version of the current interlocutor (usually its XMPP client and Operating System).',
+ shortdesc='Get the software version of a jid.')
self.resize()
self.parent_muc = self.core.get_tab_by_name(safeJID(name).bare, MucTab)
self.on = True
@@ -61,7 +69,7 @@ class PrivateTab(OneToOneTab):
def remote_user_color(self):
user = self.parent_muc.get_user_by_name(safeJID(self.name).resource)
if user:
- return dump_tuple(user.color);
+ return dump_tuple(user.color)
return super().remote_user_color()
@property
@@ -87,14 +95,16 @@ class PrivateTab(OneToOneTab):
del PrivateTab.additional_information[plugin_name]
def load_logs(self, log_nb):
- logs = logger.get_logs(safeJID(self.name).full.replace('/', '\\'), log_nb)
+ logs = logger.get_logs(
+ safeJID(self.name).full.replace('/', '\\'), log_nb)
return logs
def log_message(self, txt, nickname, time=None, typ=1):
"""
Log the messages in the archives.
"""
- if not logger.log_message(self.name, nickname, txt, date=time, typ=typ):
+ if not logger.log_message(
+ self.name, nickname, txt, date=time, typ=typ):
self.core.information('Unable to write in the log file', 'Error')
def on_close(self):
@@ -120,7 +130,9 @@ class PrivateTab(OneToOneTab):
else:
add_after = ''
self.input.auto_completion(word_list, add_after, quotify=False)
- empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
+ empty_after = self.input.get_text() == '' or (
+ self.input.get_text().startswith('/')
+ and not self.input.get_text().startswith('//'))
self.send_composing_chat_state(empty_after)
@command_args_parser.raw
@@ -145,8 +157,13 @@ class PrivateTab(OneToOneTab):
msg['replace']['id'] = self.last_sent_message['id']
if config.get_by_tabname('group_corrections', self.name):
try:
- self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'],
- user=user, jid=self.core.xmpp.boundjid, nickname=self.own_nick)
+ self.modify_message(
+ msg['body'],
+ self.last_sent_message['id'],
+ msg['id'],
+ user=user,
+ jid=self.core.xmpp.boundjid,
+ nickname=self.own_nick)
replaced = True
except:
log.error('Unable to correct a message', exc_info=True)
@@ -157,8 +174,8 @@ class PrivateTab(OneToOneTab):
msg.enable('html')
msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
msg['body'] = xhtml.clean_text(msg['body'])
- if (config.get_by_tabname('send_chat_states', self.general_jid) and
- self.remote_wants_chatstates is not False):
+ if (config.get_by_tabname('send_chat_states', self.general_jid)
+ and self.remote_wants_chatstates is not False):
needed = 'inactive' if self.inactive else 'active'
msg['chat_state'] = needed
if attention and self.remote_supports_attention:
@@ -170,13 +187,14 @@ class PrivateTab(OneToOneTab):
self.input.refresh()
return
if not replaced:
- self.add_message(msg['body'],
- nickname=self.own_nick or self.core.own_nick,
- forced_user=user,
- nick_color=get_theme().COLOR_OWN_NICK,
- identifier=msg['id'],
- jid=self.core.xmpp.boundjid,
- typ=1)
+ self.add_message(
+ msg['body'],
+ nickname=self.own_nick or self.core.own_nick,
+ forced_user=user,
+ nick_color=get_theme().COLOR_OWN_NICK,
+ identifier=msg['id'],
+ jid=self.core.xmpp.boundjid,
+ typ=1)
self.last_sent_message = msg
if self.remote_supports_receipts:
@@ -191,19 +209,22 @@ class PrivateTab(OneToOneTab):
"""
/version
"""
+
def callback(res):
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 'an unknown platform')
+ 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 'an unknown platform')
self.core.information(version, 'Info')
+
if args:
return self.core.command.version(args[0])
jid = safeJID(self.name)
- fixes.get_version(self.core.xmpp, jid,
- callback=callback)
+ fixes.get_version(self.core.xmpp, jid, callback=callback)
@command_args_parser.quoted(0, 1)
def command_info(self, arg):
@@ -226,14 +247,14 @@ class PrivateTab(OneToOneTab):
info_win_height = self.core.information_win_size
tab_win_height = Tab.tab_win_height()
- self.text_win.resize(self.height - 2 - info_win_height - tab_win_height,
- self.width, 0, 0)
+ self.text_win.resize(
+ self.height - 2 - info_win_height - tab_win_height, self.width, 0,
+ 0)
self.text_win.rebuild_everything(self._text_buffer)
- self.info_header.resize(1, self.width,
- self.height - 2 - info_win_height
- - tab_win_height,
- 0)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - info_win_height - tab_win_height,
+ 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
def refresh(self):
if self.need_resize:
@@ -251,7 +272,8 @@ class PrivateTab(OneToOneTab):
self.input.refresh()
def refresh_info_header(self):
- self.info_header.refresh(self.name, self.text_win, self.chatstate, PrivateTab.additional_information)
+ self.info_header.refresh(self.name, self.text_win, self.chatstate,
+ PrivateTab.additional_information)
self.input.refresh()
def get_nick(self):
@@ -264,7 +286,9 @@ class PrivateTab(OneToOneTab):
self.input.do_command(key, raw=raw)
if not self.on:
return False
- empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
+ empty_after = self.input.get_text() == '' or (
+ self.input.get_text().startswith('/')
+ and not self.input.get_text().startswith('//'))
tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
if tab and tab.joined:
self.send_composing_chat_state(empty_after)
@@ -279,8 +303,8 @@ class PrivateTab(OneToOneTab):
self.text_win.remove_line_separator()
self.text_win.add_line_separator(self._text_buffer)
tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
- if tab and tab.joined and config.get_by_tabname('send_chat_states',
- self.general_jid) and self.on:
+ if tab and tab.joined and config.get_by_tabname(
+ 'send_chat_states', self.general_jid) and self.on:
self.send_chat_state('inactive')
self.check_scrolled()
@@ -288,15 +312,20 @@ class PrivateTab(OneToOneTab):
self.state = 'current'
curses.curs_set(1)
tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
- if tab and tab.joined and config.get_by_tabname('send_chat_states',
- self.general_jid,) and not self.input.get_text() and self.on:
+ if tab and tab.joined and config.get_by_tabname(
+ 'send_chat_states',
+ self.general_jid,
+ ) and not self.input.get_text() and self.on:
self.send_chat_state('active')
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
- self.text_win.resize(self.height-2-self.core.information_win_size - Tab.tab_win_height(), self.width, 0, 0)
- self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0)
+ self.text_win.resize(self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), self.width, 0, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
def get_text_window(self):
return self.text_win
@@ -307,13 +336,16 @@ class PrivateTab(OneToOneTab):
The user changed her nick in the corresponding muc: update the tab’s name and
display a message.
"""
- self.add_message('\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now '
- 'known as \x19%(nick_col)s}%(new)s' % {
- 'old':old_nick, 'new': user.nick,
- 'nick_col': dump_tuple(user.color),
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
- new_jid = safeJID(self.name).bare+'/'+user.nick
+ self.add_message(
+ '\x19%(nick_col)s}%(old)s\x19%(info_col)s} is now '
+ 'known as \x19%(nick_col)s}%(new)s' % {
+ 'old': old_nick,
+ 'new': user.nick,
+ 'nick_col': dump_tuple(user.color),
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
+ new_jid = safeJID(self.name).bare + '/' + user.nick
self.name = new_jid
return self.core.current_tab() is self
@@ -323,28 +355,36 @@ class PrivateTab(OneToOneTab):
The user left the associated MUC
"""
self.deactivate()
- if config.get_by_tabname('display_user_color_in_join_part', self.general_jid):
+ if config.get_by_tabname('display_user_color_in_join_part',
+ self.general_jid):
color = dump_tuple(user.color)
else:
color = dump_tuple(get_theme().COLOR_REMOTE_USER)
if not status_message:
- self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
- '%(nick)s\x19%(info_col)s} has left the room' % {
- 'nick': user.nick, 'spec': get_theme().CHAR_QUIT,
- 'nick_col': color,
- 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ self.add_message(
+ '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
+ '%(nick)s\x19%(info_col)s} has left the room' % {
+ 'nick': user.nick,
+ 'spec': get_theme().CHAR_QUIT,
+ 'nick_col': color,
+ 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
else:
- self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
- '%(nick)s\x19%(info_col)s} has left the room'
- ' (%(status)s)' % { 'status': status_message,
- 'nick': user.nick, 'spec': get_theme().CHAR_QUIT,
- 'nick_col': color,
- 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ self.add_message(
+ '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
+ '%(nick)s\x19%(info_col)s} has left the room'
+ ' (%(status)s)' % {
+ 'status': status_message,
+ 'nick': user.nick,
+ 'spec': get_theme().CHAR_QUIT,
+ 'nick_col': color,
+ 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
return self.core.current_tab() is self
@refresh_wrapper.conditional
@@ -361,12 +401,16 @@ class PrivateTab(OneToOneTab):
user = tab.get_user_by_name(nick)
if user:
color = dump_tuple(user.color)
- self.add_message('\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19'
- '%(info_col)s} joined the room' % {'nick':nick,
- 'color': color, 'spec':get_theme().CHAR_JOIN,
- 'join_col': dump_tuple(get_theme().COLOR_JOIN_CHAR),
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
- typ=2)
+ self.add_message(
+ '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19'
+ '%(info_col)s} joined the room' % {
+ 'nick': nick,
+ 'color': color,
+ 'spec': get_theme().CHAR_JOIN,
+ 'join_col': dump_tuple(get_theme().COLOR_JOIN_CHAR),
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
+ },
+ typ=2)
return self.core.current_tab() is self
def activate(self, reason=None):
@@ -386,7 +430,10 @@ class PrivateTab(OneToOneTab):
def add_error(self, error_message):
error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK),
error_message)
- self.add_message(error, highlight=True, nickname='Error',
- nick_color=get_theme().COLOR_ERROR_MSG, typ=2)
+ self.add_message(
+ error,
+ highlight=True,
+ nickname='Error',
+ nick_color=get_theme().COLOR_ERROR_MSG,
+ typ=2)
self.core.refresh_window()
-
diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py
index 731120be..c079d550 100644
--- a/poezio/tabs/rostertab.py
+++ b/poezio/tabs/rostertab.py
@@ -36,6 +36,7 @@ class RosterInfoTab(Tab):
"""
plugin_commands = {}
plugin_keys = {}
+
def __init__(self, core):
Tab.__init__(self, core)
self.name = "Roster"
@@ -45,7 +46,8 @@ class RosterInfoTab(Tab):
self.roster_win = windows.RosterWin()
self.contact_info_win = windows.ContactInfoWin()
self.avatar_win = windows.ImageWin()
- self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show")
+ self.default_help_message = windows.HelpText(
+ "Enter commands with “/”. “o”: toggle offline show")
self.input = self.default_help_message
self.state = 'normal'
self.key_func['^I'] = self.completion
@@ -68,82 +70,110 @@ class RosterInfoTab(Tab):
self.key_func["s"] = self.start_search
self.key_func["S"] = self.start_search_slow
self.key_func["n"] = self.change_contact_name
- self.register_command('deny', self.command_deny,
- usage='[jid]',
- desc='Deny your presence to the provided JID (or the '
- 'selected contact in your roster), who is asking'
- 'you to be in his/here roster.',
- shortdesc='Deny a user your presence.',
- completion=self.completion_deny)
- self.register_command('accept', self.command_accept,
- usage='[jid]',
- desc='Allow the provided JID (or the selected contact '
- 'in your roster), to see your presence.',
- shortdesc='Allow a user your presence.',
- completion=self.completion_deny)
- self.register_command('add', self.command_add,
- usage='<jid>',
- desc='Add the specified JID to your roster, ask them to'
- ' allow you to see his presence, and allow them to'
- ' see your presence.',
- shortdesc='Add a user to your roster.')
- self.register_command('name', self.command_name,
- usage='<jid> [name]',
- shortdesc='Set the given JID\'s name.',
- completion=self.completion_name)
- self.register_command('groupadd', self.command_groupadd,
- usage='[<jid> <group>]|<group>',
- desc='Add the given JID or selected line to the given group.',
- shortdesc='Add a user to a group',
- completion=self.completion_groupadd)
- self.register_command('groupmove', self.command_groupmove,
- usage='<jid> <old group> <new group>',
- desc='Move the given JID from the old group to the new group.',
- shortdesc='Move a user to another group.',
- completion=self.completion_groupmove)
- self.register_command('groupremove', self.command_groupremove,
- usage='<jid> <group>',
- desc='Remove the given JID from the given group.',
- shortdesc='Remove a user from a group.',
- completion=self.completion_groupremove)
- self.register_command('remove', self.command_remove,
- usage='[jid]',
- desc='Remove the specified JID from your roster. This '
- 'will unsubscribe you from its presence, cancel '
- 'its subscription to yours, and remove the item '
- 'from your roster.',
- shortdesc='Remove a user from your roster.',
- completion=self.completion_remove)
- self.register_command('export', self.command_export,
- usage='[/path/to/file]',
- desc='Export your contacts into /path/to/file if '
- 'specified, or $HOME/poezio_contacts if not.',
- shortdesc='Export your roster to a file.',
- completion=partial(self.completion_file, 1))
- self.register_command('import', self.command_import,
- usage='[/path/to/file]',
- desc='Import your contacts from /path/to/file if '
- 'specified, or $HOME/poezio_contacts if not.',
- shortdesc='Import your roster from a file.',
- completion=partial(self.completion_file, 1))
- self.register_command('password', self.command_password,
- usage='<password>',
- shortdesc='Change your password')
-
- self.register_command('reconnect', self.command_reconnect,
- desc='Disconnect from the remote server if you are '
- 'currently connected and then connect to it again.',
- shortdesc='Disconnect and reconnect to the server.')
- self.register_command('disconnect', self.command_disconnect,
- desc='Disconnect from the remote server.',
- shortdesc='Disconnect from the server.')
- self.register_command('clear', self.command_clear,
- shortdesc='Clear the info buffer.')
- self.register_command('last_activity', self.command_last_activity,
+ self.register_command(
+ 'deny',
+ self.command_deny,
+ usage='[jid]',
+ desc='Deny your presence to the provided JID (or the '
+ 'selected contact in your roster), who is asking'
+ 'you to be in his/here roster.',
+ shortdesc='Deny a user your presence.',
+ completion=self.completion_deny)
+ self.register_command(
+ 'accept',
+ self.command_accept,
+ usage='[jid]',
+ desc='Allow the provided JID (or the selected contact '
+ 'in your roster), to see your presence.',
+ shortdesc='Allow a user your presence.',
+ completion=self.completion_deny)
+ self.register_command(
+ 'add',
+ self.command_add,
usage='<jid>',
- desc='Informs you of the last activity of a JID.',
- shortdesc='Get the activity of someone.',
- completion=self.core.completion.last_activity)
+ desc='Add the specified JID to your roster, ask them to'
+ ' allow you to see his presence, and allow them to'
+ ' see your presence.',
+ shortdesc='Add a user to your roster.')
+ self.register_command(
+ 'name',
+ self.command_name,
+ usage='<jid> [name]',
+ shortdesc='Set the given JID\'s name.',
+ completion=self.completion_name)
+ self.register_command(
+ 'groupadd',
+ self.command_groupadd,
+ usage='[<jid> <group>]|<group>',
+ desc='Add the given JID or selected line to the given group.',
+ shortdesc='Add a user to a group',
+ completion=self.completion_groupadd)
+ self.register_command(
+ 'groupmove',
+ self.command_groupmove,
+ usage='<jid> <old group> <new group>',
+ desc='Move the given JID from the old group to the new group.',
+ shortdesc='Move a user to another group.',
+ completion=self.completion_groupmove)
+ self.register_command(
+ 'groupremove',
+ self.command_groupremove,
+ usage='<jid> <group>',
+ desc='Remove the given JID from the given group.',
+ shortdesc='Remove a user from a group.',
+ completion=self.completion_groupremove)
+ self.register_command(
+ 'remove',
+ self.command_remove,
+ usage='[jid]',
+ desc='Remove the specified JID from your roster. This '
+ 'will unsubscribe you from its presence, cancel '
+ 'its subscription to yours, and remove the item '
+ 'from your roster.',
+ shortdesc='Remove a user from your roster.',
+ completion=self.completion_remove)
+ self.register_command(
+ 'export',
+ self.command_export,
+ usage='[/path/to/file]',
+ desc='Export your contacts into /path/to/file if '
+ 'specified, or $HOME/poezio_contacts if not.',
+ shortdesc='Export your roster to a file.',
+ completion=partial(self.completion_file, 1))
+ self.register_command(
+ 'import',
+ self.command_import,
+ usage='[/path/to/file]',
+ desc='Import your contacts from /path/to/file if '
+ 'specified, or $HOME/poezio_contacts if not.',
+ shortdesc='Import your roster from a file.',
+ completion=partial(self.completion_file, 1))
+ self.register_command(
+ 'password',
+ self.command_password,
+ usage='<password>',
+ shortdesc='Change your password')
+
+ self.register_command(
+ 'reconnect',
+ self.command_reconnect,
+ desc='Disconnect from the remote server if you are '
+ 'currently connected and then connect to it again.',
+ shortdesc='Disconnect and reconnect to the server.')
+ self.register_command(
+ 'disconnect',
+ self.command_disconnect,
+ desc='Disconnect from the remote server.',
+ shortdesc='Disconnect from the server.')
+ self.register_command(
+ 'clear', self.command_clear, shortdesc='Clear the info buffer.')
+ self.register_command(
+ 'last_activity',
+ self.command_last_activity,
+ usage='<jid>',
+ desc='Informs you of the last activity of a JID.',
+ shortdesc='Get the activity of someone.',
+ completion=self.core.completion.last_activity)
self.resize()
self.update_commands()
@@ -151,54 +181,72 @@ class RosterInfoTab(Tab):
def check_blocking(self, features):
if 'urn:xmpp:blocking' in features and not self.core.xmpp.anon:
- self.register_command('block', self.command_block,
- usage='[jid]',
- shortdesc='Prevent a JID from talking to you.',
- completion=self.completion_block)
- self.register_command('unblock', self.command_unblock,
- usage='[jid]',
- shortdesc='Allow a JID to talk to you.',
- completion=self.completion_unblock)
- self.register_command('list_blocks', self.command_list_blocks,
- shortdesc='Show the blocked contacts.')
- self.core.xmpp.del_event_handler('session_start', self.check_blocking)
- self.core.xmpp.add_event_handler('blocked_message', self.on_blocked_message)
+ self.register_command(
+ 'block',
+ self.command_block,
+ usage='[jid]',
+ shortdesc='Prevent a JID from talking to you.',
+ completion=self.completion_block)
+ self.register_command(
+ 'unblock',
+ self.command_unblock,
+ usage='[jid]',
+ shortdesc='Allow a JID to talk to you.',
+ completion=self.completion_unblock)
+ self.register_command(
+ 'list_blocks',
+ self.command_list_blocks,
+ shortdesc='Show the blocked contacts.')
+ self.core.xmpp.del_event_handler('session_start',
+ self.check_blocking)
+ self.core.xmpp.add_event_handler('blocked_message',
+ self.on_blocked_message)
def check_saslexternal(self, features):
if 'urn:xmpp:saslcert:1' in features and not self.core.xmpp.anon:
- self.register_command('certs', self.command_certs,
- desc='List the fingerprints of certificates'
- ' which can connect to your account.',
- shortdesc='List allowed client certs.')
- self.register_command('cert_add', self.command_cert_add,
- desc='Add a client certificate to the authorized ones. '
- 'It must have an unique name and be contained in '
- 'a PEM file. [management] is a boolean indicating'
- ' if a client connected using this certificate can'
- ' manage the certificates itself.',
- shortdesc='Add a client certificate.',
- usage='<name> <certificate path> [management]',
- completion=self.completion_cert_add)
- self.register_command('cert_disable', self.command_cert_disable,
- desc='Remove a certificate from the list '
- 'of allowed ones. Clients currently '
- 'using this certificate will not be '
- 'forcefully disconnected.',
- shortdesc='Disable a certificate',
- usage='<name>')
- self.register_command('cert_revoke', self.command_cert_revoke,
- desc='Remove a certificate from the list '
- 'of allowed ones. Clients currently '
- 'using this certificate will be '
- 'forcefully disconnected.',
- shortdesc='Revoke a certificate',
- usage='<name>')
- self.register_command('cert_fetch', self.command_cert_fetch,
- desc='Retrieve a certificate with its '
- 'name. It will be stored in <path>.',
- shortdesc='Fetch a certificate',
- usage='<name> <path>',
- completion=self.completion_cert_fetch)
+ self.register_command(
+ 'certs',
+ self.command_certs,
+ desc='List the fingerprints of certificates'
+ ' which can connect to your account.',
+ shortdesc='List allowed client certs.')
+ self.register_command(
+ 'cert_add',
+ self.command_cert_add,
+ desc='Add a client certificate to the authorized ones. '
+ 'It must have an unique name and be contained in '
+ 'a PEM file. [management] is a boolean indicating'
+ ' if a client connected using this certificate can'
+ ' manage the certificates itself.',
+ shortdesc='Add a client certificate.',
+ usage='<name> <certificate path> [management]',
+ completion=self.completion_cert_add)
+ self.register_command(
+ 'cert_disable',
+ self.command_cert_disable,
+ desc='Remove a certificate from the list '
+ 'of allowed ones. Clients currently '
+ 'using this certificate will not be '
+ 'forcefully disconnected.',
+ shortdesc='Disable a certificate',
+ usage='<name>')
+ self.register_command(
+ 'cert_revoke',
+ self.command_cert_revoke,
+ desc='Remove a certificate from the list '
+ 'of allowed ones. Clients currently '
+ 'using this certificate will be '
+ 'forcefully disconnected.',
+ shortdesc='Revoke a certificate',
+ usage='<name>')
+ self.register_command(
+ 'cert_fetch',
+ self.command_cert_fetch,
+ desc='Retrieve a certificate with its '
+ 'name. It will be stored in <path>.',
+ shortdesc='Fetch a certificate',
+ usage='<name> <path>',
+ completion=self.completion_cert_fetch)
@property
def selected_row(self):
@@ -209,10 +257,11 @@ class RosterInfoTab(Tab):
"""
/certs
"""
+
def cb(iq):
if iq['type'] == 'error':
- self.core.information('Unable to retrieve the certificate list.',
- 'Error')
+ self.core.information(
+ 'Unable to retrieve the certificate list.', 'Error')
return
certs = []
for item in iq['sasl_certs']['items']:
@@ -222,7 +271,8 @@ class RosterInfoTab(Tab):
if not certs:
return self.core.information('No certificates found', 'Info')
msg = 'Certificates:\n'
- msg += '\n'.join(((' %s%s' % (item[0] + (': ' if item[1] else ''), item[1])) for item in certs))
+ msg += '\n'.join(((' %s%s' % (item[0] + (': ' if item[1] else ''),
+ item[1])) for item in certs))
self.core.information(msg, 'Info')
self.core.xmpp.plugin['xep_0257'].get_certs(callback=cb, timeout=3)
@@ -234,9 +284,11 @@ class RosterInfoTab(Tab):
"""
if not args or len(args) < 2:
return self.core.command.help('cert_add')
+
def cb(iq):
if iq['type'] == 'error':
- self.core.information('Unable to add the certificate.', 'Error')
+ self.core.information('Unable to add the certificate.',
+ 'Error')
else:
self.core.information('Certificate added.', 'Info')
@@ -245,9 +297,11 @@ class RosterInfoTab(Tab):
try:
with open(args[1]) as fd:
crt = fd.read()
- crt = crt.replace(ssl.PEM_FOOTER, '').replace(ssl.PEM_HEADER, '').replace(' ', '').replace('\n', '')
+ crt = crt.replace(ssl.PEM_FOOTER, '').replace(
+ ssl.PEM_HEADER, '').replace(' ', '').replace('\n', '')
except Exception as e:
- self.core.information('Unable to read the certificate: %s' % e, 'Error')
+ self.core.information('Unable to read the certificate: %s' % e,
+ 'Error')
return
if len(args) > 2:
@@ -263,8 +317,8 @@ class RosterInfoTab(Tab):
else:
management = True
- self.core.xmpp.plugin['xep_0257'].add_cert(name, crt, callback=cb,
- allow_management=management)
+ self.core.xmpp.plugin['xep_0257'].add_cert(
+ name, crt, callback=cb, allow_management=management)
def completion_cert_add(self, the_input):
"""
@@ -286,9 +340,11 @@ class RosterInfoTab(Tab):
"""
if not args:
return self.core.command.help('cert_disable')
+
def cb(iq):
if iq['type'] == 'error':
- self.core.information('Unable to disable the certificate.', 'Error')
+ self.core.information('Unable to disable the certificate.',
+ 'Error')
else:
self.core.information('Certificate disabled.', 'Info')
@@ -303,9 +359,11 @@ class RosterInfoTab(Tab):
"""
if not args:
return self.core.command.help('cert_revoke')
+
def cb(iq):
if iq['type'] == 'error':
- self.core.information('Unable to revoke the certificate.', 'Error')
+ self.core.information('Unable to revoke the certificate.',
+ 'Error')
else:
self.core.information('Certificate revoked.', 'Info')
@@ -313,7 +371,6 @@ class RosterInfoTab(Tab):
self.core.xmpp.plugin['xep_0257'].revoke_cert(name, callback=cb)
-
@command_args_parser.quoted(2)
def command_cert_fetch(self, args):
"""
@@ -321,6 +378,7 @@ class RosterInfoTab(Tab):
"""
if not args or len(args) < 2:
return self.core.command.help('cert_fetch')
+
def cb(iq):
if iq['type'] == 'error':
self.core.information('Unable to fetch the certificate.',
@@ -364,11 +422,12 @@ class RosterInfoTab(Tab):
"""
tab = self.core.get_conversation_by_jid(message['from'], False)
if not tab:
- log.debug('Received message from nonexistent tab: %s', message['from'])
+ log.debug('Received message from nonexistent tab: %s',
+ message['from'])
message = '\x19%(info_col)s}Cannot send message to %(jid)s: contact blocked' % {
- 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
- 'jid': message['from'],
- }
+ 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
+ 'jid': message['from'],
+ }
tab.add_message(message)
@command_args_parser.quoted(0, 1)
@@ -386,7 +445,8 @@ class RosterInfoTab(Tab):
def callback(iq):
if iq['type'] == 'error':
- return self.core.information('Could not block %s.' % jid, 'Error')
+ return self.core.information('Could not block %s.' % jid,
+ 'Error')
elif iq['type'] == 'result':
return self.core.information('Blocked %s.' % jid, 'Info')
@@ -398,16 +458,19 @@ class RosterInfoTab(Tab):
"""
if the_input.get_argument_position() == 1:
jids = roster.jids()
- return Completion(the_input.new_completion, jids, 1, '', quotify=False)
+ return Completion(
+ the_input.new_completion, jids, 1, '', quotify=False)
@command_args_parser.quoted(0, 1)
def command_unblock(self, args):
"""
/unblock [jid]
"""
+
def callback(iq):
if iq['type'] == 'error':
- return self.core.information('Could not unblock the contact.', 'Error')
+ return self.core.information('Could not unblock the contact.',
+ 'Error')
elif iq['type'] == 'result':
return self.core.information('Contact unblocked.', 'Info')
@@ -424,6 +487,7 @@ class RosterInfoTab(Tab):
"""
Completion for /unblock
"""
+
def on_result(iq):
if iq['type'] == 'error':
return
@@ -439,9 +503,11 @@ class RosterInfoTab(Tab):
"""
/list_blocks
"""
+
def callback(iq):
if iq['type'] == 'error':
- return self.core.information('Could not retrieve the blocklist.', 'Error')
+ return self.core.information(
+ 'Could not retrieve the blocklist.', 'Error')
s = 'List of blocked JIDs:\n'
items = (str(item) for item in iq['blocklist']['items'])
jids = '\n'.join(items)
@@ -508,25 +574,23 @@ class RosterInfoTab(Tab):
info_width = self.width - roster_width - 1
if display_info:
- self.v_separator.resize(self.height - 1 - tab_win_height,
- 1, 0, roster_width)
- self.information_win.resize(self.height - 1 - tab_win_height
- - contact_win_h,
- info_width, 0, roster_width + 1,
- self.core.information_buffer)
+ self.v_separator.resize(self.height - 1 - tab_win_height, 1, 0,
+ roster_width)
+ self.information_win.resize(
+ self.height - 1 - tab_win_height - contact_win_h, info_width,
+ 0, roster_width + 1, self.core.information_buffer)
if display_contact_win:
y = self.height - tab_win_height - contact_win_h - 1
avatar_width = contact_win_h * 2
self.contact_info_win.resize(contact_win_h,
- info_width - avatar_width,
- y, roster_width + 1)
- self.avatar_win.resize(contact_win_h,
- avatar_width,
- y, self.width - avatar_width)
+ info_width - avatar_width, y,
+ roster_width + 1)
+ self.avatar_win.resize(contact_win_h, avatar_width, y,
+ self.width - avatar_width)
self.roster_win.resize(self.height - 1 - Tab.tab_win_height(),
roster_width, 0, 0)
- self.input.resize(1, self.width, self.height-1, 0)
- self.default_help_message.resize(1, self.width, self.height-1, 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
+ self.default_help_message.resize(1, self.width, self.height - 1, 0)
def completion(self):
# Check if we are entering a command (with the '/' key)
@@ -544,9 +608,10 @@ class RosterInfoTab(Tab):
args = shell_split(text)
n = the_input.get_argument_position()
if n == complete_number:
- if args[n-1] == '' or len(args) < n+1:
+ if args[n - 1] == '' or len(args) < n + 1:
home = os.getenv('HOME') or '/'
- return Completion(the_input.new_completion, [home, '/tmp'], n, quotify=True)
+ return Completion(
+ the_input.new_completion, [home, '/tmp'], n, quotify=True)
path_ = args[n]
if path.isdir(path_):
dir_ = path_
@@ -569,7 +634,8 @@ class RosterInfoTab(Tab):
if not name.startswith('.'):
end_list.append(value)
- return Completion(the_input.new_completion, end_list, n, quotify=True)
+ return Completion(
+ the_input.new_completion, end_list, n, quotify=True)
@command_args_parser.ignored
def command_clear(self):
@@ -578,7 +644,8 @@ class RosterInfoTab(Tab):
"""
self.core.information_buffer.messages = []
self.information_win.rebuild_everything(self.core.information_buffer)
- self.core.information_win.rebuild_everything(self.core.information_buffer)
+ self.core.information_win.rebuild_everything(
+ self.core.information_buffer)
self.refresh()
@command_args_parser.quoted(1)
@@ -586,14 +653,18 @@ class RosterInfoTab(Tab):
"""
/password <password>
"""
+
def callback(iq):
if iq['type'] == 'result':
self.core.information('Password updated', 'Account')
if config.get('password'):
config.silent_set('password', args[0])
else:
- self.core.information('Unable to change the password', 'Account')
- self.core.xmpp.plugin['xep_0077'].change_password(args[0], callback=callback)
+ self.core.information('Unable to change the password',
+ 'Account')
+
+ self.core.xmpp.plugin['xep_0077'].change_password(
+ args[0], callback=callback)
@command_args_parser.quoted(0, 1)
def command_deny(self, args):
@@ -631,7 +702,8 @@ class RosterInfoTab(Tab):
return
jid = safeJID(safeJID(args[0]).bare)
if not str(jid):
- self.core.information('The provided JID (%s) is not valid' % (args[0],), 'Error')
+ self.core.information('The provided JID (%s) is not valid' %
+ (args[0], ), 'Error')
return
if jid in roster and roster[jid].subscription in ('to', 'both'):
return self.core.information('Already subscribed.', 'Roster')
@@ -644,10 +716,12 @@ class RosterInfoTab(Tab):
"""
Set a name for the specified JID in your roster
"""
+
def callback(iq):
if not iq:
self.core.information('The name could not be set.', 'Error')
log.debug('Error in /name:\n%s', iq)
+
if args is None:
return self.core.command.help('name')
jid = safeJID(args[0]).bare
@@ -662,8 +736,12 @@ class RosterInfoTab(Tab):
if 'none' in groups:
groups.remove('none')
subscription = contact.subscription
- self.core.xmpp.update_roster(jid, name=name, groups=groups,
- subscription=subscription, callback=callback)
+ self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=groups,
+ subscription=subscription,
+ callback=callback)
@command_args_parser.quoted(1, 1)
def command_groupadd(self, args):
@@ -712,8 +790,12 @@ class RosterInfoTab(Tab):
self.core.information('The group could not be set.', 'Error')
log.debug('Error in groupadd:\n%s', iq)
- self.core.xmpp.update_roster(jid, name=name, groups=new_groups,
- subscription=subscription, callback=callback)
+ self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ callback=callback)
@command_args_parser.quoted(3)
def command_groupmove(self, args):
@@ -767,8 +849,12 @@ class RosterInfoTab(Tab):
self.core.information('The group could not be set', 'Error')
log.debug('Error in groupmove:\n%s', iq)
- self.core.xmpp.update_roster(jid, name=name, groups=new_groups,
- subscription=subscription, callback=callback)
+ self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ callback=callback)
@command_args_parser.quoted(2)
def command_groupremove(self, args):
@@ -808,8 +894,12 @@ class RosterInfoTab(Tab):
self.core.information('The group could not be set')
log.debug('Error in groupremove:\n%s', iq)
- self.core.xmpp.update_roster(jid, name=name, groups=new_groups,
- subscription=subscription, callback=callback)
+ self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ callback=callback)
@command_args_parser.quoted(0, 1)
def command_remove(self, args):
@@ -842,7 +932,8 @@ class RosterInfoTab(Tab):
else:
filepath = path.join(getenv('HOME'), 'poezio_contacts')
if not path.isfile(filepath):
- self.core.information('The file %s does not exist' % filepath, 'Error')
+ self.core.information('The file %s does not exist' % filepath,
+ 'Error')
return
try:
handle = open(filepath, 'r', encoding='utf-8')
@@ -877,7 +968,8 @@ class RosterInfoTab(Tab):
if roster.export(filepath):
self.core.information('Contacts exported to %s' % filepath, 'Info')
else:
- self.core.information('Failed to export contacts to %s' % filepath, 'Info')
+ self.core.information('Failed to export contacts to %s' % filepath,
+ 'Info')
def completion_remove(self, the_input):
"""
@@ -898,10 +990,13 @@ class RosterInfoTab(Tab):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return Completion(the_input.new_completion, jids, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
- groups = sorted(group for group in roster.groups if group != 'none')
- return Completion(the_input.new_completion, groups, n, '', quotify=True)
+ groups = sorted(
+ group for group in roster.groups if group != 'none')
+ return Completion(
+ the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_groupmove(self, the_input):
@@ -909,7 +1004,8 @@ class RosterInfoTab(Tab):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return Completion(the_input.new_completion, jids, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
contact = roster[args[1]]
if not contact:
@@ -917,10 +1013,12 @@ class RosterInfoTab(Tab):
groups = list(contact.groups)
if 'none' in groups:
groups.remove('none')
- return Completion(the_input.new_completion, groups, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, groups, n, '', quotify=True)
elif n == 3:
groups = sorted(group for group in roster.groups)
- return Completion(the_input.new_completion, groups, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_groupremove(self, the_input):
@@ -928,7 +1026,8 @@ class RosterInfoTab(Tab):
n = the_input.get_argument_position()
if n == 1:
jids = sorted(jid for jid in roster.jids())
- return Completion(the_input.new_completion, jids, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, jids, n, '', quotify=True)
elif n == 2:
contact = roster[args[1]]
if contact is None:
@@ -938,7 +1037,8 @@ class RosterInfoTab(Tab):
groups.remove('none')
except ValueError:
pass
- return Completion(the_input.new_completion, groups, n, '', quotify=True)
+ return Completion(
+ the_input.new_completion, groups, n, '', quotify=True)
return False
def completion_deny(self, the_input):
@@ -946,8 +1046,9 @@ class RosterInfoTab(Tab):
Complete the first argument from the list of the
contact with ask=='subscribe'
"""
- jids = sorted(str(contact.bare_jid) for contact in roster.contacts.values()
- if contact.pending_in)
+ jids = sorted(
+ str(contact.bare_jid) for contact in roster.contacts.values()
+ if contact.pending_in)
return Completion(the_input.new_completion, jids, 1, '', quotify=False)
@command_args_parser.quoted(0, 1)
@@ -976,8 +1077,10 @@ class RosterInfoTab(Tab):
roster.modified()
self.core.xmpp.send_presence(pto=jid, ptype='subscribed')
self.core.xmpp.client_roster.send_last_presence()
- if contact.subscription in ('from', 'none') and not contact.pending_out:
- self.core.xmpp.send_presence(pto=jid, ptype='subscribe', pnick=self.core.own_nick)
+ if contact.subscription in ('from',
+ 'none') and not contact.pending_out:
+ self.core.xmpp.send_presence(
+ pto=jid, ptype='subscribe', pnick=self.core.own_nick)
self.core.information('%s is now authorized' % jid, 'Roster')
@@ -1027,7 +1130,8 @@ class RosterInfoTab(Tab):
success = config.silent_set(option, str(not value))
roster.modified()
if not success:
- self.core.information('Unable to write in the config file', 'Error')
+ self.core.information('Unable to write in the config file',
+ 'Error')
return True
def on_slash(self):
@@ -1035,9 +1139,10 @@ class RosterInfoTab(Tab):
'/' is pressed, we enter "input mode"
"""
curses.curs_set(1)
- self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command)
- self.input.resize(1, self.width, self.height-1, 0)
- self.input.do_command("/") # we add the slash
+ self.input = windows.CommandInput("", self.reset_help_message,
+ self.execute_slash_command)
+ self.input.resize(1, self.width, self.height - 1, 0)
+ self.input.do_command("/") # we add the slash
def reset_help_message(self, _=None):
self.input = self.default_help_message
@@ -1065,13 +1170,15 @@ class RosterInfoTab(Tab):
@refresh_wrapper.conditional
def move_cursor_down(self):
- if isinstance(self.input, windows.Input) and not self.input.history_disabled:
+ if isinstance(self.input,
+ windows.Input) and not self.input.history_disabled:
return
return self.roster_win.move_cursor_down()
@refresh_wrapper.conditional
def move_cursor_up(self):
- if isinstance(self.input, windows.Input) and not self.input.history_disabled:
+ if isinstance(self.input,
+ windows.Input) and not self.input.history_disabled:
return
return self.roster_win.move_cursor_up()
@@ -1156,9 +1263,11 @@ class RosterInfoTab(Tab):
cont = selected_row
res = selected_row.get_highest_priority_resource()
acc = []
- acc.append('Contact: %s (%s)' % (cont.bare_jid, res.presence if res else 'unavailable'))
+ acc.append('Contact: %s (%s)' % (cont.bare_jid, res.presence
+ if res else 'unavailable'))
if res:
- acc.append('%s connected resource%s' % (len(cont), '' if len(cont) == 1 else 's'))
+ acc.append('%s connected resource%s' %
+ (len(cont), '' if len(cont) == 1 else 's'))
acc.append('Current status: %s' % res.status)
if cont.tune:
acc.append('Tune: %s' % common.format_tune_string(cont.tune))
@@ -1167,21 +1276,20 @@ class RosterInfoTab(Tab):
if cont.activity:
acc.append('Activity: %s' % cont.activity)
if cont.gaming:
- acc.append('Game: %s' % (common.format_gaming_string(cont.gaming)))
+ acc.append('Game: %s' %
+ (common.format_gaming_string(cont.gaming)))
msg = '\n'.join(acc)
elif isinstance(selected_row, Resource):
res = selected_row
msg = 'Resource: %s (%s)\nCurrent status: %s\nPriority: %s' % (
- res.jid,
- res.presence,
- res.status,
- res.priority)
+ res.jid, res.presence, res.status, res.priority)
elif isinstance(selected_row, RosterGroup):
rg = selected_row
msg = 'Group: %s [%s/%s] contacts online' % (
- rg.name,
- rg.get_nb_connected_contacts(),
- len(rg),)
+ rg.name,
+ rg.get_nb_connected_contacts(),
+ len(rg),
+ )
else:
msg = None
if msg:
@@ -1210,8 +1318,10 @@ class RosterInfoTab(Tab):
in it.
"""
curses.curs_set(1)
- self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.input = windows.CommandInput("[Search]", self.on_search_terminate,
+ self.on_search_terminate,
+ self.set_roster_filter)
+ self.input.resize(1, self.width, self.height - 1, 0)
self.input.disable_history()
roster.modified()
self.refresh()
@@ -1220,8 +1330,10 @@ class RosterInfoTab(Tab):
@refresh_wrapper.always
def start_search_slow(self):
curses.curs_set(1)
- self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter_slow)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.input = windows.CommandInput("[Search]", self.on_search_terminate,
+ self.on_search_terminate,
+ self.set_roster_filter_slow)
+ self.input.resize(1, self.width, self.height - 1, 0)
self.input.disable_history()
return True
@@ -1248,6 +1360,7 @@ class RosterInfoTab(Tab):
def on_close(self):
return
+
def diffmatch(search, string):
"""
Use difflib and a loop to check if search_pattern can
@@ -1259,10 +1372,12 @@ def diffmatch(search, string):
l = len(search)
ratio = 0.7
for i in range(len(string) - l + 1):
- if difflib.SequenceMatcher(None, search, string[i:i+l]).ratio() >= ratio:
+ if difflib.SequenceMatcher(None, search,
+ string[i:i + l]).ratio() >= ratio:
return True
return False
+
def jid_and_name_match(contact, txt):
"""
Match jid with text precisely
@@ -1276,13 +1391,14 @@ def jid_and_name_match(contact, txt):
return True
return False
+
def jid_and_name_match_slow(contact, txt):
"""
A function used to know if a contact in the roster should
be shown in the roster
"""
if not txt:
- return True # Everything matches when search is empty
+ return True # Everything matches when search is empty
user = safeJID(contact.bare_jid).bare
if diffmatch(txt, user):
return True
diff --git a/poezio/tabs/xmltab.py b/poezio/tabs/xmltab.py
index 65491bf4..223b6667 100644
--- a/poezio/tabs/xmltab.py
+++ b/poezio/tabs/xmltab.py
@@ -25,7 +25,6 @@ from poezio.common import safeJID
class MatchJID(object):
-
def __init__(self, jid, dest=''):
self.jid = jid
self.dest = dest
@@ -46,13 +45,15 @@ class MatchJID(object):
def __repr__(self):
return '%s%s%s' % (self.dest, ': ' if self.dest else '', self.jid)
+
MATCHERS_MAPPINGS = {
- MatchJID: ('JID', repr),
- matcher.MatcherId: ('ID', lambda obj: obj._criteria),
- matcher.MatchXMLMask: ('XMLMask', lambda obj: tostring(obj._criteria)),
- matcher.MatchXPath: ('XPath', lambda obj: obj._criteria)
+ MatchJID: ('JID', repr),
+ matcher.MatcherId: ('ID', lambda obj: obj._criteria),
+ matcher.MatchXMLMask: ('XMLMask', lambda obj: tostring(obj._criteria)),
+ matcher.MatchXPath: ('XPath', lambda obj: obj._criteria)
}
+
class XMLTab(Tab):
def __init__(self, core):
Tab.__init__(self, core)
@@ -68,41 +69,57 @@ class XMLTab(Tab):
self.core_buffer.add_window(self.text_win)
self.default_help_message = windows.HelpText("/ to enter a command")
- self.register_command('close', self.close,
- shortdesc="Close this tab.")
- self.register_command('clear', self.command_clear,
- shortdesc='Clear the current buffer.')
- self.register_command('filter_reset', self.command_filter_reset,
- shortdesc='Reset the stanza filter.')
- self.register_command('filter_id', self.command_filter_id,
- usage='<id>',
- desc='Show only the stanzas with the id <id>.',
- shortdesc='Filter by id.')
- self.register_command('filter_xpath', self.command_filter_xpath,
- usage='<xpath>',
- desc='Show only the stanzas matching the xpath <xpath>.'
- ' Any occurrences of %n will be replaced by jabber:client.',
- shortdesc='Filter by XPath.')
- self.register_command('filter_jid', self.command_filter_jid,
- usage='<jid>',
- desc='Show only the stanzas matching the jid <jid> in from= or to=.',
- shortdesc='Filter by JID.')
- self.register_command('filter_from', self.command_filter_from,
- usage='<jid>',
- desc='Show only the stanzas matching the jid <jid> in from=.',
- shortdesc='Filter by JID from.')
- self.register_command('filter_to', self.command_filter_to,
- usage='<jid>',
- desc='Show only the stanzas matching the jid <jid> in to=.',
- shortdesc='Filter by JID to.')
- self.register_command('filter_xmlmask', self.command_filter_xmlmask,
- usage='<xml mask>',
- desc='Show only the stanzas matching the given xml mask.',
- shortdesc='Filter by xml mask.')
- self.register_command('dump', self.command_dump,
- usage='<filename>',
- desc='Writes the content of the XML buffer into a file.',
- shortdesc='Write in a file.')
+ self.register_command('close', self.close, shortdesc="Close this tab.")
+ self.register_command(
+ 'clear', self.command_clear, shortdesc='Clear the current buffer.')
+ self.register_command(
+ 'filter_reset',
+ self.command_filter_reset,
+ shortdesc='Reset the stanza filter.')
+ self.register_command(
+ 'filter_id',
+ self.command_filter_id,
+ usage='<id>',
+ desc='Show only the stanzas with the id <id>.',
+ shortdesc='Filter by id.')
+ self.register_command(
+ 'filter_xpath',
+ self.command_filter_xpath,
+ usage='<xpath>',
+ desc='Show only the stanzas matching the xpath <xpath>.'
+ ' Any occurrences of %n will be replaced by jabber:client.',
+ shortdesc='Filter by XPath.')
+ self.register_command(
+ 'filter_jid',
+ self.command_filter_jid,
+ usage='<jid>',
+ desc=
+ 'Show only the stanzas matching the jid <jid> in from= or to=.',
+ shortdesc='Filter by JID.')
+ self.register_command(
+ 'filter_from',
+ self.command_filter_from,
+ usage='<jid>',
+ desc='Show only the stanzas matching the jid <jid> in from=.',
+ shortdesc='Filter by JID from.')
+ self.register_command(
+ 'filter_to',
+ self.command_filter_to,
+ usage='<jid>',
+ desc='Show only the stanzas matching the jid <jid> in to=.',
+ shortdesc='Filter by JID to.')
+ self.register_command(
+ 'filter_xmlmask',
+ self.command_filter_xmlmask,
+ usage='<xml mask>',
+ desc='Show only the stanzas matching the given xml mask.',
+ shortdesc='Filter by xml mask.')
+ self.register_command(
+ 'dump',
+ self.command_dump,
+ usage='<filename>',
+ desc='Writes the content of the XML buffer into a file.',
+ shortdesc='Write in a file.')
self.input = self.default_help_message
self.key_func['^T'] = self.close
self.key_func['^I'] = self.completion
@@ -120,8 +137,10 @@ class XMLTab(Tab):
self.filter_type = ''
self.filter = ''
return
- filter_types = map(lambda x: MATCHERS_MAPPINGS[type(x)][0], self.filters)
- filter_strings = map(lambda x: MATCHERS_MAPPINGS[type(x)][1](x), self.filters)
+ filter_types = map(lambda x: MATCHERS_MAPPINGS[type(x)][0],
+ self.filters)
+ filter_strings = map(lambda x: MATCHERS_MAPPINGS[type(x)][1](x),
+ self.filters)
self.filter_type = ','.join(filter_types)
self.filter = ','.join(filter_strings)
@@ -138,7 +157,8 @@ class XMLTab(Tab):
new_messages = []
for msg in messages:
try:
- if msg.txt.strip() and self.match_stanza(ElementBase(ET.fromstring(clean_text(msg.txt)))):
+ if msg.txt.strip() and self.match_stanza(
+ ElementBase(ET.fromstring(clean_text(msg.txt)))):
new_messages.append(msg)
except ET.ParseError:
log.debug('Malformed XML : %s', msg.txt, exc_info=True)
@@ -212,7 +232,9 @@ class XMLTab(Tab):
def command_filter_xpath(self, xpath):
"""/filter_xpath <xpath>"""
try:
- self.update_filters(matcher.MatchXPath(xpath.replace('%n', self.core.xmpp.default_ns)))
+ self.update_filters(
+ matcher.MatchXPath(
+ xpath.replace('%n', self.core.xmpp.default_ns)))
self.refresh()
except:
self.core.information('Invalid XML Path', 'Error')
@@ -239,22 +261,25 @@ class XMLTab(Tab):
xml = self.filtered_buffer.messages[:]
else:
xml = self.core_buffer.messages[:]
- text = '\n'.join(('%s %s %s' % (msg.str_time, msg.nickname, clean_text(msg.txt)) for msg in xml))
+ text = '\n'.join(('%s %s %s' % (msg.str_time, msg.nickname,
+ clean_text(msg.txt)) for msg in xml))
filename = os.path.expandvars(os.path.expanduser(args[0]))
try:
with open(filename, 'w') as fd:
fd.write(text)
except Exception as e:
- self.core.information('Could not write the XML dump: %s' % e, 'Error')
+ self.core.information('Could not write the XML dump: %s' % e,
+ 'Error')
def on_slash(self):
"""
'/' is pressed, activate the input
"""
curses.curs_set(1)
- self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command)
- self.input.resize(1, self.width, self.height-1, 0)
- self.input.do_command("/") # we add the slash
+ self.input = windows.CommandInput("", self.reset_help_message,
+ self.execute_slash_command)
+ self.input.resize(1, self.width, self.height - 1, 0)
+ self.input.do_command("/") # we add the slash
@refresh_wrapper.always
def reset_help_message(self, _=None):
@@ -266,10 +291,10 @@ class XMLTab(Tab):
return True
def on_scroll_up(self):
- return self.text_win.scroll_up(self.text_win.height-1)
+ return self.text_win.scroll_up(self.text_win.height - 1)
def on_scroll_down(self):
- return self.text_win.scroll_down(self.text_win.height-1)
+ return self.text_win.scroll_down(self.text_win.height - 1)
@command_args_parser.ignored
def command_clear(self):
@@ -314,11 +339,9 @@ class XMLTab(Tab):
self.text_win.resize(self.height - info_win_size - tab_win_height - 2,
self.width, 0, 0)
self.text_win.rebuild_everything(self.core_buffer)
- self.info_header.resize(1, self.width,
- self.height - 2 - info_win_size
- - tab_win_height,
- 0)
- self.input.resize(1, self.width, self.height-1, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - info_win_size - tab_win_height, 0)
+ self.input.resize(1, self.width, self.height - 1, 0)
def refresh(self):
if self.need_resize:
@@ -350,9 +373,10 @@ class XMLTab(Tab):
self.core.xml_tab = False
def on_info_win_size_changed(self):
- if self.core.information_win_size >= self.height-3:
+ if self.core.information_win_size >= self.height - 3:
return
- self.text_win.resize(self.height-2-self.core.information_win_size - Tab.tab_win_height(), self.width, 0, 0)
- self.info_header.resize(1, self.width, self.height-2-self.core.information_win_size - Tab.tab_win_height(), 0)
-
-
+ self.text_win.resize(self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), self.width, 0, 0)
+ self.info_header.resize(
+ 1, self.width, self.height - 2 - self.core.information_win_size -
+ Tab.tab_win_height(), 0)
diff --git a/poezio/text_buffer.py b/poezio/text_buffer.py
index 13eca399..3e6732cf 100644
--- a/poezio/text_buffer.py
+++ b/poezio/text_buffer.py
@@ -15,14 +15,26 @@ from datetime import datetime
from poezio.config import config
from poezio.theming import get_theme, dump_tuple
+
class Message:
__slots__ = ('txt', 'nick_color', 'time', 'str_time', 'nickname', 'user',
'identifier', 'highlight', 'me', 'old_message', 'revisions',
'jid', 'ack')
- def __init__(self, txt, time, nickname, nick_color, history, user,
- identifier, str_time=None, highlight=False,
- old_message=None, revisions=0, jid=None, ack=0):
+ def __init__(self,
+ txt,
+ time,
+ nickname,
+ nick_color,
+ history,
+ user,
+ identifier,
+ str_time=None,
+ highlight=False,
+ old_message=None,
+ revisions=0,
+ jid=None,
+ ack=0):
"""
Create a new Message object with parameters, check for /me messages,
and delayed messages
@@ -35,8 +47,9 @@ class Message:
else:
me = False
if history:
- txt = txt.replace('\x19o', '\x19o\x19%s}' %
- dump_tuple(get_theme().COLOR_LOG_MSG))
+ txt = txt.replace(
+ '\x19o',
+ '\x19o\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG))
str_time = time.strftime("%Y-%m-%d %H:%M:%S")
else:
if str_time is None:
@@ -87,17 +100,21 @@ class Message:
rev -= 1
return ''.join(acc)
+
class CorrectionError(Exception):
pass
+
class AckError(Exception):
pass
+
class TextBuffer(object):
"""
This class just keep trace of messages, in a list with various
information and attributes.
"""
+
def __init__(self, messages_nb_limit=None):
if messages_nb_limit is None:
@@ -117,15 +134,33 @@ class TextBuffer(object):
def last_message(self):
return self.messages[-1] if self.messages else None
- def add_message(self, txt, time=None, nickname=None,
- nick_color=None, history=None, user=None, highlight=False,
- identifier=None, str_time=None, jid=None, ack=0):
+ def add_message(self,
+ txt,
+ time=None,
+ nickname=None,
+ nick_color=None,
+ history=None,
+ user=None,
+ highlight=False,
+ identifier=None,
+ str_time=None,
+ jid=None,
+ ack=0):
"""
Create a message and add it to the text buffer
"""
- msg = Message(txt, time, nickname, nick_color, history, user,
- identifier, str_time=str_time, highlight=highlight,
- jid=jid, ack=ack)
+ msg = Message(
+ txt,
+ time,
+ nickname,
+ nick_color,
+ history,
+ user,
+ identifier,
+ str_time=str_time,
+ highlight=highlight,
+ jid=jid,
+ ack=ack)
self.messages.append(msg)
while len(self.messages) > self._messages_nb_limit:
@@ -134,12 +169,14 @@ class TextBuffer(object):
ret_val = 0
show_timestamps = config.get('show_timestamps')
nick_size = config.get('max_nick_length')
- for window in self._windows: # make the associated windows
- # build the lines from the new message
- nb = window.build_new_message(msg, history=history,
- highlight=highlight,
- timestamp=show_timestamps,
- nick_size=nick_size)
+ for window in self._windows: # make the associated windows
+ # build the lines from the new message
+ nb = window.build_new_message(
+ msg,
+ history=history,
+ highlight=highlight,
+ timestamp=show_timestamps,
+ nick_size=nick_size)
if ret_val == 0:
ret_val = nb
if window.pos != 0:
@@ -151,7 +188,7 @@ class TextBuffer(object):
"""
Find a message in the text buffer from its message id
"""
- for i in range(len(self.messages) -1, -1, -1):
+ for i in range(len(self.messages) - 1, -1, -1):
msg = self.messages[i]
if msg.identifier == old_id:
return i
@@ -175,16 +212,23 @@ class TextBuffer(object):
return
msg = self.messages[i]
if msg.jid != jid:
- raise AckError('Wrong JID for message id %s (was %s, expected %s)' %
- (old_id, msg.jid, jid))
+ raise AckError(
+ 'Wrong JID for message id %s (was %s, expected %s)' %
+ (old_id, msg.jid, jid))
msg.ack = value
if append:
msg.txt += append
return msg
- def modify_message(self, txt, old_id, new_id, highlight=False,
- time=None, user=None, jid=None):
+ def modify_message(self,
+ txt,
+ old_id,
+ new_id,
+ highlight=False,
+ time=None,
+ user=None,
+ jid=None):
"""
Correct a message in a text buffer.
"""
@@ -192,30 +236,39 @@ class TextBuffer(object):
i = self._find_message(old_id)
if i == -1:
- log.debug('Message %s not found in text_buffer, abort replacement.',
- old_id)
+ log.debug(
+ 'Message %s not found in text_buffer, abort replacement.',
+ old_id)
raise CorrectionError("nothing to replace")
msg = self.messages[i]
if msg.user and msg.user is not user:
raise CorrectionError("Different users")
- elif len(msg.str_time) > 8: # ugly
+ elif len(msg.str_time) > 8: # ugly
raise CorrectionError("Delayed message")
elif not msg.user and (msg.jid is None or jid is None):
raise CorrectionError('Could not check the '
'identity of the sender')
elif not msg.user and msg.jid != jid:
raise CorrectionError('Messages %s and %s have not been '
- 'sent by the same fullJID' %
- (old_id, new_id))
+ 'sent by the same fullJID' % (old_id,
+ new_id))
if not time:
time = msg.time
- message = Message(txt, time, msg.nickname, msg.nick_color, None,
- msg.user, new_id, highlight=highlight,
- old_message=msg, revisions=msg.revisions + 1,
- jid=jid)
+ message = Message(
+ txt,
+ time,
+ msg.nickname,
+ msg.nick_color,
+ None,
+ msg.user,
+ new_id,
+ highlight=highlight,
+ old_message=msg,
+ revisions=msg.revisions + 1,
+ jid=jid)
self.messages[i] = message
log.debug('Replacing message %s with %s.', old_id, new_id)
return message
diff --git a/poezio/theming.py b/poezio/theming.py
index d007e463..a3721f08 100755
--- a/poezio/theming.py
+++ b/poezio/theming.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Define the variables (colors and some other stuff) that are
used when drawing the interface.
@@ -80,6 +79,7 @@ from os import path
from importlib import machinery
finder = machinery.PathFinder()
+
class Theme(object):
"""
The theme class, from which all themes should inherit.
@@ -90,6 +90,7 @@ class Theme(object):
needs. Create a new theme and share it if you think it can be useful
for others.
"""
+
@classmethod
def color_role(cls, role):
role_mapping = {
@@ -104,34 +105,34 @@ class Theme(object):
@classmethod
def char_affiliation(cls, affiliation):
affiliation_mapping = {
- 'owner': cls.CHAR_AFFILIATION_OWNER,
- 'admin': cls.CHAR_AFFILIATION_ADMIN,
- 'member': cls.CHAR_AFFILIATION_MEMBER,
- 'none': cls.CHAR_AFFILIATION_NONE
+ 'owner': cls.CHAR_AFFILIATION_OWNER,
+ 'admin': cls.CHAR_AFFILIATION_ADMIN,
+ 'member': cls.CHAR_AFFILIATION_MEMBER,
+ 'none': cls.CHAR_AFFILIATION_NONE
}
return affiliation_mapping.get(affiliation, cls.CHAR_AFFILIATION_NONE)
@classmethod
def color_show(cls, show):
show_mapping = {
- 'xa': cls.COLOR_STATUS_XA,
- 'none': cls.COLOR_STATUS_NONE,
- 'dnd': cls.COLOR_STATUS_DND,
- 'away': cls.COLOR_STATUS_AWAY,
- 'chat': cls.COLOR_STATUS_CHAT,
- '': cls.COLOR_STATUS_ONLINE,
- 'available': cls.COLOR_STATUS_ONLINE,
- 'unavailable': cls.COLOR_STATUS_UNAVAILABLE,
+ 'xa': cls.COLOR_STATUS_XA,
+ 'none': cls.COLOR_STATUS_NONE,
+ 'dnd': cls.COLOR_STATUS_DND,
+ 'away': cls.COLOR_STATUS_AWAY,
+ 'chat': cls.COLOR_STATUS_CHAT,
+ '': cls.COLOR_STATUS_ONLINE,
+ 'available': cls.COLOR_STATUS_ONLINE,
+ 'unavailable': cls.COLOR_STATUS_UNAVAILABLE,
}
return show_mapping.get(show, cls.COLOR_STATUS_NONE)
@classmethod
def char_subscription(cls, sub, keep='incomplete'):
sub_mapping = {
- 'from': cls.CHAR_ROSTER_FROM,
- 'both': cls.CHAR_ROSTER_BOTH,
- 'none': cls.CHAR_ROSTER_NONE,
- 'to': cls.CHAR_ROSTER_TO,
+ 'from': cls.CHAR_ROSTER_FROM,
+ 'both': cls.CHAR_ROSTER_BOTH,
+ 'none': cls.CHAR_ROSTER_NONE,
+ 'to': cls.CHAR_ROSTER_TO,
}
if keep == 'incomplete' and sub == 'both':
return ''
@@ -141,7 +142,7 @@ class Theme(object):
# Message text color
COLOR_NORMAL_TEXT = (-1, -1)
- COLOR_INFORMATION_TEXT = (5, -1) # TODO
+ COLOR_INFORMATION_TEXT = (5, -1) # TODO
COLOR_WARNING_TEXT = (1, -1)
# Color of the commands in the help message
@@ -181,7 +182,6 @@ class Theme(object):
CHAR_AFFILIATION_MEMBER = '+'
CHAR_AFFILIATION_NONE = '-'
-
# XML Tab
CHAR_XML_IN = 'IN '
CHAR_XML_OUT = 'OUT'
@@ -238,42 +238,132 @@ class Theme(object):
# Setting more colors makes it harder to have two nicks with the same color,
# avoiding confusions.
LIST_COLOR_NICKNAMES = [
- (1, -1), (2, -1), (3, -1), (4, -1), (5, -1), (6, -1), (9, -1),
- (10, -1), (11, -1), (12, -1), (13, -1), (14, -1), (19, -1),
- (20, -1), (21, -1), (22, -1), (23, -1), (24, -1), (25, -1),
- (26, -1), (27, -1), (28, -1), (29, -1), (30, -1), (31, -1),
- (32, -1), (33, -1), (34, -1), (35, -1), (36, -1), (37, -1),
- (38, -1), (39, -1), (40, -1), (41, -1), (42, -1), (43, -1),
- (44, -1), (45, -1), (46, -1), (47, -1), (48, -1), (49, -1),
- (50, -1), (51, -1), (54, -1), (55, -1), (56, -1), (57, -1),
- (58, -1), (60, -1), (61, -1), (62, -1), (63, -1), (64, -1),
- (65, -1), (66, -1), (67, -1), (68, -1), (69, -1), (70, -1),
- (71, -1), (72, -1), (73, -1), (74, -1), (75, -1), (76, -1),
- (77, -1), (78, -1), (79, -1), (80, -1), (81, -1), (82, -1),
- (83, -1), (84, -1), (85, -1), (86, -1), (87, -1), (88, -1),
- (89, -1), (90, -1), (91, -1), (92, -1), (93, -1), (94, -1),
- (95, -1), (96, -1), (97, -1), (98, -1), (99, -1), (100, -1),
- (101, -1), (103, -1), (104, -1), (105, -1), (106, -1), (107, -1),
- (108, -1), (109, -1), (110, -1), (111, -1), (112, -1), (113, -1),
- (114, -1), (115, -1), (116, -1), (117, -1), (118, -1), (119, -1),
- (120, -1), (121, -1), (122, -1), (123, -1), (124, -1), (125, -1),
- (126, -1), (127, -1), (128, -1), (129, -1), (130, -1), (131, -1),
- (132, -1), (133, -1), (134, -1), (135, -1), (136, -1), (137, -1),
- (138, -1), (139, -1), (140, -1), (141, -1), (142, -1), (143, -1),
- (144, -1), (145, -1), (146, -1), (147, -1), (148, -1), (149, -1),
- (150, -1), (151, -1), (152, -1), (153, -1), (154, -1), (155, -1),
- (156, -1), (157, -1), (158, -1), (159, -1), (160, -1), (161, -1),
- (162, -1), (163, -1), (164, -1), (165, -1), (166, -1), (167, -1),
- (168, -1), (169, -1), (170, -1), (171, -1), (172, -1), (173, -1),
- (174, -1), (175, -1), (176, -1), (177, -1), (178, -1), (179, -1),
- (180, -1), (181, -1), (182, -1), (183, -1), (184, -1), (185, -1),
- (186, -1), (187, -1), (188, -1), (189, -1), (190, -1), (191, -1),
- (192, -1), (193, -1), (196, -1), (197, -1), (198, -1), (199, -1),
- (200, -1), (201, -1), (202, -1), (203, -1), (204, -1), (205, -1),
- (206, -1), (207, -1), (208, -1), (209, -1), (210, -1), (211, -1),
- (212, -1), (213, -1), (214, -1), (215, -1), (216, -1), (217, -1),
- (218, -1), (219, -1), (220, -1), (221, -1), (222, -1), (223, -1),
- (224, -1), (225, -1), (226, -1), (227, -1)]
+ (1, -1), (2, -1), (3, -1), (4, -1), (5, -1), (6, -1), (9, -1),
+ (10, -1), (11, -1), (12, -1), (13, -1), (14, -1), (19, -1), (20, -1),
+ (21, -1), (22, -1), (23, -1), (24, -1), (25, -1), (26, -1), (27, -1),
+ (28, -1), (29, -1), (30, -1), (31, -1), (32, -1), (33, -1), (34, -1),
+ (35, -1), (36, -1), (37, -1), (38, -1), (39, -1), (40, -1), (41, -1),
+ (42, -1), (43, -1), (44, -1), (45, -1), (46, -1), (47, -1), (48, -1),
+ (49, -1), (50, -1), (51, -1), (54, -1), (55, -1), (56, -1), (57, -1),
+ (58, -1), (60, -1), (61, -1), (62, -1), (63, -1), (64, -1), (65, -1),
+ (66, -1), (67, -1), (68, -1), (69, -1), (70, -1), (71, -1), (72, -1),
+ (73, -1), (74, -1), (75, -1), (76, -1), (77, -1), (78, -1), (79, -1),
+ (80, -1), (81, -1), (82, -1), (83, -1), (84, -1), (85, -1), (86, -1),
+ (87, -1), (88, -1), (89, -1), (90, -1), (91, -1), (92, -1), (93, -1),
+ (94, -1), (95, -1), (96, -1), (97, -1), (98, -1), (99, -1), (100, -1),
+ (101, -1), (103, -1), (104, -1), (105, -1), (106, -1), (107, -1), (108,
+ -1),
+ (109, -1), (110, -1), (111, -1), (112, -1), (113, -1), (114, -1), (115,
+ -1),
+ (116, -1), (117, -1), (118, -1), (119, -1), (120, -1), (121, -1), (122,
+ -1),
+ (123, -1), (124, -1), (125, -1), (126, -1), (127, -1), (128, -1), (129,
+ -1),
+ (130,
+ -1), (131,
+ -1), (132,
+ -1), (133,
+ -1), (134,
+ -1), (135,
+ -1), (136,
+ -1), (137,
+ -1), (138,
+ -1), (139,
+ -1), (140,
+ -1), (141,
+ -1),
+ (142,
+ -1), (143,
+ -1), (144,
+ -1), (145,
+ -1), (146,
+ -1), (147,
+ -1), (148,
+ -1), (149,
+ -1), (150,
+ -1), (151,
+ -1), (152,
+ -1), (153,
+ -1),
+ (154,
+ -1), (155,
+ -1), (156,
+ -1), (157,
+ -1), (158,
+ -1), (159,
+ -1), (160,
+ -1), (161,
+ -1), (162,
+ -1), (163,
+ -1), (164,
+ -1), (165,
+ -1),
+ (166,
+ -1), (167,
+ -1), (168,
+ -1), (169,
+ -1), (170,
+ -1), (171,
+ -1), (172,
+ -1), (173,
+ -1), (174,
+ -1), (175,
+ -1), (176,
+ -1), (177,
+ -1),
+ (178,
+ -1), (179,
+ -1), (180,
+ -1), (181,
+ -1), (182,
+ -1), (183,
+ -1), (184,
+ -1), (185,
+ -1), (186,
+ -1), (187,
+ -1), (188,
+ -1), (189,
+ -1),
+ (190,
+ -1), (191,
+ -1), (192,
+ -1), (193,
+ -1), (196,
+ -1), (197,
+ -1), (198,
+ -1), (199,
+ -1), (200,
+ -1), (201,
+ -1), (202,
+ -1), (203,
+ -1),
+ (204,
+ -1), (205,
+ -1), (206,
+ -1), (207,
+ -1), (208,
+ -1), (209,
+ -1), (210,
+ -1), (211,
+ -1), (212,
+ -1), (213,
+ -1), (214,
+ -1), (215,
+ -1),
+ (216,
+ -1), (217,
+ -1), (218,
+ -1), (219,
+ -1), (220,
+ -1), (221,
+ -1), (222,
+ -1), (223,
+ -1), (224,
+ -1), (225,
+ -1), (226,
+ -1), (227,
+ -1)
+ ]
# This is your own nickname
COLOR_OWN_NICK = (254, -1)
@@ -346,19 +436,20 @@ class Theme(object):
# Info messages color (the part before the ">")
INFO_COLORS = {
- 'info': (5, -1),
- 'error': (16, 1),
- 'warning': (1, -1),
- 'roster': (2, -1),
- 'help': (10, -1),
- 'headline': (11, -1, 'b'),
- 'tune': (6, -1),
- 'gaming': (6, -1),
- 'mood': (2, -1),
- 'activity': (3, -1),
- 'default': (7, -1),
+ 'info': (5, -1),
+ 'error': (16, 1),
+ 'warning': (1, -1),
+ 'roster': (2, -1),
+ 'help': (10, -1),
+ 'headline': (11, -1, 'b'),
+ 'tune': (6, -1),
+ 'gaming': (6, -1),
+ 'mood': (2, -1),
+ 'activity': (3, -1),
+ 'default': (7, -1),
}
+
# This is the default theme object, used if no theme is defined in the conf
theme = Theme()
@@ -369,37 +460,36 @@ theme = Theme()
curses_colors_dict = {}
table_256_to_16 = [
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
- 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
- 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
- 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
- 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
- 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
- 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
- 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
- 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
- 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
- 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
- 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
- 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
- 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
- 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 4, 4, 4, 12, 12,
+ 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
+ 14, 12, 10, 10, 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, 2,
+ 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10,
+ 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2,
+ 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1,
+ 5, 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, 3, 7, 12, 12, 10,
+ 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
+ 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10,
+ 10, 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9,
+ 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0,
+ 0, 0, 0, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
]
load_path = []
+
def color_256_to_16(color):
if color == -1:
return color
return table_256_to_16[color]
+
def dump_tuple(tup):
"""
Dump a tuple to a string of fg,bg,attr (optional)
"""
return ','.join(str(i) for i in tup)
+
def read_tuple(_str):
"""
Read a tuple dumped with dump_tumple
@@ -408,6 +498,7 @@ def read_tuple(_str):
char = attrs[2] if len(attrs) > 2 else '\0'
return (int(attrs[0]), int(attrs[1])), char
+
@functools.lru_cache(maxsize=128)
def to_curses_attr(color_tuple):
"""
@@ -451,31 +542,30 @@ def to_curses_attr(color_tuple):
curses_pair = curses_pair | curses.A_BLINK
return curses_pair
+
def get_theme():
"""
Returns the current theme
"""
return theme
+
def update_themes_dir(option=None, value=None):
global load_path
load_path = []
# import from the git sources
default_dir = path.join(
- path.dirname(path.dirname(__file__)),
- 'data/themes')
+ path.dirname(path.dirname(__file__)), 'data/themes')
if path.exists(default_dir):
load_path.append(default_dir)
# import from the user-defined prefs
themes_dir = path.expanduser(
- value or
- config.get('themes_dir') or
- path.join(os.environ.get('XDG_DATA_HOME') or
- path.join(os.environ.get('HOME'), '.local', 'share'),
- 'poezio', 'themes')
- )
+ value or config.get('themes_dir') or path.join(
+ os.environ.get('XDG_DATA_HOME')
+ or path.join(os.environ.get('HOME'), '.local', 'share'), 'poezio',
+ 'themes'))
try:
os.makedirs(themes_dir)
except OSError as e:
@@ -497,6 +587,7 @@ def update_themes_dir(option=None, value=None):
log.debug('Theme load path: %s', load_path)
+
def reload_theme():
theme_name = config.get('theme')
global theme
@@ -522,6 +613,7 @@ def reload_theme():
else:
return 'No theme present in the theme file'
+
if __name__ == '__main__':
# Display some nice text with nice colors
s = curses.initscr()
@@ -539,4 +631,3 @@ if __name__ == '__main__':
finally:
curses.endwin()
print()
-
diff --git a/poezio/timed_events.py b/poezio/timed_events.py
index 8ce94407..f203bf19 100644
--- a/poezio/timed_events.py
+++ b/poezio/timed_events.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Timed events are the standard way to schedule events for later in poezio.
@@ -15,11 +14,13 @@ Once created, they must be added to the list of checked events with
import datetime
+
class DelayedEvent(object):
"""
A TimedEvent, but with the date calculated from now + a delay in seconds.
Use it if you want an event to happen in, e.g. 6 seconds.
"""
+
def __init__(self, delay, callback, *args):
"""
Create a new DelayedEvent.
@@ -34,12 +35,14 @@ class DelayedEvent(object):
# An asyncio handler, as returned by call_later() or call_at()
self.handler = None
+
class TimedEvent(DelayedEvent):
"""
An event with a callback that is called when the specified time is passed.
The callback and its arguments should be passed as the lasts arguments.
"""
+
def __init__(self, date, callback, *args):
"""
Create a new timed event.
diff --git a/poezio/user.py b/poezio/user.py
index 0cf32f79..68d52493 100644
--- a/poezio/user.py
+++ b/poezio/user.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Define the user class.
An user is a MUC participant, not a roster contact (see contact.py)
@@ -20,22 +19,26 @@ from poezio.theming import get_theme
import logging
log = logging.getLogger(__name__)
-ROLE_DICT = {
- '':0,
- 'none':0,
- 'visitor':1,
- 'participant':2,
- 'moderator':3
- }
+ROLE_DICT = {'': 0, 'none': 0, 'visitor': 1, 'participant': 2, 'moderator': 3}
+
class User(object):
"""
keep trace of an user in a Room
"""
- __slots__ = ('last_talked', 'jid', 'chatstate', 'affiliation', 'show', 'status', 'role', 'nick', 'color')
-
- def __init__(self, nick, affiliation, show, status, role, jid, deterministic=True, color=''):
- self.last_talked = datetime(1, 1, 1) # The oldest possible time
+ __slots__ = ('last_talked', 'jid', 'chatstate', 'affiliation', 'show',
+ 'status', 'role', 'nick', 'color')
+
+ def __init__(self,
+ nick,
+ affiliation,
+ show,
+ status,
+ role,
+ jid,
+ deterministic=True,
+ color=''):
+ self.last_talked = datetime(1, 1, 1) # The oldest possible time
self.update(affiliation, show, status, role)
self.change_nick(nick)
if color != '':
diff --git a/poezio/windows/__init__.py b/poezio/windows/__init__.py
index 56657433..68250906 100644
--- a/poezio/windows/__init__.py
+++ b/poezio/windows/__init__.py
@@ -20,11 +20,13 @@ from poezio.windows.roster_win import RosterWin, ContactInfoWin
from poezio.windows.text_win import TextWin, XMLTextWin
from poezio.windows.image import ImageWin
-__all__ = ['Win', 'FormWin', 'BookmarksWin', 'Dialog', 'GlobalInfoBar',
- 'VerticalGlobalInfoBar', 'InfoWin', 'PrivateInfoWin', 'XMLInfoWin',
- 'MucListInfoWin', 'ConversationInfoWin', 'MucInfoWin',
- 'DynamicConversationInfoWin', 'ConversationStatusMessageWin',
- 'BookmarksInfoWin', 'ConfirmStatusWin', 'HelpText', 'Input',
- 'HistoryInput', 'MessageInput', 'CommandInput', 'ListWin',
- 'ColumnHeaderWin', 'VerticalSeparator', 'UserList', 'Topic',
- 'RosterWin', 'ContactInfoWin', 'TextWin', 'XMLTextWin', 'ImageWin']
+__all__ = [
+ 'Win', 'FormWin', 'BookmarksWin', 'Dialog', 'GlobalInfoBar',
+ 'VerticalGlobalInfoBar', 'InfoWin', 'PrivateInfoWin', 'XMLInfoWin',
+ 'MucListInfoWin', 'ConversationInfoWin', 'MucInfoWin',
+ 'DynamicConversationInfoWin', 'ConversationStatusMessageWin',
+ 'BookmarksInfoWin', 'ConfirmStatusWin', 'HelpText', 'Input',
+ 'HistoryInput', 'MessageInput', 'CommandInput', 'ListWin',
+ 'ColumnHeaderWin', 'VerticalSeparator', 'UserList', 'Topic', 'RosterWin',
+ 'ContactInfoWin', 'TextWin', 'XMLTextWin', 'ImageWin'
+]
diff --git a/poezio/windows/base_wins.py b/poezio/windows/base_wins.py
index 8813d8f7..f3970ff5 100644
--- a/poezio/windows/base_wins.py
+++ b/poezio/windows/base_wins.py
@@ -22,6 +22,7 @@ FORMAT_CHAR = '\x19'
# I guess. But maybe we can find better chars that are even less risky.
format_chars = '\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18'
+
class DummyWin(object):
def __getattribute__(self, name):
if name != '__bool__':
@@ -103,7 +104,7 @@ class Win(object):
has_italic = hasattr(curses, 'A_ITALIC')
while next_attr_char != -1 and text:
if next_attr_char + 1 < len(text):
- attr_char = text[next_attr_char+1].lower()
+ attr_char = text[next_attr_char + 1].lower()
else:
attr_char = str()
if next_attr_char != 0:
@@ -116,8 +117,10 @@ class Win(object):
self._win.attron(curses.A_BOLD)
elif attr_char == 'i' and has_italic:
self._win.attron(curses.A_ITALIC)
- if (attr_char in string.digits or attr_char == '-') and attr_char != '':
- color_str = text[next_attr_char+1:text.find('}', next_attr_char)]
+ if (attr_char in string.digits
+ or attr_char == '-') and attr_char != '':
+ color_str = text[next_attr_char + 1:text.find(
+ '}', next_attr_char)]
if ',' in color_str:
tup, char = read_tuple(color_str)
self._win.attron(to_curses_attr(tup))
@@ -136,9 +139,9 @@ class Win(object):
self._win.attroff(curses.A_BOLD)
elif color_str:
self._win.attron(to_curses_attr((int(color_str), -1)))
- text = text[next_attr_char+len(color_str)+2:]
+ text = text[next_attr_char + len(color_str) + 2:]
else:
- text = text[next_attr_char+2:]
+ text = text[next_attr_char + 2:]
next_attr_char = text.find(FORMAT_CHAR)
self.addstr(text)
@@ -149,6 +152,6 @@ class Win(object):
(y, x) = self._win.getyx()
size = self.width - x
if color:
- self.addnstr(' '*size, size, to_curses_attr(color))
+ self.addnstr(' ' * size, size, to_curses_attr(color))
else:
- self.addnstr(' '*size, size)
+ self.addnstr(' ' * size, size)
diff --git a/poezio/windows/bookmark_forms.py b/poezio/windows/bookmark_forms.py
index 5fbc3858..5f5d581a 100644
--- a/poezio/windows/bookmark_forms.py
+++ b/poezio/windows/bookmark_forms.py
@@ -10,6 +10,7 @@ from poezio.windows.data_forms import FieldInput, FieldInputMixin
from poezio.theming import to_curses_attr, get_theme
from poezio.common import safeJID
+
class BookmarkJIDInput(FieldInput, Input):
def __init__(self, field):
FieldInput.__init__(self, field)
@@ -29,6 +30,7 @@ class BookmarkJIDInput(FieldInput, Input):
def get_help_message(self):
return 'Edit the text'
+
class BookmarkMethodInput(FieldInputMixin):
def __init__(self, field):
FieldInput.__init__(self, field)
@@ -42,7 +44,7 @@ class BookmarkMethodInput(FieldInputMixin):
if self.val_pos > 0:
self.val_pos -= 1
elif key == 'KEY_RIGHT':
- if self.val_pos < len(self.options)-1:
+ if self.val_pos < len(self.options) - 1:
self.val_pos += 1
else:
return
@@ -51,14 +53,14 @@ class BookmarkMethodInput(FieldInputMixin):
def refresh(self):
self._win.erase()
self._win.attron(to_curses_attr(self.color))
- self.addnstr(0, 0, ' '*self.width, self.width)
+ self.addnstr(0, 0, ' ' * self.width, self.width)
if self.val_pos > 0:
self.addstr(0, 0, '←')
- if self.val_pos < len(self.options)-1:
- self.addstr(0, self.width-1, '→')
+ if self.val_pos < len(self.options) - 1:
+ self.addstr(0, self.width - 1, '→')
if self.options:
option = self.options[self.val_pos]
- self.addstr(0, self.width//2-len(option)//2, option)
+ self.addstr(0, self.width // 2 - len(option) // 2, option)
self._win.attroff(to_curses_attr(self.color))
self._refresh()
@@ -68,6 +70,7 @@ class BookmarkMethodInput(FieldInputMixin):
def get_help_message(self):
return '←, →: Select a value amongst the others'
+
class BookmarkPasswordInput(FieldInput, Input):
def __init__(self, field):
FieldInput.__init__(self, field)
@@ -80,11 +83,12 @@ class BookmarkPasswordInput(FieldInput, Input):
self._win.erase()
if self.color:
self._win.attron(to_curses_attr(self.color))
- self.addstr('*'*len(self.text[self.view_pos:self.view_pos+self.width-1]))
+ self.addstr(
+ '*' * len(self.text[self.view_pos:self.view_pos + self.width - 1]))
if self.color:
(y, x) = self._win.getyx()
- size = self.width-x
- self.addnstr(' '*size, size, to_curses_attr(self.color))
+ size = self.width - x
+ self.addnstr(' ' * size, size, to_curses_attr(self.color))
self.addstr(0, self.pos, '')
if self.color:
self._win.attroff(to_curses_attr(self.color))
@@ -96,6 +100,7 @@ class BookmarkPasswordInput(FieldInput, Input):
def get_help_message(self):
return 'Edit the secret text'
+
class BookmarkAutojoinWin(FieldInputMixin):
def __init__(self, field):
FieldInput.__init__(self, field)
@@ -138,10 +143,9 @@ class BookmarksWin(Win):
self._bookmarks = list(bookmarks)
self.lines = []
for bookmark in sorted(self._bookmarks, key=lambda x: x.jid):
- self.lines.append((BookmarkJIDInput(bookmark),
- BookmarkPasswordInput(bookmark),
- BookmarkAutojoinWin(bookmark),
- BookmarkMethodInput(bookmark)))
+ self.lines.append(
+ (BookmarkJIDInput(bookmark), BookmarkPasswordInput(bookmark),
+ BookmarkAutojoinWin(bookmark), BookmarkMethodInput(bookmark)))
@property
def current_input(self):
@@ -151,20 +155,23 @@ class BookmarksWin(Win):
def current_input(self, value):
if 0 <= self._current_input < len(self.lines):
if 0 <= value < len(self.lines):
- self.lines[self._current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[self._current_input][
+ self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self._current_input = value
else:
self._current_input = 0
def add_bookmark(self, bookmark):
- self.lines.append((BookmarkJIDInput(bookmark),
- BookmarkPasswordInput(bookmark),
- BookmarkAutojoinWin(bookmark),
- BookmarkMethodInput(bookmark)))
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines.append(
+ (BookmarkJIDInput(bookmark), BookmarkPasswordInput(bookmark),
+ BookmarkAutojoinWin(bookmark), BookmarkMethodInput(bookmark)))
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_horizontal_input = 0
self.current_input = len(self.lines) - 1
- if self.current_input - self.scroll_pos > self.height-1:
+ if self.current_input - self.scroll_pos > self.height - 1:
self.scroll_pos = self.current_input - self.height + 1
self.refresh()
@@ -185,7 +192,7 @@ class BookmarksWin(Win):
self._win = base_wins.TAB_WIN.derwin(height, width, y, x)
# Adjust the scroll position, if resizing made the window too small
# for the cursor to be visible
- while self.current_input - self.scroll_pos > self.height-1:
+ while self.current_input - self.scroll_pos > self.height - 1:
self.scroll_pos += 1
def go_to_next_line_input(self):
@@ -193,39 +200,51 @@ class BookmarksWin(Win):
return
if self.current_input == len(self.lines) - 1:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
# Adjust the scroll position if the current_input would be outside
# of the visible area
- if self.current_input + 1 - self.scroll_pos > self.height-1:
+ if self.current_input + 1 - self.scroll_pos > self.height - 1:
self.current_input += 1
self.scroll_pos += 1
self.refresh()
else:
self.current_input += 1
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[self.current_input][
+ self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def go_to_previous_line_input(self):
if not self.lines:
return
if self.current_input == 0:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_input -= 1
# Adjust the scroll position if the current_input would be outside
# of the visible area
if self.current_input < self.scroll_pos:
self.scroll_pos = self.current_input
self.refresh()
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def go_to_next_horizontal_input(self):
if not self.lines:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_horizontal_input += 1
if self.current_horizontal_input > 3:
self.current_horizontal_input = 0
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def go_to_next_page(self):
if not self.lines:
@@ -234,7 +253,9 @@ class BookmarksWin(Win):
if self.current_input == len(self.lines) - 1:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
inc = min(self.height, len(self.lines) - self.current_input - 1)
if self.current_input + inc - self.scroll_pos > self.height - 1:
@@ -243,7 +264,9 @@ class BookmarksWin(Win):
self.refresh()
else:
self.current_input += inc
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[self.current_input][
+ self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
return True
def go_to_previous_page(self):
@@ -253,7 +276,9 @@ class BookmarksWin(Win):
if self.current_input == 0:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
dec = min(self.height, self.current_input)
self.current_input -= dec
@@ -262,7 +287,9 @@ class BookmarksWin(Win):
if self.current_input < self.scroll_pos:
self.scroll_pos = self.current_input
self.refresh()
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
return True
def go_to_previous_horizontal_input(self):
@@ -270,24 +297,31 @@ class BookmarksWin(Win):
return
if self.current_horizontal_input == 0:
return
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_horizontal_input -= 1
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.lines[
+ self.current_input][self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def on_input(self, key):
if not self.lines:
return
- self.lines[self.current_input][self.current_horizontal_input].do_command(key)
+ self.lines[self.current_input][
+ self.current_horizontal_input].do_command(key)
def refresh(self):
# store the cursor status
self._win.erase()
- y = - self.scroll_pos
+ y = -self.scroll_pos
for i in range(len(self.lines)):
- self.lines[i][0].resize(1, self.width//3, y + 1, 0)
- self.lines[i][1].resize(1, self.width//3, y + 1, self.width//3)
- self.lines[i][2].resize(1, self.width//6, y + 1, 2*self.width//3)
- self.lines[i][3].resize(1, self.width//6, y + 1, 5*self.width//6)
+ self.lines[i][0].resize(1, self.width // 3, y + 1, 0)
+ self.lines[i][1].resize(1, self.width // 3, y + 1, self.width // 3)
+ self.lines[i][2].resize(1, self.width // 6, y + 1,
+ 2 * self.width // 3)
+ self.lines[i][3].resize(1, self.width // 6, y + 1,
+ 5 * self.width // 6)
y += 1
self._refresh()
for i, inp in enumerate(self.lines):
@@ -298,9 +332,12 @@ class BookmarksWin(Win):
for j in range(4):
inp[j].refresh()
- if self.lines and self.current_input < self.height-1:
- self.lines[self.current_input][self.current_horizontal_input].set_color(get_theme().COLOR_SELECTED_ROW)
- self.lines[self.current_input][self.current_horizontal_input].refresh()
+ if self.lines and self.current_input < self.height - 1:
+ self.lines[self.current_input][
+ self.current_horizontal_input].set_color(
+ get_theme().COLOR_SELECTED_ROW)
+ self.lines[self.current_input][
+ self.current_horizontal_input].refresh()
if not self.lines:
curses.curs_set(0)
else:
@@ -308,10 +345,10 @@ class BookmarksWin(Win):
def refresh_current_input(self):
if self.lines:
- self.lines[self.current_input][self.current_horizontal_input].refresh()
+ self.lines[self.current_input][
+ self.current_horizontal_input].refresh()
def save(self):
for line in self.lines:
for item in line:
item.save()
-
diff --git a/poezio/windows/confirm.py b/poezio/windows/confirm.py
index 591ff89a..f2794221 100644
--- a/poezio/windows/confirm.py
+++ b/poezio/windows/confirm.py
@@ -2,9 +2,11 @@ from poezio.windows.base_wins import Win
from poezio.theming import get_theme, to_curses_attr
+
class Dialog(Win):
str_accept = " Accept "
str_refuse = " Reject "
+
def __init__(self, helper_text, critical=False):
self.text = helper_text
self.accept = False
@@ -35,4 +37,3 @@ class Dialog(Win):
def toggle_choice(self):
self.accept = not self.accept
-
diff --git a/poezio/windows/data_forms.py b/poezio/windows/data_forms.py
index a14418d7..c600273e 100644
--- a/poezio/windows/data_forms.py
+++ b/poezio/windows/data_forms.py
@@ -12,12 +12,14 @@ from poezio.windows.inputs import Input
from poezio.theming import to_curses_attr, get_theme
+
class FieldInput(object):
"""
All input types in a data form should inherit this class,
in addition with windows.Input or any relevant class from the
'windows' library.
"""
+
def __init__(self, field):
self._field = field
self.color = get_theme().COLOR_NORMAL_TEXT
@@ -25,7 +27,6 @@ class FieldInput(object):
def update_field_value(self, value):
raise NotImplementedError
-
def is_dummy(self):
return False
@@ -42,8 +43,10 @@ class FieldInput(object):
"""
return ''
+
class FieldInputMixin(FieldInput, Win):
"Mix both FieldInput and Win"
+
def __init__(self, field):
FieldInput.__init__(self, field)
Win.__init__(self)
@@ -81,6 +84,7 @@ class DummyInput(FieldInputMixin):
"""
Used for fields that do not require any input ('fixed')
"""
+
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -93,6 +97,7 @@ class DummyInput(FieldInputMixin):
def is_dummy(self):
return True
+
class BooleanWin(FieldInputMixin):
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -108,8 +113,8 @@ class BooleanWin(FieldInputMixin):
def refresh(self):
self._win.erase()
self._win.attron(to_curses_attr(self.color))
- self.addnstr(0, 0, ' '*(8), self.width)
- self.addstr(0, 2, "%s"%self.value)
+ self.addnstr(0, 0, ' ' * (8), self.width)
+ self.addstr(0, 2, "%s" % self.value)
self.addstr(0, 8, '→')
self.addstr(0, 0, '←')
if self.last_key == 'KEY_RIGHT':
@@ -126,6 +131,7 @@ class BooleanWin(FieldInputMixin):
def get_help_message(self):
return '← and →: change the value between True and False'
+
class TextMultiWin(FieldInputMixin):
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -149,20 +155,22 @@ class TextMultiWin(FieldInputMixin):
if self.val_pos > 0:
self.val_pos -= 1
elif key == 'KEY_RIGHT':
- if self.val_pos < len(self.options)-1:
+ if self.val_pos < len(self.options) - 1:
self.val_pos += 1
elif key == '^M':
self.edition_input = Input()
self.edition_input.color = self.color
- self.edition_input.resize(self.height, self.width, self.y, self.x)
+ self.edition_input.resize(self.height, self.width, self.y,
+ self.x)
self.edition_input.text = self.options[self.val_pos]
self.edition_input.key_end()
else:
if key == '^M':
self.options[self.val_pos] = self.edition_input.get_text()
- if not self.options[self.val_pos] and self.val_pos != len(self.options) -1:
+ if not self.options[self.val_pos] and self.val_pos != len(
+ self.options) - 1:
del self.options[self.val_pos]
- if self.val_pos == len(self.options) -1:
+ if self.val_pos == len(self.options) - 1:
self.val_pos -= 1
self.edition_input = None
if not self.options or self.options[-1] != '':
@@ -175,13 +183,13 @@ class TextMultiWin(FieldInputMixin):
if not self.edition_input:
self._win.erase()
self._win.attron(to_curses_attr(self.color))
- self.addnstr(0, 0, ' '*self.width, self.width)
+ self.addnstr(0, 0, ' ' * self.width, self.width)
option = self.options[self.val_pos]
- self.addstr(0, self.width//2-len(option)//2, option)
+ self.addstr(0, self.width // 2 - len(option) // 2, option)
if self.val_pos > 0:
self.addstr(0, 0, '←')
- if self.val_pos < len(self.options)-1:
- self.addstr(0, self.width-1, '→')
+ if self.val_pos < len(self.options) - 1:
+ self.addstr(0, self.width - 1, '→')
self._win.attroff(to_curses_attr(self.color))
self._refresh()
else:
@@ -194,7 +202,7 @@ class TextMultiWin(FieldInputMixin):
def get_help_message(self):
if not self.edition_input:
help_msg = '← and →: browse the available entries. '
- if self.val_pos == len(self.options)-1:
+ if self.val_pos == len(self.options) - 1:
help_msg += 'Enter: add an entry'
else:
help_msg += 'Enter: edit this entry'
@@ -202,6 +210,7 @@ class TextMultiWin(FieldInputMixin):
help_msg = 'Enter: finish editing this entry.'
return help_msg
+
class ListMultiWin(FieldInputMixin):
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -215,7 +224,7 @@ class ListMultiWin(FieldInputMixin):
if self.val_pos > 0:
self.val_pos -= 1
elif key == 'KEY_RIGHT':
- if self.val_pos < len(self.options)-1:
+ if self.val_pos < len(self.options) - 1:
self.val_pos += 1
elif key == ' ':
self.options[self.val_pos][1] = not self.options[self.val_pos][1]
@@ -226,14 +235,15 @@ class ListMultiWin(FieldInputMixin):
def refresh(self):
self._win.erase()
self._win.attron(to_curses_attr(self.color))
- self.addnstr(0, 0, ' '*self.width, self.width)
+ self.addnstr(0, 0, ' ' * self.width, self.width)
if self.val_pos > 0:
self.addstr(0, 0, '←')
- if self.val_pos < len(self.options)-1:
- self.addstr(0, self.width-1, '→')
+ if self.val_pos < len(self.options) - 1:
+ self.addstr(0, self.width - 1, '→')
if self.options:
option = self.options[self.val_pos]
- self.addstr(0, self.width//2-len(option)//2, option[0]['label'])
+ self.addstr(0, self.width // 2 - len(option) // 2,
+ option[0]['label'])
self.addstr(0, 2, '✔' if option[1] else '☐')
self._win.attroff(to_curses_attr(self.color))
self._refresh()
@@ -241,12 +251,15 @@ class ListMultiWin(FieldInputMixin):
def reply(self):
self._field['label'] = ''
self._field.delOptions()
- values = [option[0]['value'] for option in self.options if option[1] is True]
+ values = [
+ option[0]['value'] for option in self.options if option[1] is True
+ ]
self._field.set_answer(values)
def get_help_message(self):
return '←, →: Switch between the value. Space: select or unselect a value'
+
class ListSingleWin(FieldInputMixin):
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -263,7 +276,7 @@ class ListSingleWin(FieldInputMixin):
if self.val_pos > 0:
self.val_pos -= 1
elif key == 'KEY_RIGHT':
- if self.val_pos < len(self.options)-1:
+ if self.val_pos < len(self.options) - 1:
self.val_pos += 1
else:
return
@@ -272,14 +285,14 @@ class ListSingleWin(FieldInputMixin):
def refresh(self):
self._win.erase()
self._win.attron(to_curses_attr(self.color))
- self.addnstr(0, 0, ' '*self.width, self.width)
+ self.addnstr(0, 0, ' ' * self.width, self.width)
if self.val_pos > 0:
self.addstr(0, 0, '←')
- if self.val_pos < len(self.options)-1:
- self.addstr(0, self.width-1, '→')
+ if self.val_pos < len(self.options) - 1:
+ self.addstr(0, self.width - 1, '→')
if self.options:
option = self.options[self.val_pos]['label']
- self.addstr(0, self.width//2-len(option)//2, option)
+ self.addstr(0, self.width // 2 - len(option) // 2, option)
self._win.attroff(to_curses_attr(self.color))
self._refresh()
@@ -291,6 +304,7 @@ class ListSingleWin(FieldInputMixin):
def get_help_message(self):
return '←, →: Select a value amongst the others'
+
class TextSingleWin(FieldInputMixin, Input):
def __init__(self, field):
FieldInputMixin.__init__(self, field)
@@ -307,6 +321,7 @@ class TextSingleWin(FieldInputMixin, Input):
def get_help_message(self):
return 'Edit the text'
+
class TextPrivateWin(TextSingleWin):
def __init__(self, field):
TextSingleWin.__init__(self, field)
@@ -315,11 +330,12 @@ class TextPrivateWin(TextSingleWin):
self._win.erase()
if self.color:
self._win.attron(to_curses_attr(self.color))
- self.addstr('*'*len(self.text[self.view_pos:self.view_pos+self.width-1]))
+ self.addstr(
+ '*' * len(self.text[self.view_pos:self.view_pos + self.width - 1]))
if self.color:
(y, x) = self._win.getyx()
- size = self.width-x
- self.addnstr(' '*size, size, to_curses_attr(self.color))
+ size = self.width - x
+ self.addnstr(' ' * size, size, to_curses_attr(self.color))
self.addstr(0, self.pos, '')
if self.color:
self._win.attroff(to_curses_attr(self.color))
@@ -328,6 +344,7 @@ class TextPrivateWin(TextSingleWin):
def get_help_message(self):
return 'Edit the secret text'
+
class FormWin(object):
"""
A window, with some subwins (the various inputs).
@@ -335,22 +352,24 @@ class FormWin(object):
On resize, move and resize all the subwin and define how the text will be written
On refresh, write all the text, and refresh all the subwins
"""
- input_classes = {'boolean': BooleanWin,
- 'fixed': DummyInput,
- 'jid-multi': TextMultiWin,
- 'jid-single': TextSingleWin,
- 'list-multi': ListMultiWin,
- 'list-single': ListSingleWin,
- 'text-multi': TextMultiWin,
- 'text-private': TextPrivateWin,
- 'text-single': TextSingleWin,
- }
+ input_classes = {
+ 'boolean': BooleanWin,
+ 'fixed': DummyInput,
+ 'jid-multi': TextMultiWin,
+ 'jid-single': TextSingleWin,
+ 'list-multi': ListMultiWin,
+ 'list-single': ListSingleWin,
+ 'text-multi': TextMultiWin,
+ 'text-private': TextPrivateWin,
+ 'text-single': TextSingleWin,
+ }
+
def __init__(self, form, height, width, y, x):
self._form = form
self._win = base_wins.TAB_WIN.derwin(height, width, y, x)
self.scroll_pos = 0
self.current_input = 0
- self.inputs = [] # dict list
+ self.inputs = [] # dict list
for (name, field) in self._form.getFields().items():
if field['type'] == 'hidden':
continue
@@ -363,9 +382,11 @@ class FormWin(object):
if field['type'] == 'fixed':
label = field.get_value()
inp = input_class(field)
- self.inputs.append({'label':ColoredLabel(label),
- 'description': desc,
- 'input':inp})
+ self.inputs.append({
+ 'label': ColoredLabel(label),
+ 'description': desc,
+ 'input': inp
+ })
def resize(self, height, width, y, x):
self.height = height
@@ -373,7 +394,7 @@ class FormWin(object):
self._win = base_wins.TAB_WIN.derwin(height, width, y, x)
# Adjust the scroll position, if resizing made the window too small
# for the cursor to be visible
- while self.current_input - self.scroll_pos > self.height-1:
+ while self.current_input - self.scroll_pos > self.height - 1:
self.scroll_pos += 1
def reply(self):
@@ -394,13 +415,17 @@ class FormWin(object):
return
if self.current_input == len(self.inputs) - 1:
return
- self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_NORMAL_TEXT)
- self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.inputs[self.current_input]['input'].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
+ self.inputs[self.current_input]['label'].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_input += 1
jump = 0
- while self.current_input+jump != len(self.inputs) - 1 and self.inputs[self.current_input+jump]['input'].is_dummy():
+ while self.current_input + jump != len(
+ self.inputs) - 1 and self.inputs[self.current_input
+ + jump]['input'].is_dummy():
jump += 1
- if self.inputs[self.current_input+jump]['input'].is_dummy():
+ if self.inputs[self.current_input + jump]['input'].is_dummy():
return
self.current_input += jump
# If moving made the current input out of the visible screen, we
@@ -408,24 +433,31 @@ class FormWin(object):
# call refresh() if this is not the case, because
# refresh_current_input() is always called anyway, so this is not
# needed
- if self.current_input - self.scroll_pos > self.height-1:
+ if self.current_input - self.scroll_pos > self.height - 1:
self.scroll_pos += 1
self.refresh()
- self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
- self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.inputs[self.current_input]['input'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
+ self.inputs[self.current_input]['label'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def go_to_previous_input(self):
if not self.inputs:
return
if self.current_input == 0:
return
- self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_NORMAL_TEXT)
- self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_NORMAL_TEXT)
+ self.inputs[self.current_input]['input'].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
+ self.inputs[self.current_input]['label'].set_color(
+ get_theme().COLOR_NORMAL_TEXT)
self.current_input -= 1
jump = 0
- while self.current_input-jump > 0 and self.inputs[self.current_input+jump]['input'].is_dummy():
+ while self.current_input - jump > 0 and self.inputs[self.current_input
+ +
+ jump]['input'].is_dummy(
+ ):
jump += 1
- if self.inputs[self.current_input+jump]['input'].is_dummy():
+ if self.inputs[self.current_input + jump]['input'].is_dummy():
return
# Adjust the scroll position if the current_input would be outside
# of the visible area
@@ -433,8 +465,10 @@ class FormWin(object):
self.scroll_pos = self.current_input
self.refresh()
self.current_input -= jump
- self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
- self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.inputs[self.current_input]['input'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
+ self.inputs[self.current_input]['label'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
def on_input(self, key, raw=False):
if not self.inputs:
@@ -448,8 +482,9 @@ class FormWin(object):
for name, field in self._form.getFields().items():
if field['type'] == 'hidden':
continue
- self.inputs[i]['label'].resize(1, self.width//2, y + 1, 0)
- self.inputs[i]['input'].resize(1, self.width//2, y+1, self.width//2)
+ self.inputs[i]['label'].resize(1, self.width // 2, y + 1, 0)
+ self.inputs[i]['input'].resize(1, self.width // 2, y + 1,
+ self.width // 2)
# TODO: display the field description
y += 1
i += 1
@@ -462,17 +497,19 @@ class FormWin(object):
inp['label'].refresh()
inp['input'].refresh()
inp['label'].refresh()
- if self.inputs and self.current_input < self.height-1:
- self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
+ if self.inputs and self.current_input < self.height - 1:
+ self.inputs[self.current_input]['input'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['input'].refresh()
- self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
+ self.inputs[self.current_input]['label'].set_color(
+ get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['label'].refresh()
def refresh_current_input(self):
self.inputs[self.current_input]['input'].refresh()
def get_help_message(self):
- if self.inputs and self.current_input < self.height-1 and self.inputs[self.current_input]['input']:
+ if self.inputs and self.current_input < self.height - 1 and self.inputs[self.
+ current_input]['input']:
return self.inputs[self.current_input]['input'].get_help_message()
return ''
-
diff --git a/poezio/windows/funcs.py b/poezio/windows/funcs.py
index ea2941c8..3648bac3 100644
--- a/poezio/windows/funcs.py
+++ b/poezio/windows/funcs.py
@@ -7,6 +7,7 @@ DIGITS = string.digits + '-'
from poezio.windows.base_wins import FORMAT_CHAR, format_chars
+
def find_first_format_char(text, chars=None):
if chars is None:
chars = format_chars
@@ -19,13 +20,15 @@ def find_first_format_char(text, chars=None):
pos = p
return pos
+
def truncate_nick(nick, size=10):
if size < 1:
size = 1
if nick and len(nick) > size:
- return nick[:size]+'…'
+ return nick[:size] + '…'
return nick
+
def parse_attrs(text, previous=None):
next_attr_char = text.find(FORMAT_CHAR)
if previous:
@@ -34,7 +37,7 @@ def parse_attrs(text, previous=None):
attrs = []
while next_attr_char != -1 and text:
if next_attr_char + 1 < len(text):
- attr_char = text[next_attr_char+1].lower()
+ attr_char = text[next_attr_char + 1].lower()
else:
attr_char = '\0'
if attr_char == 'o':
@@ -46,12 +49,11 @@ def parse_attrs(text, previous=None):
elif attr_char == 'i':
attrs.append('i')
if attr_char in DIGITS and attr_char:
- color_str = text[next_attr_char+1:text.find('}', next_attr_char)]
+ color_str = text[next_attr_char + 1:text.find('}', next_attr_char)]
if color_str:
attrs.append(color_str + '}')
- text = text[next_attr_char+len(color_str)+2:]
+ text = text[next_attr_char + len(color_str) + 2:]
else:
- text = text[next_attr_char+2:]
+ text = text[next_attr_char + 2:]
next_attr_char = text.find(FORMAT_CHAR)
return attrs
-
diff --git a/poezio/windows/image.py b/poezio/windows/image.py
index 57bbec71..09a11d6f 100644
--- a/poezio/windows/image.py
+++ b/poezio/windows/image.py
@@ -15,10 +15,12 @@ from poezio.windows.base_wins import Win
from poezio.theming import to_curses_attr
from poezio.xhtml import _parse_css_color
+
class ImageWin(Win):
"""
A window which contains either an image or a border.
"""
+
def __init__(self):
self._image = None
Win.__init__(self)
@@ -41,10 +43,10 @@ class ImageWin(Win):
self._refresh()
def _display_border(self):
- self._win.border(curses.ACS_VLINE, curses.ACS_VLINE,
- curses.ACS_HLINE, curses.ACS_HLINE,
- curses.ACS_ULCORNER, curses.ACS_URCORNER,
- curses.ACS_LLCORNER, curses.ACS_LRCORNER)
+ self._win.border(curses.ACS_VLINE, curses.ACS_VLINE, curses.ACS_HLINE,
+ curses.ACS_HLINE, curses.ACS_ULCORNER,
+ curses.ACS_URCORNER, curses.ACS_LLCORNER,
+ curses.ACS_LRCORNER)
@staticmethod
def _compute_size(image_size, width: int, height: int):
@@ -69,13 +71,13 @@ class ImageWin(Win):
start_y = (original_height - height // 2) // 2
start_x = (original_width - width) // 2
for y in range(height // 2):
- two_lines = data[(2 * y) * width * 3: (2 * y + 2) * width * 3]
+ two_lines = data[(2 * y) * width * 3:(2 * y + 2) * width * 3]
line1 = two_lines[:width * 3]
line2 = two_lines[width * 3:]
self.move(start_y + y, start_x)
for x in range(width):
- r, g, b = line1[x * 3: (x + 1) * 3]
+ r, g, b = line1[x * 3:(x + 1) * 3]
top_color = _parse_css_color('#%02x%02x%02x' % (r, g, b))
- r, g, b = line2[x * 3: (x + 1) * 3]
+ r, g, b = line2[x * 3:(x + 1) * 3]
bot_color = _parse_css_color('#%02x%02x%02x' % (r, g, b))
self.addstr('▄', to_curses_attr((bot_color, top_color)))
diff --git a/poezio/windows/info_bar.py b/poezio/windows/info_bar.py
index cac56db9..950813f1 100644
--- a/poezio/windows/info_bar.py
+++ b/poezio/windows/info_bar.py
@@ -10,11 +10,11 @@ log = logging.getLogger(__name__)
import curses
-
from poezio.config import config
from poezio.windows.base_wins import Win
from poezio.theming import get_theme, to_curses_attr
+
class GlobalInfoBar(Win):
def __init__(self, core):
Win.__init__(self)
@@ -23,7 +23,8 @@ class GlobalInfoBar(Win):
def refresh(self):
log.debug('Refresh: %s', self.__class__.__name__)
self._win.erase()
- self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(0, 0, "[",
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
create_gaps = config.get('create_gaps')
show_names = config.get('show_tab_names')
@@ -48,20 +49,24 @@ class GlobalInfoBar(Win):
self.addstr(' ', to_curses_attr(color))
if show_names:
if use_nicks:
- self.addstr("%s" % str(tab.get_nick()), to_curses_attr(color))
+ self.addstr("%s" % str(tab.get_nick()),
+ to_curses_attr(color))
else:
self.addstr("%s" % tab.name, to_curses_attr(color))
- self.addstr("|", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
- except: # end of line
+ self.addstr("|",
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ except: # end of line
break
(y, x) = self._win.getyx()
- self.addstr(y, x-1, '] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(y, x - 1, '] ',
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
(y, x) = self._win.getyx()
remaining_size = self.width - x
- self.addnstr(' '*remaining_size, remaining_size,
+ self.addnstr(' ' * remaining_size, remaining_size,
to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self._refresh()
+
class VerticalGlobalInfoBar(Win):
def __init__(self, core, scr):
Win.__init__(self)
@@ -79,30 +84,32 @@ class VerticalGlobalInfoBar(Win):
use_nicks = config.get('use_tab_nicks')
if nb_tabs >= height:
for y, tab in enumerate(sorted_tabs):
- if tab.vertical_color == get_theme().COLOR_VERTICAL_TAB_CURRENT:
+ if tab.vertical_color == get_theme(
+ ).COLOR_VERTICAL_TAB_CURRENT:
pos = y
break
# center the current tab as much as possible
- if pos < height//2:
+ if pos < height // 2:
sorted_tabs = sorted_tabs[:height]
- elif nb_tabs - pos <= height//2:
+ elif nb_tabs - pos <= height // 2:
sorted_tabs = sorted_tabs[-height:]
else:
- sorted_tabs = sorted_tabs[pos-height//2 : pos+height//2]
+ sorted_tabs = sorted_tabs[pos - height // 2:pos + height // 2]
asc_sort = (config.get('vertical_tab_list_sort') == 'asc')
for y, tab in enumerate(sorted_tabs):
color = tab.vertical_color
if asc_sort:
y = height - y - 1
self.addstr(y, 0, "%2d" % tab.nb,
- to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER))
+ to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER))
self.addstr('.')
if use_nicks:
- self.addnstr("%s" % tab.get_nick(), width - 4, to_curses_attr(color))
+ self.addnstr("%s" % tab.get_nick(), width - 4,
+ to_curses_attr(color))
else:
self.addnstr("%s" % tab.name, width - 4, to_curses_attr(color))
separator = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)
self._win.attron(separator)
- self._win.vline(0, width-1, curses.ACS_VLINE, height)
+ self._win.vline(0, width - 1, curses.ACS_VLINE, height)
self._win.attroff(separator)
self._refresh()
diff --git a/poezio/windows/info_wins.py b/poezio/windows/info_wins.py
index 1afb34b0..2d4d1e01 100644
--- a/poezio/windows/info_wins.py
+++ b/poezio/windows/info_wins.py
@@ -13,11 +13,13 @@ from poezio.windows.base_wins import Win
from poezio.windows.funcs import truncate_nick
from poezio.theming import get_theme, to_curses_attr
+
class InfoWin(Win):
"""
Base class for all the *InfoWin, used in various tabs. For example
MucInfoWin, etc. Provides some useful methods.
"""
+
def __init__(self):
Win.__init__(self)
@@ -29,12 +31,15 @@ class InfoWin(Win):
"""
if window.pos > 0:
plus = ' -MORE(%s)-' % window.pos
- self.addstr(plus, to_curses_attr(get_theme().COLOR_SCROLLABLE_NUMBER))
+ self.addstr(plus,
+ to_curses_attr(get_theme().COLOR_SCROLLABLE_NUMBER))
+
class XMLInfoWin(InfoWin):
"""
Info about the latest xml filter used and the state of the buffer.
"""
+
def __init__(self):
InfoWin.__init__(self)
@@ -51,11 +56,13 @@ class XMLInfoWin(InfoWin):
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self._refresh()
+
class PrivateInfoWin(InfoWin):
"""
The line above the information window, displaying information
about the MUC user we are talking to
"""
+
def __init__(self):
InfoWin.__init__(self)
@@ -75,7 +82,8 @@ class PrivateInfoWin(InfoWin):
value returned by the callbacks.
"""
for key in information:
- self.addstr(information[key](jid), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(information[key](jid),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_room_name(self, name):
jid = safeJID(name)
@@ -86,13 +94,16 @@ class PrivateInfoWin(InfoWin):
def write_chatstate(self, state):
if state:
- self.addstr(' %s' % (state,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(' %s' % (state, ),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class MucListInfoWin(InfoWin):
"""
The live above the information window, displaying informatios
about the muc server being listed
"""
+
def __init__(self, message=''):
InfoWin.__init__(self)
self.message = message
@@ -101,14 +112,17 @@ class MucListInfoWin(InfoWin):
log.debug('Refresh: %s', self.__class__.__name__)
self._win.erase()
if name:
- self.addstr(name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(name,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
else:
- self.addstr(self.message, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(self.message,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
if window:
self.print_scroll_position(window)
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self._refresh()
+
class ConversationInfoWin(InfoWin):
"""
The line above the information window, displaying information
@@ -154,7 +168,7 @@ class ConversationInfoWin(InfoWin):
"""
for key in information:
self.addstr(information[key](jid),
- to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_resource_information(self, resource):
"""
@@ -171,7 +185,8 @@ class ConversationInfoWin(InfoWin):
self.addstr(presence, to_curses_attr(color))
if resource and resource.status:
shortened = resource.status[:20] + (resource.status[:20] and '…')
- self.addstr(' %s' % shortened, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(' %s' % shortened,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.addstr(']', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_contact_information(self, contact):
@@ -179,23 +194,28 @@ class ConversationInfoWin(InfoWin):
Write the information about the contact
"""
if not contact:
- self.addstr("(contact not in roster)", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr("(contact not in roster)",
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
return
display_name = contact.name
if display_name:
- self.addstr('%s '%(display_name), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr('%s ' % (display_name),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_contact_jid(self, jid):
"""
Just write the jid that we are talking to
"""
self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
- self.addstr(jid.full, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME))
+ self.addstr(jid.full,
+ to_curses_attr(get_theme().COLOR_CONVERSATION_NAME))
self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_chatstate(self, state):
if state:
- self.addstr(' %s' % (state,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(' %s' % (state, ),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class DynamicConversationInfoWin(ConversationInfoWin):
def write_contact_jid(self, jid):
@@ -203,18 +223,23 @@ class DynamicConversationInfoWin(ConversationInfoWin):
Just displays the resource in an other color
"""
log.debug("write_contact_jid DynamicConversationInfoWin, jid: %s",
- jid.resource)
+ jid.resource)
self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
- self.addstr(jid.bare, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME))
+ self.addstr(jid.bare,
+ to_curses_attr(get_theme().COLOR_CONVERSATION_NAME))
if jid.resource:
- self.addstr("/%s" % (jid.resource,), to_curses_attr(get_theme().COLOR_CONVERSATION_RESOURCE))
+ self.addstr("/%s" % (jid.resource, ),
+ to_curses_attr(
+ get_theme().COLOR_CONVERSATION_RESOURCE))
self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class MucInfoWin(InfoWin):
"""
The line just above the information window, displaying information
about the MUC we are viewing
"""
+
def __init__(self):
InfoWin.__init__(self)
@@ -233,12 +258,15 @@ class MucInfoWin(InfoWin):
def write_room_name(self, room):
self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
- self.addstr(room.name, to_curses_attr(get_theme().COLOR_GROUPCHAT_NAME))
+ self.addstr(room.name,
+ to_curses_attr(get_theme().COLOR_GROUPCHAT_NAME))
self.addstr(']', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_participants_number(self, room):
self.addstr('{', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
- self.addstr(str(len(room.users)), to_curses_attr(get_theme().COLOR_GROUPCHAT_NAME))
+ self.addstr(
+ str(len(room.users)),
+ to_curses_attr(get_theme().COLOR_GROUPCHAT_NAME))
self.addstr('} ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_disconnected(self, room):
@@ -246,7 +274,8 @@ class MucInfoWin(InfoWin):
Shows a message if the room is not joined
"""
if not room.joined:
- self.addstr(' -!- Not connected ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(' -!- Not connected ',
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_own_nick(self, room):
"""
@@ -255,7 +284,9 @@ class MucInfoWin(InfoWin):
nick = room.own_nick
if not nick:
return
- self.addstr(truncate_nick(nick, 13), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(
+ truncate_nick(nick, 13),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_role(self, room, user):
"""
@@ -269,10 +300,12 @@ class MucInfoWin(InfoWin):
txt += user.role + ')'
self.addstr(txt, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class ConversationStatusMessageWin(InfoWin):
"""
The upper bar displaying the status message of the contact
"""
+
def __init__(self):
InfoWin.__init__(self)
@@ -293,7 +326,9 @@ class ConversationStatusMessageWin(InfoWin):
self._refresh()
def write_status_message(self, resource):
- self.addstr(resource.status, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(resource.status,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class BookmarksInfoWin(InfoWin):
def __init__(self):
@@ -307,7 +342,9 @@ class BookmarksInfoWin(InfoWin):
self._refresh()
def write_remote_status(self, preferred):
- self.addstr('Remote storage: %s' % preferred, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr('Remote storage: %s' % preferred,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+
class ConfirmStatusWin(Win):
def __init__(self, text, critical=False):
@@ -326,4 +363,3 @@ class ConfirmStatusWin(Win):
self.addstr(self.text, c_color)
self.finish_line(color)
self._refresh()
-
diff --git a/poezio/windows/input_placeholders.py b/poezio/windows/input_placeholders.py
index 3ac478fd..47cfac31 100644
--- a/poezio/windows/input_placeholders.py
+++ b/poezio/windows/input_placeholders.py
@@ -6,7 +6,6 @@ but which are not inputs.
import logging
log = logging.getLogger(__name__)
-
from poezio.windows.base_wins import Win
from poezio.theming import get_theme, to_curses_attr
@@ -17,6 +16,7 @@ class HelpText(Win):
Usually used to replace an Input when the tab is in
command mode.
"""
+
def __init__(self, text=''):
Win.__init__(self)
self.txt = text
@@ -26,7 +26,8 @@ class HelpText(Win):
if txt:
self.txt = txt
self._win.erase()
- self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(0, 0, self.txt[:self.width - 1],
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self._refresh()
diff --git a/poezio/windows/inputs.py b/poezio/windows/inputs.py
index faf0125d..0ccd179e 100644
--- a/poezio/windows/inputs.py
+++ b/poezio/windows/inputs.py
@@ -18,6 +18,7 @@ from poezio.theming import to_curses_attr
DEFAULT_ON_INPUT = lambda x: None
+
class Input(Win):
"""
The simplest Input possible, provides just a way to edit a single line
@@ -31,7 +32,8 @@ class Input(Win):
in a very flexible way.
"""
text_attributes = 'bou1234567t'
- clipboard = '' # A common clipboard for all the inputs, this makes
+ clipboard = '' # A common clipboard for all the inputs, this makes
+
# it easy cut and paste text between various input
def __init__(self):
self.key_func = {
@@ -59,16 +61,16 @@ class Input(Win):
'^?': self.key_backspace,
"M-^?": self.delete_word,
# '^J': self.add_line_break,
- }
+ }
Win.__init__(self)
self.text = ''
- self.pos = 0 # The position of the “cursor” in the text
- # (not only in the view)
+ self.pos = 0 # The position of the “cursor” in the text
+ # (not only in the view)
self.view_pos = 0 # The position (in the text) of the
- # first character displayed on the
- # screen
- self.on_input = DEFAULT_ON_INPUT # callback called on any key pressed
- self.color = None # use this color on addstr
+ # first character displayed on the
+ # screen
+ self.on_input = DEFAULT_ON_INPUT # callback called on any key pressed
+ self.color = None # use this color on addstr
def on_delete(self):
"""
@@ -101,10 +103,10 @@ class Input(Win):
"""
if self.pos == 0:
return True
- separators = string.punctuation+' '
- while self.pos > 0 and self.text[self.pos-1] in separators:
+ separators = string.punctuation + ' '
+ while self.pos > 0 and self.text[self.pos - 1] in separators:
self.key_left()
- while self.pos > 0 and self.text[self.pos-1] not in separators:
+ while self.pos > 0 and self.text[self.pos - 1] not in separators:
self.key_left()
return True
@@ -114,10 +116,11 @@ class Input(Win):
"""
if self.is_cursor_at_end():
return True
- separators = string.punctuation+' '
+ separators = string.punctuation + ' '
while not self.is_cursor_at_end() and self.text[self.pos] in separators:
self.key_right()
- while not self.is_cursor_at_end() and self.text[self.pos] not in separators:
+ while not self.is_cursor_at_end() and self.text[self.
+ pos] not in separators:
self.key_right()
return True
@@ -125,10 +128,10 @@ class Input(Win):
"""
Delete the word just before the cursor
"""
- separators = string.punctuation+' '
- while self.pos > 0 and self.text[self.pos-1] in separators:
+ separators = string.punctuation + ' '
+ while self.pos > 0 and self.text[self.pos - 1] in separators:
self.key_backspace()
- while self.pos > 0 and self.text[self.pos-1] not in separators:
+ while self.pos > 0 and self.text[self.pos - 1] not in separators:
self.key_backspace()
return True
@@ -136,10 +139,11 @@ class Input(Win):
"""
Delete the word just after the cursor
"""
- separators = string.punctuation+' '
+ separators = string.punctuation + ' '
while not self.is_cursor_at_end() and self.text[self.pos] in separators:
self.key_dc()
- while not self.is_cursor_at_end() and self.text[self.pos] not in separators:
+ while not self.is_cursor_at_end() and self.text[self.
+ pos] not in separators:
self.key_dc()
return True
@@ -182,8 +186,8 @@ class Input(Win):
"""
self.reset_completion()
if self.is_cursor_at_end():
- return True # end of line, nothing to delete
- self.text = self.text[:self.pos]+self.text[self.pos+1:]
+ return True # end of line, nothing to delete
+ self.text = self.text[:self.pos] + self.text[self.pos + 1:]
self.rewrite_text()
return True
@@ -257,7 +261,12 @@ class Input(Win):
self.normal_completion(word_list, add_after)
return True
- def new_completion(self, word_list, argument_position=-1, add_after='', quotify=True, override=False):
+ def new_completion(self,
+ word_list,
+ argument_position=-1,
+ add_after='',
+ quotify=True,
+ override=False):
"""
Complete the argument at position ``argument_postion`` in the input.
If ``quotify`` is ``True``, then the completion will operate on block of words
@@ -277,11 +286,17 @@ class Input(Win):
if argument_position == 0:
self._new_completion_first(word_list)
else:
- self._new_completion_args(word_list, argument_position, add_after, quotify, override)
+ self._new_completion_args(word_list, argument_position, add_after,
+ quotify, override)
self.rewrite_text()
return True
- def _new_completion_args(self, word_list, argument_position=-1, add_after='', quoted=True, override=False):
+ def _new_completion_args(self,
+ word_list,
+ argument_position=-1,
+ add_after='',
+ quoted=True,
+ override=False):
"""
Case for completing arguments with position ≠ 0
"""
@@ -319,12 +334,12 @@ class Input(Win):
if argument_position >= len(words):
if quoted and ' ' in self.hit_list[0]:
- words.append('"'+self.hit_list[0]+'"')
+ words.append('"' + self.hit_list[0] + '"')
else:
words.append(self.hit_list[0])
else:
if quoted and ' ' in self.hit_list[0]:
- words[argument_position] = '"'+self.hit_list[0]+'"'
+ words[argument_position] = '"' + self.hit_list[0] + '"'
else:
words[argument_position] = self.hit_list[0]
@@ -370,7 +385,7 @@ class Input(Win):
command_stop = self.text.find(' ')
if command_stop == -1 or self.pos <= command_stop:
return 0
- text = self.text[command_stop+1:]
+ text = self.text[command_stop + 1:]
pos = self.pos - len(self.text) + len(text) - 1
val = common.find_argument(pos, text, quoted=quoted) + 1
return val
@@ -387,19 +402,22 @@ class Input(Win):
Normal completion
"""
pos = self.pos
- if pos < len(self.text) and after.endswith(' ') and self.text[pos] == ' ':
- after = after[:-1] # remove the last space if we are already on a space
+ if pos < len(
+ self.text) and after.endswith(' ') and self.text[pos] == ' ':
+ after = after[:
+ -1] # remove the last space if we are already on a space
if not self.last_completion:
space_before_cursor = self.text.rfind(' ', 0, pos)
if space_before_cursor != -1:
- begin = self.text[space_before_cursor+1:pos]
+ begin = self.text[space_before_cursor + 1:pos]
else:
begin = self.text[:pos]
- hit_list = [] # list of matching hits
+ hit_list = [] # list of matching hits
for word in word_list:
if word.lower().startswith(begin.lower()):
hit_list.append(word)
- elif word.startswith('"') and word.lower()[1:].startswith(begin.lower()):
+ elif word.startswith('"') and word.lower()[1:].startswith(
+ begin.lower()):
hit_list.append(word)
if len(hit_list) == 0:
return
@@ -408,11 +426,11 @@ class Input(Win):
else:
begin = self.last_completion
end = len(begin) + len(after)
- self.hit_list.append(self.hit_list.pop(0)) # rotate list
+ self.hit_list.append(self.hit_list.pop(0)) # rotate list
- self.text = self.text[:pos-end] + self.text[pos:]
+ self.text = self.text[:pos - end] + self.text[pos:]
pos -= end
- hit = self.hit_list[0] # take the first hit
+ hit = self.hit_list[0] # take the first hit
self.text = self.text[:pos] + hit + after + self.text[pos:]
for _ in range(end):
try:
@@ -432,11 +450,11 @@ class Input(Win):
self.on_input(self.get_text())
return res
if not raw and (not key or len(key) > 1):
- return False # ignore non-handled keyboard shortcuts
+ return False # ignore non-handled keyboard shortcuts
if reset:
self.reset_completion()
# Insert the char at the cursor position
- self.text = self.text[:self.pos]+key+self.text[self.pos:]
+ self.text = self.text[:self.pos] + key + self.text[self.pos:]
self.pos += len(key)
if reset:
self.rewrite_text()
@@ -472,11 +490,11 @@ class Input(Win):
if text[format_char] == '\n':
attr_char = '|'
else:
- attr_char = self.text_attributes[
- format_chars.index(text[format_char])]
+ attr_char = self.text_attributes[format_chars.index(
+ text[format_char])]
self.addstr(text[:format_char])
self.addstr(attr_char, curses.A_REVERSE)
- text = text[format_char+1:]
+ text = text[format_char + 1:]
if attr_char == 'o':
self._win.attrset(0)
elif attr_char == 'u':
@@ -503,7 +521,9 @@ class Input(Win):
self._win.erase()
if self.color:
self._win.attron(to_curses_attr(self.color))
- displayed_text = text[self.view_pos:self.view_pos+self.width-1].replace('\t', '\x18')
+ displayed_text = text[self.view_pos:
+ self.view_pos + self.width - 1].replace(
+ '\t', '\x18')
self._win.attrset(0)
self._addstr_colored_lite(displayed_text)
# Fill the rest of the line with the input color
@@ -512,7 +532,8 @@ class Input(Win):
size = self.width - x
self.addnstr(' ' * size, size, to_curses_attr(self.color))
self.addstr(0,
- poopt.wcswidth(displayed_text[:self.pos-self.view_pos]), '')
+ poopt.wcswidth(displayed_text[:self.pos - self.view_pos]),
+ '')
if self.color:
self._win.attroff(to_curses_attr(self.color))
curses.curs_set(1)
@@ -557,6 +578,7 @@ class Input(Win):
self.clear_text()
return txt
+
class HistoryInput(Input):
"""
An input with colors and stuff, plus an history
@@ -641,13 +663,14 @@ class HistoryInput(Input):
self.key_end()
return True
+
class MessageInput(HistoryInput):
"""
The input featuring history and that is being used in
Conversation, Muc and Private tabs
Also letting the user enter colors or other text markups
"""
- history = list() # The history is common to all MessageInput
+ history = list() # The history is common to all MessageInput
def __init__(self):
HistoryInput.__init__(self)
@@ -662,11 +685,13 @@ class MessageInput(HistoryInput):
"""
Read one more char (c), add the corresponding char from formats_char to the text string
"""
+
def cb(attr_char):
if attr_char in self.text_attributes:
char = format_chars[self.text_attributes.index(attr_char)]
self.do_command(char, False)
self.rewrite_text()
+
keyboard.continuation_keys_callback = cb
def key_enter(self):
@@ -682,6 +707,7 @@ class MessageInput(HistoryInput):
self.clear_text()
return txt
+
class CommandInput(HistoryInput):
"""
An input with an help message in the left, with three given callbacks:
@@ -769,4 +795,3 @@ class CommandInput(HistoryInput):
# add the message to history, but avoid duplicates
self.history.insert(0, txt)
self.histo_pos = -1
-
diff --git a/poezio/windows/list.py b/poezio/windows/list.py
index c0dc44cf..1f51e88f 100644
--- a/poezio/windows/list.py
+++ b/poezio/windows/list.py
@@ -16,12 +16,13 @@ class ListWin(Win):
A list (with no depth, so not for the roster) that can be
scrolled up and down, with one selected line at a time
"""
+
def __init__(self, columns, with_headers=True):
Win.__init__(self)
- self._columns = columns # a dict {'column_name': tuple_index}
- self._columns_sizes = {} # a dict {'column_name': size}
- self.sorted_by = (None, None) # for example: ('name', '↑')
- self.lines = [] # a list of dicts
+ self._columns = columns # a dict {'column_name': tuple_index}
+ self._columns_sizes = {} # a dict {'column_name': size}
+ self.sorted_by = (None, None) # for example: ('name', '↑')
+ self.lines = [] # a list of dicts
self._selected_row = 0
self._starting_pos = 0 # The column number from which we start the refresh
@@ -55,8 +56,8 @@ class ListWin(Win):
elif asc:
self.lines.sort(key=lambda x: x[self._columns[col_name]])
else:
- self.lines.sort(key=lambda x: x[self._columns[col_name]],
- reverse=True)
+ self.lines.sort(
+ key=lambda x: x[self._columns[col_name]], reverse=True)
self.refresh()
curses.doupdate()
@@ -87,7 +88,7 @@ class ListWin(Win):
def refresh(self):
log.debug('Refresh: %s', self.__class__.__name__)
self._win.erase()
- lines = self.lines[self._starting_pos:self._starting_pos+self.height]
+ lines = self.lines[self._starting_pos:self._starting_pos + self.height]
for y, line in enumerate(lines):
x = 0
for col in self._columns.items():
@@ -96,12 +97,13 @@ class ListWin(Win):
except KeyError:
txt = ''
size = self._columns_sizes[col[0]]
- txt += ' ' * (size-len(txt))
+ txt += ' ' * (size - len(txt))
if not txt:
continue
if line is self.lines[self._selected_row]:
self.addstr(y, x, txt[:size],
- to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ to_curses_attr(
+ get_theme().COLOR_INFORMATION_BAR))
else:
self.addstr(y, x, txt[:size])
x += size
@@ -138,7 +140,7 @@ class ListWin(Win):
return
self._selected_row += self.height
if self._selected_row > len(self.lines) - 1:
- self._selected_row = len(self.lines) -1
+ self._selected_row = len(self.lines) - 1
while self._selected_row >= self._starting_pos + self.height:
self._starting_pos += self.height // 2
if self._starting_pos < 0:
@@ -155,10 +157,12 @@ class ListWin(Win):
self._starting_pos -= self.height // 2
return True
+
class ColumnHeaderWin(Win):
"""
A class displaying the column's names
"""
+
def __init__(self, columns):
Win.__init__(self)
self._columns = columns
@@ -186,11 +190,14 @@ class ColumnHeaderWin(Win):
txt += get_theme().CHAR_COLUMN_DESC
#⇓⇑↑↓⇧⇩▲▼
size = self._columns_sizes[col]
- txt += ' ' * (size-len(txt))
+ txt += ' ' * (size - len(txt))
if col in self._column_sel:
- self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER_SEL))
+ self.addstr(0, x, txt,
+ to_curses_attr(
+ get_theme().COLOR_COLUMN_HEADER_SEL))
else:
- self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER))
+ self.addstr(0, x, txt,
+ to_curses_attr(get_theme().COLOR_COLUMN_HEADER))
x += size
self._refresh()
@@ -214,7 +221,7 @@ class ColumnHeaderWin(Win):
if self._column_sel in self._columns:
index = self._columns.index(self._column_sel)
if index > 1:
- index = index -1
+ index = index - 1
else:
index = 0
else:
@@ -225,12 +232,11 @@ class ColumnHeaderWin(Win):
def sel_column_right(self):
if self._column_sel in self._columns:
index = self._columns.index(self._column_sel)
- if index < len(self._columns)-2:
- index = index +1
+ if index < len(self._columns) - 2:
+ index = index + 1
else:
- index = len(self._columns) -1
+ index = len(self._columns) - 1
else:
index = len(self._columns) - 1
self._column_sel = self._columns[index]
self.refresh()
-
diff --git a/poezio/windows/misc.py b/poezio/windows/misc.py
index d3d6783e..71027b7c 100644
--- a/poezio/windows/misc.py
+++ b/poezio/windows/misc.py
@@ -10,11 +10,13 @@ import curses
from poezio.windows.base_wins import Win
from poezio.theming import get_theme, to_curses_attr
+
class VerticalSeparator(Win):
"""
Just a one-column window, with just a line in it, that is
refreshed only on resize, but never on refresh, for efficiency
"""
+
def rewrite_line(self):
self._win.vline(0, 0, curses.ACS_VLINE, self.height,
to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR))
@@ -54,4 +56,3 @@ class SimpleTextWin(Win):
for y, line in enumerate(self.built_lines):
self.addstr_colored(line, y, 0)
self._refresh()
-
diff --git a/poezio/windows/muc.py b/poezio/windows/muc.py
index 553a2cfa..4251c279 100644
--- a/poezio/windows/muc.py
+++ b/poezio/windows/muc.py
@@ -13,12 +13,15 @@ from poezio import poopt
from poezio.config import config
from poezio.theming import to_curses_attr, get_theme
+
def userlist_to_cache(userlist):
result = []
for user in userlist:
- result.append((user.nick, user.status, user.chatstate, user.affiliation, user.role))
+ result.append((user.nick, user.status, user.chatstate,
+ user.affiliation, user.role))
return result
+
class UserList(Win):
def __init__(self):
Win.__init__(self)
@@ -26,23 +29,23 @@ class UserList(Win):
self.cache = []
def scroll_up(self):
- self.pos += self.height-1
+ self.pos += self.height - 1
return True
def scroll_down(self):
pos = self.pos
- self.pos -= self.height-1
+ self.pos -= self.height - 1
if self.pos < 0:
self.pos = 0
return self.pos != pos
def draw_plus(self, y):
- self.addstr(y, self.width-2, '++', to_curses_attr(get_theme().COLOR_MORE_INDICATOR))
-
+ self.addstr(y, self.width - 2, '++',
+ to_curses_attr(get_theme().COLOR_MORE_INDICATOR))
def refresh_if_changed(self, users):
old = self.cache
- new = userlist_to_cache(users[self.pos:self.pos+self.height])
+ new = userlist_to_cache(users[self.pos:self.pos + self.height])
if len(old) != len(new):
self.cache = new
self.refresh(users)
@@ -56,7 +59,7 @@ class UserList(Win):
def refresh(self, users):
log.debug('Refresh: %s', self.__class__.__name__)
if config.get('hide_user_list'):
- return # do not refresh if this win is hidden.
+ return # do not refresh if this win is hidden.
if len(users) < self.height:
self.pos = 0
elif self.pos >= len(users) - self.height and self.pos != 0:
@@ -69,12 +72,12 @@ class UserList(Win):
else:
y = 0
- for user in users[self.pos:self.pos+self.height]:
+ for user in users[self.pos:self.pos + self.height]:
self.draw_role_affiliation(y, user)
self.draw_status_chatstate(y, user)
self.addstr(y, 2,
- poopt.cut_by_columns(user.nick, self.width - 2),
- to_curses_attr(user.color))
+ poopt.cut_by_columns(user.nick, self.width - 2),
+ to_curses_attr(user.color))
if asc_sort:
y -= 1
else:
@@ -84,14 +87,14 @@ class UserList(Win):
# draw indicators of position in the list
if self.pos > 0:
if asc_sort:
- self.draw_plus(self.height-1)
+ self.draw_plus(self.height - 1)
else:
self.draw_plus(0)
if self.pos + self.height < len(users):
if asc_sort:
self.draw_plus(0)
else:
- self.draw_plus(self.height-1)
+ self.draw_plus(self.height - 1)
self._refresh()
def draw_role_affiliation(self, y, user):
@@ -119,6 +122,7 @@ class UserList(Win):
self._win.vline(0, 0, curses.ACS_VLINE, self.height)
self._win.attroff(separator)
+
class Topic(Win):
def __init__(self):
Win.__init__(self)
@@ -128,17 +132,16 @@ class Topic(Win):
log.debug('Refresh: %s', self.__class__.__name__)
self._win.erase()
if topic:
- msg = topic[:self.width-1]
+ msg = topic[:self.width - 1]
else:
- msg = self._message[:self.width-1]
+ msg = self._message[:self.width - 1]
self.addstr(0, 0, msg, to_curses_attr(get_theme().COLOR_TOPIC_BAR))
_, x = self._win.getyx()
remaining_size = self.width - x
if remaining_size:
- self.addnstr(' '*remaining_size, remaining_size,
+ self.addnstr(' ' * remaining_size, remaining_size,
to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self._refresh()
def set_message(self, message):
self._message = message
-
diff --git a/poezio/windows/roster_win.py b/poezio/windows/roster_win.py
index 73191e84..7b1e5ba7 100644
--- a/poezio/windows/roster_win.py
+++ b/poezio/windows/roster_win.py
@@ -17,11 +17,10 @@ from poezio.theming import get_theme, to_curses_attr
class RosterWin(Win):
-
def __init__(self):
Win.__init__(self)
- self.pos = 0 # cursor position in the contact list
- self.start_pos = 1 # position of the start of the display
+ self.pos = 0 # cursor position in the contact list
+ self.start_pos = 1 # position of the start of the display
self.selected_row = None
self.roster_cache = []
@@ -34,15 +33,15 @@ class RosterWin(Win):
Return True if we scrolled, False otherwise
"""
pos = self.pos
- if self.pos < self.roster_len-number:
+ if self.pos < self.roster_len - number:
self.pos += number
else:
self.pos = self.roster_len - 1
- if self.pos >= self.start_pos-1 + self.height-1:
+ if self.pos >= self.start_pos - 1 + self.height - 1:
if number == 1:
self.scroll_down(8)
else:
- self.scroll_down(self.pos-self.start_pos - self.height // 2)
+ self.scroll_down(self.pos - self.start_pos - self.height // 2)
self.update_pos()
return pos != self.pos
@@ -51,7 +50,7 @@ class RosterWin(Win):
Return True if we scrolled, False otherwise
"""
pos = self.pos
- if self.pos-number >= 0:
+ if self.pos - number >= 0:
self.pos -= number
else:
self.pos = 0
@@ -59,7 +58,7 @@ class RosterWin(Win):
if number == 1:
self.scroll_up(8)
else:
- self.scroll_up(self.start_pos-self.pos + self.height // 2)
+ self.scroll_up(self.start_pos - self.pos + self.height // 2)
self.update_pos()
return pos != self.pos
@@ -71,10 +70,10 @@ class RosterWin(Win):
def scroll_down(self, number=8):
pos = self.start_pos
- if self.start_pos + number <= self.roster_len-1:
+ if self.start_pos + number <= self.roster_len - 1:
self.start_pos += number
else:
- self.start_pos = self.roster_len-1
+ self.start_pos = self.roster_len - 1
return self.start_pos != pos
def scroll_up(self, number=8):
@@ -106,21 +105,23 @@ class RosterWin(Win):
# build the cache
for group in roster.get_groups(group_sort):
contacts_filtered = group.get_contacts()
- if (not show_offline and group.get_nb_connected_contacts() == 0) or not contacts_filtered:
- continue # Ignore empty groups
+ if (not show_offline and group.get_nb_connected_contacts() == 0
+ ) or not contacts_filtered:
+ continue # Ignore empty groups
self.roster_cache.append(group)
if group.folded:
- continue # ignore folded groups
+ continue # ignore folded groups
for contact in group.get_contacts(sort=sort):
if not show_offline and len(contact) == 0:
- continue # ignore offline contacts
+ continue # ignore offline contacts
self.roster_cache.append(contact)
if not contact.folded(group.name):
for resource in contact.get_resources():
self.roster_cache.append(resource)
roster.last_built = datetime.now()
if self.selected_row in self.roster_cache:
- if self.pos < self.roster_len and self.roster_cache[self.pos] != self.selected_row:
+ if self.pos < self.roster_len and self.roster_cache[self.
+ pos] != self.selected_row:
self.pos = self.roster_cache.index(self.selected_row)
def refresh(self, roster):
@@ -131,7 +132,8 @@ class RosterWin(Win):
log.debug('Refresh: %s', self.__class__.__name__)
self.build_roster_cache(roster)
# make sure we are within bounds
- self.move_cursor_up((self.roster_len + self.pos) if self.pos >= self.roster_len else 0)
+ self.move_cursor_up((self.roster_len + self.pos)
+ if self.pos >= self.roster_len else 0)
if not self.roster_cache:
self.selected_row = None
self._win.erase()
@@ -140,10 +142,12 @@ class RosterWin(Win):
y = 1
group = "none"
# scroll down if needed
- if self.start_pos+self.height <= self.pos+2:
- self.scroll_down(self.pos - self.start_pos - self.height + (self.height//2))
+ if self.start_pos + self.height <= self.pos + 2:
+ self.scroll_down(self.pos - self.start_pos - self.height +
+ (self.height // 2))
# draw the roster from the cache
- roster_view = self.roster_cache[self.start_pos-1:self.start_pos+self.height]
+ roster_view = self.roster_cache[self.start_pos - 1:
+ self.start_pos + self.height]
options = {
'show_roster_sub': config.get('show_roster_subscriptions'),
@@ -153,7 +157,7 @@ class RosterWin(Win):
for item in roster_view:
draw_selected = False
- if y -2 + self.start_pos == self.pos:
+ if y - 2 + self.start_pos == self.pos:
draw_selected = True
self.selected_row = item
@@ -161,7 +165,8 @@ class RosterWin(Win):
self.draw_group(y, item, draw_selected)
group = item.name
elif isinstance(item, Contact):
- self.draw_contact_line(y, item, draw_selected, group, **options)
+ self.draw_contact_line(y, item, draw_selected, group,
+ **options)
elif isinstance(item, Resource):
self.draw_resource_line(y, item, draw_selected)
@@ -169,26 +174,25 @@ class RosterWin(Win):
if self.start_pos > 1:
self.draw_plus(1)
- if self.start_pos + self.height-2 < self.roster_len:
- self.draw_plus(self.height-1)
+ if self.start_pos + self.height - 2 < self.roster_len:
+ self.draw_plus(self.height - 1)
self._refresh()
-
def draw_plus(self, y):
"""
Draw the indicator that shows that
the list is longer than what is displayed
"""
- self.addstr(y, self.width-5, '++++', to_curses_attr(get_theme().COLOR_MORE_INDICATOR))
+ self.addstr(y, self.width - 5, '++++',
+ to_curses_attr(get_theme().COLOR_MORE_INDICATOR))
def draw_roster_information(self, roster):
"""
The header at the top
"""
- self.addstr('Roster: %s/%s contacts' % (
- roster.get_nb_connected_contacts(),
- len(roster)),
- to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr('Roster: %s/%s contacts' %
+ (roster.get_nb_connected_contacts(), len(roster)),
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
def draw_group(self, y, group, colored):
@@ -202,7 +206,8 @@ class RosterWin(Win):
else:
self.addstr(y, 0, '[-] ')
contacts = " (%s/%s)" % (group.get_nb_connected_contacts(), len(group))
- self.addstr(y, 4, self.truncate_name(group.name, len(contacts)+4) + contacts)
+ self.addstr(
+ y, 4, self.truncate_name(group.name, len(contacts) + 4) + contacts)
if colored:
self._win.attroff(to_curses_attr(get_theme().COLOR_SELECTED_ROW))
self.finish_line()
@@ -212,8 +217,14 @@ class RosterWin(Win):
return name
return name[:self.width - added - 1] + '…'
- def draw_contact_line(self, y, contact, colored, group, show_roster_sub=False,
- show_s2s_errors=True, show_roster_jids=False):
+ def draw_contact_line(self,
+ y,
+ contact,
+ colored,
+ group,
+ show_roster_sub=False,
+ show_s2s_errors=True,
+ show_roster_jids=False):
"""
Draw on a line all information about one contact.
This is basically the highest priority resource's information
@@ -255,37 +266,51 @@ class RosterWin(Win):
added += len(get_theme().CHAR_ROSTER_ACTIVITY)
if contact.gaming:
added += len(get_theme().CHAR_ROSTER_GAMING)
- if show_roster_sub in ('all', 'incomplete', 'to', 'from', 'both', 'none'):
- added += len(theme.char_subscription(contact.subscription, keep=show_roster_sub))
+ if show_roster_sub in ('all', 'incomplete', 'to', 'from', 'both',
+ 'none'):
+ added += len(
+ theme.char_subscription(
+ contact.subscription, keep=show_roster_sub))
if not show_roster_jids and contact.name:
display_name = '%s' % contact.name
elif contact.name and contact.name != contact.bare_jid:
display_name = '%s (%s)' % (contact.name, contact.bare_jid)
else:
- display_name = '%s' % (contact.bare_jid,)
+ display_name = '%s' % (contact.bare_jid, )
display_name = self.truncate_name(display_name, added) + nb
if colored:
- self.addstr(display_name, to_curses_attr(get_theme().COLOR_SELECTED_ROW))
+ self.addstr(display_name,
+ to_curses_attr(get_theme().COLOR_SELECTED_ROW))
else:
self.addstr(display_name)
- if show_roster_sub in ('all', 'incomplete', 'to', 'from', 'both', 'none'):
- self.addstr(theme.char_subscription(contact.subscription, keep=show_roster_sub), to_curses_attr(theme.COLOR_ROSTER_SUBSCRIPTION))
+ if show_roster_sub in ('all', 'incomplete', 'to', 'from', 'both',
+ 'none'):
+ self.addstr(
+ theme.char_subscription(
+ contact.subscription, keep=show_roster_sub),
+ to_curses_attr(theme.COLOR_ROSTER_SUBSCRIPTION))
if contact.ask:
- self.addstr(get_theme().CHAR_ROSTER_ASKED, to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
+ self.addstr(get_theme().CHAR_ROSTER_ASKED,
+ to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
if show_s2s_errors and contact.error:
- self.addstr(get_theme().CHAR_ROSTER_ERROR, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
+ self.addstr(get_theme().CHAR_ROSTER_ERROR,
+ to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
if contact.tune:
- self.addstr(get_theme().CHAR_ROSTER_TUNE, to_curses_attr(get_theme().COLOR_ROSTER_TUNE))
+ self.addstr(get_theme().CHAR_ROSTER_TUNE,
+ to_curses_attr(get_theme().COLOR_ROSTER_TUNE))
if contact.activity:
- self.addstr(get_theme().CHAR_ROSTER_ACTIVITY, to_curses_attr(get_theme().COLOR_ROSTER_ACTIVITY))
+ self.addstr(get_theme().CHAR_ROSTER_ACTIVITY,
+ to_curses_attr(get_theme().COLOR_ROSTER_ACTIVITY))
if contact.mood:
- self.addstr(get_theme().CHAR_ROSTER_MOOD, to_curses_attr(get_theme().COLOR_ROSTER_MOOD))
+ self.addstr(get_theme().CHAR_ROSTER_MOOD,
+ to_curses_attr(get_theme().COLOR_ROSTER_MOOD))
if contact.gaming:
- self.addstr(get_theme().CHAR_ROSTER_GAMING, to_curses_attr(get_theme().COLOR_ROSTER_GAMING))
+ self.addstr(get_theme().CHAR_ROSTER_GAMING,
+ to_curses_attr(get_theme().COLOR_ROSTER_GAMING))
self.finish_line()
def draw_resource_line(self, y, resource, colored):
@@ -295,7 +320,9 @@ class RosterWin(Win):
color = get_theme().color_show(resource.presence)
self.addstr(y, 4, get_theme().CHAR_STATUS, to_curses_attr(color))
if colored:
- self.addstr(y, 8, self.truncate_name(str(resource.jid), 6), to_curses_attr(get_theme().COLOR_SELECTED_ROW))
+ self.addstr(y, 8,
+ self.truncate_name(str(resource.jid), 6),
+ to_curses_attr(get_theme().COLOR_SELECTED_ROW))
else:
self.addstr(y, 8, self.truncate_name(str(resource.jid), 6))
self.finish_line()
@@ -308,6 +335,7 @@ class RosterWin(Win):
return self.roster_cache[self.pos]
return None
+
class ContactInfoWin(Win):
def draw_contact_info(self, contact):
"""
@@ -319,23 +347,27 @@ class ContactInfoWin(Win):
elif resource:
jid = resource.jid
else:
- jid = 'example@example.com' # should never happen
+ jid = 'example@example.com' # should never happen
if resource:
presence = resource.presence
else:
presence = 'unavailable'
i = 0
- self.addstr(0, 0, '%s (%s)'%(jid, presence,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(0, 0, '%s (%s)' % (
+ jid,
+ presence,
+ ), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
i += 1
- self.addstr(i, 0, 'Subscription: %s' % (contact.subscription,))
+ self.addstr(i, 0, 'Subscription: %s' % (contact.subscription, ))
self.finish_line()
i += 1
if contact.ask:
if contact.ask == 'asked':
- self.addstr(i, 0, 'Ask: %s' % (contact.ask,), to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
+ self.addstr(i, 0, 'Ask: %s' % (contact.ask, ),
+ to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
else:
- self.addstr(i, 0, 'Ask: %s' % (contact.ask,))
+ self.addstr(i, 0, 'Ask: %s' % (contact.ask, ))
self.finish_line()
i += 1
if resource:
@@ -344,27 +376,34 @@ class ContactInfoWin(Win):
i += 1
if contact.error:
- self.addstr(i, 0, 'Error: %s' % contact.error, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
+ self.addstr(i, 0, 'Error: %s' % contact.error,
+ to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
self.finish_line()
i += 1
if contact.tune:
- self.addstr(i, 0, 'Tune: %s' % common.format_tune_string(contact.tune), to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
+ self.addstr(i, 0,
+ 'Tune: %s' % common.format_tune_string(contact.tune),
+ to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
self.finish_line()
i += 1
if contact.mood:
- self.addstr(i, 0, 'Mood: %s' % contact.mood, to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
+ self.addstr(i, 0, 'Mood: %s' % contact.mood,
+ to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
self.finish_line()
i += 1
if contact.activity:
- self.addstr(i, 0, 'Activity: %s' % contact.activity, to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
+ self.addstr(i, 0, 'Activity: %s' % contact.activity,
+ to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
self.finish_line()
i += 1
if contact.gaming:
- self.addstr(i, 0, 'Game: %s' % common.format_gaming_string(contact.gaming), to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
+ self.addstr(
+ i, 0, 'Game: %s' % common.format_gaming_string(contact.gaming),
+ to_curses_attr(get_theme().COLOR_NORMAL_TEXT))
self.finish_line()
i += 1
@@ -372,7 +411,8 @@ class ContactInfoWin(Win):
"""
draw the group information
"""
- self.addstr(0, 0, group.name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
+ self.addstr(0, 0, group.name,
+ to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
def refresh(self, selected_row):
diff --git a/poezio/windows/text_win.py b/poezio/windows/text_win.py
index 4b16043d..1521ee34 100644
--- a/poezio/windows/text_win.py
+++ b/poezio/windows/text_win.py
@@ -21,6 +21,7 @@ from poezio.theming import to_curses_attr, get_theme, dump_tuple
# text_end are the position delimiting the text in this line.
class Line:
__slots__ = ('msg', 'start_pos', 'end_pos', 'prepend')
+
def __init__(self, msg, start_pos, end_pos, prepend):
self.msg = msg
self.start_pos = start_pos
@@ -35,7 +36,7 @@ class BaseTextWin(Win):
Win.__init__(self)
self.lines_nb_limit = lines_nb_limit
self.pos = 0
- self.built_lines = [] # Each new message is built and kept here.
+ self.built_lines = [] # Each new message is built and kept here.
# on resize, we rebuild all the messages
self.lock = False
@@ -73,13 +74,20 @@ class BaseTextWin(Win):
self.pos = 0
return self.pos != pos
- def build_new_message(self, message, history=None, clean=True, highlight=False, timestamp=False, nick_size=10):
+ def build_new_message(self,
+ message,
+ history=None,
+ clean=True,
+ highlight=False,
+ timestamp=False,
+ nick_size=10):
"""
Take one message, build it and add it to the list
Return the number of lines that are built for the given
message.
"""
- lines = self.build_message(message, timestamp=timestamp, nick_size=nick_size)
+ lines = self.build_message(
+ message, timestamp=timestamp, nick_size=nick_size)
if self.lock:
self.lock_buffer.extend(lines)
else:
@@ -143,16 +151,22 @@ class BaseTextWin(Win):
with_timestamps = config.get('show_timestamps')
nick_size = config.get('max_nick_length')
for message in room.messages:
- self.build_new_message(message, clean=False, timestamp=with_timestamps, nick_size=nick_size)
+ self.build_new_message(
+ message,
+ clean=False,
+ timestamp=with_timestamps,
+ nick_size=nick_size)
if self.separator_after is message:
self.build_new_message(None)
while len(self.built_lines) > self.lines_nb_limit:
self.built_lines.pop(0)
def __del__(self):
- log.debug('** TextWin: deleting %s built lines', (len(self.built_lines)))
+ log.debug('** TextWin: deleting %s built lines',
+ (len(self.built_lines)))
del self.built_lines
+
class TextWin(BaseTextWin):
def __init__(self, lines_nb_limit=None):
BaseTextWin.__init__(self, lines_nb_limit)
@@ -179,8 +193,8 @@ class TextWin(BaseTextWin):
highlights, scroll to the end of the buffer.
"""
log.debug('Going to the next highlight…')
- if (not self.highlights or self.hl_pos != self.hl_pos or
- self.hl_pos >= len(self.highlights) - 1):
+ if (not self.highlights or self.hl_pos != self.hl_pos
+ or self.hl_pos >= len(self.highlights) - 1):
self.hl_pos = float('nan')
self.pos = 0
return
@@ -196,7 +210,7 @@ class TextWin(BaseTextWin):
try:
pos = self.built_lines.index(hl)
except ValueError:
- self.highlights = self.highlights[self.hl_pos+1:]
+ self.highlights = self.highlights[self.hl_pos + 1:]
if not self.highlights:
self.hl_pos = float('nan')
self.pos = 0
@@ -230,7 +244,7 @@ class TextWin(BaseTextWin):
try:
pos = self.built_lines.index(hl)
except ValueError:
- self.highlights = self.highlights[self.hl_pos+1:]
+ self.highlights = self.highlights[self.hl_pos + 1:]
if not self.highlights:
self.hl_pos = float('nan')
self.pos = 0
@@ -247,7 +261,8 @@ class TextWin(BaseTextWin):
present, scroll at the top of the window
"""
if None in self.built_lines:
- self.pos = len(self.built_lines) - self.built_lines.index(None) - self.height + 1
+ self.pos = len(self.built_lines) - self.built_lines.index(
+ None) - self.height + 1
if self.pos < 0:
self.pos = 0
else:
@@ -257,7 +272,8 @@ class TextWin(BaseTextWin):
# Make “next highlight” work afterwards. This makes it easy to
# review all the highlights since the separator was placed, in
# the correct order.
- self.hl_pos = len(self.highlights) - self.nb_of_highlights_after_separator - 1
+ self.hl_pos = len(
+ self.highlights) - self.nb_of_highlights_after_separator - 1
log.debug("self.hl_pos = %s", self.hl_pos)
def remove_line_separator(self):
@@ -282,13 +298,20 @@ class TextWin(BaseTextWin):
if room and room.messages:
self.separator_after = room.messages[-1]
- def build_new_message(self, message, history=None, clean=True, highlight=False, timestamp=False, nick_size=10):
+ def build_new_message(self,
+ message,
+ history=None,
+ clean=True,
+ highlight=False,
+ timestamp=False,
+ nick_size=10):
"""
Take one message, build it and add it to the list
Return the number of lines that are built for the given
message.
"""
- lines = self.build_message(message, timestamp=timestamp, nick_size=nick_size)
+ lines = self.build_message(
+ message, timestamp=timestamp, nick_size=nick_size)
if self.lock:
self.lock_buffer.extend(lines)
else:
@@ -316,8 +339,8 @@ class TextWin(BaseTextWin):
if not txt:
return []
if len(message.str_time) > 8:
- default_color = (FORMAT_CHAR + dump_tuple(get_theme().COLOR_LOG_MSG)
- + '}')
+ default_color = (
+ FORMAT_CHAR + dump_tuple(get_theme().COLOR_LOG_MSG) + '}')
else:
default_color = None
ret = []
@@ -329,11 +352,11 @@ class TextWin(BaseTextWin):
else:
offset += poopt.wcswidth(get_theme().CHAR_NACK) + 1
if nick:
- offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length
+ offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length
if message.revisions > 0:
offset += ceil(log10(message.revisions + 1))
if message.me:
- offset += 1 # '* ' before and ' ' after
+ offset += 1 # '* ' before and ' ' after
if timestamp:
if message.str_time:
offset += 1 + len(message.str_time)
@@ -341,11 +364,15 @@ class TextWin(BaseTextWin):
offset += 1
if get_theme().CHAR_TIME_RIGHT and message.str_time:
offset += 1
- lines = poopt.cut_text(txt, self.width-offset-1)
+ lines = poopt.cut_text(txt, self.width - offset - 1)
prepend = default_color if default_color else ''
attrs = []
for line in lines:
- saved = Line(msg=message, start_pos=line[0], end_pos=line[1], prepend=prepend)
+ saved = Line(
+ msg=message,
+ start_pos=line[0],
+ end_pos=line[1],
+ prepend=prepend)
attrs = parse_attrs(message.txt[line[0]:line[1]], attrs)
if attrs:
prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs)
@@ -364,7 +391,7 @@ class TextWin(BaseTextWin):
if self.pos == 0:
lines = self.built_lines[-self.height:]
else:
- lines = self.built_lines[-self.height-self.pos:-self.pos]
+ lines = self.built_lines[-self.height - self.pos:-self.pos]
with_timestamps = config.get("show_timestamps")
nick_size = config.get("max_nick_length")
self._win.move(0, 0)
@@ -374,14 +401,17 @@ class TextWin(BaseTextWin):
if line:
msg = line.msg
if line.start_pos == 0:
- offset = self.write_pre_msg(msg, with_timestamps, nick_size)
+ offset = self.write_pre_msg(msg, with_timestamps,
+ nick_size)
elif y == 0:
- offset = self.compute_offset(msg, with_timestamps, nick_size)
- self.write_text(y, offset, line.prepend
- + line.msg.txt[line.start_pos:line.end_pos])
+ offset = self.compute_offset(msg, with_timestamps,
+ nick_size)
+ self.write_text(
+ y, offset,
+ line.prepend + line.msg.txt[line.start_pos:line.end_pos])
else:
self.write_line_separator(y)
- if y != self.height-1:
+ if y != self.height - 1:
self.addstr('\n')
self._win.attrset(0)
self._refresh()
@@ -391,7 +421,7 @@ class TextWin(BaseTextWin):
if with_timestamps and msg.str_time:
offset += poopt.wcswidth(msg.str_time) + 1
- if not msg.nickname: # not a message, nothing to do afterwards
+ if not msg.nickname: # not a message, nothing to do afterwards
return offset
nick = truncate_nick(msg.nickname, nick_size)
@@ -410,13 +440,12 @@ class TextWin(BaseTextWin):
offset += self.write_revisions(msg)
return offset
-
def write_pre_msg(self, msg, with_timestamps, nick_size):
offset = 0
if with_timestamps:
offset += self.write_time(msg.str_time)
- if not msg.nickname: # not a message, nothing to do afterwards
+ if not msg.nickname: # not a message, nothing to do afterwards
return offset
nick = truncate_nick(msg.nickname, nick_size)
@@ -448,7 +477,8 @@ class TextWin(BaseTextWin):
def write_revisions(self, msg):
if msg.revisions:
- self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE))
+ self._win.attron(
+ to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE))
self.addstr('%d' % msg.revisions)
self._win.attrset(0)
return ceil(log10(msg.revisions + 1))
@@ -457,8 +487,7 @@ class TextWin(BaseTextWin):
def write_line_separator(self, y):
char = get_theme().CHAR_NEW_TEXT_SEPARATOR
self.addnstr(y, 0,
- char * (self.width // len(char) - 1),
- self.width,
+ char * (self.width // len(char) - 1), self.width,
to_curses_attr(get_theme().COLOR_NEW_TEXT_SEPARATOR))
def write_ack(self):
@@ -505,23 +534,26 @@ class TextWin(BaseTextWin):
"""
with_timestamps = config.get('show_timestamps')
nick_size = config.get('max_nick_length')
- for i in range(len(self.built_lines)-1, -1, -1):
+ for i in range(len(self.built_lines) - 1, -1, -1):
if self.built_lines[i] and self.built_lines[i].msg.identifier == old_id:
index = i
while index >= 0 and self.built_lines[index] and self.built_lines[index].msg.identifier == old_id:
self.built_lines.pop(index)
index -= 1
index += 1
- lines = self.build_message(message, timestamp=with_timestamps, nick_size=nick_size)
+ lines = self.build_message(
+ message, timestamp=with_timestamps, nick_size=nick_size)
for line in lines:
self.built_lines.insert(index, line)
index += 1
break
def __del__(self):
- log.debug('** TextWin: deleting %s built lines', (len(self.built_lines)))
+ log.debug('** TextWin: deleting %s built lines',
+ (len(self.built_lines)))
del self.built_lines
+
class XMLTextWin(BaseTextWin):
def __init__(self):
BaseTextWin.__init__(self)
@@ -534,7 +566,7 @@ class XMLTextWin(BaseTextWin):
if self.pos == 0:
lines = self.built_lines[-self.height:]
else:
- lines = self.built_lines[-self.height-self.pos:-self.pos]
+ lines = self.built_lines[-self.height - self.pos:-self.pos]
self._win.move(0, 0)
self._win.erase()
for y, line in enumerate(lines):
@@ -548,7 +580,7 @@ class XMLTextWin(BaseTextWin):
self.write_time(msg.str_time)
self.write_prefix(msg.nickname, color)
self.addstr(' ')
- if y != self.height-1:
+ if y != self.height - 1:
self.addstr('\n')
self._win.attrset(0)
for y, line in enumerate(lines):
@@ -563,9 +595,10 @@ class XMLTextWin(BaseTextWin):
# space
offset += 1
- self.write_text(y, offset, line.prepend
- + line.msg.txt[line.start_pos:line.end_pos])
- if y != self.height-1:
+ self.write_text(
+ y, offset,
+ line.prepend + line.msg.txt[line.start_pos:line.end_pos])
+ if y != self.height - 1:
self.addstr('\n')
self._win.attrset(0)
self._refresh()
@@ -577,18 +610,22 @@ class XMLTextWin(BaseTextWin):
nick = truncate_nick(message.nickname, nick_size)
offset = 0
if nick:
- offset += poopt.wcswidth(nick) + 1 # + nick + ' ' length
+ offset += poopt.wcswidth(nick) + 1 # + nick + ' ' length
if message.str_time:
offset += 1 + len(message.str_time)
if get_theme().CHAR_TIME_LEFT and message.str_time:
offset += 1
if get_theme().CHAR_TIME_RIGHT and message.str_time:
offset += 1
- lines = poopt.cut_text(txt, self.width-offset-1)
+ lines = poopt.cut_text(txt, self.width - offset - 1)
prepend = default_color if default_color else ''
attrs = []
for line in lines:
- saved = Line(msg=message, start_pos=line[0], end_pos=line[1], prepend=prepend)
+ saved = Line(
+ msg=message,
+ start_pos=line[0],
+ end_pos=line[1],
+ prepend=prepend)
attrs = parse_attrs(message.txt[line[0]:line[1]], attrs)
if attrs:
prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs)
@@ -604,4 +641,3 @@ class XMLTextWin(BaseTextWin):
self._win.attron(to_curses_attr(color))
self.addstr(truncate_nick(nickname))
self._win.attroff(to_curses_attr(color))
-
diff --git a/poezio/xhtml.py b/poezio/xhtml.py
index 2f73cd3d..ee98f23d 100644
--- a/poezio/xhtml.py
+++ b/poezio/xhtml.py
@@ -4,7 +4,6 @@
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.
-
"""
Various methods to convert
shell colors to poezio colors,
@@ -26,7 +25,7 @@ from xml.sax import saxutils
from slixmpp.xmlstream import ET
from poezio.config import config
-digits = '0123456789' # never trust the modules
+digits = '0123456789' # never trust the modules
XHTML_NS = 'http://www.w3.org/1999/xhtml'
@@ -190,8 +189,11 @@ poezio_format_trim = re.compile(r'(\x19\d+}|\x19\d|\x19[buaio]|\x19o)+\x19o')
xhtml_simple_attr_re = re.compile(r'\x19\d')
-def get_body_from_message_stanza(message, use_xhtml=False,
- tmp_dir=None, extract_images=False):
+
+def get_body_from_message_stanza(message,
+ use_xhtml=False,
+ tmp_dir=None,
+ extract_images=False):
"""
Returns a string with xhtml markups converted to
poezio colors if there's an xhtml_im element, or
@@ -205,11 +207,12 @@ def get_body_from_message_stanza(message, use_xhtml=False,
xhtml_body = xhtml.find('{http://www.w3.org/1999/xhtml}body')
if xhtml_body is None:
return message['body']
- content = xhtml_to_poezio_colors(xhtml_body, tmp_dir=tmp_dir,
- extract_images=extract_images)
+ content = xhtml_to_poezio_colors(
+ xhtml_body, tmp_dir=tmp_dir, extract_images=extract_images)
content = content if content else message['body']
return content or " "
+
def ncurses_color_to_html(color):
"""
Takes an int between 0 and 256 and returns
@@ -219,8 +222,8 @@ def ncurses_color_to_html(color):
if color <= 15:
try:
(r, g, b) = curses.color_content(color)
- except: # fallback in faulty terminals (e.g. xterm)
- (r, g, b) = curses.color_content(color%8)
+ except: # fallback in faulty terminals (e.g. xterm)
+ (r, g, b) = curses.color_content(color % 8)
r = r / 1000 * 6 - 0.01
g = g / 1000 * 6 - 0.01
b = b / 1000 * 6 - 0.01
@@ -234,7 +237,9 @@ def ncurses_color_to_html(color):
else:
color -= 232
r = g = b = color / 24 * 6
- return '#%02X%02X%02X' % (int(r*256/6), int(g*256/6), int(b*256/6))
+ return '#%02X%02X%02X' % (int(r * 256 / 6), int(g * 256 / 6),
+ int(b * 256 / 6))
+
def _parse_css_color(name):
if name[0] == '#':
@@ -257,11 +262,12 @@ def _parse_css_color(name):
if r == g == b:
return int(232 + 1.54 * r)
mult = 0.3984
- return 6*6*int(mult*r) + 6*int(mult*g) + int(mult*b) + 16
+ return 6 * 6 * int(mult * r) + 6 * int(mult * g) + int(mult * b) + 16
if name in colors:
return colors[name]
return -1
+
def _parse_css(css):
shell = ''
rules = css.split(';')
@@ -272,7 +278,7 @@ def _parse_css(css):
key = key.strip()
value = value.strip()
if key == 'background-color':
- pass#shell += '\x191'
+ pass #shell += '\x191'
elif key == 'color':
color = _parse_css_color(value)
if color != -1:
@@ -292,13 +298,17 @@ def _parse_css(css):
shell += '\x19a'
return shell
+
def _trim(string):
return re.sub(whitespace_re, ' ', string)
+
def get_hash(data: bytes) -> str:
# Currently using SHA-256, this might change in the future.
# base64 gives shorter hashes than hex, so use that.
- return b64encode(hashlib.sha256(data).digest()).rstrip(b'=').replace(b'/', b'-').decode()
+ return b64encode(hashlib.sha256(data).digest()).rstrip(b'=').replace(
+ b'/', b'-').decode()
+
class XHTMLHandler(sax.ContentHandler):
def __init__(self, force_ns=False, tmp_dir=None, extract_images=False):
@@ -317,7 +327,8 @@ class XHTMLHandler(sax.ContentHandler):
@property
def result(self):
- sanitized = re.sub(poezio_color_double, r'\1', ''.join(self.builder).strip())
+ sanitized = re.sub(poezio_color_double, r'\1',
+ ''.join(self.builder).strip())
return re.sub(poezio_format_trim, '\x19o', sanitized)
def append_formatting(self, formatting):
@@ -336,7 +347,10 @@ class XHTMLHandler(sax.ContentHandler):
return
builder = self.builder
- attrs = {name: value for ((ns, name), value) in attrs.items() if ns is None}
+ attrs = {
+ name: value
+ for ((ns, name), value) in attrs.items() if ns is None
+ }
self.attrs.append(attrs)
if 'style' in attrs and self.enable_css_parsing:
@@ -357,7 +371,9 @@ class XHTMLHandler(sax.ContentHandler):
self.append_formatting('\x19i')
elif name == 'img':
if re.match(xhtml_data_re, attrs['src']) and self.extract_images:
- type_, data = [i for i in re.split(xhtml_data_re, attrs['src']) if i]
+ type_, data = [
+ i for i in re.split(xhtml_data_re, attrs['src']) if i
+ ]
bin_data = b64decode(unquote(data))
filename = get_hash(bin_data) + '.' + type_
filepath = path.join(self.tmp_dir, filename)
@@ -408,8 +424,10 @@ class XHTMLHandler(sax.ContentHandler):
if name == 'a':
self.pop_formatting()
# do not display the link twice
- text_elements = [x for x in self.builder[self.a_start:]
- if not x.startswith('\x19')]
+ text_elements = [
+ x for x in self.builder[self.a_start:]
+ if not x.startswith('\x19')
+ ]
link_text = ''.join(text_elements).strip()
if 'href' in attrs and attrs['href'] != link_text:
builder.append(' (%s)' % _trim(attrs['href']))
@@ -429,20 +447,23 @@ class XHTMLHandler(sax.ContentHandler):
if 'title' in attrs:
builder.append(' [' + attrs['title'] + ']')
-def xhtml_to_poezio_colors(xml, force=False, tmp_dir=None, extract_images=None):
+
+def xhtml_to_poezio_colors(xml, force=False, tmp_dir=None,
+ extract_images=None):
if isinstance(xml, str):
xml = xml.encode('utf8')
elif not isinstance(xml, bytes):
xml = ET.tostring(xml)
- handler = XHTMLHandler(force_ns=force, tmp_dir=tmp_dir,
- extract_images=extract_images)
+ handler = XHTMLHandler(
+ force_ns=force, tmp_dir=tmp_dir, extract_images=extract_images)
parser = sax.make_parser()
parser.setFeature(sax.handler.feature_namespaces, True)
parser.setContentHandler(handler)
parser.parse(BytesIO(xml))
return handler.result
+
def clean_text(s):
"""
Remove all xhtml-im attributes (\x19etc) from the string with the
@@ -451,6 +472,7 @@ def clean_text(s):
s = re.sub(xhtml_attr_re, "", s)
return s
+
def clean_text_simple(string):
"""
Remove all \x19 from the string formatted with simple colors:
@@ -458,10 +480,11 @@ def clean_text_simple(string):
"""
pos = string.find('\x19')
while pos != -1:
- string = string[:pos] + string[pos+2:]
+ string = string[:pos] + string[pos + 2:]
pos = string.find('\x19')
return string
+
def convert_simple_to_full_colors(text):
"""
takes a \x19n formatted string and returns
@@ -469,15 +492,28 @@ def convert_simple_to_full_colors(text):
"""
# TODO, have a single list of this. This is some sort of
# dusplicate from windows.format_chars
- mapping = str.maketrans({'\x0E': '\x19b', '\x0F': '\x19o', '\x10': '\x19u',
- '\x11': '\x191', '\x12': '\x192', '\x13': '\x193',
- '\x14': '\x194', '\x15': '\x195', '\x16': '\x196',
- '\x17': '\x197', '\x18': '\x198', '\x19': '\x199'})
+ mapping = str.maketrans({
+ '\x0E': '\x19b',
+ '\x0F': '\x19o',
+ '\x10': '\x19u',
+ '\x11': '\x191',
+ '\x12': '\x192',
+ '\x13': '\x193',
+ '\x14': '\x194',
+ '\x15': '\x195',
+ '\x16': '\x196',
+ '\x17': '\x197',
+ '\x18': '\x198',
+ '\x19': '\x199'
+ })
text = text.translate(mapping)
+
def add_curly_bracket(match):
return match.group(0) + '}'
+
return re.sub(xhtml_simple_attr_re, add_curly_bracket, text)
+
number_to_color_names = {
1: 'red',
2: 'green',
@@ -488,9 +524,11 @@ number_to_color_names = {
7: 'white'
}
+
def format_inline_css(_dict):
return ''.join(('%s: %s;' % (key, value) for key, value in _dict.items()))
+
def poezio_colors_to_html(string):
"""
Convert poezio colors to html
@@ -514,11 +552,12 @@ def poezio_colors_to_html(string):
build.append('</span>')
while next_attr_char != -1:
- attr_char = string[next_attr_char+1].lower()
+ attr_char = string[next_attr_char + 1].lower()
if next_attr_char != 0 and string[:next_attr_char]:
if current_attrs and not tag_open:
- build.append('<span style="%s">' % format_inline_css(current_attrs))
+ build.append(
+ '<span style="%s">' % format_inline_css(current_attrs))
tag_open = True
build.append(saxutils.escape(string[:next_attr_char]))
@@ -535,15 +574,17 @@ def poezio_colors_to_html(string):
check_property('font-style', 'italic')
if attr_char in digits:
- number_str = string[next_attr_char+1:string.find('}', next_attr_char)]
+ number_str = string[next_attr_char + 1:string.find(
+ '}', next_attr_char)]
number = int(number_str)
if number in number_to_color_names:
- check_property('color', number_to_color_names.get(number, 'black'))
+ check_property('color',
+ number_to_color_names.get(number, 'black'))
else:
check_property('color', ncurses_color_to_html(number))
- string = string[next_attr_char+len(number_str)+2:]
+ string = string[next_attr_char + len(number_str) + 2:]
else:
- string = string[next_attr_char+2:]
+ string = string[next_attr_char + 2:]
next_attr_char = string.find('\x19')
if current_attrs and not tag_open and string: