From c0c0b16218aca64785307a22114f0212b3758534 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 10 May 2012 13:22:37 +0200 Subject: Fixes #2358 --- src/tabs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tabs.py b/src/tabs.py index eecdaac2..a34c1a37 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -808,6 +808,7 @@ class MucTab(ChatTab): /part [msg] """ arg = arg.strip() + msg = None if self.joined: self.disconnect() muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, arg) -- cgit v1.2.3 From 5498ad37c8e1235863040eeaa854ac5d3e6cfc89 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 10 May 2012 18:22:10 +0200 Subject: Prevent iq errors & timeouts on /join completion --- src/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core.py b/src/core.py index 9b704682..94c1af5a 100644 --- a/src/core.py +++ b/src/core.py @@ -1714,7 +1714,10 @@ class Core(object): if jid.resource or jid.full.endswith('/'): # we are writing the resource: complete the node if not the_input.last_completion: - response = self.xmpp.plugin['xep_0030'].get_items(jid=jid.server, block=True, timeout=1) + try: + response = self.xmpp.plugin['xep_0030'].get_items(jid=jid.server, block=True, timeout=1) + except: + response = None if response: items = response['disco_items'].get_items() else: -- cgit v1.2.3 From 0c6a0abe8609b870570a623348b4deb62a89cd6f Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 10 May 2012 19:25:32 +0200 Subject: Document some more options --- data/default_config.cfg | 15 +++++++++++++-- doc/en/configure.txt | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/data/default_config.cfg b/data/default_config.cfg index 2c5f0d81..85366204 100644 --- a/data/default_config.cfg +++ b/data/default_config.cfg @@ -24,7 +24,6 @@ ignore_certificate = false # value to the services default. whitespace_interval = 300 - # Path to the certificate authenticating the Authority # A server may have several certificates, but if it uses a CA, it will often # keep the same for obvious reasons, so this is a good option if your server @@ -221,6 +220,9 @@ show_muc_jid = true # poezio will only show: toto (2) show_roster_jids = true +# If set to true, the roster will display the offline contacts too +roster_show_offline = false + # The terminal can beep on various event. Put the event you want in a list # (separated by spaces). # The events can be @@ -261,7 +263,6 @@ vertical_tab_list_sort = desc # possible values: desc, asc user_list_sort = desc - # The nick of people who join, part, change their status, etc. in a MUC will # be displayed using their nick color if true. display_user_color_in_join_part = false @@ -295,6 +296,16 @@ send_time = true max_messages_in_memory = 2048 max_lines_in_memory = 2048 +# Set this to true if you want the commands to be executed remotely +# (with ssh & the daemon), see the documentation of the /link plugin +# for details +exec_remote = false + +# Path of the FIFO in which the remote commands will be sent. +# Used with exec_remote set to true, see the documentation of /link for details +# Defaults to ./poezio.fifo +remote_fifo_path = + # Defines if all tabs are resized at the same time (if set to false) # or if they are really resized only when needed (if set to true). # “true” should be the most comfortable value diff --git a/doc/en/configure.txt b/doc/en/configure.txt index 36a49206..6bbf52cc 100644 --- a/doc/en/configure.txt +++ b/doc/en/configure.txt @@ -275,6 +275,10 @@ section of this documentation. the contact names). If there is no contact name, the JID will still be displayed. +*roster_show_offline*:: false + + Set this to true if you want to display the offline contacts too. + *beep_on*:: highlight private The terminal can beep on various event. Put the event you want in a list @@ -387,6 +391,17 @@ section of this documentation. You can specify another directory to use. It will be created if it does not exist. +*exec_remote*:: false + + If this is set to true, poezio will try to send the commands to a FIFO + instead of executing them locally. This is to be used in conjunction with + ssh and the daemon.py file. See the /link documentation for details. + +*remote_fifo_path*:: ./poezio.fifo + + The path of the FIFO used to send the commands (see the exec_remote option). + + Optional section options ~~~~~~~~~~~~~~~~~~~~~~~~ These option can appear in optional sections. These section are named -- cgit v1.2.3 From 28c15a889e006769d343e729b55de66df3a00526 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 12 May 2012 20:44:38 +0200 Subject: Add the ignore_private and private_auto_response options MUC-specific options. private_auto_response is empty by default. + new event ignored_private --- doc/en/configure.txt | 32 ++++++++++++++++++++++++++++++++ doc/en/plugins.txt | 6 ++++++ src/core.py | 14 ++++++++++---- src/events.py | 6 +++++- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/doc/en/configure.txt b/doc/en/configure.txt index 6bbf52cc..e18b4fbb 100644 --- a/doc/en/configure.txt +++ b/doc/en/configure.txt @@ -435,8 +435,40 @@ foo = true *display_user_color_in_join_part*:: false + If set to true, the color of the nick will be used in MUCs information + messages, instead of the default color from the theme. + *hide_exit_join*:: -1 + Exact same thing than hide_status_change, except that it concerns + the quit message, and that it will be hidden only if the value is 0. + Default setting means: + - all quit and join notices will be displayed + *hide_status_change*:: 120 + Set a number for this setting. + The join OR status-change notices will be + displayed according to this number. + -1: the notices will ALWAYS be displayed + 0: the notices will NEVER be displayed + n: On any other number, the notices will only be displayed + if the user involved has talked since the last n seconds + if the value is incorrect, -1 is assumed + Default setting means : + - status changes won't be displayed unless + the user talked in the last 2 minutes + *highlight_on*:: [empty] + + a list of words (separated by a colon (:)) that will be + highlighted if said by someone on a room + +*ignore_private*:: false + + Ignore private messages sent from this room. + +*private_auto_response*:: "Not in private, please." + + The message you want to be sent when someone tries to message you. + diff --git a/doc/en/plugins.txt b/doc/en/plugins.txt index 9d81ad66..3e8a0447 100644 --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -337,6 +337,12 @@ The handlers for this event are called when someone gets kicked in a MUC. * _presence_: Presence received. * _tab_: Tab of the concerned MUC. +*ignored_private*:: +message+ +tab+ + +The handlers for this event are called when a private message gets ignored. + +* _message_: Message received. +* _tab_: Tab of the concerned message. + SleekXMPP events ~~~~~~~~~~~~~~~~ diff --git a/src/core.py b/src/core.py index 94c1af5a..7fad4a63 100644 --- a/src/core.py +++ b/src/core.py @@ -842,13 +842,19 @@ class Core(object): room_from = jid.bare body = xhtml.get_body_from_message_stanza(message) tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation + ignore = config.get_by_tabname('ignore_private', 'false', + room_from).lower() == 'true' if not tab: # It's the first message we receive: create the tab - if body: + if body and not ignore: tab = self.open_private_window(room_from, nick_from, False) - if not tab: - return + if ignore: + self.events.trigger('ignored_private', message, tab) + msg = config.get_by_tabname('private_auto_response', None, room_from) + if msg and body: + self.xmpp.send_message(mto=jid.full, mbody=msg, mtype='chat') + return self.events.trigger('private_msg', message, tab) - if not body: + if not body or not tab: return tab.add_message(body, time=None, nickname=nick_from, forced_user=self.get_tab_by_name(room_from, tabs.MucTab).get_user_by_name(nick_from)) diff --git a/src/events.py b/src/events.py index 8def6cb0..e66c5ee5 100644 --- a/src/events.py +++ b/src/events.py @@ -40,6 +40,7 @@ class EventHandler(object): 'muc_nickchange': [], 'muc_ban': [], 'send_normal_presence': [], + 'ignored_private': [], } def add_event_handler(self, name, callback, position=0): @@ -63,7 +64,10 @@ class EventHandler(object): """ Call all the callbacks associated to the given event name. """ - callbacks = self.events[name] + callbacks = self.events.get(name, None) + if callbacks is None: + log.debug('%s: No such event.', name) + return for callback in callbacks: callback(*args, **kwargs) -- cgit v1.2.3 From ef8a7a647f6e895c89cefba0e2f83fb20fdb80c5 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 18:39:57 +0200 Subject: Use add_tab_command in the quote plugin --- plugins/quote.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/quote.py b/plugins/quote.py index 788d4027..50c390f2 100644 --- a/plugins/quote.py +++ b/plugins/quote.py @@ -1,7 +1,7 @@ -from plugin import BasePlugin, PluginConfig +from plugin import BasePlugin from xhtml import clean_text import common - +import tabs import re timestamp_re = re.compile(r'^(\d\d\d\d-\d\d-\d\d )?\d\d:\d\d:\d\d$') @@ -12,7 +12,9 @@ log = logging.getLogger(__name__) class Plugin(BasePlugin): def init(self): - self.add_command('quote', self.command_quote, "Usage: /quote \nQuote: takes the message received at and insert it in the input, to quote it.", self.completion_quote) + self.add_tab_command(tabs.MucTab, 'quote', self.command_quote, "Usage: /quote \nQuote: takes the message received at and insert it in the input, to quote it.", self.completion_quote) + self.add_tab_command(tabs.ConversationTab, 'quote', self.command_quote, "Usage: /quote \nQuote: takes the message received at and insert it in the input, to quote it.", self.completion_quote) + self.add_tab_command(tabs.PrivateTab, 'quote', self.command_quote, "Usage: /quote \nQuote: takes the message received at and insert it in the input, to quote it.", self.completion_quote) def command_quote(self, args): args = common.shell_split(args) -- cgit v1.2.3 From d949a379a3aa353b6f1f7c48e92ac8cefaca60fc Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 18:45:40 +0200 Subject: Use add_tab_command in the link plugin --- plugins/link.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/link.py b/plugins/link.py index 2fcf9ddd..29ded32f 100644 --- a/plugins/link.py +++ b/plugins/link.py @@ -3,15 +3,18 @@ import re -from plugin import BasePlugin, PluginConfig +from plugin import BasePlugin from xhtml import clean_text import common +import tabs url_pattern = re.compile(r'\b(http[s]?://(?:\S+))\b', re.I|re.U) class Plugin(BasePlugin): def init(self): - self.add_command('link', self.command_link, "Usage: /link\nLink: opens the last link from the conversation into a browser.") + self.add_tab_command(tabs.MucTab, 'link', self.command_link, "Usage: /link\nLink: opens the last link from the conversation into a browser.") + self.add_tab_command(tabs.PrivateTab, 'link', self.command_link, "Usage: /link\nLink: opens the last link from the conversation into a browser.") + self.add_tab_command(tabs.ConversationTab, 'link', self.command_link, "Usage: /link\nLink: opens the last link from the conversation into a browser.") def find_link(self, nb): messages = self.core.get_conversation_messages() -- cgit v1.2.3 From 5bdbca688c6d57eef41de9d344d94305bb421536 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 18:46:07 +0200 Subject: Use add_tab_command in the OTR plugin --- plugins/otr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/otr.py b/plugins/otr.py index b674c0fd..971b0059 100644 --- a/plugins/otr.py +++ b/plugins/otr.py @@ -16,7 +16,7 @@ class Plugin(BasePlugin): self.add_event_handler('conversation_say_after', self.on_conversation_say) self.add_event_handler('conversation_msg', self.on_conversation_msg) - self.add_command('otr', self.command_otr, "Usage: /otr \notr: Start or stop OTR for the current conversation", self.otr_completion) + self.add_tab_command(ConversationTab, 'otr', self.command_otr, "Usage: /otr \notr: Start or stop OTR for the current conversation", self.otr_completion) ConversationTab.add_information_element('otr', self.display_encryption_status) def cleanup(self): -- cgit v1.2.3 From 43d43c69317e8f77f93ffaf2c099c320c63a5f85 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 18:52:34 +0200 Subject: Add a documentation index --- doc/en/index.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 doc/en/index.txt diff --git a/doc/en/index.txt b/doc/en/index.txt new file mode 100644 index 00000000..beb80d69 --- /dev/null +++ b/doc/en/index.txt @@ -0,0 +1,17 @@ +Poezio Documentation +==================== + +Welcome to the english documentation, here is a list of the availalble pages. + +Available pages +--------------- + + +* link:install.html[Installation] +* link:configure.html[Configuration] +* link:usage.html[Usage] +* link:themes.html[Theming] +* link:keys.html[Keys] +* link:plugins/index.html[Available Plugins] +* link:plugins.html[Developing plugins] +* link:xep.html[Current XEP support] -- cgit v1.2.3 From 4d7c01f8d0a544849c060cdc9df23cfc2fbc29ac Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 18:57:58 +0200 Subject: Mention exec_remote in the simple_notify plugin help --- doc/en/plugins/simple_notify.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/en/plugins/simple_notify.txt b/doc/en/plugins/simple_notify.txt index f9ec0f74..c210f703 100644 --- a/doc/en/plugins/simple_notify.txt +++ b/doc/en/plugins/simple_notify.txt @@ -24,3 +24,7 @@ directly in the command line by the author of the message, and the body. The example shown above will display something like this: image:../../images/simple_notify_example.png["Simple notify example", title="Simple notify example"] + +NOTE: If you set the _exec_remote_ option to _true_ into the +link:../configure.html[main configuration file], the command will be executed +remotely (as explained in the link:link.html[/link help]). -- cgit v1.2.3 From ecc40fdc5efc873f87ff10f313f3045dbdf70363 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 13 May 2012 19:01:27 +0200 Subject: Catch a possible exception when trying to retrieve the rgb value in curses Fixes #2354 --- src/xhtml.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/xhtml.py b/src/xhtml.py index cf7a5fc0..38ec690c 100644 --- a/src/xhtml.py +++ b/src/xhtml.py @@ -206,7 +206,10 @@ def ncurses_color_to_html(color): html color. """ if color <= 15: - (r, g, b) = curses.color_content(color) + try: + (r, g, b) = curses.color_content(color) + except: # fallback in faulty terminals (e.g. xterm) + (r, g, b) = curses.color_content(color%8) r = r / 1000 * 6 - 0.01 g = g / 1000 * 6 - 0.01 b = b / 1000 * 6 - 0.01 -- cgit v1.2.3 From e86ecb3a529f30123df376ca44da932288b24831 Mon Sep 17 00:00:00 2001 From: mathieui Date: Mon, 14 May 2012 00:01:19 +0200 Subject: =?UTF-8?q?Fix=20the=20=E2=80=9C/bookmark{,=5Flocal}=20*=E2=80=9D?= =?UTF-8?q?=20behaviour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core.py b/src/core.py index 7fad4a63..140a3bb9 100644 --- a/src/core.py +++ b/src/core.py @@ -1935,6 +1935,7 @@ class Core(object): else: b.method = "local" bookmark.save_local() + bookmark.save_remote(self.xmpp) self.information('Bookmarks added and saved.', 'Info') return else: @@ -1990,11 +1991,13 @@ class Core(object): if isinstance(tab, tabs.MucTab): b = bookmark.get_by_jid(tab.get_name()) if not b: - b = bookmark.Bookmark(tab.get_name(), autojoin=autojoin) + b = bookmark.Bookmark(tab.get_name(), autojoin=autojoin, + method=bookmark.preferred) bookmark.bookmarks.append(b) else: - b.method = "local" + b.method = bookmark.preferred if bookmark.save_remote(self.xmpp, self): + bookmark.save_local() self.information("Bookmarks added.", "Info") else: self.information("Could not add the bookmarks.", "Info") -- cgit v1.2.3 From 58a41fe0652021481ebfa96fb3d238cddd93ca95 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 01:59:32 +0200 Subject: Add status code handling - Fixes #2338 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get status codes in presence and message stanzas, and show information related to them. If the change hinders privacy (logs added, or public JIDs), then a red “Warning” message is shown instead of the classic “Info”. --- src/core.py | 43 +++++++++++++++++++++++++++++++++++++++++++ src/tabs.py | 22 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/core.py b/src/core.py index 140a3bb9..c2624584 100644 --- a/src/core.py +++ b/src/core.py @@ -246,6 +246,7 @@ class Core(object): self.xmpp.add_event_handler("groupchat_message", self.on_groupchat_message) self.xmpp.add_event_handler("groupchat_invite", self.on_groupchat_invite) self.xmpp.add_event_handler("groupchat_decline", self.on_groupchat_decline) + self.xmpp.add_event_handler("groupchat_config_status", self.on_status_codes) self.xmpp.add_event_handler("groupchat_subject", self.on_groupchat_subject) self.xmpp.add_event_handler("message", self.on_message) self.xmpp.add_event_handler("got_online" , self.on_got_online) @@ -1412,6 +1413,48 @@ class Core(object): if config.get_by_tabname('disable_beep', 'false', room_from, False).lower() != 'true': curses.beep() + def on_status_codes(self, message): + """ + Handle groupchat messages with status codes. + Those are received when a room configuration change occurs. + """ + room_from = message['from'] + tab = self.get_tab_by_name(room_from, tabs.MucTab) + status_codes = set([s.attrib['code'] for s in message.findall('{%s}x/{%s}status' % (tabs.NS_MUC_USER, tabs.NS_MUC_USER))]) + if '101' in status_codes: + self.information('Your affiliation in the room %s changed' % room_from, 'Info') + elif tab and status_codes: + show_unavailable = '102' in status_codes + hide_unavailable = '103' in status_codes + non_priv = '104' in status_codes + logging_on = '170' in status_codes + logging_off= '171' in status_codes + non_anon = '172' in status_codes + semi_anon = '173' in status_codes + full_anon = '174' in status_codes + modif = False + if show_unavailable or hide_unavailable or non_priv or logging_off\ + or non_anon or semi_anon or full_anon: + tab.add_message('\x19%(info_col)s}Info: A configuration change not privacy-related occured.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + modif = True + if show_unavailable: + tab.add_message('\x19%(info_col)s}Info: The unavailable members are now shown.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + elif hide_unavailable: + tab.add_message('\x19%(info_col)s}Info: The unavailable members are now hidden.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + if non_anon: + tab.add_message('\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + elif semi_anon: + tab.add_message('\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + elif full_anon: + tab.add_message('\x19%(info_col)s}Info: The room is now fully anonymous.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + if logging_on: + tab.add_message('\x191}Warning: \x19%(info_col)s}This room is publicly logged' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + elif logging_off: + tab.add_message('\x19%(info_col)s}Info: This room is not logged anymore.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + if modif: + self.refresh_window() + + def add_message_to_text_buffer(self, buff, txt, time=None, nickname=None, history=None): """ Add the message to the room if possible, else, add it to the Info window diff --git a/src/tabs.py b/src/tabs.py index a34c1a37..f798df69 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -1180,8 +1180,12 @@ class MucTab(ChatTab): self.send_chat_state('active') new_user.color = get_theme().COLOR_OWN_NICK self.add_message(_("\x19%(info_col)s}Your nickname is \x193}%(nick)s") % {'nick': from_nick, 'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + if '201' in status_codes: + self.add_message('\x19%(info_col)s}Info: The room has been created' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) if '170' in status_codes: self.add_message('\x191}Warning: \x19%(info_col)s}this room is publicly logged' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + if '100' in status_codes: + self.add_message('\x191}Warning: \x19%(info_col)s}This room is not anonymous.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) if self.core.current_tab() is not self: self.refresh_tab_win() self.core.current_tab().input.refresh() @@ -1191,6 +1195,8 @@ class MucTab(ChatTab): change_nick = '303' in status_codes kick = '307' in status_codes and typ == 'unavailable' ban = '301' in status_codes and typ == 'unavailable' + shutdown = '332' in status_codes and typ == 'unavailable' + non_member = '322' in status_codes and typ == 'unavailable' user = self.get_user_by_name(from_nick) # New user if not user: @@ -1209,6 +1215,12 @@ class MucTab(ChatTab): self.core.events.trigger('muc_kick', presence, self) self.core.on_user_left_private_conversation(from_room, from_nick, status) self.on_user_kicked(presence, user, from_nick) + elif shutdown: + self.core.events.trigger('muc_shutdown', presence, self) + self.on_muc_shutdown() + elif non_member: + self.core.events.trigger('muc_shutdown', presence, self) + self.on_non_member_kick() # user quit elif typ == 'unavailable': self.on_user_leave_groupchat(user, jid, status, from_nick, from_room) @@ -1222,6 +1234,16 @@ class MucTab(ChatTab): self.input.refresh() self.core.doupdate() + def on_non_member_kicked(self): + """We have been kicked because the MUC is members-only""" + self.add_message('\x19%(info_col)s}%You have been kicked because you are not a member and the room is now members-only.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + self.disconnect() + + def on_muc_shutdown(self): + """We have been kicked because the MUC service is shutting down""" + self.add_message('\x19%(info_col)s}%You have been kicked because the MUC service is shutting down.' % {'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}) + self.disconnect() + def on_user_join(self, from_nick, affiliation, show, status, role, jid): """ When a new user joins the groupchat -- cgit v1.2.3 From c04f0e97837dfc0c73defa3aaeada5c3848e2c59 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 02:01:50 +0200 Subject: =?UTF-8?q?Prevent=20the=20rooms=20from=20going=20=E2=80=9Coffline?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a blacklist inside the roster that contains the bare JIDs of all the rooms ever joined in this session, so that no JID using this server will ever be shown as getting “offline”. If there is a cleaner way to do that (discriminating JIDs), I welcome it. --- src/core.py | 5 +++++ src/roster.py | 6 +++++- src/tabs.py | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core.py b/src/core.py index c2624584..a89e7b25 100644 --- a/src/core.py +++ b/src/core.py @@ -658,12 +658,17 @@ class Core(object): self.add_tab(form_tab, True) def on_got_offline(self, presence): + """ + A JID got offline + """ jid = presence['from'] logger.log_roster_change(jid.bare, 'got offline') # If a resource got offline, display the message in the conversation with this # precise resource. if jid.resource: self.add_information_message_to_conversation_tab(jid.full, '\x195}%s is \x191}offline' % (jid.full)) + if jid.server in roster.blacklist: + return self.add_information_message_to_conversation_tab(jid.bare, '\x195}%s is \x191}offline' % (jid.bare)) self.information('\x193}%s \x195}is \x191}offline' % (jid.bare), 'Roster') if isinstance(self.current_tab(), tabs.RosterInfoTab): diff --git a/src/roster.py b/src/roster.py index 7f93c4b2..e1251024 100644 --- a/src/roster.py +++ b/src/roster.py @@ -19,6 +19,10 @@ from sleekxmpp.xmlstream.stanzabase import JID from sleekxmpp.exceptions import IqError class Roster(object): + + # MUC domains to blacklist from the contacts roster + blacklist = set() + def __init__(self): """ node: the RosterSingle from SleekXMPP @@ -103,7 +107,7 @@ class Roster(object): def jids(self): """List of the contact JIDS""" - return [key for key in self.__node.keys() if key not in self.__mucs and key != self.jid] + return [key for key in self.__node.keys() if JID(key).server not in self.blacklist and key != self.jid] def get_contacts(self): """ diff --git a/src/tabs.py b/src/tabs.py index f798df69..be8085e8 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -1171,6 +1171,7 @@ class MucTab(ChatTab): self.core.events.trigger('muc_join', presence, self) if from_nick == self.own_nick: self.joined = True + roster.blacklist.add(JID(from_room).server) if self.get_name() in self.core.initial_joins: self.core.initial_joins.remove(self.get_name()) self._state = 'normal' -- cgit v1.2.3 From 1263b9be835868367d44fe442346dfc0e57c3261 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 16:53:33 +0200 Subject: Update the OTR documentation --- doc/en/plugins/otr.txt | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/doc/en/plugins/otr.txt b/doc/en/plugins/otr.txt index 26a6ed4e..533f6b4b 100644 --- a/doc/en/plugins/otr.txt +++ b/doc/en/plugins/otr.txt @@ -37,41 +37,45 @@ If not, then you will have to install it by hand. First, clone the repo and go inside the created directory: ============================================== +[source,bash] +------------- +git clone https://github.com/teisenbe/libopenotr.git - git clone https://git.teisen.be/repo/libopenotr.git - - cd libopenotr - +cd libopenotr +------------- ============================================== then run autogen.sh and configure ============ +[source,bash] +------------- +sh autogen.sh - sh autogen.sh - - ./configure --enable-gaping-security-hole - +./configure --enable-gaping-security-hole +------------- ============ Then compile & install the lib: ============ +[source,bash] +------------- +make - make - - sudo make install - +sudo make install +------------- ============ Finally, install the python module: ============================= +[source,bash] +------------- +python3 setup.py build - python3 setup.py build - - sudo python3 setup.py install - +sudo python3 setup.py install +------------- ============================= -- cgit v1.2.3 From 1e07cd4f58a2c0c9bc29bb9f5d2e62d4b2366f74 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 17:22:13 +0200 Subject: Put a space after completion only if there is one (and only one) command --- src/tabs.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/tabs.py b/src/tabs.py index be8085e8..1ac301a1 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -216,11 +216,8 @@ class Tab(object): # one possibily. The next tab will complete the argument. # Otherwise we would need to add a useless space before being # able to complete the arguments. - hit_copy = the_input.hit_list[:] - for w in hit_copy[:]: - while hit_copy.count(w) > 1: - hit_copy.remove(w) - if len(hit_copy) in (1, 0): + hit_copy = set(the_input.hit_list) + if len(hit_copy) == 1: the_input.do_command(' ') return True return False -- cgit v1.2.3 From 155914470da7f63d7a41025a17c1fd0827ed7df9 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 17:39:14 +0200 Subject: Complete the commands differently. If there is 0 match for the beginning of command, delete the last letter (over and over) until there is a match, then complete that. --- src/tabs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tabs.py b/src/tabs.py index 1ac301a1..a66d81cd 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -217,6 +217,10 @@ class Tab(object): # Otherwise we would need to add a useless space before being # able to complete the arguments. hit_copy = set(the_input.hit_list) + while not hit_copy: + the_input.key_backspace() + the_input.auto_completion(words, '', quotify=False) + hit_copy = set(the_input.hit_list) if len(hit_copy) == 1: the_input.do_command(' ') return True -- cgit v1.2.3 From da30c8c79f5950ef85296ba8f749b929b1bbbd57 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 16 May 2012 19:50:56 +0200 Subject: Put color in the topic again --- src/core.py | 9 +++++++-- src/tabs.py | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core.py b/src/core.py index a89e7b25..ee7fcf2f 100644 --- a/src/core.py +++ b/src/core.py @@ -1361,9 +1361,14 @@ class Core(object): if not subject or not tab: return if nick_from: - self.add_message_to_text_buffer(tab._text_buffer, _("%(nick)s set the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=None) + self.add_message_to_text_buffer(tab._text_buffer, + _("\x19%(info_col)s}%(nick)s set the subject to: %(subject)s") % + {'info_col': get_theme().COLOR_INFORMATION_TEXT[0], 'nick':nick_from, 'subject':subject}, + time=None) else: - self.add_message_to_text_buffer(tab._text_buffer, _("The subject is: %(subject)s") % {'subject':subject}, time=None) + self.add_message_to_text_buffer(tab._text_buffer, _("\x19%(info_col)s}The subject is: %(subject)s") % + {'subject':subject, 'info_col': get_theme().COLOR_INFORMATION_TEXT[0]}, + time=None) tab.topic = subject if self.get_tab_by_name(room_from, tabs.MucTab) is self.current_tab(): self.refresh_window() diff --git a/src/tabs.py b/src/tabs.py index a66d81cd..39b81df7 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -853,7 +853,8 @@ class MucTab(ChatTab): /topic [new topic] """ if not arg.strip(): - self._text_buffer.add_message(_("The subject of the room is: %s") % self.topic) + self._text_buffer.add_message(_("\x19%s}The subject of the room is: %s") % + (get_theme().COLOR_INFORMATION_TEXT[0], self.topic)) self.text_win.refresh() self.input.refresh() return -- cgit v1.2.3 From 0f7bda20b8b89bf334d67da45f4fc3e4dc8117f9 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 01:00:35 +0200 Subject: Add a way to review room highlights - Fixes #1673 This new features is available with M-p and M-n (previous/next). It saves the last highlight viewed, meaning that if you scroll in the buffer, M-n or M-p will take you to the next or previous hl compared to the one before you started scrolling. For convenience, going to the previous highlight of the first highlight will take you to the bottom of the buffer, and going to the next highlight of the last highlight will do *the same*. If there are several highlights in one message, only the first line will be considered a highlight. --- doc/en/keys.txt | 4 +++ src/tabs.py | 20 +++++++++++++- src/text_buffer.py | 4 +-- src/windows.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 102 insertions(+), 7 deletions(-) diff --git a/doc/en/keys.txt b/doc/en/keys.txt index c1b9b7fd..07fd7d17 100644 --- a/doc/en/keys.txt +++ b/doc/en/keys.txt @@ -105,6 +105,10 @@ These keys work only in the MultiUserChat tab. *Alt-y*:: Scroll the user list up. +*Alt-p*:: Scroll to the previous highlight. + +*Alt-n*:: Scroll to the next highlight. + *tabulation*:: Complete a nick. *Ctrl-c*:: Insert xhtml formatting. You have to press Ctrl-c then a character diff --git a/src/tabs.py b/src/tabs.py index 39b81df7..f97c7f03 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -600,6 +600,8 @@ class MucTab(ChatTab): self.key_func['^I'] = self.completion self.key_func['M-u'] = self.scroll_user_list_down self.key_func['M-y'] = self.scroll_user_list_up + self.key_func['M-n'] = self.go_to_next_hl + self.key_func['M-p'] = self.go_to_prev_hl # commands self.commands['ignore'] = (self.command_ignore, _("Usage: /ignore \nIgnore: Ignore a specified nickname."), self.completion_ignore) self.commands['unignore'] = (self.command_unignore, _("Usage: /unignore \nUnignore: Remove the specified nickname from the ignore list."), self.completion_unignore) @@ -630,6 +632,22 @@ class MucTab(ChatTab): def general_jid(self): return self.get_name() + def go_to_next_hl(self): + """ + Go to the next HL in the room, or the last + """ + self.text_win.next_highlight() + self.refresh() + self.core.doupdate() + + def go_to_prev_hl(self): + """ + Go to the previous HL in the room, or the first + """ + self.text_win.previous_highlight() + self.refresh() + self.core.doupdate() + def completion_version(self, the_input): """Completion for /version""" compare_users = lambda x: x.last_talked @@ -1502,7 +1520,7 @@ class MucTab(ChatTab): if highlight: nick_color = highlight time = time or datetime.now() - self._text_buffer.add_message(txt, time, nickname, nick_color, history, user) + self._text_buffer.add_message(txt, time, nickname, nick_color, history, user, highlight=highlight) return highlight class PrivateTab(ChatTab): diff --git a/src/text_buffer.py b/src/text_buffer.py index 9b717882..2d83ab7e 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -35,7 +35,7 @@ class TextBuffer(object): def add_window(self, win): self.windows.append(win) - def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None, user=None): + def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None, user=None, highlight=False): time = time or datetime.now() if txt.startswith('/me '): if nick_color: @@ -57,7 +57,7 @@ class TextBuffer(object): ret_val = None for window in self.windows: # make the associated windows # build the lines from the new message - nb = window.build_new_message(msg, history=history) + nb = window.build_new_message(msg, history=history, highlight=highlight) if ret_val is None: ret_val = nb if window.pos != 0: diff --git a/src/windows.py b/src/windows.py index 7185346e..ae79fb74 100644 --- a/src/windows.py +++ b/src/windows.py @@ -622,6 +622,8 @@ class TextWin(Win): # on resize, we rebuild all the messages self.lock = False self.lock_buffer = [] + self.highlights = [] + self.hl_pos = -1 def toggle_lock(self): if self.lock: @@ -637,6 +639,74 @@ class TextWin(Win): self.built_lines.append(line) self.lock = False + def next_highlight(self): + """ + Go to the next highlight in the buffer. + (depending on which highlight was selected before) + if the buffer is already positionned on the last, of if there are no + highlights, scroll to the end of the buffer. + """ + log.debug('Going to the next highlight…') + if not self.highlights or self.hl_pos == -1 or \ + self.hl_pos == len(self.highlights)-1: + self.hl_pos = -1 + self.pos = 0 + return + hl_size = len(self.highlights) - 1 + if self.hl_pos < hl_size: + self.hl_pos += 1 + else: + self.hl_pos = hl_size + + hl = self.highlights[self.hl_pos] + pos = None + while not pos: + try: + pos = self.built_lines.index(hl) + except ValueError: + self.highlights = self.highlights[self.hl_pos+1:] + if not self.highlights: + self.hl_pos = -1 + self.pos = 0 + return + hl = self.highlights[0] + self.pos = len(self.built_lines) - pos - self.height + if self.pos < 0 or self.pos >= len(self.built_lines): + self.pos = 0 + + def previous_highlight(self): + """ + Go to the previous highlight in the buffer. + (depending on which highlight was selected before) + if the buffer is already positionned on the first, or if there are no + highlights, scroll to the end of the buffer. + """ + log.debug('Going to the previous highlight…') + if not self.highlights or self.hl_pos == 0: + self.hl_pos = -1 + self.pos = 0 + return + if self.hl_pos < 0: + self.hl_pos = len(self.highlights) - 1 + elif self.hl_pos > 0: + self.hl_pos -= 1 + + hl = self.highlights[self.hl_pos] + pos = None + while not pos: + try: + pos = self.built_lines.index(hl) + except ValueError: + self.highlights = self.highlights[self.hl_pos+1:] + if not self.highlights: + self.hl_pos = -1 + self.pos = 0 + return + hl = self.highlights[0] + self.pos = len(self.built_lines) - pos - self.height + if self.pos < 0 or self.pos >= len(self.built_lines): + self.pos = 0 + def scroll_up(self, dist=14): self.pos += dist if self.pos + self.height > len(self.built_lines): @@ -676,7 +746,7 @@ class TextWin(Win): if None not in self.built_lines: self.built_lines.append(None) - def build_new_message(self, message, history=None, clean=True): + def build_new_message(self, message, history=None, clean=True, highlight=False): """ Take one message, build it and add it to the list Return the number of lines that are built for the given @@ -703,10 +773,13 @@ class TextWin(Win): start_pos=line[0], end_pos=line[1])) else: + for line in lines: - self.built_lines.append(Line(msg=message, - start_pos=line[0], - end_pos=line[1])) + saved_line = Line(msg=message,start_pos=line[0],end_pos=line[1]) + self.built_lines.append(saved_line) + if highlight: + highlight = False + self.highlights.append(saved_line) if clean: while len(self.built_lines) > self.lines_nb_limit: self.built_lines.pop(0) -- cgit v1.2.3 From 4c0a3fb5a2eca27133e558fa8394b3d07d0203ba Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 03:34:04 +0200 Subject: Resolves separator persistence problems - Fixes #2073 Now we have to pass the textbuffer object when we want to add a line separator. --- src/tabs.py | 8 ++++---- src/windows.py | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/tabs.py b/src/tabs.py index f97c7f03..be2a886d 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -535,7 +535,7 @@ class ChatTab(Tab): def move_separator(self): self.text_win.remove_line_separator() - self.text_win.add_line_separator() + self.text_win.add_line_separator(self._text_buffer) self.text_win.refresh() self.input.refresh() @@ -1150,7 +1150,7 @@ class MucTab(ChatTab): else: self.state = 'disconnected' self.text_win.remove_line_separator() - self.text_win.add_line_separator() + self.text_win.add_line_separator(self._text_buffer) if config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true' and not self.input.get_text(): self.send_chat_state('inactive') @@ -1686,7 +1686,7 @@ class PrivateTab(ChatTab): def on_lose_focus(self): self.state = 'normal' self.text_win.remove_line_separator() - self.text_win.add_line_separator() + self.text_win.add_line_separator(self._text_buffer) tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab) if tab and tab.joined and config.get_by_tabname( 'send_chat_states', 'true', self.general_jid, True) == 'true'\ @@ -2604,7 +2604,7 @@ class ConversationTab(ChatTab): resource = None self.state = 'normal' self.text_win.remove_line_separator() - self.text_win.add_line_separator() + self.text_win.add_line_separator(self._text_buffer) if config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true' and (not self.input.get_text() or not self.input.get_text().startswith('//')): if resource: self.send_chat_state('inactive') diff --git a/src/windows.py b/src/windows.py index ae79fb74..ea2cb3f7 100644 --- a/src/windows.py +++ b/src/windows.py @@ -620,11 +620,17 @@ class TextWin(Win): self.pos = 0 self.built_lines = [] # Each new message is built and kept here. # on resize, we rebuild all the messages + self.lock = False self.lock_buffer = [] + + # the Lines of the highlights in that buffer self.highlights = [] + # the current HL position in that list self.hl_pos = -1 + self.separator_after = None + def toggle_lock(self): if self.lock: self.release_lock() @@ -738,13 +744,18 @@ class TextWin(Win): log.debug('remove_line_separator') if None in self.built_lines: self.built_lines.remove(None) + self.separator_after = None - def add_line_separator(self): + def add_line_separator(self, room=None): """ add a line separator at the end of messages list + room is a textbuffer that is needed to get the previous message + (in case of resize) """ if None not in self.built_lines: self.built_lines.append(None) + if room: + self.separator_after = room.messages[-1] def build_new_message(self, message, history=None, clean=True, highlight=False): """ @@ -862,6 +873,8 @@ class TextWin(Win): self.built_lines = [] for message in room.messages: self.build_new_message(message, clean=False) + if self.separator_after is message: + self.build_new_message(None) while len(self.built_lines) > self.lines_nb_limit: self.built_lines.pop(0) -- cgit v1.2.3 From 65062754e194e2c1fadf4a30677444730e73ec71 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 14:11:02 +0200 Subject: Fix a crash if there are no messages in the room --- src/windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows.py b/src/windows.py index ea2cb3f7..e5973ec9 100644 --- a/src/windows.py +++ b/src/windows.py @@ -754,7 +754,7 @@ class TextWin(Win): """ if None not in self.built_lines: self.built_lines.append(None) - if room: + if room and room.messages: self.separator_after = room.messages[-1] def build_new_message(self, message, history=None, clean=True, highlight=False): -- cgit v1.2.3 From 64defba0ae18812196caadab26cdea0d24849be5 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 16:45:40 +0200 Subject: Show subscription changes in the info buffer - Fixes #2234 --- src/core.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/core.py b/src/core.py index ee7fcf2f..3ac0a0b5 100644 --- a/src/core.py +++ b/src/core.py @@ -991,6 +991,8 @@ class Core(object): """subscribed received""" jid = presence['from'].bare contact = roster[jid] + if contact.subscription not in ('both', 'from'): + self.information('%s accepted your contact proposal' % jid, 'Roster') if contact.pending_out: contact.pending_out = False if isinstance(self.current_tab(), tabs.RosterInfoTab): @@ -1000,9 +1002,10 @@ class Core(object): """unsubscribe received""" jid = presence['from'].bare contact = roster[jid] - if contact.subscription in ('to', 'both'): - self.information('%s does not want to receive your status anymore.' % jid, 'Roster') - self.get_tab_by_number(0).state = 'highlight' + if not contact: + return + self.information('%s does not want to receive your status anymore.' % jid, 'Roster') + self.get_tab_by_number(0).state = 'highlight' if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() @@ -1010,13 +1013,14 @@ class Core(object): """unsubscribed received""" jid = presence['from'].bare contact = roster[jid] - if contact.subscription in ('both', 'from'): - self.information('%s does not want you to receive his status anymore.'%jid, 'Roster') - self.get_tab_by_number(0).state = 'highlight' - elif contact.pending_out: - self.information('%s rejected your contact proposal.' % jid, 'Roster') - self.get_tab_by_number(0).state = 'highlight' + if not contact: + return + if contact.pending_out: + self.information('%s rejected your contact proposal' % jid, 'Roster') contact.pending_out = False + else: + self.information('%s does not want you to receive his/her/its status anymore.'%jid, 'Roster') + self.get_tab_by_number(0).state = 'highlight' if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() -- cgit v1.2.3 From 0f8a5abdc0818b4071bf4623a12ee95223b11ba3 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 16:55:31 +0200 Subject: Add an option to always show the separator - Fixes #2240 --- data/default_config.cfg | 4 ++++ doc/en/configure.txt | 4 ++++ src/tabs.py | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/data/default_config.cfg b/data/default_config.cfg index 85366204..61c30c21 100644 --- a/data/default_config.cfg +++ b/data/default_config.cfg @@ -296,6 +296,10 @@ send_time = true max_messages_in_memory = 2048 max_lines_in_memory = 2048 +# Show the separator at the bottom of the text buffer, even if no one +# spoke +show_useless_separator = false + # Set this to true if you want the commands to be executed remotely # (with ssh & the daemon), see the documentation of the /link plugin # for details diff --git a/doc/en/configure.txt b/doc/en/configure.txt index e18b4fbb..e5098da1 100644 --- a/doc/en/configure.txt +++ b/doc/en/configure.txt @@ -438,6 +438,10 @@ foo = true If set to true, the color of the nick will be used in MUCs information messages, instead of the default color from the theme. +*show_useless_separator*:: false + + If true, show the separator in a chat room, even if no one spoke. + *hide_exit_join*:: -1 Exact same thing than hide_status_change, except that it concerns diff --git a/src/tabs.py b/src/tabs.py index be2a886d..92b913b0 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -1156,7 +1156,7 @@ class MucTab(ChatTab): def on_gain_focus(self): self.state = 'current' - if self.text_win.built_lines and self.text_win.built_lines[-1] is None: + if self.text_win.built_lines and self.text_win.built_lines[-1] is None and config.getl('show_useless_separator', 'false') != 'true': self.text_win.remove_line_separator() curses.curs_set(1) if self.joined and config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true' and not self.input.get_text(): -- cgit v1.2.3 From 3411d8ca83591adf9a92b3c1c78fbd74a4612fe7 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 17:15:15 +0200 Subject: Add a shortcut to go to the first unread message (separator) with M-p --- doc/en/keys.txt | 1 + src/tabs.py | 6 ++++++ src/windows.py | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/en/keys.txt b/doc/en/keys.txt index 07fd7d17..874614df 100644 --- a/doc/en/keys.txt +++ b/doc/en/keys.txt @@ -95,6 +95,7 @@ height of the conversation window - 1. *Alt-v*:: Move the separator at the bottom of the tab. +*Alt-h*:: Scroll to the separator, if there is one. MultiUserChat tab input keys ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/tabs.py b/src/tabs.py index 92b913b0..61b322a0 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -394,6 +394,7 @@ class ChatTab(Tab): # since the last input self.remote_supports_attention = False self.key_func['M-v'] = self.move_separator + self.key_func['M-h'] = self.scroll_separator self.key_func['M-/'] = self.last_words_completion self.key_func['^M'] = self.on_enter self.commands['say'] = (self.command_say, @@ -566,6 +567,11 @@ class ChatTab(Tab): def on_half_scroll_down(self): self.text_win.scroll_down((self.text_win.height-1) // 2) + def scroll_separator(self): + self.text_win.scroll_to_separator() + self.refresh() + self.core.doupdate() + class MucTab(ChatTab): """ diff --git a/src/windows.py b/src/windows.py index e5973ec9..29bf5953 100644 --- a/src/windows.py +++ b/src/windows.py @@ -731,11 +731,11 @@ class TextWin(Win): present, scroll at the top of the window """ if None in self.built_lines: - self.pos = self.built_lines.index(None) + self.pos = len(self.built_lines) - self.built_lines.index(None) - self.height + 1 + if self.pos < 0: + self.pos = 0 # Chose a proper position (not too high) self.scroll_up(0) - else: # Go at the top of the win - self.pos = len(self.built_lines) - self.height def remove_line_separator(self): """ -- cgit v1.2.3 From fb450a71386d39afe4059e84632b03f6a230109c Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 17:28:53 +0200 Subject: Use a different theme variable for the /me message --- src/text_buffer.py | 2 +- src/theming.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/text_buffer.py b/src/text_buffer.py index 2d83ab7e..b615e96c 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -45,7 +45,7 @@ class TextBuffer(object): else: color = None # TODO: display the bg color too. - txt = ("\x19%(info_col)s}* \x19%(col)s}" % {'col':color or 5, 'info_col':get_theme().COLOR_INFORMATION_TEXT[0]})+ nickname + ' \x19%(info_col)s}' % {'info_col':get_theme().COLOR_INFORMATION_TEXT[0]} + txt[4:] + txt = '\x19%(info_col)s}* \x19%(col)s}%(nick)s \x19%(info_col)s}%(msg)s' % {'info_col':get_theme().COLOR_ME_MESSAGE[0], 'col': color or 5, 'nick': nickname, 'msg': txt[4:]} nickname = None msg = Message(txt='%s\x19o'%(txt.replace('\t', ' '),), nick_color=nick_color, time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\ diff --git a/src/theming.py b/src/theming.py index e45a25ff..94d7b005 100644 --- a/src/theming.py +++ b/src/theming.py @@ -108,6 +108,9 @@ class Theme(object): CHAR_AFFILIATION_MEMBER = '+' CHAR_AFFILIATION_NONE = '-' + # Color for the /me message + COLOR_ME_MESSAGE = (6, -1) + # Separators COLOR_VERTICAL_SEPARATOR = (4, -1) COLOR_NEW_TEXT_SEPARATOR = (2, -1) -- cgit v1.2.3 From c77e2878b891f000ba1c3a030acd0c195c7e1948 Mon Sep 17 00:00:00 2001 From: mathieui Date: Thu, 17 May 2012 20:48:46 +0200 Subject: =?UTF-8?q?Do=20not=20add=20a=20'=E2=80=A6'=20if=20the=20nick=20ha?= =?UTF-8?q?s=20the=20exact=20same=20size=20as=20the=20limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/windows.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows.py b/src/windows.py index 29bf5953..06214abb 100644 --- a/src/windows.py +++ b/src/windows.py @@ -51,7 +51,7 @@ def truncate_nick(nick, size=None): size = size or config.get('max_nick_length', 25) if size < 1: size = 1 - if nick and len(nick) >= size: + if nick and len(nick) > size: return nick[:size]+'…' return nick -- cgit v1.2.3 From dc8b39709ffa2746c6c8ae2c03fad4818a08ba4b Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 19 May 2012 21:56:13 +0200 Subject: Make the alias plugin behave like documented. --- plugins/alias.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/plugins/alias.py b/plugins/alias.py index d6a46b6f..5a35d1c6 100644 --- a/plugins/alias.py +++ b/plugins/alias.py @@ -1,3 +1,9 @@ +""" +Alias plugin. + +Allows the creation and the removal of personal aliases. +""" + from plugin import BasePlugin import common from common import parse_command_args_to_alias as parse @@ -9,14 +15,16 @@ class Plugin(BasePlugin): self.commands = {} def command_alias(self, line): + """ + /alias [args] + """ arg = common.shell_split(line) if len(arg) < 2: self.core.information('Alias: Not enough parameters', 'Error') return alias = arg[0] - tmp_args = common.shell_split(arg[1]) - command = tmp_args.pop(0) - tmp_args = arg[1][len(command)+1:] + command = arg[1] + tmp_args = arg[2] if len(arg) > 2 else '' if alias in self.core.commands or alias in self.commands: self.core.information('Alias: command already exists', 'Error') @@ -26,6 +34,9 @@ class Plugin(BasePlugin): self.core.information('Alias /%s successfuly created' % alias, 'Info') def command_unalias(self, alias): + """ + /unalias + """ if alias in self.commands: del self.commands[alias] self.del_command(alias) -- cgit v1.2.3 From 51c788ad96703d215942499ffefe6fdc98326b6b Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 19 May 2012 22:28:30 +0200 Subject: Allow nick completion in the Private tabs as well. --- src/tabs.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tabs.py b/src/tabs.py index 61b322a0..2126cef9 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -8,9 +8,9 @@ """ a Tab object is a way to organize various Windows (see windows.py) around the screen at once. -A tab is then composed of multiple Buffer. +A tab is then composed of multiple Buffers. Each Tab object has different refresh() and resize() methods, defining how its -Windows are displayed, resized, etc +Windows are displayed, resized, etc. """ MIN_WIDTH = 42 @@ -1566,7 +1566,26 @@ class PrivateTab(ChatTab): self.parent_muc.privates.remove(self) def completion(self): - self.complete_commands(self.input) + """ + Called when Tab is pressed, complete the nickname in the input + """ + if self.complete_commands(self.input): + return + + # If we are not completing a command or a command's argument, complete a nick + compare_users = lambda x: x.last_talked + word_list = [user.nick for user in sorted(self.parent_muc.users, key=compare_users, reverse=True)\ + if user.nick != self.own_nick] + after = config.get('after_completion', ',')+" " + input_pos = self.input.pos + self.input.line_pos + if ' ' not in self.input.get_text()[:input_pos] or (self.input.last_completion and\ + self.input.get_text()[:input_pos] == self.input.last_completion + after): + add_after = after + else: + add_after = '' + self.input.auto_completion(word_list, add_after, quotify=False) + empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) + self.send_composing_chat_state(empty_after) def command_say(self, line, attention=False): if not self.on: -- cgit v1.2.3