Create a more generic ListTab class, and make MucListTab inherite from it
This commit is contained in:
parent
b46f0f5e26
commit
36620901e8
3 changed files with 215 additions and 171 deletions
|
@ -6,5 +6,6 @@ from . privatetab import PrivateTab
|
|||
from . conversationtab import ConversationTab, StaticConversationTab,\
|
||||
DynamicConversationTab
|
||||
from . xmltab import XMLTab
|
||||
from . listtab import ListTab
|
||||
from . muclisttab import MucListTab
|
||||
from . data_forms import DataFormsTab
|
||||
|
|
203
src/tabs/listtab.py
Normal file
203
src/tabs/listtab.py
Normal file
|
@ -0,0 +1,203 @@
|
|||
"""
|
||||
A generic tab that displays a serie of items in a scrollable, searchable,
|
||||
sortable list. It should be inherited, to actually provide methods that
|
||||
insert items in the list, and that lets the user interact with them.
|
||||
"""
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
import curses
|
||||
import collections
|
||||
|
||||
import windows
|
||||
from common import safeJID
|
||||
from decorators import refresh_wrapper
|
||||
|
||||
from . import Tab
|
||||
|
||||
|
||||
class ListTab(Tab):
|
||||
plugin_commands = {}
|
||||
plugin_keys = {}
|
||||
|
||||
def __init__(self, name, help_message, header_text, cols):
|
||||
"""Parameters:
|
||||
name: The name of the tab
|
||||
help_message: The default help message displayed instead of the
|
||||
input
|
||||
header_text: The text displayed on the header line, at the top of
|
||||
the tab
|
||||
cols: a tuple of 2-tuples. e.g. (('column1_name', number),
|
||||
('column2_name', number))
|
||||
"""
|
||||
Tab.__init__(self)
|
||||
self.state = 'normal'
|
||||
self.name = name
|
||||
columns = collections.OrderedDict()
|
||||
for col, num in cols:
|
||||
columns[col] = num
|
||||
self.list_header = windows.ColumnHeaderWin(list(columns))
|
||||
self.listview = windows.ListWin(columns)
|
||||
self.info_header = windows.MucListInfoWin(header_text)
|
||||
self.default_help_message = windows.HelpText(help_message)
|
||||
self.input = self.default_help_message
|
||||
self.key_func["KEY_DOWN"] = self.move_cursor_down
|
||||
self.key_func["KEY_UP"] = self.move_cursor_up
|
||||
self.key_func['^I'] = self.completion
|
||||
self.key_func["/"] = self.on_slash
|
||||
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.resize()
|
||||
self.update_keys()
|
||||
self.update_commands()
|
||||
|
||||
def get_columns_sizes(self):
|
||||
"""
|
||||
Must be implemented in subclasses. Must return a dict like this:
|
||||
{'column1_name': size1,
|
||||
'column2_name': size2}
|
||||
Where the size are calculated based on the size of the tab etc
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def refresh(self):
|
||||
if self.need_resize:
|
||||
self.resize()
|
||||
log.debug(' TAB Refresh: %s', self.__class__.__name__)
|
||||
if self.size.tab_degrade_y:
|
||||
display_info_win = False
|
||||
else:
|
||||
display_info_win = True
|
||||
|
||||
self.info_header.refresh(window=self.listview)
|
||||
if display_info_win:
|
||||
self.info_win.refresh()
|
||||
self.refresh_tab_win()
|
||||
self.list_header.refresh()
|
||||
self.listview.refresh()
|
||||
self.input.refresh()
|
||||
|
||||
def resize(self):
|
||||
if self.size.tab_degrade_y:
|
||||
info_win_height = 0
|
||||
tab_win_height = 0
|
||||
else:
|
||||
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)
|
||||
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.push_size()
|
||||
|
||||
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
|
||||
|
||||
def close(self, arg=None):
|
||||
self.input.on_delete()
|
||||
self.core.close_tab(self)
|
||||
|
||||
def set_error(self, msg, code, body):
|
||||
"""
|
||||
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.info_header.message = self._error_message
|
||||
self.info_header.refresh()
|
||||
curses.doupdate()
|
||||
|
||||
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)
|
||||
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)
|
||||
self.list_header.set_order(True)
|
||||
self.list_header.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
@refresh_wrapper.always
|
||||
def reset_help_message(self, _=None):
|
||||
curses.curs_set(0)
|
||||
self.input = self.default_help_message
|
||||
self.input.resize(1, self.width, self.height-1, 0)
|
||||
return True
|
||||
|
||||
def execute_slash_command(self, txt):
|
||||
if txt.startswith('/'):
|
||||
self.input.key_enter()
|
||||
self.execute_command(txt)
|
||||
return self.reset_help_message()
|
||||
|
||||
def completion(self):
|
||||
if isinstance(self.input, windows.Input):
|
||||
self.complete_commands(self.input)
|
||||
|
||||
def on_input(self, key, raw):
|
||||
res = self.input.do_command(key, raw=raw)
|
||||
if res and not isinstance(self.input, windows.Input):
|
||||
return True
|
||||
elif res:
|
||||
return False
|
||||
if not raw and key in self.key_func:
|
||||
return self.key_func[key]()
|
||||
|
||||
def on_info_win_size_changed(self):
|
||||
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)
|
||||
|
||||
def on_lose_focus(self):
|
||||
self.state = 'normal'
|
||||
|
||||
def on_gain_focus(self):
|
||||
self.state = 'current'
|
||||
curses.curs_set(0)
|
||||
|
||||
def on_scroll_up(self):
|
||||
return self.listview.scroll_up()
|
||||
|
||||
def on_scroll_down(self):
|
||||
return self.listview.scroll_down()
|
||||
|
||||
def move_cursor_up(self):
|
||||
self.listview.move_cursor_up()
|
||||
self.listview.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def move_cursor_down(self):
|
||||
self.listview.move_cursor_down()
|
||||
self.listview.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def matching_names(self):
|
||||
return [(2, self.name)]
|
||||
|
||||
|
|
@ -9,20 +9,11 @@ from gettext import gettext as _
|
|||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
import curses
|
||||
import collections
|
||||
from datetime import datetime
|
||||
from . import ListTab
|
||||
|
||||
from sleekxmpp.plugins.xep_0030.stanza.items import DiscoItem
|
||||
|
||||
import windows
|
||||
from common import safeJID
|
||||
from decorators import refresh_wrapper
|
||||
|
||||
from . import Tab
|
||||
|
||||
|
||||
class MucListTab(Tab):
|
||||
class MucListTab(ListTab):
|
||||
"""
|
||||
A tab listing rooms from a specific server, displaying various information,
|
||||
scrollable, and letting the user join them, etc
|
||||
|
@ -31,100 +22,23 @@ class MucListTab(Tab):
|
|||
plugin_keys = {}
|
||||
|
||||
def __init__(self, server):
|
||||
Tab.__init__(self)
|
||||
self.state = 'normal'
|
||||
self.name = server
|
||||
columns = collections.OrderedDict()
|
||||
columns['node-part'] = 0
|
||||
columns['name'] = 2
|
||||
columns['users'] = 3
|
||||
self.list_header = windows.ColumnHeaderWin(list(columns))
|
||||
self.listview = windows.ListWin(columns)
|
||||
self.info_header = windows.MucListInfoWin(_('Chatroom list on server %s (Loading)') % self.name)
|
||||
self.default_help_message = windows.HelpText("“j”: join room.")
|
||||
self.input = self.default_help_message
|
||||
self.key_func["KEY_DOWN"] = self.move_cursor_down
|
||||
self.key_func["KEY_UP"] = self.move_cursor_up
|
||||
self.key_func['^I'] = self.completion
|
||||
self.key_func["/"] = self.on_slash
|
||||
ListTab.__init__(self, server,
|
||||
"“j”: join room.",
|
||||
_('Chatroom list on server %s (Loading)') % server,
|
||||
(('node-part', 0), ('name', 2), ('users', 3)))
|
||||
self.key_func['j'] = self.join_selected
|
||||
self.key_func['J'] = self.join_selected_no_focus
|
||||
self.key_func['^M'] = self.join_selected
|
||||
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.resize()
|
||||
self.update_keys()
|
||||
self.update_commands()
|
||||
|
||||
def refresh(self):
|
||||
if self.need_resize:
|
||||
self.resize()
|
||||
log.debug(' TAB Refresh: %s', self.__class__.__name__)
|
||||
if self.size.tab_degrade_y:
|
||||
display_info_win = False
|
||||
else:
|
||||
display_info_win = True
|
||||
|
||||
self.info_header.refresh(window=self.listview)
|
||||
if display_info_win:
|
||||
self.info_win.refresh()
|
||||
self.refresh_tab_win()
|
||||
self.list_header.refresh()
|
||||
self.listview.refresh()
|
||||
self.input.refresh()
|
||||
|
||||
def resize(self):
|
||||
if self.size.tab_degrade_y:
|
||||
info_win_height = 0
|
||||
tab_win_height = 0
|
||||
else:
|
||||
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)
|
||||
column_size = {'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)}
|
||||
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.push_size()
|
||||
|
||||
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
|
||||
|
||||
def close(self, arg=None):
|
||||
self.input.on_delete()
|
||||
self.core.close_tab(self)
|
||||
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)}
|
||||
|
||||
def join_selected_no_focus(self):
|
||||
return
|
||||
|
||||
def set_error(self, msg, code, body):
|
||||
"""
|
||||
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.info_header.message = self._error_message
|
||||
self.info_header.refresh()
|
||||
curses.doupdate()
|
||||
|
||||
def on_muc_list_item_received(self, iq):
|
||||
"""
|
||||
Callback called when a disco#items result is received
|
||||
|
@ -150,83 +64,9 @@ class MucListTab(Tab):
|
|||
self.refresh_tab_win()
|
||||
self.core.doupdate()
|
||||
|
||||
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)
|
||||
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)
|
||||
self.list_header.set_order(True)
|
||||
self.list_header.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def join_selected(self):
|
||||
row = self.listview.get_selected_row()
|
||||
if not row:
|
||||
return
|
||||
self.core.command_join(row[1])
|
||||
|
||||
@refresh_wrapper.always
|
||||
def reset_help_message(self, _=None):
|
||||
curses.curs_set(0)
|
||||
self.input = self.default_help_message
|
||||
self.input.resize(1, self.width, self.height-1, 0)
|
||||
return True
|
||||
|
||||
def execute_slash_command(self, txt):
|
||||
if txt.startswith('/'):
|
||||
self.input.key_enter()
|
||||
self.execute_command(txt)
|
||||
return self.reset_help_message()
|
||||
|
||||
def completion(self):
|
||||
if isinstance(self.input, windows.Input):
|
||||
self.complete_commands(self.input)
|
||||
|
||||
def on_input(self, key, raw):
|
||||
res = self.input.do_command(key, raw=raw)
|
||||
if res and not isinstance(self.input, windows.Input):
|
||||
return True
|
||||
elif res:
|
||||
return False
|
||||
if not raw and key in self.key_func:
|
||||
return self.key_func[key]()
|
||||
|
||||
def on_info_win_size_changed(self):
|
||||
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)
|
||||
|
||||
def on_lose_focus(self):
|
||||
self.state = 'normal'
|
||||
|
||||
def on_gain_focus(self):
|
||||
self.state = 'current'
|
||||
curses.curs_set(0)
|
||||
|
||||
def on_scroll_up(self):
|
||||
return self.listview.scroll_up()
|
||||
|
||||
def on_scroll_down(self):
|
||||
return self.listview.scroll_down()
|
||||
|
||||
def move_cursor_up(self):
|
||||
self.listview.move_cursor_up()
|
||||
self.listview.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def move_cursor_down(self):
|
||||
self.listview.move_cursor_down()
|
||||
self.listview.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def matching_names(self):
|
||||
return [(2, self.name)]
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue