From f4c24380466fa54924a925a12817e28d603fd4d1 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 18 Jun 2011 16:23:41 +0200 Subject: Oups. --- src/tabs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/tabs.py b/src/tabs.py index f69ef919..801e0793 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -919,7 +919,7 @@ class MucTab(ChatTab): if status: leave_msg += ' (%s)' % status room.add_message(leave_msg) - self.refresh_window() + self.core.refresh_window() self.core.on_user_left_private_conversation(from_room, from_nick, status) def on_user_change_status(self, room, user, from_nick, from_room, affiliation, role, show, status): -- cgit v1.2.3 From ca0b4af3647b935adbe8b6c0dcb30764507ed083 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 20 Jun 2011 03:19:37 +0200 Subject: First pubsub browser iteration: can list root nodes, create an item and receive items --- src/connection.py | 1 + src/core.py | 18 +++++ src/pubsub.py | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 src/pubsub.py (limited to 'src') diff --git a/src/connection.py b/src/connection.py index 8bef6eb2..d021f44b 100644 --- a/src/connection.py +++ b/src/connection.py @@ -56,6 +56,7 @@ class Connection(sleekxmpp.ClientXMPP): self.register_plugin('xep_0030') self.register_plugin('xep_0004') self.register_plugin('xep_0045') + self.register_plugin('xep_0060') self.register_plugin('xep_0071') self.register_plugin('xep_0085') if config.get('send_poezio_info', 'true') == 'true': diff --git a/src/core.py b/src/core.py index 3f24fb1b..6db97919 100644 --- a/src/core.py +++ b/src/core.py @@ -40,6 +40,7 @@ import multiuserchat as muc import tabs import xhtml +import pubsub import windows import connection import timed_events @@ -129,6 +130,7 @@ class Core(object): 'version': (self.command_version, _('Usage: /version \nVersion: get the software version of the given JID (usually its XMPP client and Operating System)'), None), 'reconnect': (self.command_reconnect, _('Usage: /connect\nConnect: disconnect from the remote server if you are currently connected and then connect to it again'), None), 'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: disconnect and reconnects in all the rooms in domain.'), None), + 'pubsub': (self.command_pubsub, _('Usage: /pubsub \nPubsub: Open a pubsub browser on the given domain'), None), } self.key_func = { @@ -1369,6 +1371,22 @@ class Core(object): tab.get_room().joined = False self.command_join(tab.get_name()) + def command_pubsub(self, args): + """ + Opens a pubsub browser on the given domain + """ + args = common.shell_split(args) + if len(args) != 1: + return self.command_help('pubsub') + domain = args[0] + tab = self.get_tab_by_name('%s@@pubsubbrowser' % (domain,), pubsub.PubsubBrowserTab) + if tab: + self.command_win('%s' % tab.nb) + else: + new_tab = pubsub.PubsubBrowserTab(domain) + self.add_tab(new_tab, True) + self.refresh_window() + def go_to_room_number(self): """ Read 2 more chars and go to the tab diff --git a/src/pubsub.py b/src/pubsub.py new file mode 100644 index 00000000..73c03bcc --- /dev/null +++ b/src/pubsub.py @@ -0,0 +1,197 @@ +# Copyright 2010-2011 Le Coz Florent +# +# This file is part of Poezio. +# +# Poezio is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# Poezio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Poezio. If not, see . + +import logging +log = logging.getLogger(__name__) + +import curses + +import windows +import tabs + +from sleekxmpp.xmlstream import ElementBase, ET + +class PubsubNode(object): + node_type = None # unknown yet + def __init__(self, name): + self.items = [] + self.name = name + + +class LeafNode(PubsubNode): + node_type = "leaf" + def __init__(self, name): + PubsubNode.__init__(self, name) + + +class CollectionNode(PubsubNode): + node_type = "collection" + def __init__(self, name): + PubsubNode.__init__(self, name) + self.subnodes = [] + + +class PubsubItem(object): + def __init__(self, idd, content): + self.idd = idd + self.content = content + + +class PubsubBrowserTab(tabs.Tab): + """ + A tab containing a pubsub browser letting the user + list nodes and items, view, add and delete items, etc + """ + def __init__(self, server): + """ + Server is the name of the pubsub server, for example: + pubsub.example.com + All action done in this tab will be made on that server. + """ + tabs.Tab.__init__(self) + self.current_node = None # the subnode we are listing. None means the root + self.server = server + self.nodes = [] # the lower level of nodes + + self.tab_win = windows.GlobalInfoBar() + self.upper_message = windows.Topic() + self.upper_message.set_message('Pubsub server: %s' % (self.server,)) + + # Node List View + node_columns = ('node', 'name',) + self.node_list_header = windows.ColumnHeaderWin(node_columns) + self.node_listview = windows.ListWin(node_columns) + + # Item List View + item_columns = ('name',) + self.item_list_header = windows.ColumnHeaderWin(item_columns) + self.item_listview = windows.ListWin(item_columns) + + self.default_help_message = windows.HelpText("“c”: create a node.") + self.input = self.default_help_message + + self.key_func['c'] = self.command_create_node + self.key_func["KEY_DOWN"] = self.node_listview.move_cursor_down + self.key_func["KEY_UP"] = self.node_listview.move_cursor_up + self.resize() + + self.get_nodes() + + def resize(self): + self.upper_message.resize(1, self.width, 0, 0) + self.tab_win.resize(1, self.width, self.height-2, 0) + + column_size = {'node': self.width//4, + 'name': self.width//4,} + self.node_list_header.resize_columns(column_size) + self.node_list_header.resize(1, self.width//2, 1, 0) + self.node_listview.resize_columns(column_size) + self.node_listview.resize(self.height//2-2, self.width//2, 2, 0) + + column_size = {'name': self.width//2,} + self.item_list_header.resize_columns(column_size) + self.item_list_header.resize(self.height//2+1, self.width//2, self.height//2, 0) + self.item_listview.resize_columns(column_size) + self.item_listview.resize(self.height//2-4, self.width//2, self.height//2+3, 0) + + self.input.resize(1, self.width, self.height-1, 0) + + def refresh(self): + if self.need_resize: + self.resize() + log.debug(' TAB Refresh: %s'%self.__class__.__name__) + self.upper_message.refresh() + self.node_list_header.refresh() + self.node_listview.refresh() + self.item_list_header.refresh() + self.item_listview.refresh() + self.tab_win.refresh() + self.input.refresh() + + def get_name(self): + return '%s@@pubsubbrowser' % (self.server,) + + def on_input(self, key): + res = self.input.do_command(key) + if res: + return True + if key in self.key_func: + return self.key_func[key]() + + def get_items(self, node): + """ + Get all items in the given node + """ + items = self.core.xmpp.plugin['xep_0060'].get_items(self.server, node.name) + item_list = [] + if items: + for it in items: + item_list.append(PubsubItem(it.attrib['id'], ET.tostring(it))) + node.items = item_list + log.debug('Item on node %s: %s' % (node.name, item_list)) + + def add_nodes(self, node_list, parent=None): + """ + Add Node objects to the list of the parent. + If parent is None, they are added to the root list. + If the current selected node is parent, we add + them directly to the node_listview + """ + log.debug('Adding nodes to %s: %s' % (node_list, parent,)) + if not parent: + list_to_append = self.nodes + else: + list_to_append = parent.nodes + for node in node_list: + new_node = LeafNode(node['node']) + list_to_append.append(new_node) + self.get_items(new_node) + self.node_listview.add_lines(node_list) + + def get_nodes(self, node=None): + """ + Get all subnodes of the given node. If no node is given, get + the root nodes + """ + nodes = self.core.xmpp.plugin['xep_0060'].get_nodes(self.server) + lines = [{'name': nodes[node] or '', + 'node': node} for node in nodes.keys()] + self.add_nodes(lines) + + def create_node(self, node_name): + if node_name: + res = self.core.xmpp.plugin['xep_0060'].create_node(self.server, node_name) + if res: + self.node_listview.add_lines([{'name': '', 'node': node_name}]) + self.reset_help_message() + return True + + def reset_help_message(self, txt=None): + """ + Just reset the help message when a command ends + """ + curses.curs_set(0) + self.input = self.default_help_message + return True + + def command_create_node(self): + """ + Prompt for a node name and create it on Enter key + """ + curses.curs_set(1) + self.input = windows.CommandInput("[Create node]", self.reset_help_message, self.create_node, None) + self.input.resize(1, self.width, self.height-1, 0) + return True -- cgit v1.2.3 From 511ecf6195597ceda94730bf66017645d368ea5f Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Jun 2011 00:10:41 +0200 Subject: Meta + up and down scrolls the nodes, up and down scrolls the items --- src/pubsub.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/pubsub.py b/src/pubsub.py index 73c03bcc..1b4a8e1e 100644 --- a/src/pubsub.py +++ b/src/pubsub.py @@ -26,21 +26,22 @@ from sleekxmpp.xmlstream import ElementBase, ET class PubsubNode(object): node_type = None # unknown yet - def __init__(self, name): + def __init__(self, name, parent=None): self.items = [] self.name = name + self.parent = parent class LeafNode(PubsubNode): node_type = "leaf" - def __init__(self, name): - PubsubNode.__init__(self, name) + def __init__(self, name, parent=None): + PubsubNode.__init__(self, name, parent) class CollectionNode(PubsubNode): node_type = "collection" - def __init__(self, name): - PubsubNode.__init__(self, name) + def __init__(self, name, parent=None): + PubsubNode.__init__(self, name, parent) self.subnodes = [] @@ -84,8 +85,10 @@ class PubsubBrowserTab(tabs.Tab): self.input = self.default_help_message self.key_func['c'] = self.command_create_node - self.key_func["KEY_DOWN"] = self.node_listview.move_cursor_down - self.key_func["KEY_UP"] = self.node_listview.move_cursor_up + self.key_func["M-KEY_DOWN"] = self.node_listview.move_cursor_down + self.key_func["M-KEY_UP"] = self.node_listview.move_cursor_up + self.key_func["KEY_DOWN"] = self.item_listview.move_cursor_down + self.key_func["KEY_UP"] = self.item_listview.move_cursor_up self.resize() self.get_nodes() @@ -139,7 +142,7 @@ class PubsubBrowserTab(tabs.Tab): item_list = [] if items: for it in items: - item_list.append(PubsubItem(it.attrib['id'], ET.tostring(it))) + item_list.append(PubsubItem(it.attrib['id'], it)) node.items = item_list log.debug('Item on node %s: %s' % (node.name, item_list)) -- cgit v1.2.3 From 06f4dd783ed946168be00abdfdf4ffc127ba21e5 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Jun 2011 01:58:24 +0200 Subject: Nodes can be scrolled and the item list is updated accordingly --- src/pubsub.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/windows.py | 3 +++ 2 files changed, 78 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/pubsub.py b/src/pubsub.py index 1b4a8e1e..0b8b02c5 100644 --- a/src/pubsub.py +++ b/src/pubsub.py @@ -47,9 +47,17 @@ class CollectionNode(PubsubNode): class PubsubItem(object): def __init__(self, idd, content): - self.idd = idd + self.id = idd self.content = content + def to_dict(self, columns): + """ + returns a dict of the values listed in columns + """ + ret = {} + for col in columns: + ret[col] = self.__dict__.get(col) or '' + return ret class PubsubBrowserTab(tabs.Tab): """ @@ -69,7 +77,7 @@ class PubsubBrowserTab(tabs.Tab): self.tab_win = windows.GlobalInfoBar() self.upper_message = windows.Topic() - self.upper_message.set_message('Pubsub server: %s' % (self.server,)) + self.upper_message.set_message('Pubsub server: %s/%s' % (self.server,self.current_node or '')) # Node List View node_columns = ('node', 'name',) @@ -77,7 +85,7 @@ class PubsubBrowserTab(tabs.Tab): self.node_listview = windows.ListWin(node_columns) # Item List View - item_columns = ('name',) + item_columns = ('id',) self.item_list_header = windows.ColumnHeaderWin(item_columns) self.item_listview = windows.ListWin(item_columns) @@ -85,8 +93,8 @@ class PubsubBrowserTab(tabs.Tab): self.input = self.default_help_message self.key_func['c'] = self.command_create_node - self.key_func["M-KEY_DOWN"] = self.node_listview.move_cursor_down - self.key_func["M-KEY_UP"] = self.node_listview.move_cursor_up + self.key_func["M-KEY_DOWN"] = self.scroll_node_down + self.key_func["M-KEY_UP"] = self.scroll_node_up self.key_func["KEY_DOWN"] = self.item_listview.move_cursor_down self.key_func["KEY_UP"] = self.item_listview.move_cursor_up self.resize() @@ -104,11 +112,11 @@ class PubsubBrowserTab(tabs.Tab): self.node_listview.resize_columns(column_size) self.node_listview.resize(self.height//2-2, self.width//2, 2, 0) - column_size = {'name': self.width//2,} + column_size = {'id': self.width//2,} self.item_list_header.resize_columns(column_size) self.item_list_header.resize(self.height//2+1, self.width//2, self.height//2, 0) self.item_listview.resize_columns(column_size) - self.item_listview.resize(self.height//2-4, self.width//2, self.height//2+3, 0) + self.item_listview.resize(self.height//2-3, self.width//2, self.height//2+1, 0) self.input.resize(1, self.width, self.height-1, 0) @@ -134,6 +142,26 @@ class PubsubBrowserTab(tabs.Tab): if key in self.key_func: return self.key_func[key]() + def get_selected_node_name(self): + """ + From the node_view_list, returns the node name of the selected + one. None can be returned + """ + line = self.node_listview.get_selected_row() + if not line: + return None + return line['node'] + + def get_node_by_name(self, name): + """ + in the current browsed node (or on the root), return the node with that name + """ + nodes = self.current_node and self.current_node.subnodes or self.nodes + for node in nodes: + if node.name == name: + return node + return None + def get_items(self, node): """ Get all items in the given node @@ -144,8 +172,22 @@ class PubsubBrowserTab(tabs.Tab): for it in items: item_list.append(PubsubItem(it.attrib['id'], it)) node.items = item_list + log.debug('get_selected_node_name: %s' % self.get_selected_node_name()) + if self.get_selected_node_name() == node.name: + self.display_items_from_node(node) log.debug('Item on node %s: %s' % (node.name, item_list)) + def display_items_from_node(self, node): + """ + takes a node, and set fill the item_listview with that + node’s items + """ + columns = self.item_list_header.get_columns() + self.item_listview.lines = [] + log.debug('display_items_from_node: %s' % node.items) + for item in node.items: + self.item_listview.lines.append(item.to_dict(columns)) + def add_nodes(self, node_list, parent=None): """ Add Node objects to the list of the parent. @@ -158,11 +200,11 @@ class PubsubBrowserTab(tabs.Tab): list_to_append = self.nodes else: list_to_append = parent.nodes + self.node_listview.add_lines(node_list) for node in node_list: new_node = LeafNode(node['node']) list_to_append.append(new_node) self.get_items(new_node) - self.node_listview.add_lines(node_list) def get_nodes(self, node=None): """ @@ -198,3 +240,28 @@ class PubsubBrowserTab(tabs.Tab): self.input = windows.CommandInput("[Create node]", self.reset_help_message, self.create_node, None) self.input.resize(1, self.width, self.height-1, 0) return True + + def scroll_node_up(self): + """ + scroll the node up, and update the item list if needed + """ + selected_node_before = self.get_selected_node_name() + self.node_listview.move_cursor_up() + selected_node_after = self.get_selected_node_name() + if selected_node_after is not selected_node_before: + self.display_items_from_node(self.get_node_by_name(selected_node_after)) + return True + return False + + def scroll_node_down(self): + """ + scroll the node down, and update the item list if needed + """ + selected_node_before = self.get_selected_node_name() + self.node_listview.move_cursor_down() + selected_node_after = self.get_selected_node_name() + if selected_node_after is not selected_node_before: + self.display_items_from_node(self.get_node_by_name(selected_node_after)) + return True + return False + diff --git a/src/windows.py b/src/windows.py index eb6dde43..512b6f01 100644 --- a/src/windows.py +++ b/src/windows.py @@ -1603,6 +1603,9 @@ class ColumnHeaderWin(Win): def resize_columns(self, dic): self._columns_sizes = dic + def get_columns(self): + return self._columns + def refresh(self): log.debug('Refresh: %s'%self.__class__.__name__) with g_lock: -- cgit v1.2.3 From 0fa3cf756a50dffe01e46c725245445dc1cfd39b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Jun 2011 02:01:23 +0200 Subject: Reset the cursor position from the item list when node changes --- src/pubsub.py | 2 +- src/windows.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pubsub.py b/src/pubsub.py index 0b8b02c5..30ffd624 100644 --- a/src/pubsub.py +++ b/src/pubsub.py @@ -183,7 +183,7 @@ class PubsubBrowserTab(tabs.Tab): node’s items """ columns = self.item_list_header.get_columns() - self.item_listview.lines = [] + self.item_listview.empty() log.debug('display_items_from_node: %s' % node.items) for item in node.items: self.item_listview.lines.append(item.to_dict(columns)) diff --git a/src/windows.py b/src/windows.py index 512b6f01..9f6fc676 100644 --- a/src/windows.py +++ b/src/windows.py @@ -1512,6 +1512,14 @@ class ListWin(Win): self._selected_row = 0 self._starting_pos = 0 # The column number from which we start the refresh + def empty(self): + """ + emtpy the list and reset some important values as well + """ + self.lines = [] + self._selected_row = 0 + self._starting_pos = 0 + def resize_columns(self, dic): """ Resize the width of the columns -- cgit v1.2.3 From 76cacb32a6a13fbd69593fffba011c97bcf8825e Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Jun 2011 02:43:59 +0200 Subject: Enter displays the content of the selected item --- src/pubsub.py | 42 +++++++++++++++++++++++++++++++++++++++++- src/windows.py | 4 ---- 2 files changed, 41 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/pubsub.py b/src/pubsub.py index 30ffd624..af9ca001 100644 --- a/src/pubsub.py +++ b/src/pubsub.py @@ -89,6 +89,8 @@ class PubsubBrowserTab(tabs.Tab): self.item_list_header = windows.ColumnHeaderWin(item_columns) self.item_listview = windows.ListWin(item_columns) + # Item viewer + self.item_viewer = windows.SimpleTextWin('') self.default_help_message = windows.HelpText("“c”: create a node.") self.input = self.default_help_message @@ -97,6 +99,7 @@ class PubsubBrowserTab(tabs.Tab): self.key_func["M-KEY_UP"] = self.scroll_node_up self.key_func["KEY_DOWN"] = self.item_listview.move_cursor_down self.key_func["KEY_UP"] = self.item_listview.move_cursor_up + self.key_func["^M"] = self.open_selected_item self.resize() self.get_nodes() @@ -118,6 +121,7 @@ class PubsubBrowserTab(tabs.Tab): self.item_listview.resize_columns(column_size) self.item_listview.resize(self.height//2-3, self.width//2, self.height//2+1, 0) + self.item_viewer.resize(self.height-3, self.width//2+1, 1, self.width//2) self.input.resize(1, self.width, self.height-1, 0) def refresh(self): @@ -129,6 +133,7 @@ class PubsubBrowserTab(tabs.Tab): self.node_listview.refresh() self.item_list_header.refresh() self.item_listview.refresh() + self.item_viewer.refresh() self.tab_win.refresh() self.input.refresh() @@ -154,7 +159,7 @@ class PubsubBrowserTab(tabs.Tab): def get_node_by_name(self, name): """ - in the current browsed node (or on the root), return the node with that name + in the currently browsed node (or on the root), return the node with that name """ nodes = self.current_node and self.current_node.subnodes or self.nodes for node in nodes: @@ -162,6 +167,30 @@ class PubsubBrowserTab(tabs.Tab): return node return None + def get_item_by_id(self, idd): + """ + in the currently selected node, return the item with that id + """ + selected_node_name = self.get_selected_node_name() + if not selected_node_name: + return None + selected_node = self.get_node_by_name(selected_node_name) + if not selected_node: + return None + for item in selected_node.items: + if item.id == idd: + return item + return None + + def get_selected_item_id(self): + """ + returns the id of the currently selected item + """ + line = self.item_listview.get_selected_row() + if not line: + return None + return line['id'] + def get_items(self, node): """ Get all items in the given node @@ -265,3 +294,14 @@ class PubsubBrowserTab(tabs.Tab): return True return False + def open_selected_item(self): + """ + displays the currently selected item in the item view window + """ + selected_item = self.get_item_by_id(self.get_selected_item_id()) + if not selected_item: + return + log.debug('Content: %s'%ET.tostring(selected_item.content)) + self.item_viewer._text = str(ET.tostring(selected_item.content)) + self.item_viewer.rebuild_text() + return True diff --git a/src/windows.py b/src/windows.py index 9f6fc676..8176da67 100644 --- a/src/windows.py +++ b/src/windows.py @@ -1633,10 +1633,6 @@ class SimpleTextWin(Win): self._text = text self.built_lines = [] - def resize(self, height, width, y, x, stdscr): - self._resize(height, width, y, x, stdscr) - self.rebuild_text() - def rebuild_text(self): """ Transform the text in lines than can then be -- cgit v1.2.3