204 lines
5.7 KiB
Python
204 lines
5.7 KiB
Python
|
"""
|
||
|
``reorder`` plugin: Reorder the tabs according to a layout
|
||
|
|
||
|
Commands
|
||
|
--------
|
||
|
|
||
|
.. glossary::
|
||
|
|
||
|
/reorder
|
||
|
**Usage:** ``/reorder``
|
||
|
|
||
|
Reorder the tabs according to the configuration.
|
||
|
|
||
|
/save_order
|
||
|
**Usage:** ``/save_order``
|
||
|
|
||
|
Save the current tab order to the configuration.
|
||
|
|
||
|
Configuration
|
||
|
-------------
|
||
|
|
||
|
The configuration file must contain a section ``[reorder]`` and each option
|
||
|
must be formatted like ``[tab number] = [tab type]:[tab name]``.
|
||
|
|
||
|
For example:
|
||
|
|
||
|
.. code-block:: ini
|
||
|
|
||
|
[reorder]
|
||
|
1 = muc:toto@conference.example.com
|
||
|
2 = muc:example@muc.example.im
|
||
|
3 = dynamic:robert@example.org
|
||
|
|
||
|
The ``[tab number]`` must be at least ``1``; if the range is not entirely
|
||
|
covered, e.g.:
|
||
|
|
||
|
.. code-block:: ini
|
||
|
|
||
|
[reorder]
|
||
|
1 = muc:toto@conference.example.com
|
||
|
3 = dynamic:robert@example.org
|
||
|
|
||
|
Poezio will insert gaps between the tabs in order to keep the specified
|
||
|
numbering (so in this case, there will be a tab 1, a tab 3, but no tab 2).
|
||
|
|
||
|
|
||
|
The ``[tab type]`` must be one of:
|
||
|
|
||
|
- ``muc`` (for multi-user chats)
|
||
|
- ``private`` (for chats with a specific user inside a multi-user chat)
|
||
|
- ``dynamic`` (for normal, dynamic conversations tabs)
|
||
|
- ``static`` (for conversations with a specific resource)
|
||
|
|
||
|
And finally, the ``[tab name]`` must be:
|
||
|
|
||
|
- For a type ``muc``, the bare JID of the room
|
||
|
- For a type ``private``, the full JID of the user (room JID with the username as a resource)
|
||
|
- For a type ``dynamic``, the bare JID of the contact
|
||
|
- For a type ``static``, the full JID of the contact
|
||
|
"""
|
||
|
|
||
|
from slixmpp import InvalidJID, JID
|
||
|
|
||
|
from poezio import tabs
|
||
|
from poezio.decorators import command_args_parser
|
||
|
from poezio.plugin import BasePlugin
|
||
|
from poezio.config import config
|
||
|
|
||
|
TEXT_TO_TAB = {
|
||
|
'muc': tabs.MucTab,
|
||
|
'private': tabs.PrivateTab,
|
||
|
'dynamic': tabs.DynamicConversationTab,
|
||
|
'static': tabs.StaticConversationTab,
|
||
|
'empty': tabs.GapTab
|
||
|
}
|
||
|
|
||
|
TAB_TO_TEXT = {
|
||
|
tabs.MucTab: 'muc',
|
||
|
tabs.DynamicConversationTab: 'dynamic',
|
||
|
tabs.PrivateTab: 'private',
|
||
|
tabs.StaticConversationTab: 'static',
|
||
|
tabs.GapTab: 'empty'
|
||
|
}
|
||
|
|
||
|
|
||
|
def parse_config(tab_config):
|
||
|
result = {}
|
||
|
for option in tab_config.options('reorder'):
|
||
|
if not option.isdecimal():
|
||
|
continue
|
||
|
pos = int(option)
|
||
|
if pos in result or pos <= 0:
|
||
|
return None
|
||
|
|
||
|
spec = tab_config.get(option, default=':').split(':', maxsplit=1)
|
||
|
# Gap tabs are recreated automatically if there's a gap in indices.
|
||
|
if spec == 'empty':
|
||
|
return None
|
||
|
typ, name = spec
|
||
|
if typ not in TEXT_TO_TAB:
|
||
|
return None
|
||
|
result[pos] = (TEXT_TO_TAB[typ], name)
|
||
|
|
||
|
return result
|
||
|
|
||
|
|
||
|
def check_tab(tab):
|
||
|
for cls, rep in TAB_TO_TEXT.items():
|
||
|
if isinstance(tab, cls):
|
||
|
return rep
|
||
|
return ''
|
||
|
|
||
|
|
||
|
def parse_runtime_tablist(tablist):
|
||
|
props = []
|
||
|
i = 0
|
||
|
for tab in tablist[1:]:
|
||
|
i += 1
|
||
|
result = check_tab(tab)
|
||
|
# Don't serialize gap tabs as they're recreated automatically
|
||
|
if result != 'empty' and isinstance(tab, TEXT_TO_TAB.values()):
|
||
|
props.append((i, '%s:%s' % (result, tab.jid.full)))
|
||
|
return props
|
||
|
|
||
|
|
||
|
class Plugin(BasePlugin):
|
||
|
"""reorder plugin"""
|
||
|
|
||
|
def init(self):
|
||
|
self.api.add_command(
|
||
|
'reorder',
|
||
|
self.command_reorder,
|
||
|
help='Reorder all tabs using the pre-defined'
|
||
|
' layout from the configuration file.')
|
||
|
self.api.add_command(
|
||
|
'save_order',
|
||
|
self.command_save_order,
|
||
|
help='Save the current tab layout')
|
||
|
|
||
|
@command_args_parser.ignored
|
||
|
def command_save_order(self) -> None:
|
||
|
"""
|
||
|
/save_order
|
||
|
"""
|
||
|
conf = parse_runtime_tablist(self.core.tabs)
|
||
|
for key, value in conf:
|
||
|
self.config.set(key, value)
|
||
|
self.api.information('Tab order saved', 'Info')
|
||
|
|
||
|
@command_args_parser.ignored
|
||
|
def command_reorder(self) -> None:
|
||
|
"""
|
||
|
/reorder
|
||
|
"""
|
||
|
tabs_spec = parse_config(self.config)
|
||
|
if not tabs_spec:
|
||
|
self.api.information('Invalid reorder config', 'Error')
|
||
|
return None
|
||
|
|
||
|
old_tabs = self.core.tabs.get_tabs()
|
||
|
roster = old_tabs.pop(0)
|
||
|
|
||
|
create_gaps = config.get('create_gaps')
|
||
|
|
||
|
new_tabs = [roster]
|
||
|
last = 0
|
||
|
for pos in sorted(tabs_spec):
|
||
|
if create_gaps and pos > last + 1:
|
||
|
new_tabs += [
|
||
|
tabs.GapTab() for i in range(pos - last - 1)
|
||
|
]
|
||
|
cls, jid = tabs_spec[pos]
|
||
|
try:
|
||
|
jid = JID(jid)
|
||
|
tab = self.core.tabs.by_name_and_class(str(jid), cls=cls)
|
||
|
if tab and tab in old_tabs:
|
||
|
new_tabs.append(tab)
|
||
|
old_tabs.remove(tab)
|
||
|
else:
|
||
|
# TODO: Add support for MucTab. Requires nickname.
|
||
|
if cls in (tabs.DynamicConversationTab, tabs.StaticConversationTab):
|
||
|
self.api.information('Tab %s not found. Creating it' % jid, 'Warning')
|
||
|
new_tab = cls(self.core, jid)
|
||
|
new_tabs.append(new_tab)
|
||
|
else:
|
||
|
new_tabs.append(tabs.GapTab())
|
||
|
except:
|
||
|
self.api.information('Failed to create tab \'%s\'.' % jid, 'Error')
|
||
|
if create_gaps:
|
||
|
new_tabs.append(tabs.GapTab())
|
||
|
finally:
|
||
|
last = pos
|
||
|
|
||
|
for tab in old_tabs:
|
||
|
if tab:
|
||
|
new_tabs.append(tab)
|
||
|
|
||
|
# TODO: Ensure we don't break poezio and call this with whatever
|
||
|
# tablist we have. The roster tab at least needs to be in there.
|
||
|
self.core.tabs.replace_tabs(new_tabs)
|
||
|
self.core.refresh_window()
|
||
|
|
||
|
return None
|