summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2011-11-06 03:41:00 +0100
committerFlorent Le Coz <louiz@louiz.org>2011-11-06 03:41:00 +0100
commitd76b37f825b53e0b55eb0f8d95c7f8eb7d3d7eff (patch)
tree426e51859068761c4252d302e51a8c8db456da5f
parent432afd6179e69b70b290fef68ea3bb263ff3b4c1 (diff)
parent9ff6380149980581bc74f33ddd0e38fb0627623b (diff)
downloadpoezio-d76b37f825b53e0b55eb0f8d95c7f8eb7d3d7eff.tar.gz
poezio-d76b37f825b53e0b55eb0f8d95c7f8eb7d3d7eff.tar.bz2
poezio-d76b37f825b53e0b55eb0f8d95c7f8eb7d3d7eff.tar.xz
poezio-d76b37f825b53e0b55eb0f8d95c7f8eb7d3d7eff.zip
Merge branch 'master' into plugins
-rw-r--r--Makefile4
-rw-r--r--README3
-rw-r--r--data/default_config.cfg12
-rw-r--r--doc/en/configure.txt265
-rw-r--r--doc/en/install.txt94
-rw-r--r--doc/en/keys.txt66
-rw-r--r--doc/en/themes.txt89
-rw-r--r--doc/images/theme_256_colors.pngbin0 -> 44763 bytes
-rw-r--r--doc/poezio.txt83
-rw-r--r--src/core.py177
-rw-r--r--src/room.py139
-rw-r--r--src/tabs.py605
-rw-r--r--src/text_buffer.py7
-rw-r--r--src/windows.py47
14 files changed, 1087 insertions, 504 deletions
diff --git a/Makefile b/Makefile
index e055d203..7e71edb1 100644
--- a/Makefile
+++ b/Makefile
@@ -34,5 +34,9 @@ uninstall:
rm -rf $(DESTDIR)$(DATADIR)/poezio
rm -rf $(DESTDIR)$(MANDIR)/man1/poezio.1
+doc:
+ find doc -name \*.txt -exec asciidoc {} \;
pot:
xgettext src/*.py --from-code=utf-8 --keyword=_ -o locale/poezio.pot
+
+.PHONY : doc \ No newline at end of file
diff --git a/README b/README
index dbce3f79..53f36de0 100644
--- a/README
+++ b/README
@@ -28,6 +28,9 @@ You need python 3.0 (and the associated devel package, to build C modules)
or higher, and the SleekXMPP python library.
In the developpement version, you’ll need this fork of SleekXMPP
http://github.com/louiz/SleekXMPP.
+Additionally, you’ll need asciidoc to build the html documentation pages.
+You can read the documentation using the .txt files, as well, if you don’t
+have asciidoc, or read it on the web.
The simplest way to have up-to-date dependencies and to be able to test
this developpement version is to use the update.sh script that downloads
diff --git a/data/default_config.cfg b/data/default_config.cfg
index 8ff4805d..e803a7d1 100644
--- a/data/default_config.cfg
+++ b/data/default_config.cfg
@@ -112,9 +112,9 @@ alternative_nickname =
# Limit the number of messages you want to receive when the
# multiuserchat rooms send you recent history
# 0: You won't receive any
-# -1: You will receive the maximum (default)
+# -1: You will receive the maximum
# n: You will receive at most n messages
-muc_history_length = -1
+muc_history_length = 50
# set to 'true' if you want to save logs of all the messages
# in files.
@@ -156,7 +156,7 @@ beep_on = highlight private
# Theme
# If themes_dir is not set, logs will searched for in $XDG_DATA_HOME/poezio/themes,
-# i.e. in ~/.local/share/poezio/themes/. Si you should specify the directory you
+# i.e. in ~/.local/share/poezio/themes/. So you should specify the directory you
# want to use instead. This directory will be created at startup if it doesn't
# exist
themes_dir =
@@ -165,7 +165,11 @@ themes_dir =
# in the theme_dir directory.
# If the file is not found (or no filename is specified) the default
# theme will be used instead
-theme = poezio
+theme =
+
+# 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
# if true, chat states will be sent to the people you are talking to.
# Chat states are, for example, messages informing that you are composing
diff --git a/doc/en/configure.txt b/doc/en/configure.txt
new file mode 100644
index 00000000..94f8e121
--- /dev/null
+++ b/doc/en/configure.txt
@@ -0,0 +1,265 @@
+Configure
+=========
+
+The configuration is located in the file *~/.config/poezio/poezio.cfg*
+On its first startup, poezio will create that file (and its containing
+directories) with the default configuration. You can edit that file manually
+or use the */set* command to edit some of its values directly from poezio.
+This file is also used to configure key bindings, but this is explained
+in the _keys_ documentation file.
+
+That file is read at each startup and the configuration is saved when poezio
+is closed.
+
+This configuration file *requires* all the options to be in a section
+named [Poezio].
+
+An option is formatted with the form
+======================
+option = value
+======================
+
+An empty value *doesn’t* mean that the default value will be used. That’s
+just an empty value. To use the default value, just comment or remove the
+option entirely.
+
+Here is a list of all the avalaible configuration options, their meaning
+and their default value.
+
+Configuration options
+---------------------
+
+[horizontal]
+*server*:: anon.louiz.org
+
+ The server to use for *anonymous* authentication.
+ Make sure it accepts anonymous authentification
+ Note that this option doesn’t do anything at all if you’re using your own JID.
+
+*port*:: 5222
+
+ The port you’ll use to connect.
+
+*resource*:: [empty]
+
+ the resource you will use
+ If it's empty, your resource will be chosen (most likely randomly) by the server
+ It is not recommended to use a resource that is easy to guess, because it can lead
+ to presence leak.
+
+
+*default_nick*:: [empty]
+
+ the nick you will use when joining a room with no associated nick
+ If this is empty, the $USER environnement variable will be used
+
+
+*jid*:: [empty]
+
+ Jabber identifiant. Specify it only if you want to connect using an existing
+ account on a server. This is optional and useful only for some features,
+ like room administration, nickname registration.
+ The 'server' option will be ignored if you specify a JID (Jabber identifiant)
+ It should be in the form nickname@server.tld
+
+*password*:: [empty]
+
+ A password is needed only if you specified a jid. It will be ignored otherwise
+ If you leave this empty, the password will be asked at each startup
+
+
+
+*rooms*:: poezio@muc.poezio.eu
+
+ the rooms you will join automatically on startup, with associated nickname or not
+ format : room@server.tld/nickname:room2@server.tld/nickname2
+ default_nick will be used if "/nickname" is not specified
+
+*completion*:: normal
+
+ the completion type you will use to complete nicknames
+ if "normal", complete the entire name to the first available completion
+ and then cycle through the possible completion with the next TABs
+ if "shell", if there's more than one nick for this completion, complete
+ only the part that all then nicks have in common (like in a shell)
+
+
+*after_completion*:: ,
+
+ what will be put after the name, when using autocompletion
+ a SPACE will always be added after that
+
+*highlight_on*:: [empty]
+
+ a list of words (separated by a colon (:)) that will be
+ highlighted if said by someone on a room
+
+*enable_xhtml_im*:: true
+
+ XHTML-IM is an XMPP extension letting users send messages
+ containing XHTML and CSS formating. We can use this to make
+ colored text for example.
+ It is disabled by default because this is only in an experimental
+ state: you could miss some part of a message (mainly the URL)
+ but you can still send colored messages. You just won’t be able te see
+ the colors, though
+ Set to true if you want to see colored messages
+
+*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
+
+*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
+
+
+*information_buffer_popup_on*:: error roster warning help info
+
+ Some informational messages (error, a contact getting connected, etc)
+ are sometimes added to the information buffer. These settings can make
+ that buffer grow temporarly so you can read these information when they
+ appear.
+
+ A list of message types that should make the information buffer grow
+ Possible values; error, roster, warning, info, help
+
+*popup_time*:: 4
+
+ The time the message will be visible in the information buffer when it
+ pops up.
+ If the message takes more than one line, the popup will stay visible
+ two more second per additional lines
+
+*autorejoin*:: false
+
+ set to 'true' if you want to automatically rejoin the
+ room when you're kicked
+
+*alternative_nickname*:: [empty]
+
+ If you want poezio to join
+ the room with an alternative nickname when
+ your nickname is already in use in the room you
+ wanted to join, put a non-empty value.
+ Else, poezio won't join the room
+ This value will be added to your nickname to
+ create the alternative nickname.
+ For example, if you set "\_", and wanted to use
+ the nickname "john", your alternative nickname
+ will be "john_"
+
+*muc_history_length*:: 50
+
+ Limit the number of messages you want to receive when the
+ multiuserchat rooms send you recent history
+ 0: You won't receive any
+ -1: You will receive the maximum
+ n: You will receive at most n messages
+ Note that if you set a huge number (like the default value), you
+ may not receive that much messages. The server has its own
+ maximum too
+
+*use_log*:: true
+
+ set to 'false' if you don’t want to save logs of all the messages
+ in files.
+
+
+*log_dir*:: [empty]
+
+ If log_dir is not set, logs will be saved in $XDG_DATA_HOME/poezio/logs,
+ i.e. in ~/.local/share/poezio/logs/. So, you should specify the directory
+ you want to use instead. This directory will be created if it doesn't exist
+
+*show_inactive_tabs*:: true
+
+ If you want to show all the tabs in the Tab bar, even those
+ with no activity, set to true. Else, set to false
+
+
+*beep_on*:: highlight private
+
+ The terminal can beep on various event. Put the event you want in a list
+ (separated by spaces).
+ The events can be
+ - highlight (when you are highlighted in a MUC)
+ - private (when a new private message is received, from your contacts or
+ someone from a MUC)
+ - message (any message from a MUC)
+
+*themes_dir*:: [empty]
+
+ If themes_dir is not set, themes will searched for in $XDG_DATA_HOME/poezio/themes,
+ i.e. in ~/.local/share/poezio/themes/. So you should specify the directory you
+ want to use instead. This directory will be created at startup if it doesn't
+ exist
+
+
+*theme*:: [empty]
+
+ The name of the theme file (without the .py extension) that will be used.
+ The file should be located in the theme_dir directory.
+ If the file is not found (or no filename is specified) the default
+ theme will be used instead
+
+*send_chat_states*:: true
+
+ if true, chat states will be sent to the people you are talking to.
+ Chat states are, for example, messages informing that you are composing
+ a message or that you closed the tab, etc
+ Set to false if you don't want people to know these information
+ Note that you won’t receive the chat states of your contacts
+ if you don't send yours.
+
+
+*send_poezio_info*:: true
+
+ if true, information about the software (name and version)
+ will be sent if requested by anyone
+ Set to false if you don't want people to know these information
+
+
+*send_os_info*:: true
+
+ if true, information about the Operation System you're using
+ will be sent when requested by anyone
+ Set to false if you don't want people to know these information
+ Note that this information will not be sent if send_poezio_info is False
+
+*send_time*:: true
+
+ if true, your current time will be sent if asked
+ Set to false if you don't want people to know that information
+
+
+*max_messages_in_memory*:: 2048
+
+ Configure the number of maximum messages (for each tab) that
+ can be kept in memory. If poezio consumes too much memory, lower these
+ values
+
+*max_lines_in_memory*:: 2048
+
+ Configure the number of maximum lines (for each tab) that
+ can be kept in memory. If poezio consumes too much memory, lower these
+ values
+
+*lazy_resize*:: true
+
+ 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/install.txt b/doc/en/install.txt
new file mode 100644
index 00000000..75cc0ea8
--- /dev/null
+++ b/doc/en/install.txt
@@ -0,0 +1,94 @@
+Install
+=======
+
+
+Poezio in the GNU/Linux distributions
+-------------------------------------
+
+As far as I know, Poezio is available in the following distributions, you just have to install it by using the package manager of the distribution, if you're using one of these.
+
+* *Archlinux*: A poezio and poezio-git packages are in AUR (use your favourite AUR wrapper to install them)
+* *Frugalware*: Just use pacmang-g2 to install the poezio package. (Thanks to its maintainer, Kooda)
+* *Debian*: Use an other distro.
+
+(If an other distribution provides a poezio package, please tell us and we will add it to the list)
+
+
+Install poezio from the sources
+-------------------------------
+
+You can download poezio's https://dev.louiz.org/project/poezio/download[stable sources] or fetch the development version (trunk), using git:
+============================
+git clone https://git.louiz.org/poezio
+============================
+
+In order for poezio to correctly work, you need the libs SleekXMPP and dnspython. You can install them by downloading it from the https://github.com/fritzy/SleekXMPP/[SleekXMPP] page and the http://www.dnspython.org/[dnspython] page , but you'll need the development versions. Alternatively, you can download poezio's sources including SleekXMPP and dnspython, that's the easier way.
+
+As for dnspython, you will have to use our python3 fork, or poke them to accept patches.
+
+=== Dependencies ===
+
+
+If you want to install SleekXMPP and dnspython yourself, follow these instructions. Else, go to the next section.
+
+
+Download SleekXMPP
+============================
+git clone git://github.com/louiz/SleekXMPP.git
+============================
+
+Make sure you're using the develop branch by typing
+============================
+cd SleekXMPP
+
+git checkout develop
+============================
+
+Install SleekXMPP with
+============================
+python3 setup.py build
+
+su -c "python3 setup.py install"
+============================
+
+Clone the repository at http://hg.louiz.org/dnspython (this is a fork, because upstream is unresponsive and didn’t fix an important bug).
+============================
+hg clone http://hg.louiz.org/dnspython
+
+cd dnspython
+============================
+
+And do the same again:
+============================
+python3 setup.py build
+
+su -c "python3 setup.py install"
+============================
+
+
+=== Poezio installation ===
+
+If you skipped the installation of the dependencies and you only want to test poezio without a system-wide install, do, in the _poezio_ directory:
+============================
+sh update.sh
+============================
+
+If you have git and hg installed, it will download and update locally the libraries for you.
+
+
+If you don't want to install poezio but just test it, do:
+============================
+ ./launch.sh
+============================
+
+
+To install poezio, do, as root (or sudo with ubuntu or whatever):
+============================
+make install
+============================
+
+And then start it with:
+============================
+poezio
+============================
+
diff --git a/doc/en/keys.txt b/doc/en/keys.txt
new file mode 100644
index 00000000..eaae961d
--- /dev/null
+++ b/doc/en/keys.txt
@@ -0,0 +1,66 @@
+Keys
+====
+
+This file describes the default keys of poezio and explains how to
+configure them.
+
+By default, most keys manipulating the input (where you type your
+messages and commands) behave like emacs does.
+
+Note that keys are case sensitive. Ctrl-X is not the same than Ctrl-x
+
+Key bindings listing
+--------------------
+Some key bindings are available only in some tabs, others are global.
+
+Global keys
+~~~~~~~~~~~
+These keys work in *any* tab.
+
+*Ctrl-n*:: Go to the next tab.
+
+*Ctrl-p*:: Go to the previous tab.
+
+*Alt-number*:: Go to tab number x.
+
+*Alt-j*:: Waits for you to type a two-digits number. Go to tab number xx.
+
+Input keys
+~~~~~~~~~~
+These keys concern only the inputs.
+
+*Ctrl-a*:: Move the cursor to the beginning of line.
+
+*Ctrl-e*:: Move the cursor to the end of line.
+
+Chat tab input keys
+~~~~~~~~~~~~~~~~~~~~~
+These keys work in any conversation tab (MultiUserChat, Private or Conversation tabs)
+
+*Key Up*:: Use the previous message from the message history.
+
+*Key Down*:: Use the next message from the message history.
+
+*Page Up*:: Scroll up in the conversation by x lines, where x is the height of the conversation window - 1.
+
+*Page Down*:: Likfe Page Up, but down.
+
+*Alt-/*:: Complete what you’re typing using the "recent" words from the current conversation, if any.
+
+Key configuration
+-----------------
+Bindings are keyboard shortcut aliases. You can use them
+to define your own keys to replace the default ones.
+where ^x means Control + x
+and M-x means Alt + x
+
+To know exactly what the code of a key is, just run
+==================
+python3 src/keyboard.py
+==================
+And enter any keys
+
+.Turn Alt-i into a tab key (completion, etc)
+==================
+M-i = ^I
+==================
diff --git a/doc/en/themes.txt b/doc/en/themes.txt
new file mode 100644
index 00000000..ebf654fe
--- /dev/null
+++ b/doc/en/themes.txt
@@ -0,0 +1,89 @@
+Themes
+======
+
+This page describes how themes work in poezio and how to create or
+modify one.
+
+A theme contains color attributes and character definitions. Poezio can display
+up to _256_ colors if your terminal supports it. Most of the time,
+if it doesn’t work, that’s because the _$TERM_ environnment variable is
+wrong. For example with tmux or screen, set it to _screen-256color_, in
+_xterm_, set it to _xterm-256color_, etc. If your terminal doesn’t have 256,
+only 8 color will be available, and poezio will replace the colors by one
+of the 8 values available. Thus, some theme file may not work properly
+if you only have 8 colors for example light gray on dark gray may
+be converted to black on black if only 8 colors are available, making
+the text impossible to read). The default theme should work properly in any
+case. If not, that’s a bug.
+
+A theme file is a python file (with the .py extension) containing a
+class, inheriting the *themimg.Theme* class defined into the *theming*
+poezio module.
+
+Create a theme
+--------------
+
+To create a theme named foo, create a file named foo.py into the theme
+directory (by default it’s _~/.local/share/poezio/themes/_) and insert
+into it:
+
+[source,python]
+----
+import theming
+
+class FooTheme(theming.Theme):
+ # Define here colors for that theme
+theme = FooTheme()
+----
+
+To define a _color pair_ and assign it to the COLOR_NAME option, just do
+[source,python]
+----
+class FooTheme(theming.Theme):
+ COLOR_NAME = (fg_color, bg_color, opt_attr)
+----
+
+You do not have to define all the <<available-options,available options>>,
+you can decide that your theme will only change some options, the other
+one will just have the default value (from the default theme).
+
+Colors and attributes
+~~~~~~~~~~~~~~~~~~~~~
+A color pair defines how the text will be displayed on the screen. It
+has a _foreground color_ (fg_color), a _background color_ (bg_color)
+and an *_optional_* _attribute_ (opt_attr).
+
+Colors
+^^^^^^
+A color is a number between -1 and 255. If it -1, this is the default
+color defined by your terminal (for example if your terminal displays
+text white on black by default, a fg_color of -1 is white, and a bg_color
+of -1 is black). If it’s between 0 and 256 it represents one of the colors
+on the image:
+
+image::../images/theme_256_colors.png["The list of all 256 colors", title="The list of all 256 colors"]
+
+Attributes
+^^^^^^^^^^
+An attribute is a python string (so, it has to be surrounded by
+*" "* or *' '*). It can be one of the following
+
+* *'b'*: bold text
+* *'u'*: underlined text
+
+Use a theme
+-----------
+To use a theme, just define the _theme_ option into the
+link:configure.html[configuration file] to the name of the theme you want
+to use. If that theme is not found, the default theme will be used instead.
+Note that the default theme is defined directly into poezio’s source code,
+and note in a theme file.
+
+[[available-options]]
+Available options
+-----------------
+
+CAUTION: This section is not complete.
+
+All available options can be found into the default theme, which is into the
+_theming.py_ file from the poezio’s source code.
diff --git a/doc/images/theme_256_colors.png b/doc/images/theme_256_colors.png
new file mode 100644
index 00000000..00e6c51d
--- /dev/null
+++ b/doc/images/theme_256_colors.png
Binary files differ
diff --git a/doc/poezio.txt b/doc/poezio.txt
new file mode 100644
index 00000000..47497434
--- /dev/null
+++ b/doc/poezio.txt
@@ -0,0 +1,83 @@
+Poezio documentation
+====================
+
+This page is the documentation for poezio.
+
+Poezio is an XMPP console client mostly written in python and a little
+bit in C.
+
+It uses curses to draw its user interface.
+
+It has been written to create an XMPP client that could very easily be used by
+any IRC user. Its interface tries to be like the ones of famous clients such
+as irssi or weechat.
+
+:numbered:
+== Usage ==
+
+Poezio is composed of tabs which can be of various types. Each tab type has
+a distinct interface, list of commands and list of key shortcuts, in addition
+to the global commands and key shortcuts.
+
+
+=== Commands ===
+
+Commands start with the */* character and can take a list of any number
+of arguments, separated by spaces. If an argument should contain a space,
+you can use the *"* character to surround this argument.
+
+.The command nick with only one argument
+==========================================
+/nick "my new nick"
+==========================================
+
+.The command status with two arguments
+==========================================
+/status away "on vacation"
+==========================================
+
+.Note
+The character *'* cannot be used instead of *"*.
+
+
+To know the list of all available commands, use the *help* command with no
+argument. To know more about the command (what it does and how to use it),
+use the *help* command and pass the command name as its first argument.
+
+The list of all global commands is as follow:
+
+[horizontal]
+*help*:: [command_name] +
+ Displays the list of all available commands in the current tab, or displays
+ the usage of the given command.
+*message*:: <jid> [message] +
+ Open a conversation with the specified JID, and send a message to it,
+ if specified.
+
+
+
+
+
+.Get information on the status command
+==========================================
+/help status
+==========================================
+
+
+=== Tabs ===
+This section lists and describes all the tab types.
+
+
+==== Roster Tab ====
+This is the first tab that you will see when starting poezio.
+
+It contains your roster
+
+[glossary]
+== Glossary ==
+
+This glossary explains some terms that are used in this documentation.
+
+[glossary]
+Roster::
+ The list of contacts, sorted by groups, status, or anything the client wishes.
diff --git a/src/core.py b/src/core.py
index 88a726ea..2176f43d 100644
--- a/src/core.py
+++ b/src/core.py
@@ -43,7 +43,6 @@ from data_forms import DataFormsTab
from config import config, options
from logger import logger
from user import User
-from room import Room
from roster import Roster, RosterGroup, roster
from contact import Contact, Resource
from text_buffer import TextBuffer
@@ -299,6 +298,9 @@ class Core(object):
return self.information_buffer
def grow_information_win(self, nb=1):
+ if self.information_win_size >= self.current_tab().height -5 or \
+ self.information_win_size+nb >= self.current_tab().height-4:
+ return
if self.information_win_size == 14:
return
self.information_win_size += nb
@@ -401,10 +403,10 @@ class Core(object):
nick = message['mucnick']
room_from = message.getMucroom()
tab = self.get_tab_by_name(room_from, tabs.MucTab)
- if tab and tab.get_room() and tab.get_room().get_user_by_name(nick):
- tab.get_room().get_user_by_name(nick).chatstate = state
+ if tab and tab.get_user_by_name(nick):
+ tab.get_user_by_name(nick).chatstate = state
if tab == self.current_tab():
- tab.user_win.refresh(tab._room.users)
+ tab.user_win.refresh(tab.users)
tab.input.refresh()
self.doupdate()
@@ -423,7 +425,6 @@ class Core(object):
logger.log_roster_change(jid.bare, 'got offline')
if not contact:
return
- log.debug('on_got_offline: %s' % presence)
resource = contact.get_resource_by_fulljid(jid.full)
if not resource:
return
@@ -469,7 +470,7 @@ class Core(object):
"""
tab = self.get_tab_by_name(jid, tabs.ConversationTab)
if tab:
- self.add_message_to_text_buffer(tab.get_room(), msg)
+ self.add_message_to_text_buffer(tab._text_buffer, msg)
def on_failed_connection(self):
"""
@@ -483,7 +484,7 @@ class Core(object):
"""
for tab in self.tabs:
if isinstance(tab, tabs.MucTab):
- tab.get_room().disconnect()
+ tab.disconnect()
self.information(_("Disconnected from server."))
def on_failed_auth(self, event):
@@ -584,7 +585,7 @@ class Core(object):
def on_user_changed_status_in_private(self, jid, msg):
tab = self.get_tab_by_name(jid)
if tab: # display the message in private
- tab.get_room().add_message(msg)
+ tab.add_message(msg)
def on_message(self, message):
"""
@@ -610,16 +611,16 @@ class Core(object):
jid = message['from']
nick_from = jid.resource
room_from = jid.bare
- room = self.get_room_by_name(jid.full) # get the tab with the private conversation
- if not room: # It's the first message we receive: create the tab
- room = self.open_private_window(room_from, nick_from, False)
- if not room:
+ tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation
+ if not tab: # It's the first message we receive: create the tab
+ tab = self.open_private_window(room_from, nick_from, False)
+ if not tab:
return
body = xhtml.get_body_from_message_stanza(message)
if not body:
return
- room.add_message(body, time=None, nickname=nick_from,
- forced_user=self.get_room_by_name(room_from).get_user_by_name(nick_from))
+ 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))
conversation = self.get_tab_by_name(jid.full, tabs.PrivateTab)
if conversation and conversation.remote_wants_chatstates is None:
if message['chat_state']:
@@ -673,7 +674,7 @@ class Core(object):
remote_nick = roster.get_contact_by_jid(jid.bare).get_name() or jid.user
else:
remote_nick = jid.user
- conversation.get_room().add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER)
+ conversation._text_buffer.add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER)
if conversation.remote_wants_chatstates is None:
if message['chat_state']:
conversation.remote_wants_chatstates = True
@@ -683,7 +684,7 @@ class Core(object):
if 'private' in config.get('beep_on', 'highlight private').split():
curses.beep()
if self.current_tab() is not conversation:
- conversation.set_color_state(get_theme().COLOR_TAB_PRIVATE)
+ conversation.state = 'private'
self.refresh_tab_win()
else:
self.refresh_window()
@@ -745,7 +746,7 @@ class Core(object):
roster.add_contact(contact, jid)
roster.edit_groups_of_contact(contact, [])
contact.set_ask('asked')
- self.get_tab_by_number(0).set_color_state(get_theme().COLOR_TAB_HIGHLIGHT)
+ self.get_tab_by_number(0).state = 'highlight'
self.information('%s wants to subscribe to your presence'%jid, 'Roster')
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
@@ -833,7 +834,7 @@ class Core(object):
for tab in self.tabs:
if isinstance(tab, tabs.ConversationTab):
if tab.get_name() == jid:
- return tab.get_room()
+ return tab
return None
def get_tab_by_name(self, name, typ=None):
@@ -854,16 +855,6 @@ class Core(object):
return tab
return None
- def get_room_by_name(self, name):
- """
- returns the room that has this name
- """
- for tab in self.tabs:
- if (isinstance(tab, tabs.MucTab) or
- isinstance(tab, tabs.PrivateTab)) and tab.get_name() == name:
- return tab.get_room()
- return None
-
def init_curses(self, stdscr):
"""
ncurses initialization
@@ -896,7 +887,7 @@ class Core(object):
"""
Refresh everything
"""
- self.current_tab().set_color_state(get_theme().COLOR_TAB_CURRENT)
+ self.current_tab().state = 'current'
self.current_tab().refresh()
self.doupdate()
@@ -925,8 +916,7 @@ class Core(object):
"""
Open a new tab.MucTab containing a muc Room, using the specified nick
"""
- r = Room(room, nick)
- new_tab = tabs.MucTab(r)
+ new_tab = tabs.MucTab(room, nick)
self.add_tab(new_tab, focus)
self.refresh_window()
@@ -944,19 +934,19 @@ class Core(object):
- A Muc with any new message
"""
for tab in self.tabs:
- if tab.get_color_state() == get_theme().COLOR_TAB_PRIVATE:
+ if tab.state == 'private':
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
- if tab.get_color_state() == get_theme().COLOR_TAB_HIGHLIGHT:
+ if tab.state == 'highlight':
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
- if tab.get_color_state() == get_theme().COLOR_TAB_NEW_MESSAGE:
+ if tab.state == 'message':
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
- if tab.get_color_state() == get_theme().COLOR_TAB_DISCONNECTED:
+ if tab.state == 'disconnected':
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
@@ -1013,20 +1003,20 @@ class Core(object):
def room_error(self, error, room_name):
"""
- Display the error on the room window
+ Display the error in the tab
"""
- room = self.get_room_by_name(room_name)
+ tab = self.get_tab_by_name(room_name)
error_message = self.get_error_message_from_error_stanza(error)
- self.add_message_to_text_buffer(room, error_message)
+ self.add_message_to_text_buffer(tab._text_buffer, error_message)
code = error['error']['code']
if code == '401':
msg = _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)')
- self.add_message_to_text_buffer(room, msg)
+ self.add_message_to_text_buffer(tab._text_buffer, msg)
if code == '409':
if config.get('alternative_nickname', '') != '':
- self.command_join('%s/%s'% (room.name, room.own_nick+config.get('alternative_nickname', '')))
+ self.command_join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname', '')))
else:
- self.add_message_to_text_buffer(room, _('You can join the room with an other nick, by typing "/join /other_nick"'))
+ self.add_message_to_text_buffer(tab._text_buffer, _('You can join the room with an other nick, by typing "/join /other_nick"'))
self.refresh_window()
def open_conversation_window(self, jid, focus=True):
@@ -1040,6 +1030,8 @@ class Core(object):
return tab
new_tab = tabs.ConversationTab(jid)
# insert it in the rooms
+ if not focus:
+ new_tab.state = "private"
self.add_tab(new_tab, focus)
self.refresh_window()
return new_tab
@@ -1050,19 +1042,18 @@ class Core(object):
if isinstance(tab, tabs.PrivateTab):
if tab.get_name() == complete_jid:
self.command_win('%s' % tab.nb)
- return tab.get_room()
+ return tab
# create the new tab
- room = self.get_room_by_name(room_name)
- if not room:
+ tab = self.get_tab_by_name(room_name, tabs.MucTab)
+ if not tab:
return None
- own_nick = room.own_nick
- r = Room(complete_jid, own_nick) # PrivateRoom here
- new_tab = tabs.PrivateTab(r)
+ new_tab = tabs.PrivateTab(complete_jid, tab.own_nick)
+ if not focus:
+ new_tab.state = "private"
# insert it in the tabs
self.add_tab(new_tab, focus)
- # self.window.new_room(r)
self.refresh_window()
- return r
+ return new_tab
def on_groupchat_subject(self, message):
"""
@@ -1070,15 +1061,15 @@ class Core(object):
"""
nick_from = message['mucnick']
room_from = message.getMucroom()
- room = self.get_room_by_name(room_from)
+ tab = self.get_tab_by_name(room_from, tabs.MucTab)
subject = message['subject']
- if not subject or not room:
+ if not subject or not tab:
return
if nick_from:
- self.add_message_to_text_buffer(room, _("%(nick)s set the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=None)
+ self.add_message_to_text_buffer(tab._text_buffer, _("%(nick)s set the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=None)
else:
- self.add_message_to_text_buffer(room, _("The subject is: %(subject)s") % {'subject':subject}, time=None)
- room.topic = subject
+ self.add_message_to_text_buffer(tab._text_buffer, _("The subject is: %(subject)s") % {'subject':subject}, time=None)
+ tab.topic = subject
if self.get_tab_by_name(room_from, tabs.MucTab) is self.current_tab():
self.refresh_window()
@@ -1106,34 +1097,33 @@ class Core(object):
room_from = message.getMucroom()
if message['type'] == 'error': # Check if it's an error
return self.room_error(message, room_from)
- room = self.get_room_by_name(room_from)
tab = self.get_tab_by_name(room_from, tabs.MucTab)
- if tab and tab.get_room() and tab.get_room().get_user_by_name(nick_from) and\
- tab.get_room().get_user_by_name(nick_from) in tab.ignores:
- return
- if not room:
+ if not tab:
self.information(_("message received for a non-existing room: %s") % (room_from))
return
+ if tab.get_user_by_name(nick_from) and\
+ tab.get_user_by_name(nick_from) in tab.ignores:
+ return
body = xhtml.get_body_from_message_stanza(message)
if body:
date = date if delayed == True else None
- self.add_message_to_text_buffer(room, body, date, nick_from, history=True if date else False)
+ tab.add_message(body, date, nick_from, history=True if date else False)
if tab is self.current_tab():
- tab.text_win.refresh(tab._room)
- tab.info_header.refresh(tab._room, tab.text_win)
+ tab.text_win.refresh()
+ tab.info_header.refresh(tab, tab.text_win)
self.refresh_tab_win()
if 'message' in config.get('beep_on', 'highlight private').split():
curses.beep()
- def add_message_to_text_buffer(self, room, txt, time=None, nickname=None, history=None):
+ 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
(in the Info tab of the info window in the RosterTab)
"""
- if not room:
+ if not buff:
self.information('Trying to add a message in no room: %s' % txt, 'Error')
else:
- room.add_message(txt, time, nickname, history=history)
+ buff.add_message(txt, time, nickname, history=history)
def command_help(self, arg):
"""
@@ -1183,12 +1173,14 @@ class Core(object):
pres['type'] = show
pres.send()
current = self.current_tab()
- if isinstance(current, tabs.MucTab) and current.get_room().joined:
+ if isinstance(current, tabs.MucTab) and current.joined and show in ('away', 'xa'):
current.send_chat_state('inactive')
for tab in self.tabs:
- if isinstance(tab, tabs.MucTab) and tab.get_room().joined:
- muc.change_show(self.xmpp, tab.get_room().name, tab.get_room().own_nick, show, msg)
+ if isinstance(tab, tabs.MucTab) and tab.joined:
+ muc.change_show(self.xmpp, tab.name, tab.own_nick, show, msg)
self.set_status(show, msg)
+ if isinstance(current, tabs.MucTab) and current.joined and show not in ('away', 'xa'):
+ current.send_chat_state('active')
def completion_status(self, the_input):
return the_input.auto_completion([status for status in possible_show], ' ')
@@ -1363,11 +1355,11 @@ class Core(object):
args = common.shell_split(arg)
password = None
if len(args) == 0:
- t = self.current_tab()
- if not isinstance(t, tabs.MucTab) and not isinstance(t, tabs.PrivateTab):
+ tab = self.current_tab()
+ if not isinstance(tab, tabs.MucTab) and not isinstance(tab, tabs.PrivateTab):
return
- room = JID(t.get_name()).bare
- nick = t.get_room().own_nick
+ room = JID(tab.get_name()).bare
+ nick = tab.own_nick
else:
info = JID(args[0])
if info.resource == '':
@@ -1378,12 +1370,12 @@ class Core(object):
else:
nick = info.resource
if info.bare == '': # happens with /join /nickname, which is OK
- t = self.current_tab()
- if not isinstance(t, tabs.MucTab):
+ tab = self.current_tab()
+ if not isinstance(tab, tabs.MucTab):
return
- room = t.get_name()
+ room = tab.get_name()
if nick == '':
- nick = t.get_room().own_nick
+ nick = tab.own_nick
else:
room = info.bare
if room.find('@') == -1: # no server is provided, like "/join hello"
@@ -1396,25 +1388,25 @@ class Core(object):
self.information(_("You didn't specify a server for the room you want to join"), 'Error')
return
room = room.lower()
- r = self.get_room_by_name(room)
+ tab = self.get_tab_by_name(room, tabs.MucTab)
if len(args) == 2: # a password is provided
password = args[1]
- if r and r.joined: # if we are already in the room
- self.focus_tab_named(r.name)
+ if tab and tab.joined: # if we are already in the room
+ self.focus_tab_named(tab.name)
return
if room.startswith('@'):
room = room[1:]
current_status = self.get_status()
- if r and not r.joined:
+ if tab and not tab.joined:
muc.join_groupchat(self.xmpp, room, nick, password,
histo_length, current_status.message, current_status.show)
- if not r: # if the room window exists, we don't recreate it.
+ if not tab:
self.open_new_room(room, nick)
muc.join_groupchat(self.xmpp, room, nick, password,
histo_length, current_status.message, current_status.show)
else:
- r.own_nick = nick
- r.users = []
+ tab.own_nick = nick
+ tab.users = []
self.enable_private_tabs(room)
def get_bookmark_nickname(self, room_name):
@@ -1438,10 +1430,10 @@ class Core(object):
if len(args) == 0 and not isinstance(self.current_tab(), tabs.MucTab):
return
if len(args) == 0:
- room = self.current_tab().get_room()
- roomname = self.current_tab().get_name()
- if room.joined:
- nick = room.own_nick
+ tab = self.current_tab()
+ roomname = tab.get_name()
+ if tab.joined:
+ nick = tab.own_nick
else:
info = JID(args[0])
if info.resource != '':
@@ -1541,9 +1533,9 @@ class Core(object):
return
for tab in self.tabs:
if isinstance(tab, tabs.MucTab) and JID(tab.get_name()).domain == domain:
- if tab.get_room().joined:
- muc.leave_groupchat(tab.core.xmpp, tab.get_name(), tab.get_room().own_nick, message)
- tab.get_room().joined = False
+ if tab.joined:
+ muc.leave_groupchat(tab.core.xmpp, tab.get_name(), tab.own_nick, message)
+ tab.joined = False
self.command_join(tab.get_name())
def command_bind(self, arg):
@@ -1600,7 +1592,7 @@ class Core(object):
self.pop_information_win_up(nb_lines, popup_time)
else:
if self.information_win_size != 0:
- self.information_win.refresh(self.information_buffer)
+ self.information_win.refresh()
self.current_tab().input.refresh()
def disconnect(self, msg=None, reconnect=False):
@@ -1610,7 +1602,7 @@ class Core(object):
"""
for tab in self.tabs:
if isinstance(tab, tabs.MucTab):
- muc.leave_groupchat(self.xmpp, tab.get_room().name, tab.get_room().own_nick, msg)
+ muc.leave_groupchat(self.xmpp, tab.name, tab.own_nick, msg)
roster.empty()
self.save_config()
# Ugly fix thanks to gmail servers
@@ -1659,7 +1651,6 @@ class Core(object):
def remove_timed_event(self, event):
if event and event in self.timed_events:
- log.debug('removing event')
self.timed_events.remove(event)
def add_timed_event(self, event):
diff --git a/src/room.py b/src/room.py
deleted file mode 100644
index b97dd0b6..00000000
--- a/src/room.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org>
-#
-# This file is part of Poezio.
-#
-# Poezio is free software: you can redistribute it and/or modify
-# it under the terms of the zlib license. See the COPYING file.
-
-from text_buffer import TextBuffer, Message
-from datetime import datetime
-from random import randrange
-from config import config
-from logger import logger
-
-import common
-from theming import get_theme
-
-import logging
-import curses
-
-log = logging.getLogger(__name__)
-
-class Room(TextBuffer):
- def __init__(self, name, nick, messages_nb_limit=config.get('max_messages_in_memory', 2048)):
- TextBuffer.__init__(self, messages_nb_limit)
- self.name = name
- self.own_nick = nick
- self.color_state = get_theme().COLOR_TAB_NORMAL # color used in RoomInfo
- self.joined = False # false until self presence is receied
- self.users = [] # User objects
- self.topic = ''
-
- def disconnect(self):
- """
- Set the state of the room as not joined, so
- we can know if we can join it, send messages to it, etc
- """
- self.users = []
- self.color_state = get_theme().COLOR_TAB_DISCONNECTED
- self.joined = False
-
- def get_single_line_topic(self):
- """
- Return the topic as a single-line string (for the window header)
- """
- return self.topic.replace('\n', '|')
-
- def log_message(self, txt, time, nickname):
- """
- Log the messages in the archives, if it needs
- to be
- """
- if time is None and self.joined: # don't log the history messages
- logger.log_message(self.name, nickname, txt)
-
- def do_highlight(self, txt, time, nickname):
- """
- Set the tab color and returns the nick color
- """
- color = None
- if not time and nickname and nickname != self.own_nick and self.joined:
- if self.own_nick.lower() in txt.lower():
- if self.color_state != get_theme().COLOR_TAB_CURRENT:
- self.set_color_state(get_theme().COLOR_TAB_HIGHLIGHT)
- color = get_theme().COLOR_HIGHLIGHT_NICK
- else:
- highlight_words = config.get('highlight_on', '').split(':')
- for word in highlight_words:
- if word and word.lower() in txt.lower():
- if self.color_state != get_theme().COLOR_TAB_CURRENT:
- self.set_color_state(get_theme().COLOR_TAB_HIGHLIGHT)
- color = get_theme().COLOR_HIGHLIGHT_NICK
- break
- if color:
- beep_on = config.get('beep_on', 'highlight private').split()
- if 'highlight' in beep_on and 'message' not in beep_on:
- curses.beep()
- return color
-
- def get_user_by_name(self, nick):
- """
- Gets the user associated with the given nick, or None if not found
- """
- for user in self.users:
- if user.nick == nick:
- return user
- return None
-
- def set_color_state(self, color):
- """
- Set the color that will be used to display the room's
- number in the RoomInfo window
- """
- self.color_state = color
-
- def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, history=None):
- """
- Note that user can be None even if nickname is not None. It happens
- when we receive an history message said by someone who is not
- in the room anymore
- """
- self.log_message(txt, time, nickname)
- special_message = False
- if txt.startswith('/me '):
- txt = "\x192}* \x195}" + nickname + ' ' + txt[4:]
- special_message = True
- user = self.get_user_by_name(nickname) if nickname is not None else None
- if user:
- user.set_last_talked(datetime.now())
- if not user and forced_user:
- user = forced_user
- if not time and nickname and\
- nickname != self.own_nick and\
- self.color_state != get_theme().COLOR_TAB_CURRENT:
- if self.color_state != get_theme().COLOR_TAB_HIGHLIGHT:
- self.set_color_state(get_theme().COLOR_TAB_NEW_MESSAGE)
- nick_color = nick_color or None
- if not nickname or time:
- txt = '\x195}%s' % (txt,)
- else: # TODO
- highlight = self.do_highlight(txt, time, nickname)
- if highlight:
- nick_color = highlight
- if special_message:
- txt = '\x195}%s' % (txt,)
- nickname = None
- time = time or datetime.now()
- message = Message(txt='%s\x19o'%(txt.replace('\t', ' '),), nick_color=nick_color,
- time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\
- if history else time.strftime("%H:%M:%S"),\
- nickname=nickname, user=user)
- while len(self.messages) > self.messages_nb_limit:
- self.messages.pop(0)
- self.messages.append(message)
- for window in self.windows: # make the associated windows
- # build the lines from the new message
- nb = window.build_new_message(message, history=history)
- if window.pos != 0:
- window.scroll_up(nb)
- return nb
diff --git a/src/tabs.py b/src/tabs.py
index b0de89c0..c18f6d76 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -13,8 +13,8 @@ Each Tab object has different refresh() and resize() methods, defining how its
Windows are displayed, resized, etc
"""
-MIN_WIDTH = 50
-MIN_HEIGHT = 22
+MIN_WIDTH = 42
+MIN_HEIGHT = 6
import logging
log = logging.getLogger(__name__)
@@ -41,6 +41,7 @@ from sleekxmpp.xmlstream.stanzabase import JID
from config import config
from roster import RosterGroup, roster
from contact import Contact, Resource
+from text_buffer import TextBuffer
from user import User
from os import getenv, path
from logger import logger
@@ -57,12 +58,32 @@ SHOW_NAME = {
NS_MUC_USER = 'http://jabber.org/protocol/muc#user'
+STATE_COLORS = {
+ 'disconnected': lambda: get_theme().COLOR_TAB_DISCONNECTED,
+ 'message': lambda: get_theme().COLOR_TAB_NEW_MESSAGE,
+ 'highlight': lambda: get_theme().COLOR_TAB_HIGHLIGHT,
+ 'private': lambda: get_theme().COLOR_TAB_PRIVATE,
+ 'normal': lambda: get_theme().COLOR_TAB_NORMAL,
+ 'current': lambda: get_theme().COLOR_TAB_CURRENT,
+# 'attention': lambda: get_theme().COLOR_TAB_ATTENTION,
+ }
+
+STATE_PRIORITY = {
+ 'normal': -1,
+ 'current': -1,
+ 'disconnected': 0,
+ 'message': 1,
+ 'highlight': 2,
+ 'private': 2,
+# 'attention': 3
+ }
+
class Tab(object):
number = 0
tab_core = None
def __init__(self):
self.input = None
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
self.need_resize = False
self.nb = Tab.number
Tab.number += 1
@@ -87,6 +108,25 @@ class Tab(object):
def info_win(self):
return self.core.information_win
+ @property
+ def color(self):
+ return STATE_COLORS[self._state]()
+
+ @property
+ def state(self):
+ return self._state
+
+ @state.setter
+ def state(self, value):
+ if not value in STATE_COLORS:
+ log.debug("WARNING: invalid value for tab state")
+ return
+ elif STATE_PRIORITY[value] < STATE_PRIORITY[self._state] and \
+ value != 'current':
+ log.debug("WARNING: did not set status because of lower priority")
+ return
+ self._state = value
+
@staticmethod
def resize(scr):
Tab.size = (Tab.height, Tab.width) = scr.getmaxyx()
@@ -167,18 +207,6 @@ class Tab(object):
"""
raise NotImplementedError
- def get_color_state(self):
- """
- returns the color that should be used in the GlobalInfoBar
- """
- return self._color_state
-
- def set_color_state(self, color):
- """
- set the color state
- """
- pass
-
def get_name(self):
"""
get the name of the tab
@@ -198,22 +226,13 @@ class Tab(object):
"""
called when this tab loses the focus.
"""
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
def on_gain_focus(self):
"""
called when this tab gains the focus.
"""
- self._color_state = get_theme().COLOR_TAB_CURRENT
-
- def add_message(self):
- """
- Adds a message in the tab.
- If the tab cannot add a message in itself (for example
- FormTab, where text is not intented to be appened), it returns False.
- If the tab can, it returns True
- """
- return False
+ self._state = 'current'
def on_scroll_down(self):
"""
@@ -258,9 +277,9 @@ class ChatTab(Tab):
Also, ^M is already bound to on_enter
And also, add the /say command
"""
- def __init__(self, room):
+ def __init__(self):
Tab.__init__(self)
- self._room = room
+ self._text_buffer = TextBuffer()
self.remote_wants_chatstates = None # change this to True or False when
# we know that the remote user wants chatstates, or not.
# None means we don’t know yet, and we send only "active" chatstates
@@ -286,7 +305,7 @@ class ChatTab(Tab):
# build the list of the recent words
char_we_dont_want = string.punctuation+' '
words = list()
- for msg in self._room.messages[:-40:-1]:
+ for msg in self.messages[:-40:-1]:
if not msg:
continue
txt = xhtml.clean_text(msg.txt)
@@ -312,7 +331,7 @@ class ChatTab(Tab):
"""
Send an empty chatstate message
"""
- if not isinstance(self, MucTab) or self.get_room().joined:
+ if not isinstance(self, MucTab) or self.joined:
if state in ('active', 'inactive', 'gone') and self.core.status.show in ('xa', 'away') and not always_send:
return
msg = self.core.xmpp.make_message(self.get_name())
@@ -371,7 +390,7 @@ class ChatTab(Tab):
def move_separator(self):
self.text_win.remove_line_separator()
self.text_win.add_line_separator()
- self.text_win.refresh(self._room)
+ self.text_win.refresh(self._text_buffer)
self.input.refresh()
def get_conversation_messages(self):
@@ -386,15 +405,20 @@ class MucTab(ChatTab):
It contains an userlist, an input, a topic, an information and a chat zone
"""
message_type = 'groupchat'
- def __init__(self, room):
- ChatTab.__init__(self, room)
+ def __init__(self, jid, nick):
+ ChatTab.__init__(self)
+ self.own_nick = nick
+ self.name = jid
+ self.joined = False
+ self.users = []
+ self.topic = ''
self.remote_wants_chatstates = True
# We send active, composing and paused states to the MUC because
# the chatstate may or may not be filtered by the MUC,
# that’s not our problem.
self.topic_win = windows.Topic()
self.text_win = windows.TextWin()
- room.add_window(self.text_win)
+ self._text_buffer.add_window(self.text_win)
self.v_separator = windows.VerticalSeparator()
self.user_win = windows.UserList()
self.info_header = windows.MucInfoWin()
@@ -413,6 +437,7 @@ class MucTab(ChatTab):
self.commands['topic'] = (self.command_topic, _("Usage: /topic <subject>\nTopic: Change the subject of the room"), self.completion_topic)
self.commands['query'] = (self.command_query, _('Usage: /query <nick> [message]\nQuery: Open a private conversation with <nick>. This nick has to be present in the room you\'re currently in. If you specified a message after the nickname, it will immediately be sent to this user'), None)
self.commands['part'] = (self.command_part, _("Usage: /part [message]\nPart: disconnect from a room. You can specify an optional message."), None)
+ self.commands['close'] = (self.command_close, _("Usage: /close [message]\nClose: disconnect from a room and close the tab. You can specify an optional message if you are still connected."), None)
self.commands['nick'] = (self.command_nick, _("Usage: /nick <nickname>\nNick: Change your nickname in the current room"), None)
self.commands['recolor'] = (self.command_recolor, _('Usage: /recolor\nRecolor: Re-assign a color to all participants of the current room, based on the last time they talked. Use this if the participants currently talking have too many identical colors.'), None)
self.commands['cycle'] = (self.command_cycle, _('Usage: /cycle [message]\nCycle: Leaves the current room and rejoin it immediately'), None)
@@ -426,19 +451,19 @@ class MucTab(ChatTab):
def scroll_user_list_up(self):
self.user_win.scroll_up()
- self.user_win.refresh(self._room.users)
+ self.user_win.refresh(self.users)
self.input.refresh()
def scroll_user_list_down(self):
self.user_win.scroll_down()
- self.user_win.refresh(self._room.users)
+ self.user_win.refresh(self.users)
self.input.refresh()
def command_info(self, arg):
args = common.shell_split(arg)
if len(args) != 1:
return self.core.information("Info command takes only 1 argument")
- user = self.get_room().get_user_by_name(args[0])
+ user = self.get_user_by_name(args[0])
if not user:
return self.core.information("Unknown user: %s" % args[0])
info = '%s%s: show: %s, affiliation: %s, role: %s%s' % (args[0],
@@ -473,35 +498,34 @@ class MucTab(ChatTab):
"""
/clear
"""
- self._room.messages = []
- self.text_win.rebuild_everything(self._room)
+ self.messages = []
+ self.text_win.rebuild_everything(self._text_buffer)
self.refresh()
self.core.doupdate()
def command_cycle(self, arg):
- if self.get_room().joined:
- muc.leave_groupchat(self.core.xmpp, self.get_name(), self.get_room().own_nick, arg)
- self.get_room().disconnect()
- self.core.disable_private_tabs(self.get_room().name)
- self.core.command_join('"/%s"' % self.core.get_bookmark_nickname(self.get_room().name), '0')
+ if self.joined:
+ muc.leave_groupchat(self.core.xmpp, self.get_name(), self.own_nick, arg)
+ self.disconnect()
+ self.core.disable_private_tabs(self.name)
+ self.core.command_join('"/%s"' % self.core.get_bookmark_nickname(self.name), '0')
self.user_win.pos = 0
def command_recolor(self, arg):
"""
Re-assign color to the participants of the room
"""
- room = self.get_room()
compare_users = lambda x: x.last_talked
- users = list(room.users)
+ users = list(self.users)
# search our own user, to remove it from the room
for user in users:
- if user.nick == room.own_nick:
+ if user.nick == self.own_nick:
users.remove(user)
nb_color = len(get_theme().LIST_COLOR_NICKNAMES)
for i, user in enumerate(sorted(users, key=compare_users, reverse=True)):
user.color = get_theme().LIST_COLOR_NICKNAMES[i % nb_color]
- self.text_win.rebuild_everything(self._room)
- self.text_win.refresh(self._room)
+ self.text_win.rebuild_everything(self._text_buffer)
+ self.text_win.refresh()
self.input.refresh()
def command_version(self, arg):
@@ -520,8 +544,8 @@ class MucTab(ChatTab):
args = common.shell_split(arg)
if len(args) < 1:
return
- if args[0] in [user.nick for user in self.get_room().users]:
- jid = self._room.name + '/' + args[0]
+ if args[0] in [user.nick for user in self.users]:
+ jid = self.name + '/' + args[0]
else:
jid = args[0]
self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback)
@@ -534,26 +558,35 @@ class MucTab(ChatTab):
if len(args) != 1:
return
nick = args[0]
- room = self.get_room()
- if not room.joined:
+ if not self.joined:
return
current_status = self.core.get_status()
- muc.change_nick(self.core.xmpp, room.name, nick, current_status.message, current_status.show)
+ muc.change_nick(self.core.xmpp, self.name, nick, current_status.message, current_status.show)
def command_part(self, arg):
"""
/part [msg]
"""
args = arg.split()
- room = self.get_room()
if len(args):
msg = ' '.join(args)
else:
msg = None
- if self.get_room().joined:
- muc.leave_groupchat(self.core.xmpp, room.name, room.own_nick, arg)
+ if self.joined:
+ muc.leave_groupchat(self.core.xmpp, self.name, self.own_nick, arg)
+ self.joined = False
+ self.add_message(_("\x195}You left the chatroom\x193}"))
+ self.refresh()
+ self.core.doupdate()
+ self.core.disable_private_tabs(self.name)
+
+ def command_close(self, arg):
+ """
+ /close [msg]
+ """
+ self.command_part(arg)
self.core.close_tab()
- self.core.disable_private_tabs(self.get_room().name)
+
def command_query(self, arg):
"""
@@ -563,11 +596,10 @@ class MucTab(ChatTab):
if len(args) < 1:
return
nick = args[0]
- room = self.get_room()
r = None
- for user in room.users:
+ for user in self.users:
if user.nick == nick:
- r = self.core.open_private_window(room.name, user.nick)
+ r = self.core.open_private_window(self.name, user.nick)
if r and len(args) > 1:
msg = arg[len(nick)+1:]
self.core.current_tab().command_say(msg)
@@ -579,23 +611,22 @@ class MucTab(ChatTab):
/topic [new topic]
"""
if not arg.strip():
- self.core.add_message_to_text_buffer(self.get_room(),
- _("The subject of the room is: %s") % self.get_room().topic)
- self.text_win.refresh(self.get_room())
+ self._text_buffer.add_message_to_text_buffer(self,
+ _("The subject of the room is: %s") % self.topic)
+ self.text_win.refresh(self._text_buffer)
self.input.refresh()
return
subject = arg
- muc.change_subject(self.core.xmpp, self.get_room().name, subject)
+ muc.change_subject(self.core.xmpp, self.name, subject)
def command_names(self, arg=None):
"""
/names
"""
- room = self.get_room()
- if not room.joined:
+ if not self.joined:
return
users, visitors, moderators, participants, others = [], [], [], [], []
- for user in room.users:
+ for user in self.users:
if user.role == 'visitor':
visitors.append(user.nick)
elif user.role == 'participant':
@@ -617,12 +648,13 @@ class MucTab(ChatTab):
message += '%s, ' % item
message += '%s\n' % last
- self.core.add_message_to_text_buffer(room, message)
- self.text_win.refresh(self.get_room())
+ # self.core.add_message_to_text_buffer(room, message)
+ self._text_buffer.add_message(message)
+ self.text_win.refresh()
self.input.refresh()
def completion_topic(self, the_input):
- current_topic = self.get_room().topic
+ current_topic = self.topic
return the_input.auto_completion([current_topic], '')
def command_kick(self, arg):
@@ -633,7 +665,7 @@ class MucTab(ChatTab):
if not len(args):
self.core.command_help('kick')
else:
- self.command_role('none '+arg)
+ self.command_role(arg+ ' none')
def command_role(self, arg):
"""
@@ -650,7 +682,7 @@ class MucTab(ChatTab):
reason = ' '.join(args[2:])
else:
reason = ''
- if not self.get_room().joined or \
+ if not self.joined or \
not role in ('none', 'visitor', 'participant', 'moderator'):
return
res = muc.set_user_role(self.core.xmpp, self.get_name(), nick, reason, role)
@@ -672,7 +704,7 @@ class MucTab(ChatTab):
reason = ' '.join(args[2:])
else:
reason = ''
- if not self.get_room().joined or \
+ if not self.joined or \
not affiliation in ('none', 'member', 'admin', 'owner'):
# replace this ↑ with this ↓ when the ban list support is done
# not affiliation in ('outcast', 'none', 'member', 'admin', 'owner'):
@@ -705,7 +737,7 @@ class MucTab(ChatTab):
self.core.command_help('ignore')
return
nick = args[0]
- user = self._room.get_user_by_name(nick)
+ user = self.get_user_by_name(nick)
if not user:
self.core.information(_('%s is not in the room') % nick)
elif user in self.ignores:
@@ -723,7 +755,7 @@ class MucTab(ChatTab):
self.core.command_help('unignore')
return
nick = args[0]
- user = self._room.get_user_by_name(nick)
+ user = self.get_user_by_name(nick)
if not user:
self.core.information(_('%s is not in the room') % nick)
elif user not in self.ignores:
@@ -746,7 +778,7 @@ class MucTab(ChatTab):
self.topic_win.resize(1, self.width, 0, 0)
self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10))
self.text_win.resize(self.height-4-self.core.information_win_size, text_width, 1, 0)
- self.text_win.rebuild_everything(self._room)
+ self.text_win.rebuild_everything(self._text_buffer)
self.user_win.resize(self.height-3-self.core.information_win_size-1, self.width-text_width-1, 1, text_width+1)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
self.input.resize(1, self.width, self.height-1, 0)
@@ -755,13 +787,13 @@ class MucTab(ChatTab):
if self.need_resize:
self.resize()
log.debug(' TAB Refresh: %s'%self.__class__.__name__)
- self.topic_win.refresh(self._room.get_single_line_topic())
- self.text_win.refresh(self._room)
+ self.topic_win.refresh(self.get_single_line_topic())
+ self.text_win.refresh()
self.v_separator.refresh()
- self.user_win.refresh(self._room.users)
- self.info_header.refresh(self._room, self.text_win)
+ self.user_win.refresh(self.users)
+ self.info_header.refresh(self, self.text_win)
self.tab_win.refresh()
- self.info_win.refresh(self.core.informations)
+ self.info_win.refresh()
self.input.refresh()
def on_input(self, key):
@@ -782,8 +814,8 @@ class MucTab(ChatTab):
# 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._room.users, key=compare_users, reverse=True)\
- if user.nick != self._room.own_nick]
+ word_list = [user.nick for user in sorted(self.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\
@@ -796,33 +828,38 @@ class MucTab(ChatTab):
self.send_composing_chat_state(empty_after)
def get_color_state(self):
- return self._room.color_state
+ return self.color_state
def set_color_state(self, color):
- self._room.set_color_state(color)
+ self.set_color_state(color)
def get_name(self):
- return self._room.name
+ return self.name
def get_text_window(self):
return self.text_win
- def get_room(self):
- return self._room
+ @property
+ def state(self):
+ return self._state
+
+ @state.setter
+ def state(self, value):
+ self._state = value
def on_lose_focus(self):
- self._room.set_color_state(get_theme().COLOR_TAB_NORMAL)
+ self._state = 'normal'
self.text_win.remove_line_separator()
self.text_win.add_line_separator()
if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
self.send_chat_state('inactive')
def on_gain_focus(self):
- self._room.set_color_state(get_theme().COLOR_TAB_CURRENT)
+ self._state = 'current'
if self.text_win.built_lines and self.text_win.built_lines[-1] is None:
self.text_win.remove_line_separator()
curses.curs_set(1)
- if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
+ if self.joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
self.send_chat_state('active')
def on_scroll_up(self):
@@ -852,88 +889,89 @@ class MucTab(ChatTab):
role = presence['muc']['role']
jid = presence['muc']['jid']
typ = presence['type']
- room = self.get_room()
- if not room.joined: # user in the room BEFORE us.
+ if not self.joined: # user in the room BEFORE us.
# ignore redondant presence message, see bug #1509
- if from_nick not in [user.nick for user in room.users] and typ != "unavailable":
+ if from_nick not in [user.nick for user in self.users] and typ != "unavailable":
new_user = User(from_nick, affiliation, show, status, role, jid)
- room.users.append(new_user)
- if from_nick == room.own_nick:
- room.joined = True
+ self.users.append(new_user)
+ if from_nick == self.own_nick:
+ self.joined = True
if self.core.current_tab() == self and self.core.status.show not in ('xa', 'away'):
self.send_chat_state('active')
new_user.color = get_theme().COLOR_OWN_NICK
- room.add_message(_("\x195}Your nickname is \x193}%s") % (from_nick))
+ self.add_message(_("\x195}Your nickname is \x193}%s") % (from_nick))
if '170' in status_codes:
- room.add_message('\x191}Warning: \x195}this room is publicly logged')
+ self.add_message('\x191}Warning: \x195}this room is publicly logged')
else:
change_nick = '303' in status_codes
kick = '307' in status_codes and typ == 'unavailable'
ban = '301' in status_codes and typ == 'unavailable'
- user = room.get_user_by_name(from_nick)
+ user = self.get_user_by_name(from_nick)
# New user
if not user:
- self.on_user_join(room, from_nick, affiliation, show, status, role, jid)
+ self.on_user_join(from_nick, affiliation, show, status, role, jid)
# nick change
elif change_nick:
- self.on_user_nick_change(room, presence, user, from_nick, from_room)
+ self.on_user_nick_change(presence, user, from_nick, from_room)
elif ban:
- self.on_user_banned(room, presence, user, from_nick)
+ self.on_user_banned(presence, user, from_nick)
# kick
elif kick:
- self.on_user_kicked(room, presence, user, from_nick)
+ self.on_user_kicked(presence, user, from_nick)
# user quit
elif typ == 'unavailable':
- self.on_user_leave_groupchat(room, user, jid, status, from_nick, from_room)
+ self.on_user_leave_groupchat(user, jid, status, from_nick, from_room)
# status change
else:
- self.on_user_change_status(room, user, from_nick, from_room, affiliation, role, show, status)
+ self.on_user_change_status(user, from_nick, from_room, affiliation, role, show, status)
if self.core.current_tab() is self:
- self.text_win.refresh(self._room)
- self.user_win.refresh(self._room.users)
- self.info_header.refresh(self._room, self.text_win)
+ self.text_win.refresh()
+ self.user_win.refresh(self.users)
+ self.info_header.refresh(self, self.text_win)
self.input.refresh()
self.core.doupdate()
- def on_user_join(self, room, from_nick, affiliation, show, status, role, jid):
+ def on_user_join(self, from_nick, affiliation, show, status, role, jid):
"""
When a new user joins the groupchat
"""
- room.users.append(User(from_nick, affiliation,
- show, status, role, jid))
+ user = User(from_nick, affiliation,
+ show, status, role, jid)
+ self.users.append(user)
hide_exit_join = config.get('hide_exit_join', -1)
if hide_exit_join != 0:
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
if not jid.full:
- room.add_message('\x194}%(spec)s \x193}%(nick)s\x195} joined the room' % {'nick':from_nick, 'spec':get_theme().CHAR_JOIN})
+ self.add_message('\x194}%(spec)s \x19%(color)d}%(nick)s\x195} joined the room' % {'nick':from_nick, 'color':color, 'spec':get_theme().CHAR_JOIN})
else:
- room.add_message('\x194}%(spec)s \x193}%(nick)s \x195}(\x194}%(jid)s\x195}) joined the room' % {'spec':get_theme().CHAR_JOIN, 'nick':from_nick, 'jid':jid.full})
+ self.add_message('\x194}%(spec)s \x19%(color)d}%(nick)s \x195}(\x194}%(jid)s\x195}) joined the room' % {'spec':get_theme().CHAR_JOIN, 'nick':from_nick, 'color':color, 'jid':jid.full})
self.core.on_user_rejoined_private_conversation(room.name, from_nick)
-
- def on_user_nick_change(self, room, presence, user, from_nick, from_room):
+ def on_user_nick_change(self, presence, user, from_nick, from_room):
new_nick = presence.find('{%s}x/{%s}item' % (NS_MUC_USER, NS_MUC_USER)).attrib['nick']
- if user.nick == room.own_nick:
- room.own_nick = new_nick
+ if user.nick == self.own_nick:
+ self.own_nick = new_nick
# also change our nick in all private discussion of this room
for _tab in self.core.tabs:
- if isinstance(_tab, PrivateTab) and JID(_tab.get_name()).bare == room.name:
- _tab.get_room().own_nick = new_nick
+ if isinstance(_tab, PrivateTab) and JID(_tab.get_name()).bare == self.name:
+ _tab.own_nick = new_nick
user.change_nick(new_nick)
- room.add_message('\x193}%(old)s\x195} is now known as \x193}%(new)s' % {'old':from_nick, 'new':new_nick})
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
+ self.add_message('\x19%(color)d}%(old)s\x195} is now known as \x19%(color)d}%(new)s' % {'old':from_nick, 'new':new_nick, 'color':color})
# rename the private tabs if needed
- self.core.rename_private_tabs(room.name, from_nick, new_nick)
+ self.core.rename_private_tabs(self.name, from_nick, new_nick)
- def on_user_banned(self, room, presence, user, from_nick):
+ def on_user_banned(self, presence, user, from_nick):
"""
When someone is banned from a muc
"""
- room.users.remove(user)
+ self.users.remove(user)
by = presence.find('{%s}x/{%s}item/{%s}actor' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER))
reason = presence.find('{%s}x/{%s}item/{%s}reason' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER))
by = by.attrib['jid'] if by is not None else None
- if from_nick == room.own_nick: # we are banned
- room.disconnect()
- self.core.disable_private_tabs(room.name)
+ if from_nick == self.own_nick: # we are banned
+ self.disconnect()
+ self.core.disable_private_tabs(self.name)
self.tab_win.refresh()
self.core.doupdate()
if by:
@@ -941,25 +979,26 @@ class MucTab(ChatTab):
else:
kick_msg = _('\x191}%(spec)s \x193}You\x195} have been banned.') % {'spec':get_theme().CHAR_KICK}
else:
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
if by:
- kick_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} has been banned by \x194}%(by)s') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick, 'by':by}
+ kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been banned by \x194}%(by)s') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick, 'color':color, 'by':by}
else:
- kick_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} has been banned') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"')}
+ kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been banned') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"'), 'color':color}
if reason is not None and reason.text:
kick_msg += _('\x195} Reason: \x196}%(reason)s\x195}') % {'reason': reason.text}
- room.add_message(kick_msg)
+ self._text_buffer.add_message(kick_msg)
- def on_user_kicked(self, room, presence, user, from_nick):
+ def on_user_kicked(self, presence, user, from_nick):
"""
When someone is kicked from a muc
"""
- room.users.remove(user)
+ self.users.remove(user)
by = presence.find('{%s}x/{%s}item/{%s}actor' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER))
reason = presence.find('{%s}x/{%s}item/{%s}reason' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER))
by = by.attrib['jid'] if by is not None else None
- if from_nick == room.own_nick: # we are kicked
- room.disconnect()
- self.core.disable_private_tabs(room.name)
+ if from_nick == self.own_nick: # we are kicked
+ self.disconnect()
+ self.core.disable_private_tabs(self.name)
self.tab_win.refresh()
self.core.doupdate()
if by:
@@ -968,47 +1007,54 @@ class MucTab(ChatTab):
kick_msg = _('\x191}%(spec)s \x193}You\x195} have been kicked.') % {'spec':get_theme().CHAR_KICK}
# try to auto-rejoin
if config.get('autorejoin', 'false') == 'true':
- muc.join_groupchat(self.core.xmpp, room.name, room.own_nick)
+ muc.join_groupchat(self.core.xmpp, self.name, self.own_nick)
else:
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
if by:
- kick_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} has been kicked by \x193}%(by)s') % {'spec':get_theme().CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'by':by.replace('"', '\\"')}
+ kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been kicked by \x193}%(by)s') % {'spec':get_theme().CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'color':color, 'by':by.replace('"', '\\"')}
else:
- kick_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} has been kicked') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"')}
+ kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been kicked') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"'), 'color':color}
if reason is not None and reason.text:
kick_msg += _('\x195} Reason: \x196}%(reason)s') % {'reason': reason.text}
- room.add_message(kick_msg)
+ self.add_message(kick_msg)
- def on_user_leave_groupchat(self, room, user, jid, status, from_nick, from_room):
+ def on_user_leave_groupchat(self, user, jid, status, from_nick, from_room):
"""
When an user leaves a groupchat
"""
- room.users.remove(user)
- if room.own_nick == user.nick:
+ self.users.remove(user)
+ if self.own_nick == user.nick:
# We are now out of the room. Happens with some buggy (? not sure) servers
- room.disconnect()
+ self.disconnect()
self.core.disable_private_tabs(from_room)
self.tab_win.refresh()
self.core.doupdate()
hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1
if hide_exit_join == -1 or user.has_talked_since(hide_exit_join):
+ log.debug("\n\nALLO: USERCOLOR: %s\n\n" % user.color.__repr__())
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
if not jid.full:
- leave_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} has left the room') % {'nick':from_nick, 'spec':get_theme().CHAR_QUIT}
+ leave_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has left the room') % {'nick':from_nick, 'color':color, 'spec':get_theme().CHAR_QUIT}
else:
- leave_msg = _('\x191}%(spec)s \x193}%(nick)s\x195} (\x194}%(jid)s\x195}) has left the room') % {'spec':get_theme().CHAR_QUIT, 'nick':from_nick, 'jid':jid.full}
+ leave_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} (\x194}%(jid)s\x195}) has left the room') % {'spec':get_theme().CHAR_QUIT, 'nick':from_nick, 'color':color, 'jid':jid.full}
if status:
leave_msg += ' (%s)' % status
- room.add_message(leave_msg)
+ self.add_message(leave_msg)
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):
+ def on_user_change_status(self, user, from_nick, from_room, affiliation, role, show, status):
"""
When an user changes her status
"""
# build the message
display_message = False # flag to know if something significant enough
# to be displayed has changed
- msg = _('\x193}%s\x195} changed: ')% from_nick.replace('"', '\\"')
+ color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3
+ if from_nick == self.own_nick:
+ msg = _('\x193}You\x195} changed: ')
+ else:
+ msg = _('\x19%(color)d}%(nick)s\x195} changed: ') % {'nick': from_nick.replace('"', '\\"'), 'color': color}
if show not in SHOW_NAME:
self.core.information("%s from room %s sent an invalid show: %s" %\
(from_nick, from_room, show), "warning")
@@ -1032,30 +1078,124 @@ class MucTab(ChatTab):
if not display_message:
return
msg = msg[:-2] # remove the last ", "
- hide_status_change = config.get('hide_status_change', -1) if config.get('hide_status_change', -1) >= -1 else -1
+ hide_status_change = config.get('hide_status_change', -1)
+ if hide_status_change < -1:
+ hide_status_change = -1
if (hide_status_change == -1 or \
user.has_talked_since(hide_status_change) or\
- user.nick == room.own_nick)\
+ user.nick == self.own_nick)\
and\
(affiliation != user.affiliation or\
role != user.role or\
show != user.show or\
status != user.status):
# display the message in the room
- room.add_message(msg)
+ self._text_buffer.add_message(msg)
self.core.on_user_changed_status_in_private('%s/%s' % (from_room, from_nick), msg)
# finally, effectively change the user status
user.update(affiliation, show, status, role)
+ def disconnect(self):
+ """
+ Set the state of the room as not joined, so
+ we can know if we can join it, send messages to it, etc
+ """
+ self.users = []
+ self._state = 'disconnected'
+ self.joined = False
+
+ def get_single_line_topic(self):
+ """
+ Return the topic as a single-line string (for the window header)
+ """
+ return self.topic.replace('\n', '|')
+
+ def log_message(self, txt, time, nickname):
+ """
+ Log the messages in the archives, if it needs
+ to be
+ """
+ if time is None and self.joined: # don't log the history messages
+ logger.log_message(self.name, nickname, txt)
+
+ def do_highlight(self, txt, time, nickname):
+ """
+ Set the tab color and returns the nick color
+ """
+ color = None
+ if not time and nickname and nickname != self.own_nick and self.joined:
+ if self.own_nick.lower() in txt.lower():
+ if self._state != 'current':
+ self._state = 'highlight'
+ color = get_theme().COLOR_HIGHLIGHT_NICK
+ else:
+ highlight_words = config.get('highlight_on', '').split(':')
+ for word in highlight_words:
+ if word and word.lower() in txt.lower():
+ if self._state != 'current':
+ self._state = 'highlight'
+ color = get_theme().COLOR_HIGHLIGHT_NICK
+ break
+ if color:
+ beep_on = config.get('beep_on', 'highlight private').split()
+ if 'highlight' in beep_on and 'message' not in beep_on:
+ curses.beep()
+ return color
+
+ def get_user_by_name(self, nick):
+ """
+ Gets the user associated with the given nick, or None if not found
+ """
+ for user in self.users:
+ if user.nick == nick:
+ return user
+ return None
+
+ def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, history=None):
+ """
+ Note that user can be None even if nickname is not None. It happens
+ when we receive an history message said by someone who is not
+ in the room anymore
+ """
+ self.log_message(txt, time, nickname)
+ special_message = False
+ if txt.startswith('/me '):
+ txt = "\x192}* \x195}" + nickname + ' ' + txt[4:]
+ special_message = True
+ user = self.get_user_by_name(nickname) if nickname is not None else None
+ if user:
+ user.set_last_talked(datetime.now())
+ if not user and forced_user:
+ user = forced_user
+ if not time and nickname and\
+ nickname != self.own_nick and\
+ self._state != 'current':
+ if self._state != 'highlight':
+ self._state = 'message'
+ nick_color = nick_color or None
+ if not nickname or time:
+ txt = '\x195}%s' % (txt,)
+ else: # TODO
+ highlight = self.do_highlight(txt, time, nickname)
+ if highlight:
+ nick_color = highlight
+ if special_message:
+ txt = '\x195}%s' % (txt,)
+ nickname = None
+ time = time or datetime.now()
+ self._text_buffer.add_message(txt, time, nickname, nick_color, history, user)
+
class PrivateTab(ChatTab):
"""
The tab containg a private conversation (someone from a MUC)
"""
message_type = 'chat'
- def __init__(self, room):
- ChatTab.__init__(self, room)
+ def __init__(self, name, nick):
+ ChatTab.__init__(self)
+ self.own_nick = nick
+ self.name = name
self.text_win = windows.TextWin()
- room.add_window(self.text_win)
+ self._text_buffer.add_window(self.text_win)
self.info_header = windows.PrivateInfoWin()
self.input = windows.MessageInput()
# keys
@@ -1063,10 +1203,10 @@ class PrivateTab(ChatTab):
# commands
self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Display some information about the user in the MUC: '), None)
self.commands['unquery'] = (self.command_unquery, _("Usage: /unquery\nUnquery: close the tab"), None)
- self.commands['part'] = (self.command_unquery, _("Usage: /part\nPart: close the tab"), None)
+ self.commands['close'] = (self.command_unquery, _("Usage: /close\nClose: close the tab"), None)
self.commands['version'] = (self.command_version, _('Usage: /version\nVersion: get the software version of the current interlocutor (usually its XMPP client and Operating System)'), None)
self.resize()
- self.parent_muc = self.core.get_tab_by_name(JID(room.name).bare, MucTab)
+ self.parent_muc = self.core.get_tab_by_name(JID(name).bare, MucTab)
self.on = True
def completion(self):
@@ -1086,10 +1226,9 @@ class PrivateTab(ChatTab):
needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
msg.send()
- self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick or self.get_room().own_nick)
- logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
+ self.core.add_message_to_text_buffer(self._text_buffer, line, None, self.core.own_nick or self.own_nick)
self.cancel_paused_delay()
- self.text_win.refresh(self._room)
+ self.text_win.refresh()
self.input.refresh()
def command_unquery(self, arg):
@@ -1110,7 +1249,7 @@ class PrivateTab(ChatTab):
res.get('version') or _('unknown'),
res.get('os') or _('on an unknown platform'))
self.core.information(version, 'Info')
- jid = self.get_room().name
+ jid = self.name
self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback)
def command_info(self, arg):
@@ -1120,7 +1259,7 @@ class PrivateTab(ChatTab):
if arg:
self.parent_muc.command_info(arg)
else:
- user = JID(self.get_room().name).resource
+ user = JID(self.name).resource
self.parent_muc.command_info(user)
def resize(self):
@@ -1128,7 +1267,7 @@ class PrivateTab(ChatTab):
return
self.need_resize = False
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0)
- self.text_win.rebuild_everything(self._room)
+ self.text_win.rebuild_everything(self._text_buffer)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
self.input.resize(1, self.width, self.height-1, 0)
@@ -1136,27 +1275,26 @@ class PrivateTab(ChatTab):
if self.need_resize:
self.resize()
log.debug(' TAB Refresh: %s'%self.__class__.__name__)
- self.text_win.refresh(self._room)
- self.info_header.refresh(self._room, self.text_win, self.chatstate)
- self.info_win.refresh(self.core.informations)
+ self.text_win.refresh()
+ self.info_header.refresh(self.name, self.text_win, self.chatstate)
+ self.info_win.refresh()
self.tab_win.refresh()
self.input.refresh()
def refresh_info_header(self):
- self.info_header.refresh(self._room, self.text_win, self.chatstate)
+ self.info_header.refresh(self.name, self.text_win, self.chatstate)
self.input.refresh()
- def get_color_state(self):
- if self._room.color_state == get_theme().COLOR_TAB_NORMAL or\
- self._room.color_state == get_theme().COLOR_TAB_CURRENT:
- return self._room.color_state
- return get_theme().COLOR_TAB_PRIVATE
+ @property
+ def state(self):
+ return self._state
- def set_color_state(self, color):
- self._room.color_state = color
+ @state.setter
+ def state(self, value):
+ self._state = value
def get_name(self):
- return self._room.name
+ return self.name
def on_input(self, key):
if key in self.key_func:
@@ -1166,22 +1304,24 @@ class PrivateTab(ChatTab):
if not self.on:
return False
empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
- tab = self.core.get_tab_by_name(JID(self.get_room().name).bare, MucTab)
- if tab and tab.get_room().joined:
+ tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab)
+ if tab and tab.joined:
self.send_composing_chat_state(empty_after)
return False
def on_lose_focus(self):
- self._room.set_color_state(get_theme().COLOR_TAB_NORMAL)
+ self._state = 'normal'
self.text_win.remove_line_separator()
self.text_win.add_line_separator()
- if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
+ tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab)
+ if tab.joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
self.send_chat_state('inactive')
def on_gain_focus(self):
- self._room.set_color_state(get_theme().COLOR_TAB_CURRENT)
+ self._state = 'current'
curses.curs_set(1)
- if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
+ tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab)
+ if tab.joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text():
self.send_chat_state('active')
def on_scroll_up(self):
@@ -1196,9 +1336,6 @@ class PrivateTab(ChatTab):
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
- def get_room(self):
- return self._room
-
def get_text_window(self):
return self.text_win
@@ -1207,9 +1344,9 @@ class PrivateTab(ChatTab):
The user changed her nick in the corresponding muc: update the tab’s name and
display a message.
"""
- self.get_room().add_message(_('"[%(old_nick)s]" is now known as "[%(new_nick)s]"') % {'old_nick':old_nick.replace('"', '\\"'), 'new_nick':new_nick.replace('"', '\\"')})
- new_jid = JID(self.get_room().name).bare+'/'+new_nick
- self.get_room().name = new_jid
+ self.add_message('\x193}%(old)s\x195} is now known as \x193}%(new)s' % {'old':old_nick, 'new':new_nick})
+ new_jid = JID(self.name).bare+'/'+new_nick
+ self.name = new_jid
def user_left(self, status_message, from_nick):
"""
@@ -1217,9 +1354,9 @@ class PrivateTab(ChatTab):
"""
self.deactivate()
if not status_message:
- self.get_room().add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT.replace('"', '\\"')})
+ self.add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT.replace('"', '\\"')})
else:
- self.get_room().add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT, 'status': status_message.replace('"', '\\"')})
+ self.add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT, 'status': status_message.replace('"', '\\"')})
if self.core.current_tab() is self:
self.refresh()
self.core.doupdate()
@@ -1229,18 +1366,26 @@ class PrivateTab(ChatTab):
The user (or at least someone with the same nick) came back in the MUC
"""
self.activate()
- self.get_room().add_message('\x194}%(spec)s \x193}%(nick)s\x195} joined the room' % {'nick':nick, 'spec':get_theme().CHAR_JOIN})
+ tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab)
+ color = None
+ if tab:
+ user = tab.get_user_by_name(nick)
+ if user:
+ color = user.color
+ self.add_message('\x194}%(spec)s \x19%(color)d}%(nick)s\x195} joined the room' % {'nick':nick, 'color': color or 3, 'spec':get_theme().CHAR_JOIN})
if self.core.current_tab() is self:
self.refresh()
self.core.doupdate()
-
def activate(self):
self.on = True
def deactivate(self):
self.on = False
+ def add_message(self, txt, time=None, nickname=None, forced_user=None):
+ self._text_buffer.add_message(txt, time, nickname, None, None, forced_user)
+
class RosterInfoTab(Tab):
"""
A tab, splitted in two, containing the roster and infos
@@ -1255,7 +1400,7 @@ class RosterInfoTab(Tab):
self.contact_info_win = windows.ContactInfoWin()
self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show")
self.input = self.default_help_message
- self.set_color_state(get_theme().COLOR_TAB_NORMAL)
+ self._state = 'normal'
self.key_func['^I'] = self.completion
self.key_func[' '] = self.on_space
self.key_func["/"] = self.on_slash
@@ -1562,19 +1707,13 @@ class RosterInfoTab(Tab):
self.v_separator.refresh()
self.roster_win.refresh(roster)
self.contact_info_win.refresh(self.roster_win.get_selected_row())
- self.information_win.refresh(self.core.informations)
+ self.information_win.refresh()
self.tab_win.refresh()
self.input.refresh()
def get_name(self):
return self.name
- def get_color_state(self):
- return self._color_state
-
- def set_color_state(self, color):
- self._color_state = color
-
def on_input(self, key):
if key == '^M':
selected_row = self.roster_win.get_selected_row()
@@ -1622,19 +1761,18 @@ class RosterInfoTab(Tab):
return self.reset_help_message()
def on_lose_focus(self):
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
def on_gain_focus(self):
- self._color_state = get_theme().COLOR_TAB_CURRENT
+ self._state = 'current'
if isinstance(self.input, windows.HelpText):
curses.curs_set(0)
else:
curses.curs_set(1)
- def add_message(self):
- return False
-
def move_cursor_down(self):
+ if isinstance(self.input, windows.CommandInput):
+ return
self.roster_win.move_cursor_down()
self.roster_win.refresh(roster)
self.contact_info_win.refresh(self.roster_win.get_selected_row())
@@ -1642,6 +1780,8 @@ class RosterInfoTab(Tab):
self.core.doupdate()
def move_cursor_up(self):
+ if isinstance(self.input, windows.CommandInput):
+ return
self.roster_win.move_cursor_up()
self.roster_win.refresh(roster)
self.contact_info_win.refresh(self.roster_win.get_selected_row())
@@ -1726,12 +1866,11 @@ class ConversationTab(ChatTab):
"""
message_type = 'chat'
def __init__(self, jid):
- txt_buff = text_buffer.TextBuffer()
- ChatTab.__init__(self, txt_buff)
- self.color_state = get_theme().COLOR_TAB_NORMAL
+ ChatTab.__init__(self)
+ self._state = 'normal'
self._name = jid # a conversation tab is linked to one specific full jid OR bare jid
self.text_win = windows.TextWin()
- txt_buff.add_window(self.text_win)
+ self._text_buffer.add_window(self.text_win)
self.upper_bar = windows.ConversationStatusMessageWin()
self.info_header = windows.ConversationInfoWin()
self.input = windows.MessageInput()
@@ -1739,7 +1878,7 @@ class ConversationTab(ChatTab):
self.key_func['^I'] = self.completion
# commands
self.commands['unquery'] = (self.command_unquery, _("Usage: /unquery\nUnquery: close the tab"), None)
- self.commands['part'] = (self.command_unquery, _("Usage: /part\Part: close the tab"), None)
+ self.commands['close'] = (self.command_unquery, _("Usage: /close\Close: close the tab"), None)
self.resize()
def completion(self):
@@ -1757,10 +1896,10 @@ class ConversationTab(ChatTab):
needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
msg.send()
- self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick)
+ self.core.add_message_to_text_buffer(self._text_buffer, line, None, self.core.own_nick)
logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
self.cancel_paused_delay()
- self.text_win.refresh(self._room)
+ self.text_win.refresh()
self.input.refresh()
def command_unquery(self, arg):
@@ -1771,7 +1910,7 @@ class ConversationTab(ChatTab):
return
self.need_resize = False
self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0)
- self.text_win.rebuild_everything(self._room)
+ self.text_win.rebuild_everything(self._text_buffer)
self.upper_bar.resize(1, self.width, 0, 0)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
self.input.resize(1, self.width, self.height-1, 0)
@@ -1780,26 +1919,17 @@ class ConversationTab(ChatTab):
if self.need_resize:
self.resize()
log.debug(' TAB Refresh: %s'%self.__class__.__name__)
- self.text_win.refresh(self._room)
+ self.text_win.refresh()
self.upper_bar.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()))
- self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win, self.chatstate)
- self.info_win.refresh(self.core.informations)
+ self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate)
+ self.info_win.refresh()
self.tab_win.refresh()
self.input.refresh()
def refresh_info_header(self):
- self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win, self.chatstate)
+ self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate)
self.input.refresh()
- def get_color_state(self):
- if self.color_state == get_theme().COLOR_TAB_NORMAL or\
- self.color_state == get_theme().COLOR_TAB_CURRENT:
- return self.color_state
- return get_theme().COLOR_TAB_PRIVATE
-
- def set_color_state(self, color):
- self.color_state = color
-
def get_name(self):
return self._name
@@ -1813,14 +1943,14 @@ class ConversationTab(ChatTab):
return False
def on_lose_focus(self):
- self.set_color_state(get_theme().COLOR_TAB_NORMAL)
+ self._state = 'normal'
self.text_win.remove_line_separator()
self.text_win.add_line_separator()
if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'):
self.send_chat_state('inactive')
def on_gain_focus(self):
- self.set_color_state(get_theme().COLOR_TAB_CURRENT)
+ self._state = 'current'
curses.curs_set(1)
if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'):
self.send_chat_state('active')
@@ -1837,9 +1967,6 @@ class ConversationTab(ChatTab):
self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
- def get_room(self):
- return self._room
-
def get_text_window(self):
return self.text_win
@@ -1855,7 +1982,7 @@ class MucListTab(Tab):
"""
def __init__(self, server):
Tab.__init__(self)
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
self.name = server
self.upper_message = windows.Topic()
self.upper_message.set_message('Chatroom list on server %s (Loading)' % self.name)
@@ -1969,15 +2096,12 @@ class MucListTab(Tab):
return self.key_func[key]()
def on_lose_focus(self):
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
def on_gain_focus(self):
- self._color_state = get_theme().COLOR_TAB_CURRENT
+ self._state = 'current'
curses.curs_set(0)
- def get_color_state(self):
- return self._color_state
-
def on_scroll_up(self):
self.listview.scroll_up()
@@ -1992,7 +2116,7 @@ class SimpleTextTab(Tab):
"""
def __init__(self, text):
Tab.__init__(self)
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
self.text_win = windows.SimpleTextWin(text)
self.default_help_message = windows.HelpText("“Ctrl+q”: close")
self.input = self.default_help_message
@@ -2035,15 +2159,12 @@ class SimpleTextTab(Tab):
self.input.refresh()
def on_lose_focus(self):
- self._color_state = get_theme().COLOR_TAB_NORMAL
+ self._state = 'normal'
def on_gain_focus(self):
- self._color_state = get_theme().COLOR_TAB_CURRENT
+ self._state = 'current'
curses.curs_set(0)
- def get_color_state(self):
- return self._color_state
-
def diffmatch(search, string):
"""
Use difflib and a loop to check if search_pattern can
diff --git a/src/text_buffer.py b/src/text_buffer.py
index f39f147a..eb4b7b79 100644
--- a/src/text_buffer.py
+++ b/src/text_buffer.py
@@ -34,19 +34,20 @@ 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):
+ def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None, user=None):
time = time or datetime.now()
msg = Message(txt='%s\x19o'%(txt.replace('\t', ' '),), nick_color=nick_color,
time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\
if history else time.strftime("%H:%M:%S"),\
- nickname=nickname, user=None)
+ nickname=nickname, user=user)
+ log.debug('Coucou, le message ajouté : %s' % (msg,))
self.messages.append(msg)
while len(self.messages) > self.messages_nb_limit:
self.messages.pop(0)
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)
+ nb = window.build_new_message(msg, history=history)
if ret_val is None:
ret_val = nb
if window.pos != 0:
diff --git a/src/windows.py b/src/windows.py
index 4f2c68c6..42c9bfa0 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -62,9 +62,10 @@ class Win(object):
self._win = None
def _resize(self, height, width, y, x):
- self.height, self.width, self.x, self.y = height, width, x, y
if height == 0 or width == 0:
+ self.height, self.width = height, width
return
+ self.height, self.width, self.x, self.y = height, width, x, y
if not self._win:
self._win = curses.newwin(height, width, y, x)
else:
@@ -191,19 +192,19 @@ class UserList(Win):
def __init__(self):
Win.__init__(self)
self.pos = 0
- self.color_role = {'moderator': get_theme().COLOR_USER_MODERATOR,
- 'participant':get_theme().COLOR_USER_PARTICIPANT,
- 'visitor':get_theme().COLOR_USER_VISITOR,
- 'none':get_theme().COLOR_USER_NONE,
- '':get_theme().COLOR_USER_NONE
- }
- self.color_show = {'xa':get_theme().COLOR_STATUS_XA,
- 'none':get_theme().COLOR_STATUS_NONE,
- '':get_theme().COLOR_STATUS_NONE,
- 'dnd':get_theme().COLOR_STATUS_DND,
- 'away':get_theme().COLOR_STATUS_AWAY,
- 'chat':get_theme().COLOR_STATUS_CHAT
- }
+ self.color_role = {'moderator': lambda: get_theme().COLOR_USER_MODERATOR,
+ 'participant': lambda: get_theme().COLOR_USER_PARTICIPANT,
+ 'visitor': lambda: get_theme().COLOR_USER_VISITOR,
+ 'none': lambda: get_theme().COLOR_USER_NONE,
+ '': lambda: get_theme().COLOR_USER_NONE
+ }
+ self.color_show = {'xa': lambda: get_theme().COLOR_STATUS_XA,
+ 'none': lambda: get_theme().COLOR_STATUS_NONE,
+ '': lambda: get_theme().COLOR_STATUS_NONE,
+ 'dnd': lambda: get_theme().COLOR_STATUS_DND,
+ 'away': lambda: get_theme().COLOR_STATUS_AWAY,
+ 'chat': lambda: get_theme().COLOR_STATUS_CHAT
+ }
def scroll_up(self):
self.pos += self.height-1
@@ -228,11 +229,11 @@ class UserList(Win):
if not user.role in self.color_role:
role_col = get_theme().COLOR_USER_NONE
else:
- role_col = self.color_role[user.role]
+ role_col = self.color_role[user.role]()
if not user.show in self.color_show:
show_col = get_theme().COLOR_STATUS_NONE
else:
- show_col = self.color_show[user.show]
+ show_col = self.color_show[user.show]()
if user.chatstate == 'composing':
char = 'X'
elif user.chatstate == 'active':
@@ -297,7 +298,7 @@ class GlobalInfoBar(Win):
self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
sorted_tabs = sorted(self.core.tabs, key=comp)
for tab in sorted_tabs:
- color = tab.get_color_state()
+ color = tab.color
if config.get('show_inactive_tabs', 'true') == 'false' and\
color == get_theme().COLOR_TAB_NORMAL:
continue
@@ -340,18 +341,18 @@ class PrivateInfoWin(InfoWin):
def __init__(self):
InfoWin.__init__(self)
- def refresh(self, room, window, chatstate):
+ def refresh(self, name, window, chatstate):
log.debug('Refresh: %s'%self.__class__.__name__)
with g_lock:
self._win.erase()
- self.write_room_name(room)
+ self.write_room_name(name)
self.print_scroll_position(window)
self.write_chatstate(chatstate)
self.finish_line(get_theme().COLOR_INFORMATION_BAR)
self._refresh()
- def write_room_name(self, room):
- jid = JID(room.name)
+ def write_room_name(self, name):
+ jid = JID(name)
room_name, nick = jid.bare, jid.resource
self.addstr(nick, to_curses_attr(get_theme().COLOR_PRIVATE_NAME))
txt = ' from room %s' % room_name
@@ -379,7 +380,7 @@ class ConversationInfoWin(InfoWin):
def __init__(self):
InfoWin.__init__(self)
- def refresh(self, jid, contact, text_buffer, window, chatstate):
+ def refresh(self, jid, contact, window, chatstate):
# contact can be None, if we receive a message
# from someone not in our roster. In this case, we display
# only the maximum information from the message we can get.
@@ -605,7 +606,7 @@ class TextWin(Win):
self.built_lines.pop(0)
return len(lines)
- def refresh(self, room):
+ def refresh(self):
log.debug('Refresh: %s'%self.__class__.__name__)
if self.height <= 0:
return