merge
This commit is contained in:
commit
b7b1faebdb
8 changed files with 380 additions and 75 deletions
2
Makefile
2
Makefile
|
@ -6,6 +6,8 @@ LOCALEDIR=$(DATADIR)/locale
|
||||||
MANDIR=$(DATADIR)/man
|
MANDIR=$(DATADIR)/man
|
||||||
|
|
||||||
all: Makefile
|
all: Makefile
|
||||||
|
python3 setup.py build
|
||||||
|
cp build/lib.linux-x86_64-3.2/poopt.cpython-32mu.so src/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find ./ -name \*.pyc -delete
|
find ./ -name \*.pyc -delete
|
||||||
|
|
16
setup.py
Normal file
16
setup.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
|
module_poopt = Extension('poopt',
|
||||||
|
sources = ['src/pooptmodule.c'])
|
||||||
|
|
||||||
|
setup (name = 'BuildLines',
|
||||||
|
version = '0.0.1',
|
||||||
|
description = 'Poezio Optimizations',
|
||||||
|
ext_modules = [module_poopt],
|
||||||
|
author = 'Florent Le Coz',
|
||||||
|
author_email = 'louiz@louiz.org',
|
||||||
|
long_description = """
|
||||||
|
a python3 module for poezio, used to replace some time-critical
|
||||||
|
python functions that are too slow. If compiled, poezio will use this module,
|
||||||
|
otherwise it will just use the equivalent python functions.
|
||||||
|
""")
|
11
src/core.py
11
src/core.py
|
@ -355,7 +355,8 @@ class Core(object):
|
||||||
return
|
return
|
||||||
log.debug('on_got_offline: %s' % presence)
|
log.debug('on_got_offline: %s' % presence)
|
||||||
resource = contact.get_resource_by_fulljid(jid.full)
|
resource = contact.get_resource_by_fulljid(jid.full)
|
||||||
assert resource
|
if not resource:
|
||||||
|
return
|
||||||
# If a resource got offline, display the message in the conversation with this
|
# If a resource got offline, display the message in the conversation with this
|
||||||
# precise resource.
|
# precise resource.
|
||||||
self.add_information_message_to_conversation_tab(jid.full, '\x195%s is \x191offline' % (resource.get_jid().full))
|
self.add_information_message_to_conversation_tab(jid.full, '\x195%s is \x191offline' % (resource.get_jid().full))
|
||||||
|
@ -674,7 +675,7 @@ class Core(object):
|
||||||
roster.add_contact(contact, jid)
|
roster.add_contact(contact, jid)
|
||||||
roster.edit_groups_of_contact(contact, [])
|
roster.edit_groups_of_contact(contact, [])
|
||||||
contact.set_ask('asked')
|
contact.set_ask('asked')
|
||||||
self.tabs[0].set_color_state(theme.COLOR_TAB_HIGHLIGHT)
|
self.get_tab_by_number(0).set_color_state(theme.COLOR_TAB_HIGHLIGHT)
|
||||||
self.information('%s wants to subscribe to your presence'%jid, 'Roster')
|
self.information('%s wants to subscribe to your presence'%jid, 'Roster')
|
||||||
if isinstance(self.current_tab(), tabs.RosterInfoTab):
|
if isinstance(self.current_tab(), tabs.RosterInfoTab):
|
||||||
self.refresh_window()
|
self.refresh_window()
|
||||||
|
@ -777,6 +778,12 @@ class Core(object):
|
||||||
return tab
|
return tab
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_tab_by_number(self, number):
|
||||||
|
for tab in self.tabs:
|
||||||
|
if tab.nb == number:
|
||||||
|
return tab
|
||||||
|
return None
|
||||||
|
|
||||||
def get_room_by_name(self, name):
|
def get_room_by_name(self, name):
|
||||||
"""
|
"""
|
||||||
returns the room that has this name
|
returns the room that has this name
|
||||||
|
|
279
src/pooptmodule.c
Normal file
279
src/pooptmodule.c
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
/* 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 MIT license. See the COPYING file. */
|
||||||
|
|
||||||
|
/** The poopt python3 module
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* This file is a python3 module for poezio, used to replace some time-critical
|
||||||
|
python functions that are too slow. If compiled, poezio will use this module,
|
||||||
|
otherwise it will just use the equivalent python functions. */
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
PyObject *ErrorObject;
|
||||||
|
|
||||||
|
/***
|
||||||
|
The module functions
|
||||||
|
***/
|
||||||
|
|
||||||
|
/* cut_text: takes a string and returns a tuple of int.
|
||||||
|
Each two int tuple is a line, represented by the ending position it (where it should be cut).
|
||||||
|
Not that this position is calculed using the position of the python string characters,
|
||||||
|
not just the individual bytes.
|
||||||
|
For example, poopt_cut_text("vivent les frigidaires", 6);
|
||||||
|
will return [(0, 6), (7, 10), (11, 17), (17, 22)], meaning that the lines are
|
||||||
|
"vivent", "les", "frigid" and "aires"
|
||||||
|
*/
|
||||||
|
PyDoc_STRVAR(poopt_cut_text_doc, "cut_text(width, text)\n\n\nReturn the list of strings, cut according to the given size.");
|
||||||
|
|
||||||
|
static PyObject *poopt_cut_text(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
/* int length; */
|
||||||
|
unsigned char *buffer;
|
||||||
|
int width;
|
||||||
|
|
||||||
|
if (PyArg_ParseTuple(args, "si", &buffer, &width) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int bpos = 0; /* the real position in the char* */
|
||||||
|
int spos = 0; /* the position, considering UTF-8 chars */
|
||||||
|
int last_space = -1;
|
||||||
|
int start_pos = 0;
|
||||||
|
|
||||||
|
PyObject* retlist = PyList_New(0);
|
||||||
|
|
||||||
|
while (buffer[bpos])
|
||||||
|
{
|
||||||
|
if (buffer[bpos] == ' ')
|
||||||
|
last_space = spos;
|
||||||
|
else if (buffer[bpos] == '\n')
|
||||||
|
{
|
||||||
|
if (PyList_Append(retlist, Py_BuildValue("ii", start_pos, spos)) == -1)
|
||||||
|
return NULL;
|
||||||
|
start_pos = spos + 1;
|
||||||
|
last_space = -1;
|
||||||
|
}
|
||||||
|
else if ((spos - start_pos) >= width)
|
||||||
|
{
|
||||||
|
if (last_space == -1)
|
||||||
|
{
|
||||||
|
if (PyList_Append(retlist, Py_BuildValue("ii", start_pos, spos)) == -1)
|
||||||
|
return NULL;
|
||||||
|
start_pos = spos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PyList_Append(retlist, Py_BuildValue("ii", start_pos, last_space)) == -1)
|
||||||
|
return NULL;
|
||||||
|
start_pos = last_space + 1;
|
||||||
|
last_space = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer[bpos] == 25) /* \x19 */
|
||||||
|
{
|
||||||
|
spos++;
|
||||||
|
bpos += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (buffer[bpos] <= 127) /* ASCII char on one byte */
|
||||||
|
bpos += 1;
|
||||||
|
else if (buffer[bpos] >= 194 && buffer[bpos] <= 223)
|
||||||
|
bpos += 2;
|
||||||
|
else if (buffer[bpos] >= 224 && buffer[bpos] <= 239)
|
||||||
|
bpos += 3;
|
||||||
|
else if (buffer[bpos] >= 240 && buffer[bpos] <= 244)
|
||||||
|
bpos += 4;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
spos++;
|
||||||
|
}
|
||||||
|
if (PyList_Append(retlist, Py_BuildValue("(i,i)", start_pos, spos)) == -1)
|
||||||
|
return NULL;
|
||||||
|
return retlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
Module initialization. Just taken from the xxmodule.c template from the python sources.
|
||||||
|
***/
|
||||||
|
static PyTypeObject Str_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"pooptmodule.Str", /*tp_name*/
|
||||||
|
0, /*tp_basicsize*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
/* methods */
|
||||||
|
0, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_reserved*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
0, /*tp_hash*/
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||||
|
0, /*tp_doc*/
|
||||||
|
0, /*tp_traverse*/
|
||||||
|
0, /*tp_clear*/
|
||||||
|
0, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
0, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
0, /*tp_getset*/
|
||||||
|
0, /* see PyInit_xx */ /*tp_base*/
|
||||||
|
0, /*tp_dict*/
|
||||||
|
0, /*tp_descr_get*/
|
||||||
|
0, /*tp_descr_set*/
|
||||||
|
0, /*tp_dictoffset*/
|
||||||
|
0, /*tp_init*/
|
||||||
|
0, /*tp_alloc*/
|
||||||
|
0, /*tp_new*/
|
||||||
|
0, /*tp_free*/
|
||||||
|
0, /*tp_is_gc*/
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- */
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
null_richcompare(PyObject *self, PyObject *other, int op)
|
||||||
|
{
|
||||||
|
Py_INCREF(Py_NotImplemented);
|
||||||
|
return Py_NotImplemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyTypeObject Null_Type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"pooptmodule.Null", /*tp_name*/
|
||||||
|
0, /*tp_basicsize*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
/* methods */
|
||||||
|
0, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_reserved*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
0, /*tp_hash*/
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||||
|
0, /*tp_doc*/
|
||||||
|
0, /*tp_traverse*/
|
||||||
|
0, /*tp_clear*/
|
||||||
|
null_richcompare, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
0, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
0, /*tp_getset*/
|
||||||
|
0, /* see PyInit_xx */ /*tp_base*/
|
||||||
|
0, /*tp_dict*/
|
||||||
|
0, /*tp_descr_get*/
|
||||||
|
0, /*tp_descr_set*/
|
||||||
|
0, /*tp_dictoffset*/
|
||||||
|
0, /*tp_init*/
|
||||||
|
0, /*tp_alloc*/
|
||||||
|
0, /* see PyInit_xx */ /*tp_new*/
|
||||||
|
0, /*tp_free*/
|
||||||
|
0, /*tp_is_gc*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* List of functions defined in the module */
|
||||||
|
|
||||||
|
static PyMethodDef poopt_methods[] = {
|
||||||
|
{"cut_text", poopt_cut_text, METH_VARARGS,
|
||||||
|
poopt_cut_text_doc},
|
||||||
|
{NULL, NULL} /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
PyDoc_STRVAR(module_doc,
|
||||||
|
"This is a template module just for instruction. And poopt.");
|
||||||
|
|
||||||
|
/* Initialization function for the module (*must* be called PyInit_xx) */
|
||||||
|
|
||||||
|
static struct PyModuleDef pooptmodule = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"poopt",
|
||||||
|
module_doc,
|
||||||
|
-1,
|
||||||
|
poopt_methods,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
PyInit_poopt(void)
|
||||||
|
{
|
||||||
|
PyObject *m = NULL;
|
||||||
|
|
||||||
|
/* Due to cross platform compiler issues the slots must be filled
|
||||||
|
* here. It's required for portability to Windows without requiring
|
||||||
|
* C++. */
|
||||||
|
Null_Type.tp_base = &PyBaseObject_Type;
|
||||||
|
Null_Type.tp_new = PyType_GenericNew;
|
||||||
|
Str_Type.tp_base = &PyUnicode_Type;
|
||||||
|
|
||||||
|
/* Finalize the type object including setting type of the new type
|
||||||
|
* object; doing it here is required for portability, too. */
|
||||||
|
/* if (PyType_Ready(&Xxo_Type) < 0) */
|
||||||
|
/* goto fail; */
|
||||||
|
|
||||||
|
/* Create the module and add the functions */
|
||||||
|
m = PyModule_Create(&pooptmodule);
|
||||||
|
if (m == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Add some symbolic constants to the module */
|
||||||
|
if (ErrorObject == NULL) {
|
||||||
|
ErrorObject = PyErr_NewException("poopt.error", NULL, NULL);
|
||||||
|
if (ErrorObject == NULL)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
Py_INCREF(ErrorObject);
|
||||||
|
PyModule_AddObject(m, "error", ErrorObject);
|
||||||
|
|
||||||
|
/* Add Str */
|
||||||
|
if (PyType_Ready(&Str_Type) < 0)
|
||||||
|
goto fail;
|
||||||
|
PyModule_AddObject(m, "Str", (PyObject *)&Str_Type);
|
||||||
|
|
||||||
|
/* Add Null */
|
||||||
|
if (PyType_Ready(&Null_Type) < 0)
|
||||||
|
goto fail;
|
||||||
|
PyModule_AddObject(m, "Null", (PyObject *)&Null_Type);
|
||||||
|
return m;
|
||||||
|
fail:
|
||||||
|
Py_XDECREF(m);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /\* test function *\/ */
|
||||||
|
/* int main(void) */
|
||||||
|
/* { */
|
||||||
|
/* char coucou[] = "vive le foutre, le beurre et le caca boudin"; */
|
||||||
|
|
||||||
|
/* cut_text(coucou, 8); */
|
||||||
|
/* } */
|
|
@ -120,7 +120,9 @@ class Room(TextBuffer):
|
||||||
nick_color = highlight
|
nick_color = highlight
|
||||||
time = time or datetime.now()
|
time = time or datetime.now()
|
||||||
message = Message(txt='%s\x19o'%(txt,), nick_color=nick_color,
|
message = Message(txt='%s\x19o'%(txt,), nick_color=nick_color,
|
||||||
time=time, nickname=nickname, user=user)
|
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:
|
while len(self.messages) > self.messages_nb_limit:
|
||||||
self.messages.pop(0)
|
self.messages.pop(0)
|
||||||
self.messages.append(message)
|
self.messages.append(message)
|
||||||
|
|
18
src/tabs.py
18
src/tabs.py
|
@ -14,7 +14,7 @@ Windows are displayed, resized, etc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MIN_WIDTH = 50
|
MIN_WIDTH = 50
|
||||||
MIN_HEIGHT = 16
|
MIN_HEIGHT = 22
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -89,6 +89,10 @@ class Tab(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resize(scr):
|
def resize(scr):
|
||||||
Tab.size = (Tab.height, Tab.width) = scr.getmaxyx()
|
Tab.size = (Tab.height, Tab.width) = scr.getmaxyx()
|
||||||
|
if Tab.height < MIN_HEIGHT or Tab.width < MIN_WIDTH:
|
||||||
|
Tab.visible = False
|
||||||
|
else:
|
||||||
|
Tab.visible = True
|
||||||
|
|
||||||
def complete_commands(self, the_input):
|
def complete_commands(self, the_input):
|
||||||
"""
|
"""
|
||||||
|
@ -671,6 +675,8 @@ class MucTab(ChatTab):
|
||||||
"""
|
"""
|
||||||
Resize the whole window. i.e. all its sub-windows
|
Resize the whole window. i.e. all its sub-windows
|
||||||
"""
|
"""
|
||||||
|
if not self.visible:
|
||||||
|
return
|
||||||
text_width = (self.width//10)*9
|
text_width = (self.width//10)*9
|
||||||
self.topic_win.resize(1, self.width, 0, 0)
|
self.topic_win.resize(1, self.width, 0, 0)
|
||||||
self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10))
|
self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10))
|
||||||
|
@ -1018,7 +1024,7 @@ class PrivateTab(ChatTab):
|
||||||
self.core.close_tab()
|
self.core.close_tab()
|
||||||
|
|
||||||
def resize(self):
|
def resize(self):
|
||||||
if self.core.information_win_size >= self.height-3:
|
if self.core.information_win_size >= self.height-3 or not self.visible:
|
||||||
return
|
return
|
||||||
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0)
|
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._room)
|
||||||
|
@ -1165,6 +1171,8 @@ class RosterInfoTab(Tab):
|
||||||
self.resize()
|
self.resize()
|
||||||
|
|
||||||
def resize(self):
|
def resize(self):
|
||||||
|
if not self.visible:
|
||||||
|
return
|
||||||
roster_width = self.width//2
|
roster_width = self.width//2
|
||||||
info_width = self.width-roster_width-1
|
info_width = self.width-roster_width-1
|
||||||
self.v_separator.resize(self.height-2, 1, 0, roster_width)
|
self.v_separator.resize(self.height-2, 1, 0, roster_width)
|
||||||
|
@ -1518,7 +1526,7 @@ class ConversationTab(ChatTab):
|
||||||
self.core.close_tab()
|
self.core.close_tab()
|
||||||
|
|
||||||
def resize(self):
|
def resize(self):
|
||||||
if self.core.information_win_size >= self.height-3:
|
if self.core.information_win_size >= self.height-3 or not self.visible:
|
||||||
return
|
return
|
||||||
self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0)
|
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._room)
|
||||||
|
@ -1635,6 +1643,8 @@ class MucListTab(Tab):
|
||||||
self.input.refresh()
|
self.input.refresh()
|
||||||
|
|
||||||
def resize(self):
|
def resize(self):
|
||||||
|
if not self.visible:
|
||||||
|
return
|
||||||
self.upper_message.resize(1, self.width, 0, 0)
|
self.upper_message.resize(1, self.width, 0, 0)
|
||||||
column_size = {'node-part': (self.width-5)//4,
|
column_size = {'node-part': (self.width-5)//4,
|
||||||
'name': (self.width-5)//4*3,
|
'name': (self.width-5)//4*3,
|
||||||
|
@ -1761,6 +1771,8 @@ class SimpleTextTab(Tab):
|
||||||
self.core.close_tab()
|
self.core.close_tab()
|
||||||
|
|
||||||
def resize(self):
|
def resize(self):
|
||||||
|
if not self.visible:
|
||||||
|
return
|
||||||
self.text_win.resize(self.height-2, self.width, 0, 0)
|
self.text_win.resize(self.height-2, self.width, 0, 0)
|
||||||
self.input.resize(1, self.width, self.height-1, 0)
|
self.input.resize(1, self.width, self.height-1, 0)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from datetime import datetime
|
||||||
import theme
|
import theme
|
||||||
from config import config
|
from config import config
|
||||||
|
|
||||||
Message = collections.namedtuple('Message', 'txt nick_color time nickname user')
|
Message = collections.namedtuple('Message', 'txt nick_color time str_time nickname user')
|
||||||
|
|
||||||
class TextBuffer(object):
|
class TextBuffer(object):
|
||||||
"""
|
"""
|
||||||
|
@ -45,8 +45,11 @@ class TextBuffer(object):
|
||||||
self.windows.append(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):
|
||||||
|
time = time or datetime.now()
|
||||||
msg = Message(txt='%s\x19o'%(txt,), nick_color=nick_color,
|
msg = Message(txt='%s\x19o'%(txt,), nick_color=nick_color,
|
||||||
time=time or datetime.now(), nickname=nickname, user=None)
|
time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\
|
||||||
|
if history else time.strftime("%H:%M:%S"),\
|
||||||
|
nickname=nickname, user=None)
|
||||||
self.messages.append(msg)
|
self.messages.append(msg)
|
||||||
while len(self.messages) > self.messages_nb_limit:
|
while len(self.messages) > self.messages_nb_limit:
|
||||||
self.messages.pop(0)
|
self.messages.pop(0)
|
||||||
|
|
118
src/windows.py
118
src/windows.py
|
@ -28,6 +28,7 @@ from threading import Lock
|
||||||
|
|
||||||
from contact import Contact, Resource
|
from contact import Contact, Resource
|
||||||
from roster import RosterGroup, roster
|
from roster import RosterGroup, roster
|
||||||
|
from poopt import cut_text
|
||||||
|
|
||||||
# from message import Line
|
# from message import Line
|
||||||
from tabs import MIN_WIDTH, MIN_HEIGHT
|
from tabs import MIN_WIDTH, MIN_HEIGHT
|
||||||
|
@ -41,7 +42,10 @@ import wcwidth
|
||||||
import singleton
|
import singleton
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
Line = collections.namedtuple('Line', 'text text_offset nickname_color time nickname')
|
# msg is a reference to the corresponding Message tuple. text_start and text_end are the position
|
||||||
|
# delimiting the text in this line.
|
||||||
|
# first is a bool telling if this is the first line of the message.
|
||||||
|
Line = collections.namedtuple('Line', 'msg start_pos end_pos first')
|
||||||
|
|
||||||
g_lock = Lock()
|
g_lock = Lock()
|
||||||
|
|
||||||
|
@ -89,6 +93,12 @@ class Win(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def move(self, y, x):
|
||||||
|
try:
|
||||||
|
self._win.move(y, x)
|
||||||
|
except:
|
||||||
|
self._win.move(0, 0)
|
||||||
|
|
||||||
def addstr_colored(self, text, y=None, x=None):
|
def addstr_colored(self, text, y=None, x=None):
|
||||||
"""
|
"""
|
||||||
Write a string on the window, setting the
|
Write a string on the window, setting the
|
||||||
|
@ -101,7 +111,7 @@ class Win(object):
|
||||||
one of 'u', 'b', 'c[0-9]'
|
one of 'u', 'b', 'c[0-9]'
|
||||||
"""
|
"""
|
||||||
if y is not None and x is not None:
|
if y is not None and x is not None:
|
||||||
self._win.move(y, x)
|
self.move(y, x)
|
||||||
next_attr_char = text.find('\x19')
|
next_attr_char = text.find('\x19')
|
||||||
while next_attr_char != -1:
|
while next_attr_char != -1:
|
||||||
if next_attr_char + 1 < len(text):
|
if next_attr_char + 1 < len(text):
|
||||||
|
@ -529,30 +539,14 @@ class TextWin(Win):
|
||||||
if None not in self.built_lines:
|
if None not in self.built_lines:
|
||||||
self.built_lines.append(None)
|
self.built_lines.append(None)
|
||||||
|
|
||||||
def build_new_message(self, message, history=None):
|
def build_new_message(self, message, history=None, clean=True):
|
||||||
"""
|
"""
|
||||||
Take one message, build it and add it to the list
|
Take one message, build it and add it to the list
|
||||||
Return the number of lines that are built for the given
|
Return the number of lines that are built for the given
|
||||||
message.
|
message.
|
||||||
"""
|
"""
|
||||||
def cut_text(text, width):
|
|
||||||
"""
|
|
||||||
returns the text that should be displayed on the line, and the rest
|
|
||||||
of the text, in a tuple
|
|
||||||
"""
|
|
||||||
cutted = wcwidth.widthcut(text, width) or text[:width]
|
|
||||||
limit = cutted.find('\n')
|
|
||||||
if limit >= 0:
|
|
||||||
return (text[limit+1:], text[:limit])
|
|
||||||
if not wcwidth.wcsislonger(text, width):
|
|
||||||
return ('', text)
|
|
||||||
limit = cutted.rfind(' ')
|
|
||||||
if limit <= 0:
|
|
||||||
return (text[len(cutted):], cutted)
|
|
||||||
else:
|
|
||||||
return (text[limit+1:], text[:limit])
|
|
||||||
|
|
||||||
if message is None: # line separator
|
if message is None: # line separator
|
||||||
|
log.debug('je build NON, cool non ? +++++++++++++++++++++++++++')
|
||||||
self.built_lines.append(None)
|
self.built_lines.append(None)
|
||||||
return 0
|
return 0
|
||||||
txt = message.txt
|
txt = message.txt
|
||||||
|
@ -560,53 +554,32 @@ class TextWin(Win):
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
txt = txt.replace('\t', ' ')
|
txt = txt.replace('\t', ' ')
|
||||||
# length of the time
|
nick = message.nickname
|
||||||
if history:
|
if nick and len(nick) >= 25:
|
||||||
offset = 20
|
nick = nick[:25]+'…'
|
||||||
else:
|
offset = 1 + len(message.str_time)
|
||||||
offset = 9
|
|
||||||
if theme.CHAR_TIME_RIGHT:
|
|
||||||
offset += 1
|
|
||||||
if theme.CHAR_TIME_RIGHT:
|
|
||||||
offset += 1
|
|
||||||
nickname = message.nickname
|
|
||||||
if nickname and len(nickname) >= 25:
|
|
||||||
nick = nickname[:25]+'…'
|
|
||||||
else:
|
|
||||||
nick = nickname
|
|
||||||
if nick:
|
if nick:
|
||||||
offset += wcwidth.wcswidth(nick) + 2 # + nick + spaces length
|
offset += wcwidth.wcswidth(nick) + 2 # + nick + spaces length
|
||||||
|
if nick:
|
||||||
|
offset += wcwidth.wcswidth(nick) + 2 # + nick + spaces length
|
||||||
|
if theme.CHAR_TIME_LEFT:
|
||||||
|
offset += 1
|
||||||
|
if theme.CHAR_TIME_RIGHT:
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
lines = cut_text(txt, self.width-offset-1)
|
||||||
|
|
||||||
first = True
|
first = True
|
||||||
nb = 0
|
for line in lines:
|
||||||
while txt != '':
|
self.built_lines.append(Line(msg=message,
|
||||||
(txt, cutted_txt) = cut_text(txt, self.width-offset-1)
|
start_pos=line[0],
|
||||||
if first:
|
end_pos=line[1],
|
||||||
if message.nick_color:
|
first=first))
|
||||||
color = message.nick_color
|
|
||||||
elif message.user:
|
|
||||||
color = message.user.color
|
|
||||||
else:
|
|
||||||
color = None
|
|
||||||
else:
|
|
||||||
color = None
|
|
||||||
if first:
|
|
||||||
if history:
|
|
||||||
time = message.time.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
else:
|
|
||||||
time = message.time.strftime("%H:%M:%S")
|
|
||||||
nickname = nick
|
|
||||||
else:
|
|
||||||
time = None
|
|
||||||
nickname = None
|
|
||||||
self.built_lines.append(Line(text=cutted_txt,
|
|
||||||
text_offset=offset,
|
|
||||||
nickname_color=color, time=time,
|
|
||||||
nickname=nickname))
|
|
||||||
nb += 1
|
|
||||||
first = False
|
first = False
|
||||||
while len(self.built_lines) > self.lines_nb_limit:
|
if clean:
|
||||||
self.built_lines.pop(0)
|
while len(self.built_lines) > self.lines_nb_limit:
|
||||||
return nb
|
self.built_lines.pop(0)
|
||||||
|
return len(lines)
|
||||||
|
|
||||||
def refresh(self, room):
|
def refresh(self, room):
|
||||||
log.debug('Refresh: %s'%self.__class__.__name__)
|
log.debug('Refresh: %s'%self.__class__.__name__)
|
||||||
|
@ -621,17 +594,26 @@ class TextWin(Win):
|
||||||
self._win.erase()
|
self._win.erase()
|
||||||
for y, line in enumerate(lines):
|
for y, line in enumerate(lines):
|
||||||
if line is None:
|
if line is None:
|
||||||
|
log.debug('COUCOU JE SUIS NONE\n\n-----------------')
|
||||||
self.write_line_separator()
|
self.write_line_separator()
|
||||||
else:
|
else:
|
||||||
self.write_time(line.time)
|
msg = line.msg
|
||||||
self.write_nickname(line.nickname, line.nickname_color)
|
if line.first:
|
||||||
|
if msg.nick_color:
|
||||||
|
color = msg.nick_color
|
||||||
|
elif msg.user:
|
||||||
|
color = msg.user.color
|
||||||
|
else:
|
||||||
|
color = None
|
||||||
|
self.write_time(msg.str_time)
|
||||||
|
self.write_nickname(msg.nickname, color)
|
||||||
if y != self.height-1:
|
if y != self.height-1:
|
||||||
self.addstr('\n')
|
self.addstr('\n')
|
||||||
self._win.attrset(0)
|
self._win.attrset(0)
|
||||||
for y, line in enumerate(lines):
|
for y, line in enumerate(lines):
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
self.write_text(y, line.text_offset, line.text)
|
self.write_text(y, (3 if line.msg.nickname else 1) + len(line.msg.str_time)+len(line.msg.nickname or ''), line.msg.txt[line.start_pos:line.end_pos])
|
||||||
if y != self.height-1:
|
if y != self.height-1:
|
||||||
self.addstr('\n')
|
self.addstr('\n')
|
||||||
self._win.attrset(0)
|
self._win.attrset(0)
|
||||||
|
@ -676,7 +658,9 @@ class TextWin(Win):
|
||||||
def rebuild_everything(self, room):
|
def rebuild_everything(self, room):
|
||||||
self.built_lines = []
|
self.built_lines = []
|
||||||
for message in room.messages:
|
for message in room.messages:
|
||||||
self.build_new_message(message)
|
self.build_new_message(message, clean=False)
|
||||||
|
while len(self.built_lines) > self.lines_nb_limit:
|
||||||
|
self.built_lines.pop(0)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
log.debug('** TextWin: deleting %s built lines' % (len(self.built_lines)))
|
log.debug('** TextWin: deleting %s built lines' % (len(self.built_lines)))
|
||||||
|
|
Loading…
Reference in a new issue