summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tabs/__init__.py1
-rw-r--r--src/tabs/listtab.py203
-rw-r--r--src/tabs/muclisttab.py182
3 files changed, 215 insertions, 171 deletions
diff --git a/src/tabs/__init__.py b/src/tabs/__init__.py
index 67c686b5..8826ede2 100644
--- a/src/tabs/__init__.py
+++ b/src/tabs/__init__.py
@@ -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
diff --git a/src/tabs/listtab.py b/src/tabs/listtab.py
new file mode 100644
index 00000000..49588a0d
--- /dev/null
+++ b/src/tabs/listtab.py
@@ -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)]
+
+
diff --git a/src/tabs/muclisttab.py b/src/tabs/muclisttab.py
index 3d43928a..d7c68588 100644
--- a/src/tabs/muclisttab.py
+++ b/src/tabs/muclisttab.py
@@ -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)]
-
-