diff options
Diffstat (limited to 'src/window.py')
-rw-r--r-- | src/window.py | 329 |
1 files changed, 226 insertions, 103 deletions
diff --git a/src/window.py b/src/window.py index 8eff8c15..28229e2f 100644 --- a/src/window.py +++ b/src/window.py @@ -48,8 +48,7 @@ class Win(object): # (°> also, a penguin # //\ # V_/_ - pass - self.win.idlok(1) + return self.win.leaveok(1) def refresh(self): @@ -98,7 +97,7 @@ class UserList(Win): role_col = self.color_role[user.role] show_col = self.color_show[user.show] self.addstr(y, 0, theme.CHAR_STATUS, curses.color_pair(show_col)) - self.addnstr(y, 1, user.nick, self.width-1, curses.color_pair(role_col)) + self.addnstr(y, 1, user.nick, self.width-2, curses.color_pair(role_col)) y += 1 if y == self.height: break @@ -110,6 +109,9 @@ class UserList(Win): if not visible: return self._resize(height, width, y, x, stdscr) + self.win.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self.win.vline(0, 0, curses.ACS_VLINE, self.height) + self.win.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) class Topic(Win): def __init__(self, height, width, y, x, parent_win, visible): @@ -119,29 +121,21 @@ class Topic(Win): def resize(self, height, width, y, x, stdscr, visible): self._resize(height, width, y, x, stdscr) - def refresh(self, topic, jid=None): + def refresh(self, topic): if not self.visible: return g_lock.acquire() self.win.erase() - if not jid: - self.addnstr(0, 0, topic[:self.width-1], self.width-1, curses.color_pair(theme.COLOR_TOPIC_BAR)) - (y, x) = self.win.getyx() - remaining_size = self.width - x - if remaining_size: - self.addnstr(' '*remaining_size, remaining_size, - curses.color_pair(theme.COLOR_INFORMATION_BAR)) - elif jid: - room = jid.split('/')[0] - nick = '/'.join(jid.split('/')[1:]) - topic = _('%(nick)s from room %(room)s' % {'nick': nick, 'room':room}) - self.addnstr(0, 0, topic + " "*(self.width-len(topic)), self.width-1 - , curses.color_pair(theme.COLOR_PRIVATE_ROOM_BAR)) - + self.addnstr(0, 0, topic[:self.width-1], self.width-1, curses.color_pair(theme.COLOR_TOPIC_BAR)) + (y, x) = self.win.getyx() + remaining_size = self.width - x + if remaining_size: + self.addnstr(' '*remaining_size, remaining_size, + curses.color_pair(theme.COLOR_INFORMATION_BAR)) self.win.refresh() g_lock.release() -class RoomInfo(Win): +class GlobalInfoBar(Win): def __init__(self, height, width, y, x, parent_win, visible): self.visible = visible Win.__init__(self, height, width, y, x, parent_win) @@ -149,17 +143,7 @@ class RoomInfo(Win): def resize(self, height, width, y, x, stdscr, visible): self._resize(height, width, y, x, stdscr) - def print_scroll_position(self, current_room): - """ - Print, link in Weechat, a -PLUS(n)- where n - is the number of available lines to scroll - down - """ - if current_room.pos > 0: - plus = ' -PLUS(%s)-' % current_room.pos - self.addnstr(plus, len(plus), curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD) - - def refresh(self, rooms, current): + def refresh(self, tabs, current): if not self.visible: return def compare_room(a): @@ -170,17 +154,16 @@ class RoomInfo(Win): self.win.erase() self.addnstr(0, 0, "[", self.width ,curses.color_pair(theme.COLOR_INFORMATION_BAR)) - sorted_rooms = sorted(rooms, key=comp) - for room in sorted_rooms: - color = room.color_state + sorted_tabs = sorted(tabs, key=comp) + for tab in sorted_tabs: + color = tab.get_color_state() try: - self.addstr("%s" % str(room.nb), curses.color_pair(color)) + self.addstr("%s" % str(tab.nb), curses.color_pair(color)) self.addstr("|", curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: # end of line break (y, x) = self.win.getyx() - self.addnstr(y, x-1, '] '+ current.name, len(current.name)+2, curses.color_pair(theme.COLOR_INFORMATION_BAR)) - self.print_scroll_position(current) + self.addnstr(y, x-1, '] ', 2, curses.color_pair(theme.COLOR_INFORMATION_BAR)) (y, x) = self.win.getyx() remaining_size = self.width - x self.addnstr(' '*remaining_size, remaining_size, @@ -188,6 +171,132 @@ class RoomInfo(Win): self.win.refresh() g_lock.release() +class InfoWin(Win): + """ + Base class for all the *InfoWin, used in various tabs. For example + MucInfoWin, etc. Provides some useful methods. + """ + def __init__(self, height, width, y, x, parent_win, visible): + self.visible = visible + Win.__init__(self, height, width, y, x, parent_win) + + def print_scroll_position(self, text_buffer): + """ + Print, link in Weechat, a -PLUS(n)- where n + is the number of available lines to scroll + down + """ + if text_buffer.pos > 0: + plus = ' -PLUS(%s)-' % text_buffer.pos + self.addnstr(plus, len(plus), curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD) + + def finish_line(self): + """ + Write colored spaces until the end of line + """ + (y, x) = self.win.getyx() + size = self.width-x + self.addnstr(' '*size, size, curses.color_pair(theme.COLOR_INFORMATION_BAR)) + +class PrivateInfoWin(InfoWin): + """ + The live above the information window, displaying informations + about the MUC user we are talking to + """ + def __init__(self, height, width, y, x, parent_win, visible): + InfoWin.__init__(self, height, width, y, x, parent_win, visible) + + def resize(self, height, width, y, x, stdscr, visible): + self._resize(height, width, y, x, stdscr) + + def refresh(self, room): + if not self.visible: + return + g_lock.acquire() + self.win.erase() + self.write_room_name(room) + self.print_scroll_position(room) + self.finish_line() + self.win.refresh() + g_lock.release() + + def write_room_name(self, room): + (room_name, nick) = room.name.split('/', 1) + self.addnstr(nick, len(nick), curses.color_pair(13)) + txt = ' from room %s' % room_name + self.addnstr(txt, len(txt), curses.color_pair(theme.COLOR_INFORMATION_BAR)) + +class MucInfoWin(InfoWin): + """ + The line just above the information window, displaying informations + about the MUC we are viewing + """ + def __init__(self, height, width, y, x, parent_win, visible): + InfoWin.__init__(self, height, width, y, x, parent_win, visible) + + def resize(self, height, width, y, x, stdscr, visible): + self._resize(height, width, y, x, stdscr) + + def refresh(self, room): + if not self.visible: + return + g_lock.acquire() + self.win.erase() + self.write_room_name(room) + self.write_own_nick(room) + self.write_disconnected(room) + self.write_role(room) + self.print_scroll_position(room) + self.finish_line() + self.win.refresh() + g_lock.release() + + def write_room_name(self, room): + """ + """ + self.addnstr('[', 1, curses.color_pair(theme.COLOR_INFORMATION_BAR)) + self.addnstr(room.name, len(room.name), curses.color_pair(13)) + self.addnstr('] ', 2, curses.color_pair(theme.COLOR_INFORMATION_BAR)) + + def write_disconnected(self, room): + """ + Shows a message if the room is not joined + """ + if not room.joined: + self.addnstr(' -!- Not connected ', 21, curses.color_pair(theme.COLOR_INFORMATION_BAR)) + def write_own_nick(self, room): + """ + Write our own nick in the info bar + """ + nick = room.own_nick + if not nick: + return + if len(nick) > 13: + nick = nick[:13]+'…' + length = 14 + else: + length = len(nick) + self.addnstr(nick, length, curses.color_pair(theme.COLOR_INFORMATION_BAR)) + + def write_role(self, room): + """ + Write our own role and affiliation + """ + from common import debug + + own_user = None + for user in room.users: + if user.nick == room.own_nick: + own_user = user + break + if not own_user: + return + txt = ' (' + if own_user.affiliation != 'none': + txt += own_user.affiliation+', ' + txt += own_user.role+')' + self.addnstr(txt, len(txt), curses.color_pair(theme.COLOR_INFORMATION_BAR)) + class TextWin(Win): """ Just keep ONE single window for the text area and rewrite EVERYTHING @@ -235,7 +344,7 @@ class TextWin(Win): limit = txt[:self.width-offset].rfind(' ') this_line_was_broken_by_space = True if limit <= 0: - limit = self.width-offset-1 + limit = self.width-offset this_line_was_broken_by_space = False else: limit = self.width-offset-1 @@ -269,6 +378,8 @@ class TextWin(Win): """ if not self.visible: return + if self.height <= 0: + return g_lock.acquire() self.win.erase() lines = self.build_lines_from_messages(room.messages) @@ -300,7 +411,7 @@ class TextWin(Win): """ """ self.win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) - self.addstr(' -'*(self.width//2)) + self.addstr(' -'*(self.width//2-1)) self.win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) def write_text(self, y, x, txt, color, colorized): @@ -322,7 +433,12 @@ class TextWin(Win): theme.CHAR_QUIT: theme.COLOR_QUIT_CHAR, theme.CHAR_KICK: theme.COLOR_KICK_CHAR, } - for word in shlex.split(txt): + try: + splitted = shlex.split(txt) + except ValueError: + txt += '"' + splitted = shlex.split(txt) + for word in splitted: if word in list(special_words.keys()): self.addstr(word, curses.color_pair(special_words[word])) elif word.startswith('(') and word.endswith(')'): @@ -392,9 +508,34 @@ class Input(Win): The line where text is entered """ def __init__(self, height, width, y, x, stdscr, visible): + self.key_func = { + "KEY_LEFT": self.key_left, + "M-D": self.key_left, + "KEY_RIGHT": self.key_right, + "M-C": self.key_right, + "KEY_UP": self.key_up, + "M-A": self.key_up, + "KEY_END": self.key_end, + "KEY_HOME": self.key_home, + "KEY_DOWN": self.key_down, + "M-B": self.key_down, + "KEY_DC": self.key_dc, + '^D': self.key_dc, + 'M-b': self.jump_word_left, + '^W': self.delete_word, + '^K': self.delete_end_of_line, + '^U': self.delete_begining_of_line, + '^Y': self.paste_clipboard, + '^A': self.key_home, + '^E': self.key_end, + 'M-f': self.jump_word_right, + "KEY_BACKSPACE": self.key_backspace, + '^?': self.key_backspace, + '^J': self.get_text, + '\n': self.get_text, + } + Win.__init__(self, height, width, y, x, stdscr) - curses.curs_set(1) - self.win.leaveok(0) self.visible = visible self.history = [] self.text = '' @@ -412,7 +553,6 @@ class Input(Win): if not visible: return self._resize(height, width, y, x, stdscr) - self.win.leaveok(0) self.win.clear() self.addnstr(0, 0, self.text, self.width-1) @@ -686,6 +826,10 @@ class Input(Win): self.key_end(False) def do_command(self, key, reset=True): + if key in self.key_func: + return self.key_func[key]() + # if not key or len(key) > 1: + # return # ignore non-handled keyboard shortcuts self.reset_completion() self.text = self.text[:self.pos+self.line_pos]+key+self.text[self.pos+self.line_pos:] (y, x) = self.win.getyx() @@ -707,6 +851,7 @@ class Input(Win): if len(txt) != 0: self.history.append(txt) self.histo_pos = len(self.history)-1 + self.rewrite_text() return txt def rewrite_text(self): @@ -716,14 +861,14 @@ class Input(Win): g_lock.acquire() self.clear_text() self.addstr(self.text[self.line_pos:self.line_pos+self.width-1]) - self.win.move(0, self.pos) - self.refresh() + self.win.chgat(0, self.pos, 1, curses.A_REVERSE) + self.win.refresh() g_lock.release() def refresh(self): if not self.visible: return - self.win.refresh() + self.rewrite_text() def clear_text(self): self.win.erase() @@ -733,61 +878,39 @@ class Window(object): The whole "screen" that can be seen at once in the terminal. It contains an userlist, an input zone, a topic zone and a chat zone """ - def __init__(self, stdscr): - """ - name is the name of the Tab, and it's also - the JID of the chatroom. - A particular tab is the "Info" tab which has no - name (None). This info tab should be unique. - The stdscr should be passed to know the size of the - terminal - """ - self.size = (self.height, self.width) = stdscr.getmaxyx() - if self.height < 10 or self.width < 60: - visible = False - else: - visible = True - if visible: - stdscr.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - stdscr.vline(1, 9*(self.width//10), curses.ACS_VLINE, self.height-2) - stdscr.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - self.user_win = UserList(self.height-3, (self.width//10)-1, 1, 9*(self.width//10)+1, stdscr, visible) - self.topic_win = Topic(1, self.width, 0, 0, stdscr, visible) - self.info_win = RoomInfo(1, self.width, self.height-2, 0, stdscr, visible) - self.text_win = TextWin(self.height-3, (self.width//10)*9, 1, 0, stdscr, visible) - self.input = Input(1, self.width, self.height-1, 0, stdscr, visible) - - def resize(self, stdscr): - """ - Resize the whole window. i.e. all its sub-windows - """ - self.size = (self.height, self.width) = stdscr.getmaxyx() - if self.height < 10 or self.width < 50: - visible = False - else: - visible = True - if visible: - stdscr.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - stdscr.vline(1, 9*(self.width//10), curses.ACS_VLINE, self.height-2) - stdscr.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - text_width = (self.width//10)*9; - self.topic_win.resize(1, self.width, 0, 0, stdscr, visible) - self.info_win.resize(1, self.width, self.height-2, 0, stdscr, visible) - self.text_win.resize(self.height-3, text_width, 1, 0, stdscr, visible) - self.user_win.resize(self.height-3, self.width-text_width-1, 1, text_width+1, stdscr, visible) - self.input.resize(1, self.width, self.height-1, 0, stdscr, visible) - - def refresh(self, rooms): - """ - 'room' is the current one - """ - room = rooms[0] # get current room - self.text_win.refresh(room) - self.user_win.refresh(room.users) - self.topic_win.refresh(room.topic, room.jid) - self.info_win.refresh(rooms, room) - self.input.refresh() - - def do_command(self, key): - self.input.do_command(key) - self.input.refresh() + + + # TODO JidWindow + # elif jid: + # room = jid.split('/')[0] + # nick = '/'.join(jid.split('/')[1:]) + # topic = _('%(nick)s from room %(room)s' % {'nick': nick, 'room':room}) + # self.addnstr(0, 0, topic + " "*(self.width-len(topic)), self.width-1 + # , curses.color_pair(theme.COLOR_PRIVATE_ROOM_BAR)) + +class VerticalSeparator(Win): + """ + Just a one-column window, with just a line in it, that is + refreshed only on resize, but never on refresh, for efficiency + """ + def __init__(self, height, width, y, x, parent_win, visible): + Win.__init__(self, height, width, y, x, parent_win) + self.visible = visible + + def rewrite_line(self): + g_lock.acquire() + self.win.vline(0, 0, curses.ACS_VLINE, self.height, curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self.win.refresh() + g_lock.release() + + def resize(self, height, width, y, x, stdscr, visible): + self.visible = visible + self._resize(height, width, y, x, stdscr) + if not visible: + return + self.rewrite_line() + + def refresh(self): + if not self.visible: + return + self.rewrite_line() |