reorder.py 5.12 KB
Newer Older
mathieui's avatar
mathieui committed
1 2 3 4 5 6 7 8 9 10 11 12 13
"""
``reorder`` plugin: Reorder the tabs according to a layout

Commands
--------

.. glossary::

    /reorder
        **Usage:** ``/reorder``

        Reorder the tabs according to the configuration.

14 15 16 17
    /save_order
        **Usage:** ``/save_order``

        Save the current tab order to the configuration.
mathieui's avatar
mathieui committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

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
"""
61

Maxime Buquet's avatar
Maxime Buquet committed
62 63
from slixmpp import InvalidJID, JID

64 65
from poezio import tabs
from poezio.decorators import command_args_parser
66
from poezio.plugin import BasePlugin
67
from poezio.config import config
mathieui's avatar
mathieui committed
68

69
TEXT_TO_TAB = {
mathieui's avatar
mathieui committed
70 71 72 73 74 75 76
    'muc': tabs.MucTab,
    'private': tabs.PrivateTab,
    'dynamic': tabs.DynamicConversationTab,
    'static': tabs.StaticConversationTab,
    'empty': tabs.GapTab
}

77 78 79 80 81 82 83 84
TAB_TO_TEXT = {
    tabs.MucTab: 'muc',
    tabs.DynamicConversationTab: 'dynamic',
    tabs.PrivateTab: 'private',
    tabs.StaticConversationTab: 'static',
    tabs.GapTab: 'empty'
}

mathieui's avatar
mathieui committed
85

mathieui's avatar
mathieui committed
86
def parse_config(tab_config):
mathieui's avatar
mathieui committed
87
    result = {}
mathieui's avatar
mathieui committed
88
    for option in tab_config.options('reorder'):
mathieui's avatar
mathieui committed
89 90 91 92
        if not option.isdecimal():
            continue
        pos = int(option)
        if pos in result or pos <= 0:
mathieui's avatar
mathieui committed
93
            return None
mathieui's avatar
mathieui committed
94

mathieui's avatar
mathieui committed
95
        typ, name = tab_config.get(option, default=':').split(':', maxsplit=1)
96
        if typ not in TEXT_TO_TAB:
mathieui's avatar
mathieui committed
97
            return None
98
        result[pos] = (TEXT_TO_TAB[typ], name)
mathieui's avatar
mathieui committed
99 100 101

    return result

mathieui's avatar
mathieui committed
102

103 104 105 106 107 108
def check_tab(tab):
    for cls, rep in TAB_TO_TEXT.items():
        if isinstance(tab, cls):
            return rep
    return ''

mathieui's avatar
mathieui committed
109

110 111 112 113 114 115 116
def parse_runtime_tablist(tablist):
    props = []
    i = 0
    for tab in tablist[1:]:
        i += 1
        result = check_tab(tab)
        if result:
117
            props.append((i, '%s:%s' % (result, tab.jid.full)))
118 119
    return props

mathieui's avatar
mathieui committed
120

mathieui's avatar
mathieui committed
121
class Plugin(BasePlugin):
122 123
    """reorder plugin"""

mathieui's avatar
mathieui committed
124
    def init(self):
mathieui's avatar
mathieui committed
125 126 127 128 129 130 131 132 133
        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')
134 135

    @command_args_parser.ignored
136 137 138 139
    def command_save_order(self) -> None:
        """
        /save_order
        """
140 141 142 143
        conf = parse_runtime_tablist(self.core.tabs)
        for key, value in conf:
            self.config.set(key, value)
        self.api.information('Tab order saved', 'Info')
mathieui's avatar
mathieui committed
144 145

    @command_args_parser.ignored
146
    def command_reorder(self) -> None:
mathieui's avatar
mathieui committed
147 148 149 150 151
        """
        /reorder
        """
        tabs_spec = parse_config(self.config)
        if not tabs_spec:
152 153
            self.api.information('Invalid reorder config', 'Error')
            return None
mathieui's avatar
mathieui committed
154

mathieui's avatar
mathieui committed
155 156
        old_tabs = self.core.tabs.get_tabs()
        roster = old_tabs.pop(0)
mathieui's avatar
mathieui committed
157

158 159
        create_gaps = config.get('create_gaps')

mathieui's avatar
mathieui committed
160
        new_tabs = [roster]
mathieui's avatar
mathieui committed
161 162
        last = 0
        for pos in sorted(tabs_spec):
163
            if create_gaps and pos > last + 1:
mathieui's avatar
mathieui committed
164 165 166
                new_tabs += [
                    tabs.GapTab(self.core) for i in range(pos - last - 1)
                ]
Maxime Buquet's avatar
Maxime Buquet committed
167 168 169 170 171 172 173
            cls, jid = tabs_spec[pos]
            try:
                jid = JID(jid)
            except InvalidJID:
                self.api.information('Reorder: Invalid JID \'%s\'.' % jid, 'Warning')
                continue
            tab = self.core.tabs.by_name_and_class(str(jid), cls=cls)
mathieui's avatar
mathieui committed
174 175 176 177
            if tab and tab in old_tabs:
                new_tabs.append(tab)
                old_tabs.remove(tab)
            else:
Maxime Buquet's avatar
Maxime Buquet committed
178
                self.api.information('Tab %s not found' % jid, 'Warning')
179 180
                if create_gaps:
                    new_tabs.append(tabs.GapTab(self.core))
mathieui's avatar
mathieui committed
181 182 183 184 185 186
            last = pos

        for tab in old_tabs:
            if tab:
                new_tabs.append(tab)

Maxime Buquet's avatar
Maxime Buquet committed
187 188
        # 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.
mathieui's avatar
mathieui committed
189
        self.core.tabs.replace_tabs(new_tabs)
mathieui's avatar
mathieui committed
190
        self.core.refresh_window()
191 192

        return None