From 271fd662a18d0a289fa73c1f42885a451accb1b8 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 1 Aug 2014 16:00:01 +0200 Subject: Entirely remove the g_lock (used to avoid a few race conditions with ncures) --- src/windows/__init__.py | 3 +- src/windows/base_wins.py | 5 +- src/windows/data_forms.py | 169 ++++++++++++++++++-------------------- src/windows/info_bar.py | 133 +++++++++++++++--------------- src/windows/info_wins.py | 108 ++++++++++++------------ src/windows/input_placeholders.py | 20 ++--- src/windows/inputs.py | 39 +++++---- src/windows/list.py | 77 +++++++++-------- src/windows/misc.py | 17 ++-- src/windows/muc.py | 107 ++++++++++++------------ src/windows/roster_win.py | 159 ++++++++++++++++++----------------- src/windows/text_win.py | 167 ++++++++++++++++++------------------- 12 files changed, 485 insertions(+), 519 deletions(-) (limited to 'src/windows') diff --git a/src/windows/__init__.py b/src/windows/__init__.py index adb07cbe..9e165201 100644 --- a/src/windows/__init__.py +++ b/src/windows/__init__.py @@ -2,7 +2,8 @@ Module exporting all the Windows, which are wrappers around curses wins used to display information on the screen """ -from . base_wins import Win, g_lock + +from . base_wins import Win from . data_forms import FormWin from . info_bar import GlobalInfoBar, VerticalGlobalInfoBar from . info_wins import InfoWin, XMLInfoWin, PrivateInfoWin, MucListInfoWin, \ diff --git a/src/windows/base_wins.py b/src/windows/base_wins.py index 44c62e91..574eee89 100644 --- a/src/windows/base_wins.py +++ b/src/windows/base_wins.py @@ -32,8 +32,6 @@ allowed_color_digits = ('0', '1', '2', '3', '4', '5', '6', '7') # text_end are the position delimiting the text in this line. Line = collections.namedtuple('Line', 'msg start_pos end_pos prepend') -g_lock = RLock() - LINES_NB_LIMIT = 4096 class DummyWin(object): @@ -69,8 +67,7 @@ class Win(object): """ Override if something has to be done on resize """ - with g_lock: - self._resize(height, width, y, x) + self._resize(height, width, y, x) def _refresh(self): self._win.noutrefresh() diff --git a/src/windows/data_forms.py b/src/windows/data_forms.py index 0b27291c..d6e2cc66 100644 --- a/src/windows/data_forms.py +++ b/src/windows/data_forms.py @@ -6,7 +6,7 @@ does not inherit from the Win base class), as it will create the others when needed. """ -from . import g_lock, Win +from . import Win from . inputs import Input from theming import to_curses_attr, get_theme @@ -61,12 +61,11 @@ class ColoredLabel(Win): self.refresh() def refresh(self): - with g_lock: - self._win.erase() - self._win.attron(to_curses_attr(self.color)) - self.addstr(0, 0, self.text) - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + self._win.attron(to_curses_attr(self.color)) + self.addstr(0, 0, self.text) + self._win.attroff(to_curses_attr(self.color)) + self._refresh() class DummyInput(FieldInput, Win): @@ -100,19 +99,18 @@ class BooleanWin(FieldInput, Win): self.refresh() def refresh(self): - with g_lock: - self._win.erase() - self._win.attron(to_curses_attr(self.color)) - self.addnstr(0, 0, ' '*(8), self.width) - self.addstr(0, 2, "%s"%self.value) - self.addstr(0, 8, '→') - self.addstr(0, 0, '←') - if self.last_key == 'KEY_RIGHT': - self.addstr(0, 8, '') - else: - self.addstr(0, 0, '') - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + self._win.attron(to_curses_attr(self.color)) + self.addnstr(0, 0, ' '*(8), self.width) + self.addstr(0, 2, "%s"%self.value) + self.addstr(0, 8, '→') + self.addstr(0, 0, '←') + if self.last_key == 'KEY_RIGHT': + self.addstr(0, 8, '') + else: + self.addstr(0, 0, '') + self._win.attroff(to_curses_attr(self.color)) + self._refresh() def reply(self): self._field['label'] = '' @@ -166,18 +164,17 @@ class TextMultiWin(FieldInput, Win): def refresh(self): if not self.edition_input: - with g_lock: - self._win.erase() - self._win.attron(to_curses_attr(self.color)) - self.addnstr(0, 0, ' '*self.width, self.width) - option = self.options[self.val_pos] - self.addstr(0, self.width//2-len(option)//2, option) - if self.val_pos > 0: - self.addstr(0, 0, '←') - if self.val_pos < len(self.options)-1: - self.addstr(0, self.width-1, '→') - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + self._win.attron(to_curses_attr(self.color)) + self.addnstr(0, 0, ' '*self.width, self.width) + option = self.options[self.val_pos] + self.addstr(0, self.width//2-len(option)//2, option) + if self.val_pos > 0: + self.addstr(0, 0, '←') + if self.val_pos < len(self.options)-1: + self.addstr(0, self.width-1, '→') + self._win.attroff(to_curses_attr(self.color)) + self._refresh() else: self.edition_input.refresh() @@ -219,20 +216,19 @@ class ListMultiWin(FieldInput, Win): self.refresh() def refresh(self): - with g_lock: - self._win.erase() - self._win.attron(to_curses_attr(self.color)) - self.addnstr(0, 0, ' '*self.width, self.width) - if self.val_pos > 0: - self.addstr(0, 0, '←') - if self.val_pos < len(self.options)-1: - self.addstr(0, self.width-1, '→') - if self.options: - option = self.options[self.val_pos] - self.addstr(0, self.width//2-len(option)//2, option[0]['label']) - self.addstr(0, 2, '✔' if option[1] else '☐') - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + self._win.attron(to_curses_attr(self.color)) + self.addnstr(0, 0, ' '*self.width, self.width) + if self.val_pos > 0: + self.addstr(0, 0, '←') + if self.val_pos < len(self.options)-1: + self.addstr(0, self.width-1, '→') + if self.options: + option = self.options[self.val_pos] + self.addstr(0, self.width//2-len(option)//2, option[0]['label']) + self.addstr(0, 2, '✔' if option[1] else '☐') + self._win.attroff(to_curses_attr(self.color)) + self._refresh() def reply(self): self._field['label'] = '' @@ -267,19 +263,18 @@ class ListSingleWin(FieldInput, Win): self.refresh() def refresh(self): - with g_lock: - self._win.erase() - self._win.attron(to_curses_attr(self.color)) - self.addnstr(0, 0, ' '*self.width, self.width) - if self.val_pos > 0: - self.addstr(0, 0, '←') - if self.val_pos < len(self.options)-1: - self.addstr(0, self.width-1, '→') - if self.options: - option = self.options[self.val_pos]['label'] - self.addstr(0, self.width//2-len(option)//2, option) - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + self._win.attron(to_curses_attr(self.color)) + self.addnstr(0, 0, ' '*self.width, self.width) + if self.val_pos > 0: + self.addstr(0, 0, '←') + if self.val_pos < len(self.options)-1: + self.addstr(0, self.width-1, '→') + if self.options: + option = self.options[self.val_pos]['label'] + self.addstr(0, self.width//2-len(option)//2, option) + self._win.attroff(to_curses_attr(self.color)) + self._refresh() def reply(self): self._field['label'] = '' @@ -310,19 +305,18 @@ class TextPrivateWin(TextSingleWin): TextSingleWin.__init__(self, field) def rewrite_text(self): - with g_lock: - self._win.erase() - if self.color: - self._win.attron(to_curses_attr(self.color)) - self.addstr('*'*len(self.text[self.view_pos:self.view_pos+self.width-1])) - if self.color: - (y, x) = self._win.getyx() - size = self.width-x - self.addnstr(' '*size, size, to_curses_attr(self.color)) - self.addstr(0, self.pos, '') - if self.color: - self._win.attroff(to_curses_attr(self.color)) - self._refresh() + self._win.erase() + if self.color: + self._win.attron(to_curses_attr(self.color)) + self.addstr('*'*len(self.text[self.view_pos:self.view_pos+self.width-1])) + if self.color: + (y, x) = self._win.getyx() + size = self.width-x + self.addnstr(' '*size, size, to_curses_attr(self.color)) + self.addstr(0, self.pos, '') + if self.color: + self._win.attroff(to_curses_attr(self.color)) + self._refresh() def get_help_message(self): return 'Edit the secret text' @@ -346,8 +340,7 @@ class FormWin(object): } def __init__(self, form, height, width, y, x): self._form = form - with g_lock: - self._win = Win._tab_win.derwin(height, width, y, x) + self._win = Win._tab_win.derwin(height, width, y, x) self.scroll_pos = 0 self.current_input = 0 self.inputs = [] # dict list @@ -370,8 +363,7 @@ class FormWin(object): def resize(self, height, width, y, x): self.height = height self.width = width - with g_lock: - self._win = Win._tab_win.derwin(height, width, y, x) + self._win = Win._tab_win.derwin(height, width, y, x) # Adjust the scroll position, if resizing made the window too small # for the cursor to be visible while self.current_input - self.scroll_pos > self.height-1: @@ -443,19 +435,18 @@ class FormWin(object): self.inputs[self.current_input]['input'].do_command(key) def refresh(self): - with g_lock: - self._win.erase() - y = -self.scroll_pos - i = 0 - for name, field in self._form.getFields().items(): - if field['type'] == 'hidden': - continue - self.inputs[i]['label'].resize(1, self.width//2, y + 1, 0) - self.inputs[i]['input'].resize(1, self.width//2, y+1, self.width//2) - # TODO: display the field description - y += 1 - i += 1 - self._win.refresh() + self._win.erase() + y = -self.scroll_pos + i = 0 + for name, field in self._form.getFields().items(): + if field['type'] == 'hidden': + continue + self.inputs[i]['label'].resize(1, self.width//2, y + 1, 0) + self.inputs[i]['input'].resize(1, self.width//2, y+1, self.width//2) + # TODO: display the field description + y += 1 + i += 1 + self._win.refresh() for i, inp in enumerate(self.inputs): if i < self.scroll_pos: continue diff --git a/src/windows/info_bar.py b/src/windows/info_bar.py index 9917fa6a..390b7aea 100644 --- a/src/windows/info_bar.py +++ b/src/windows/info_bar.py @@ -12,7 +12,7 @@ import curses from config import config -from . import Win, g_lock +from . import Win from theming import get_theme, to_curses_attr class GlobalInfoBar(Win): @@ -21,39 +21,38 @@ class GlobalInfoBar(Win): def refresh(self): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self._win.erase() + self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - create_gaps = config.get('create_gaps', False) - show_names = config.get('show_tab_names', False) - show_nums = config.get('show_tab_numbers', True) - use_nicks = config.get('use_tab_nicks', True) - # ignore any remaining gap tabs if the feature is not enabled - if create_gaps: - sorted_tabs = self.core.tabs[:] - else: - sorted_tabs = [tab for tab in self.core.tabs if tab] + create_gaps = config.get('create_gaps', False) + show_names = config.get('show_tab_names', False) + show_nums = config.get('show_tab_numbers', True) + use_nicks = config.get('use_tab_nicks', True) + # ignore any remaining gap tabs if the feature is not enabled + if create_gaps: + sorted_tabs = self.core.tabs[:] + else: + sorted_tabs = [tab for tab in self.core.tabs if tab] - for nb, tab in enumerate(sorted_tabs): - if not tab: continue - color = tab.color - if not config.get('show_inactive_tabs', True) and\ - color is get_theme().COLOR_TAB_NORMAL: - continue - try: - if show_nums or not show_names: - self.addstr("%s" % str(nb), to_curses_attr(color)) - if show_names: - self.addstr(' ', to_curses_attr(color)) + for nb, tab in enumerate(sorted_tabs): + if not tab: continue + color = tab.color + if not config.get('show_inactive_tabs', True) and\ + color is get_theme().COLOR_TAB_NORMAL: + continue + try: + if show_nums or not show_names: + self.addstr("%s" % str(nb), to_curses_attr(color)) if show_names: - if use_nicks: - self.addstr("%s" % str(tab.get_nick()), to_curses_attr(color)) - else: - self.addstr("%s" % tab.name, to_curses_attr(color)) - self.addstr("|", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - except: # end of line - break + self.addstr(' ', to_curses_attr(color)) + if show_names: + if use_nicks: + self.addstr("%s" % str(tab.get_nick()), to_curses_attr(color)) + else: + self.addstr("%s" % tab.name, to_curses_attr(color)) + self.addstr("|", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + except: # end of line + break (y, x) = self._win.getyx() self.addstr(y, x-1, '] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) (y, x) = self._win.getyx() @@ -68,42 +67,40 @@ class VerticalGlobalInfoBar(Win): self._win = scr def refresh(self): - with g_lock: - height, width = self._win.getmaxyx() - self._win.erase() - sorted_tabs = [tab for tab in self.core.tabs if tab] - if not config.get('show_inactive_tabs', True): - sorted_tabs = [tab for tab in sorted_tabs if\ - tab.vertical_color != get_theme().COLOR_VERTICAL_TAB_NORMAL] - nb_tabs = len(sorted_tabs) - use_nicks = config.get('use_tab_nicks', True) - if nb_tabs >= height: - for y, tab in enumerate(sorted_tabs): - if tab.vertical_color == get_theme().COLOR_VERTICAL_TAB_CURRENT: - pos = y - break - # center the current tab as much as possible - if pos < height//2: - sorted_tabs = sorted_tabs[:height] - elif nb_tabs - pos <= height//2: - sorted_tabs = sorted_tabs[-height:] - else: - sorted_tabs = sorted_tabs[pos-height//2 : pos+height//2] + height, width = self._win.getmaxyx() + self._win.erase() + sorted_tabs = [tab for tab in self.core.tabs if tab] + if not config.get('show_inactive_tabs', True): + sorted_tabs = [tab for tab in sorted_tabs if\ + tab.vertical_color != get_theme().COLOR_VERTICAL_TAB_NORMAL] + nb_tabs = len(sorted_tabs) + use_nicks = config.get('use_tab_nicks', True) + if nb_tabs >= height: for y, tab in enumerate(sorted_tabs): - color = tab.vertical_color - - if not config.get('vertical_tab_list_sort', 'desc') != 'asc': - y = height - y - 1 - self.addstr(y, 0, "%2d" % tab.nb, - to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER)) - self.addstr('.') - if use_nicks: - self.addnstr("%s" % tab.get_nick(), width - 4, to_curses_attr(color)) - else: - self.addnstr("%s" % tab.name, width - 4, to_curses_attr(color)) - separator = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR) - self._win.attron(separator) - self._win.vline(0, width-1, curses.ACS_VLINE, height) - self._win.attroff(separator) - self._refresh() + if tab.vertical_color == get_theme().COLOR_VERTICAL_TAB_CURRENT: + pos = y + break + # center the current tab as much as possible + if pos < height//2: + sorted_tabs = sorted_tabs[:height] + elif nb_tabs - pos <= height//2: + sorted_tabs = sorted_tabs[-height:] + else: + sorted_tabs = sorted_tabs[pos-height//2 : pos+height//2] + for y, tab in enumerate(sorted_tabs): + color = tab.vertical_color + if not config.get('vertical_tab_list_sort', 'desc') != 'asc': + y = height - y - 1 + self.addstr(y, 0, "%2d" % tab.nb, + to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER)) + self.addstr('.') + if use_nicks: + self.addnstr("%s" % tab.get_nick(), width - 4, to_curses_attr(color)) + else: + self.addnstr("%s" % tab.name, width - 4, to_curses_attr(color)) + separator = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR) + self._win.attron(separator) + self._win.vline(0, width-1, curses.ACS_VLINE, height) + self._win.attroff(separator) + self._refresh() diff --git a/src/windows/info_wins.py b/src/windows/info_wins.py index 7c659a6c..766afb75 100644 --- a/src/windows/info_wins.py +++ b/src/windows/info_wins.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) from common import safeJID -from . import Win, g_lock +from . import Win from . funcs import truncate_nick from theming import get_theme, to_curses_attr @@ -39,17 +39,16 @@ class XMLInfoWin(InfoWin): def refresh(self, filter_t='', filter='', window=None): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - bar = to_curses_attr(get_theme().COLOR_INFORMATION_BAR) - if not filter_t: - self.addstr('[No filter]', bar) - else: - info = '[%s] %s' % (filter_t, filter) - self.addstr(info, bar) - self.print_scroll_position(window) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + bar = to_curses_attr(get_theme().COLOR_INFORMATION_BAR) + if not filter_t: + self.addstr('[No filter]', bar) + else: + info = '[%s] %s' % (filter_t, filter) + self.addstr(info, bar) + self.print_scroll_position(window) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() class PrivateInfoWin(InfoWin): """ @@ -61,14 +60,13 @@ class PrivateInfoWin(InfoWin): def refresh(self, name, window, chatstate, informations): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - self.write_room_name(name) - self.print_scroll_position(window) - self.write_chatstate(chatstate) - self.write_additional_informations(informations, name) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + self.write_room_name(name) + self.print_scroll_position(window) + self.write_chatstate(chatstate) + self.write_additional_informations(informations, name) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() def write_additional_informations(self, informations, jid): """ @@ -100,16 +98,15 @@ class MucListInfoWin(InfoWin): def refresh(self, name=None, window=None): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - if name: - self.addstr(name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - else: - self.addstr(self.message, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - if window: - self.print_scroll_position(window) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + if name: + self.addstr(name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + else: + self.addstr(self.message, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + if window: + self.print_scroll_position(window) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() class ConversationInfoWin(InfoWin): """ @@ -138,16 +135,15 @@ class ConversationInfoWin(InfoWin): # If contact is a Contact, then # resource can now be a Resource: user is in the roster and online # or resource is None: user is in the roster but offline - with g_lock: - self._win.erase() - self.write_contact_jid(jid) - self.write_contact_informations(contact) - self.write_resource_information(resource) - self.print_scroll_position(window) - self.write_chatstate(chatstate) - self.write_additional_informations(informations, jid) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + self.write_contact_jid(jid) + self.write_contact_informations(contact) + self.write_resource_information(resource) + self.print_scroll_position(window) + self.write_chatstate(chatstate) + self.write_additional_informations(informations, jid) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() def write_additional_informations(self, informations, jid): """ @@ -217,17 +213,16 @@ class MucInfoWin(InfoWin): def refresh(self, room, window=None): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - self.write_room_name(room) - self.write_participants_number(room) - self.write_own_nick(room) - self.write_disconnected(room) - self.write_role(room) - if window: - self.print_scroll_position(window) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + self.write_room_name(room) + self.write_participants_number(room) + self.write_own_nick(room) + self.write_disconnected(room) + self.write_role(room) + if window: + self.print_scroll_position(window) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() def write_room_name(self, room): self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) @@ -289,12 +284,11 @@ class ConversationStatusMessageWin(InfoWin): resource = contact.get_highest_priority_resource() else: resource = None - with g_lock: - self._win.erase() - if resource: - self.write_status_message(resource) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + if resource: + self.write_status_message(resource) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() def write_status_message(self, resource): self.addstr(resource.status, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) diff --git a/src/windows/input_placeholders.py b/src/windows/input_placeholders.py index 796cf0ad..6fd7975a 100644 --- a/src/windows/input_placeholders.py +++ b/src/windows/input_placeholders.py @@ -7,7 +7,7 @@ import logging log = logging.getLogger(__name__) -from . import Win, g_lock +from . import Win from theming import get_theme, to_curses_attr @@ -25,11 +25,10 @@ class HelpText(Win): log.debug('Refresh: %s', self.__class__.__name__) if txt: self.txt = txt - with g_lock: - self._win.erase() - self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - self.finish_line(get_theme().COLOR_INFORMATION_BAR) - self._refresh() + self._win.erase() + self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) + self._refresh() def do_command(self, key, raw=False): return False @@ -58,11 +57,10 @@ class YesNoInput(Win): log.debug('Refresh: %s', self.__class__.__name__) if txt: self.txt = txt - with g_lock: - self._win.erase() - self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_WARNING_PROMPT)) - self.finish_line(get_theme().COLOR_WARNING_PROMPT) - self._refresh() + self._win.erase() + self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_WARNING_PROMPT)) + self.finish_line(get_theme().COLOR_WARNING_PROMPT) + self._refresh() def do_command(self, key, raw=False): if key.lower() in self.key_func: diff --git a/src/windows/inputs.py b/src/windows/inputs.py index db339b77..ff13a562 100644 --- a/src/windows/inputs.py +++ b/src/windows/inputs.py @@ -10,7 +10,7 @@ import string import common import poopt -from . import Win, g_lock +from . import Win from . base_wins import format_chars from . funcs import find_first_format_char from config import config @@ -494,25 +494,24 @@ class Input(Win): length of text to display, and the position of the cursor. """ self.adjust_view_pos() - with g_lock: - text = self.text - self._win.erase() - if self.color: - self._win.attron(to_curses_attr(self.color)) - displayed_text = text[self.view_pos:self.view_pos+self.width-1].replace('\t', '\x18') - self._win.attrset(0) - self.addstr_colored_lite(displayed_text) - # Fill the rest of the line with the input color - if self.color: - (_, x) = self._win.getyx() - size = self.width - x - self.addnstr(' ' * size, size, to_curses_attr(self.color)) - self.addstr(0, - poopt.wcswidth(displayed_text[:self.pos-self.view_pos]), '') - if self.color: - self._win.attroff(to_curses_attr(self.color)) - curses.curs_set(1) - self._refresh() + text = self.text + self._win.erase() + if self.color: + self._win.attron(to_curses_attr(self.color)) + displayed_text = text[self.view_pos:self.view_pos+self.width-1].replace('\t', '\x18') + self._win.attrset(0) + self.addstr_colored_lite(displayed_text) + # Fill the rest of the line with the input color + if self.color: + (_, x) = self._win.getyx() + size = self.width - x + self.addnstr(' ' * size, size, to_curses_attr(self.color)) + self.addstr(0, + poopt.wcswidth(displayed_text[:self.pos-self.view_pos]), '') + if self.color: + self._win.attroff(to_curses_attr(self.color)) + curses.curs_set(1) + self._refresh() def adjust_view_pos(self): """ diff --git a/src/windows/list.py b/src/windows/list.py index 3cfb8af5..677df6ff 100644 --- a/src/windows/list.py +++ b/src/windows/list.py @@ -7,7 +7,7 @@ log = logging.getLogger(__name__) import curses -from . import Win, g_lock +from . import Win from theming import to_curses_attr, get_theme @@ -86,26 +86,26 @@ class ListWin(Win): def refresh(self): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - lines = self.lines[self._starting_pos:self._starting_pos+self.height] - for y, line in enumerate(lines): - x = 0 - for col in self._columns.items(): - try: - txt = line[col[1]] or '' - except KeyError: - txt = '' - size = self._columns_sizes[col[0]] - txt += ' ' * (size-len(txt)) - if not txt: - continue - if line is self.lines[self._selected_row]: - self.addstr(y, x, txt[:size], to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - else: - self.addstr(y, x, txt[:size]) - x += size - self._refresh() + self._win.erase() + lines = self.lines[self._starting_pos:self._starting_pos+self.height] + for y, line in enumerate(lines): + x = 0 + for col in self._columns.items(): + try: + txt = line[col[1]] or '' + except KeyError: + txt = '' + size = self._columns_sizes[col[0]] + txt += ' ' * (size-len(txt)) + if not txt: + continue + if line is self.lines[self._selected_row]: + self.addstr(y, x, txt[:size], + to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + else: + self.addstr(y, x, txt[:size]) + x += size + self._refresh() def move_cursor_down(self): """ @@ -175,25 +175,24 @@ class ColumnHeaderWin(Win): def refresh(self): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - x = 0 - for col in self._columns: - txt = col - if col in self._column_order: - if self._column_order_asc: - txt += get_theme().CHAR_COLUMN_ASC - else: - txt += get_theme().CHAR_COLUMN_DESC - #⇓⇑↑↓⇧⇩▲▼ - size = self._columns_sizes[col] - txt += ' ' * (size-len(txt)) - if col in self._column_sel: - self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER_SEL)) + self._win.erase() + x = 0 + for col in self._columns: + txt = col + if col in self._column_order: + if self._column_order_asc: + txt += get_theme().CHAR_COLUMN_ASC else: - self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER)) - x += size - self._refresh() + txt += get_theme().CHAR_COLUMN_DESC + #⇓⇑↑↓⇧⇩▲▼ + size = self._columns_sizes[col] + txt += ' ' * (size-len(txt)) + if col in self._column_sel: + self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER_SEL)) + else: + self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER)) + x += size + self._refresh() def sel_column(self, dic): self._column_sel = dic diff --git a/src/windows/misc.py b/src/windows/misc.py index 0f6bce59..07c91bbd 100644 --- a/src/windows/misc.py +++ b/src/windows/misc.py @@ -7,7 +7,7 @@ log = logging.getLogger(__name__) import curses -from . import Win, g_lock +from . import Win from theming import get_theme, to_curses_attr class VerticalSeparator(Win): @@ -19,9 +19,9 @@ class VerticalSeparator(Win): Win.__init__(self) def rewrite_line(self): - with g_lock: - self._win.vline(0, 0, curses.ACS_VLINE, self.height, to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)) - self._refresh() + self._win.vline(0, 0, curses.ACS_VLINE, self.height, + to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)) + self._refresh() def refresh(self): log.debug('Refresh: %s', self.__class__.__name__) @@ -53,9 +53,8 @@ class SimpleTextWin(Win): def refresh(self): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - for y, line in enumerate(self.built_lines): - self.addstr_colored(line, y, 0) - self._refresh() + self._win.erase() + for y, line in enumerate(self.built_lines): + self.addstr_colored(line, y, 0) + self._refresh() diff --git a/src/windows/muc.py b/src/windows/muc.py index ce296e26..02bc58ef 100644 --- a/src/windows/muc.py +++ b/src/windows/muc.py @@ -7,7 +7,7 @@ log = logging.getLogger(__name__) import curses -from . import Win, g_lock +from . import Win import poopt from config import config @@ -36,44 +36,43 @@ class UserList(Win): log.debug('Refresh: %s', self.__class__.__name__) if config.get("hide_user_list", False): return # do not refresh if this win is hidden. - with g_lock: - self._win.erase() + self._win.erase() + if config.get('user_list_sort', 'desc').lower() == 'asc': + y, x = self._win.getmaxyx() + y -= 1 + users = sorted(users) + else: + y = 0 + users = sorted(users) + + if len(users) < self.height: + self.pos = 0 + elif self.pos >= len(users) - self.height and self.pos != 0: + self.pos = len(users) - self.height + for user in users[self.pos:]: + self.draw_role_affiliation(y, user) + self.draw_status_chatstate(y, user) + self.addstr(y, 2, + poopt.cut_by_columns(user.nick, self.width - 2), + to_curses_attr(user.color)) if config.get('user_list_sort', 'desc').lower() == 'asc': - y, x = self._win.getmaxyx() y -= 1 - users = sorted(users) else: - y = 0 - users = sorted(users) - - if len(users) < self.height: - self.pos = 0 - elif self.pos >= len(users) - self.height and self.pos != 0: - self.pos = len(users) - self.height - for user in users[self.pos:]: - self.draw_role_affiliation(y, user) - self.draw_status_chatstate(y, user) - self.addstr(y, 2, - poopt.cut_by_columns(user.nick, self.width - 2), - to_curses_attr(user.color)) - if config.get('user_list_sort', 'desc').lower() == 'asc': - y -= 1 - else: - y += 1 - if y == self.height: - break - # draw indicators of position in the list - if self.pos > 0: - if config.get('user_list_sort', 'desc').lower() == 'asc': - self.draw_plus(self.height-1) - else: - self.draw_plus(0) - if self.pos + self.height < len(users): - if config.get('user_list_sort', 'desc').lower() == 'asc': - self.draw_plus(0) - else: - self.draw_plus(self.height-1) - self._refresh() + y += 1 + if y == self.height: + break + # draw indicators of position in the list + if self.pos > 0: + if config.get('user_list_sort', 'desc').lower() == 'asc': + self.draw_plus(self.height-1) + else: + self.draw_plus(0) + if self.pos + self.height < len(users): + if config.get('user_list_sort', 'desc').lower() == 'asc': + self.draw_plus(0) + else: + self.draw_plus(self.height-1) + self._refresh() def draw_role_affiliation(self, y, user): theme = get_theme() @@ -94,12 +93,11 @@ class UserList(Win): self.addstr(y, 0, char, to_curses_attr(show_col)) def resize(self, height, width, y, x): - with g_lock: - separator = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR) - self._resize(height, width, y, x) - self._win.attron(separator) - self._win.vline(0, 0, curses.ACS_VLINE, self.height) - self._win.attroff(separator) + separator = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR) + self._resize(height, width, y, x) + self._win.attron(separator) + self._win.vline(0, 0, curses.ACS_VLINE, self.height) + self._win.attroff(separator) class Topic(Win): def __init__(self): @@ -108,19 +106,18 @@ class Topic(Win): def refresh(self, topic=None): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - if topic: - msg = topic[:self.width-1] - else: - msg = self._message[:self.width-1] - self.addstr(0, 0, msg, to_curses_attr(get_theme().COLOR_TOPIC_BAR)) - (y, x) = self._win.getyx() - remaining_size = self.width - x - if remaining_size: - self.addnstr(' '*remaining_size, remaining_size, - to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) - self._refresh() + self._win.erase() + if topic: + msg = topic[:self.width-1] + else: + msg = self._message[:self.width-1] + self.addstr(0, 0, msg, to_curses_attr(get_theme().COLOR_TOPIC_BAR)) + (y, x) = self._win.getyx() + remaining_size = self.width - x + if remaining_size: + self.addnstr(' '*remaining_size, remaining_size, + to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self._refresh() def set_message(self, message): self._message = message diff --git a/src/windows/roster_win.py b/src/windows/roster_win.py index d5f6d958..f9858a3a 100644 --- a/src/windows/roster_win.py +++ b/src/windows/roster_win.py @@ -7,7 +7,7 @@ log = logging.getLogger(__name__) from datetime import datetime -from . import Win, g_lock +from . import Win import common from config import config @@ -89,39 +89,39 @@ class RosterWin(Win): """ Regenerates the roster cache if needed """ - with g_lock: - if roster.needs_rebuild: - log.debug('The roster has changed, rebuilding the cache…') - # This is a search - if roster.contact_filter: - self.roster_cache = [] - sort = config.get('roster_sort', 'jid:show') or 'jid:show' - for contact in roster.get_contacts_sorted_filtered(sort): - self.roster_cache.append(contact) - else: - show_offline = config.get('roster_show_offline', False) or roster.contact_filter - sort = config.get('roster_sort', 'jid:show') or 'jid:show' - group_sort = config.get('roster_group_sort', 'name') or 'name' - self.roster_cache = [] - # build the cache - for group in roster.get_groups(group_sort): - contacts_filtered = group.get_contacts(roster.contact_filter) - if (not show_offline and group.get_nb_connected_contacts() == 0) or not contacts_filtered: - continue # Ignore empty groups - self.roster_cache.append(group) - if group.folded: - continue # ignore folded groups - for contact in group.get_contacts(roster.contact_filter, sort): - if not show_offline and len(contact) == 0: - continue # ignore offline contacts - self.roster_cache.append(contact) - if not contact.folded(group.name): - for resource in contact.get_resources(): - self.roster_cache.append(resource) - roster.last_built = datetime.now() - if self.selected_row in self.roster_cache: - if self.pos < self.roster_len and self.roster_cache[self.pos] != self.selected_row: - self.pos = self.roster_cache.index(self.selected_row) + if not roster.needs_rebuild: + return + log.debug('The roster has changed, rebuilding the cache…') + # This is a search + if roster.contact_filter: + self.roster_cache = [] + sort = config.get('roster_sort', 'jid:show') or 'jid:show' + for contact in roster.get_contacts_sorted_filtered(sort): + self.roster_cache.append(contact) + else: + show_offline = config.get('roster_show_offline', False) or roster.contact_filter + sort = config.get('roster_sort', 'jid:show') or 'jid:show' + group_sort = config.get('roster_group_sort', 'name') or 'name' + self.roster_cache = [] + # build the cache + for group in roster.get_groups(group_sort): + contacts_filtered = group.get_contacts(roster.contact_filter) + if (not show_offline and group.get_nb_connected_contacts() == 0) or not contacts_filtered: + continue # Ignore empty groups + self.roster_cache.append(group) + if group.folded: + continue # ignore folded groups + for contact in group.get_contacts(roster.contact_filter, sort): + if not show_offline and len(contact) == 0: + continue # ignore offline contacts + self.roster_cache.append(contact) + if not contact.folded(group.name): + for resource in contact.get_resources(): + self.roster_cache.append(resource) + roster.last_built = datetime.now() + if self.selected_row in self.roster_cache: + if self.pos < self.roster_len and self.roster_cache[self.pos] != self.selected_row: + self.pos = self.roster_cache.index(self.selected_row) def refresh(self, roster): """ @@ -130,43 +130,42 @@ class RosterWin(Win): """ log.debug('Refresh: %s', self.__class__.__name__) self.build_roster_cache(roster) - with g_lock: - # make sure we are within bounds - self.move_cursor_up((self.roster_len + self.pos) if self.pos >= self.roster_len else 0) - if not self.roster_cache: - self.selected_row = None - self._win.erase() - self._win.move(0, 0) - self.draw_roster_information(roster) - y = 1 - group = "none" - # scroll down if needed - if self.start_pos+self.height <= self.pos+2: - self.scroll_down(self.pos - self.start_pos - self.height + (self.height//2)) - # draw the roster from the cache - roster_view = self.roster_cache[self.start_pos-1:self.start_pos+self.height] - - for item in roster_view: - draw_selected = False - if y -2 + self.start_pos == self.pos: - draw_selected = True - self.selected_row = item - - if isinstance(item, RosterGroup): - self.draw_group(y, item, draw_selected) - group = item.name - elif isinstance(item, Contact): - self.draw_contact_line(y, item, draw_selected, group) - elif isinstance(item, Resource): - self.draw_resource_line(y, item, draw_selected) - - y += 1 - - if self.start_pos > 1: - self.draw_plus(1) - if self.start_pos + self.height-2 < self.roster_len: - self.draw_plus(self.height-1) - self._refresh() + # make sure we are within bounds + self.move_cursor_up((self.roster_len + self.pos) if self.pos >= self.roster_len else 0) + if not self.roster_cache: + self.selected_row = None + self._win.erase() + self._win.move(0, 0) + self.draw_roster_information(roster) + y = 1 + group = "none" + # scroll down if needed + if self.start_pos+self.height <= self.pos+2: + self.scroll_down(self.pos - self.start_pos - self.height + (self.height//2)) + # draw the roster from the cache + roster_view = self.roster_cache[self.start_pos-1:self.start_pos+self.height] + + for item in roster_view: + draw_selected = False + if y -2 + self.start_pos == self.pos: + draw_selected = True + self.selected_row = item + + if isinstance(item, RosterGroup): + self.draw_group(y, item, draw_selected) + group = item.name + elif isinstance(item, Contact): + self.draw_contact_line(y, item, draw_selected, group) + elif isinstance(item, Resource): + self.draw_resource_line(y, item, draw_selected) + + y += 1 + + if self.start_pos > 1: + self.draw_plus(1) + if self.start_pos + self.height-2 < self.roster_len: + self.draw_plus(self.height-1) + self._refresh() def draw_plus(self, y): @@ -373,13 +372,11 @@ class ContactInfoWin(Win): def refresh(self, selected_row): log.debug('Refresh: %s', self.__class__.__name__) - with g_lock: - self._win.erase() - if isinstance(selected_row, RosterGroup): - self.draw_group_info(selected_row) - elif isinstance(selected_row, Contact): - self.draw_contact_info(selected_row) - # elif isinstance(selected_row, Resource): - # self.draw_contact_info(None, selected_row) - self._refresh() - + self._win.erase() + if isinstance(selected_row, RosterGroup): + self.draw_group_info(selected_row) + elif isinstance(selected_row, Contact): + self.draw_contact_info(selected_row) + # elif isinstance(selected_row, Resource): + # self.draw_contact_info(None, selected_row) + self._refresh() diff --git a/src/windows/text_win.py b/src/windows/text_win.py index de9b0625..86aea1ef 100644 --- a/src/windows/text_win.py +++ b/src/windows/text_win.py @@ -9,7 +9,7 @@ log = logging.getLogger(__name__) import curses from math import ceil, log10 -from . import Win, g_lock +from . import Win from . base_wins import FORMAT_CHAR, Line from . funcs import truncate_nick, parse_attrs @@ -266,74 +266,73 @@ class TextWin(Win): else: lines = self.built_lines[-self.height-self.pos:-self.pos] with_timestamps = config.get("show_timestamps", True) - with g_lock: - self._win.move(0, 0) - self._win.erase() - for y, line in enumerate(lines): - if line: - msg = line.msg - if line.start_pos == 0: - if msg.nick_color: - color = msg.nick_color - elif msg.user: - color = msg.user.color - else: - color = None - if with_timestamps: - self.write_time(msg.str_time) - if msg.ack: - self.write_ack() - if msg.me: - self._win.attron(to_curses_attr(get_theme().COLOR_ME_MESSAGE)) - self.addstr('* ') - self.write_nickname(msg.nickname, color, msg.highlight) - if msg.revisions: - self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) - self.addstr('%d' % msg.revisions) - self._win.attrset(0) - self.addstr(' ') - else: - self.write_nickname(msg.nickname, color, msg.highlight) - if msg.revisions: - self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) - self.addstr('%d' % msg.revisions) - self._win.attrset(0) - self.addstr('> ') - if y != self.height-1: - self.addstr('\n') - self._win.attrset(0) - for y, line in enumerate(lines): - if not line: - self.write_line_separator(y) - else: - offset = 0 - # Offset for the timestamp (if any) plus a space after it + self._win.move(0, 0) + self._win.erase() + for y, line in enumerate(lines): + if line: + msg = line.msg + if line.start_pos == 0: + if msg.nick_color: + color = msg.nick_color + elif msg.user: + color = msg.user.color + else: + color = None if with_timestamps: - offset += len(line.msg.str_time) - if offset: - offset += 1 - - # Offset for the nickname (if any) - # plus a space and a > after it - if line.msg.nickname: - offset += poopt.wcswidth( - truncate_nick(line.msg.nickname)) - if line.msg.me: - offset += 3 - else: - offset += 2 - offset += ceil(log10(line.msg.revisions + 1)) - - if line.msg.ack: - offset += 1 + poopt.wcswidth( - get_theme().CHAR_ACK_RECEIVED) - - self.write_text(y, offset, - line.prepend+line.msg.txt[line.start_pos:line.end_pos]) - if y != self.height-1: - self.addstr('\n') - self._win.attrset(0) - self._refresh() + self.write_time(msg.str_time) + if msg.ack: + self.write_ack() + if msg.me: + self._win.attron(to_curses_attr(get_theme().COLOR_ME_MESSAGE)) + self.addstr('* ') + self.write_nickname(msg.nickname, color, msg.highlight) + if msg.revisions: + self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) + self.addstr('%d' % msg.revisions) + self._win.attrset(0) + self.addstr(' ') + else: + self.write_nickname(msg.nickname, color, msg.highlight) + if msg.revisions: + self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) + self.addstr('%d' % msg.revisions) + self._win.attrset(0) + self.addstr('> ') + if y != self.height-1: + self.addstr('\n') + self._win.attrset(0) + for y, line in enumerate(lines): + if not line: + self.write_line_separator(y) + else: + offset = 0 + # Offset for the timestamp (if any) plus a space after it + if with_timestamps: + offset += len(line.msg.str_time) + if offset: + offset += 1 + + # Offset for the nickname (if any) + # plus a space and a > after it + if line.msg.nickname: + offset += poopt.wcswidth( + truncate_nick(line.msg.nickname)) + if line.msg.me: + offset += 3 + else: + offset += 2 + offset += ceil(log10(line.msg.revisions + 1)) + + if line.msg.ack: + offset += 1 + poopt.wcswidth( + get_theme().CHAR_ACK_RECEIVED) + + self.write_text(y, offset, + line.prepend+line.msg.txt[line.start_pos:line.end_pos]) + if y != self.height-1: + self.addstr('\n') + self._win.attrset(0) + self._refresh() def write_line_separator(self, y): char = get_theme().CHAR_NEW_TEXT_SEPARATOR @@ -385,23 +384,21 @@ class TextWin(Win): self.addstr(' ') def resize(self, height, width, y, x, room=None): - with g_lock: - if hasattr(self, 'width'): - old_width = self.width - else: - old_width = None - self._resize(height, width, y, x) - if room and self.width != old_width: - self.rebuild_everything(room) - - # reposition the scrolling after resize - # (see #2450) - buf_size = len(self.built_lines) - if buf_size - self.pos < self.height: - self.pos = buf_size - self.height - if self.pos < 0: - self.pos = 0 - + if hasattr(self, 'width'): + old_width = self.width + else: + old_width = None + self._resize(height, width, y, x) + if room and self.width != old_width: + self.rebuild_everything(room) + + # reposition the scrolling after resize + # (see #2450) + buf_size = len(self.built_lines) + if buf_size - self.pos < self.height: + self.pos = buf_size - self.height + if self.pos < 0: + self.pos = 0 def rebuild_everything(self, room): self.built_lines = [] -- cgit v1.2.3