summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gui.py41
-rw-r--r--src/tab.py31
-rw-r--r--src/window.py145
3 files changed, 128 insertions, 89 deletions
diff --git a/src/gui.py b/src/gui.py
index f56828b2..9fc649d9 100644
--- a/src/gui.py
+++ b/src/gui.py
@@ -69,9 +69,6 @@ SHOW_NAME = {
resize_lock = threading.Lock()
-def doupdate():
- curses.doupdate()
-
class Gui(object):
"""
User interface using ncurses
@@ -82,6 +79,7 @@ class Gui(object):
self.xmpp = xmpp
default_tab = InfoTab(self.stdscr, "Info") if self.xmpp.anon\
else RosterInfoTab(self.stdscr)
+ default_tab.on_gain_focus()
self.tabs = [default_tab]
self.roster = Roster()
# a unique buffer used to store global informations
@@ -281,7 +279,7 @@ class Gui(object):
else:
self.on_user_change_status(room, user, from_nick, from_room, affiliation, role, show, status)
self.refresh_window()
- doupdate()
+ self.doupdate()
def on_user_join(self, room, from_nick, affiliation, show, status, role, jid):
"""
@@ -426,7 +424,7 @@ class Gui(object):
We received a Private Message (from someone in a Muc)
"""
jid = message['from']
- nick_from = jid.boundjid.resource
+ 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
@@ -436,7 +434,7 @@ class Gui(object):
body = message['body']
self.add_message_to_text_buffer(room, body, None, nick_from)
self.refresh_window()
- doupdate()
+ self.doupdate()
def focus_tab_named(self, tab_name):
for tab in self.tabs:
@@ -504,14 +502,15 @@ class Gui(object):
This is to avoid multiple unnecessary software resizes (this
can be heavy on resource on slow computers or networks)
"""
- with resize_lock:
- if self.resize_timer:
- # a recent terminal resize occured.
- # Cancel the programmed software resize
- self.resize_timer.cancel()
- # add the new timer
- self.resize_timer = threading.Timer(0.15, self.resize_window)
- self.resize_timer.start()
+ # with resize_lock:
+ # if self.resize_timer:
+ # # a recent terminal resize occured.
+ # # Cancel the programmed software resize
+ # self.resize_timer.cancel()
+ # # add the new timer
+ # self.resize_timer = threading.Timer(0.15, self.resize_window)
+ # self.resize_timer.start()
+ self.resize_window()
def resize_window(self):
"""
@@ -529,7 +528,7 @@ class Gui(object):
"""
self.refresh_window()
while True:
- doupdate()
+ self.doupdate()
char=read_char(self.stdscr)
# search for keyboard shortcut
if char in list(self.key_func.keys()):
@@ -567,8 +566,10 @@ class Gui(object):
"""
ncurses initialization
"""
- theme.init_colors()
+ curses.curs_set(1)
curses.noecho()
+ # curses.raw()
+ theme.init_colors()
stdscr.keypad(True)
def reset_curses(self):
@@ -586,7 +587,6 @@ class Gui(object):
"""
self.current_tab().set_color_state(theme.COLOR_TAB_CURRENT)
self.current_tab().refresh(self.tabs, self.information_buffer, self.roster)
- doupdate()
def open_new_room(self, room, nick, focus=True):
"""
@@ -778,7 +778,7 @@ class Gui(object):
date = date if delayed == True else None
self.add_message_to_text_buffer(room, body, date, nick_from)
self.refresh_window()
- doupdate()
+ self.doupdate()
def add_message_to_text_buffer(self, room, txt, time=None, nickname=None, colorized=False):
"""
@@ -1334,4 +1334,7 @@ class Gui(object):
self.add_message_to_text_buffer(self.current_tab().get_room(), line, None, self.current_tab().get_room().own_nick)
elif isinstance(self.current_tab(), MucTab):
muc.send_groupchat_message(self.xmpp, self.current_tab().get_name(), line)
- doupdate()
+ self.doupdate()
+
+ def doupdate(self):
+ curses.doupdate()
diff --git a/src/tab.py b/src/tab.py
index 8cb3ed0c..4acfab08 100644
--- a/src/tab.py
+++ b/src/tab.py
@@ -27,6 +27,7 @@ MIN_HEIGHT = 16
import window
import theme
+import curses
from roster import RosterGroup
from common import debug
@@ -118,6 +119,14 @@ class Tab(object):
"""
raise NotImplementedError
+ def just_before_refresh(self):
+ """
+ Method called just before the screen refresh.
+ Particularly useful to move the cursor at the
+ correct position.
+ """
+ raise NotImplementedError
+
class InfoTab(Tab):
"""
The information tab, used to display global informations
@@ -159,6 +168,7 @@ class InfoTab(Tab):
def on_gain_focus(self):
self.color_state = theme.COLOR_TAB_CURRENT
+ curses.curs_set(0)
def on_scroll_up(self):
pass
@@ -169,6 +179,9 @@ class InfoTab(Tab):
def on_info_win_size_changed(self, size, stdscr):
return
+ def just_before_refresh(self):
+ return
+
class MucTab(Tab):
"""
The tab containing a multi-user-chat room.
@@ -276,6 +289,7 @@ class MucTab(Tab):
def on_gain_focus(self):
self._room.set_color_state(theme.COLOR_TAB_CURRENT)
+ curses.curs_set(1)
def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1)
@@ -290,6 +304,9 @@ class MucTab(Tab):
self.info_header.resize(1, (self.width//10)*9, self.height-3-self.info_win_size, 0, stdscr, self.visible)
self.info_win.resize(self.info_win_size, (self.width//10)*9, self.height-2-self.info_win_size, 0, stdscr, self.visible)
+ def just_before_refresh(self):
+ self.input.move_cursor_to_pos()
+
class PrivateTab(Tab):
"""
The tab containg a private conversation (someone from a MUC)
@@ -341,6 +358,7 @@ class PrivateTab(Tab):
def on_gain_focus(self):
self._room.set_color_state(theme.COLOR_TAB_CURRENT)
+ curses.curs_set(1)
def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1)
@@ -357,6 +375,9 @@ class PrivateTab(Tab):
def get_room(self):
return self._room
+ def just_before_refresh(self):
+ return
+
class RosterInfoTab(Tab):
"""
A tab, splitted in two, containing the roster and infos
@@ -386,9 +407,9 @@ class RosterInfoTab(Tab):
self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible)
def refresh(self, tabs, informations, roster):
+ self.v_separator.refresh()
self.roster_win.refresh(roster)
self.contact_info_win.refresh(self.roster_win.get_selected_row())
- self.v_separator.refresh()
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
@@ -412,6 +433,7 @@ class RosterInfoTab(Tab):
def on_gain_focus(self):
self._color_state = theme.COLOR_TAB_CURRENT
+ curses.curs_set(0)
def add_message(self):
return False
@@ -429,6 +451,9 @@ class RosterInfoTab(Tab):
selected_row = self.roster_win.get_selected_row()
return selected_row
+ def just_before_refresh(self):
+ return
+
class ConversationTab(Tab):
"""
The tab containg a normal conversation (someone from our roster)
@@ -457,6 +482,7 @@ class ConversationTab(Tab):
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0])
self.input.refresh()
+ curses.curs_set(1)
def get_color_state(self):
if self._room.color_state == theme.COLOR_TAB_NORMAL or\
@@ -495,3 +521,6 @@ class ConversationTab(Tab):
def get_room(self):
return self._room
+
+ def just_before_refresh(self):
+ return
diff --git a/src/window.py b/src/window.py
index 19912ed1..64db72ae 100644
--- a/src/window.py
+++ b/src/window.py
@@ -47,28 +47,27 @@ class Win(object):
if not visible:
return
self.height, self.width, self.x, self.y = height, width, x, y
- try:
- self.win = curses.newwin(height, width, y, x)
- except:
- # When resizing in a too little height (less than 3 lines)
- # We don't need to resize the window, since this size
- # just makes no sense
- # Just don't crash when this happens.
- # (°> also, a penguin
- # //\
- # V_/_
- return
- self.win.leaveok(1)
-
- def refresh(self):
- self.win.noutrefresh()
+ # try:
+ self._win = curses.newwin(height, width, y, x)
+ # except:
+ # # When resizing in a too little height (less than 3 lines)
+ # # We don't need to resize the window, since this size
+ # # just makes no sense
+ # # Just don't crash when this happens.
+ # # (°> also, a penguin
+ # # //\
+ # # V_/_
+ # return
+
+ def _refresh(self):
+ self._win.noutrefresh()
def addnstr(self, *args):
"""
Safe call to addnstr
"""
try:
- self.win.addnstr(*args)
+ self._win.addnstr(*args)
except:
pass
@@ -77,7 +76,7 @@ class Win(object):
Safe call to addstr
"""
try:
- self.win.addstr(*args)
+ self._win.addstr(*args)
except:
pass
@@ -85,7 +84,7 @@ class Win(object):
"""
Write colored spaces until the end of line
"""
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
size = self.width-x
self.addnstr(' '*size, size, curses.color_pair(color))
@@ -111,7 +110,7 @@ class UserList(Win):
if not self.visible:
return
with g_lock:
- self.win.erase()
+ self._win.erase()
y = 0
for user in sorted(users):
if not user.role in self.color_role:
@@ -127,16 +126,16 @@ class UserList(Win):
y += 1
if y == self.height:
break
- self.win.refresh()
+ self._refresh()
def resize(self, height, width, y, x, stdscr, visible):
self.visible = visible
if not visible:
return
self._resize(height, width, y, x, stdscr, visible)
- 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))
+ 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):
@@ -150,14 +149,14 @@ class Topic(Win):
if not self.visible:
return
with g_lock:
- self.win.erase()
+ self._win.erase()
self.addstr(0, 0, topic[:self.width-1], curses.color_pair(theme.COLOR_TOPIC_BAR))
- (y, x) = self.win.getyx()
+ (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()
+ self._refresh()
class GlobalInfoBar(Win):
def __init__(self, height, width, y, x, parent_win, visible):
@@ -175,7 +174,7 @@ class GlobalInfoBar(Win):
return a.nb
comp = lambda x: x.nb
with g_lock:
- self.win.erase()
+ self._win.erase()
self.addstr(0, 0, "[", curses.color_pair(theme.COLOR_INFORMATION_BAR))
sorted_tabs = sorted(tabs, key=comp)
for tab in sorted_tabs:
@@ -185,13 +184,13 @@ class GlobalInfoBar(Win):
self.addstr("|", curses.color_pair(theme.COLOR_INFORMATION_BAR))
except: # end of line
break
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
self.addstr(y, x-1, '] ', curses.color_pair(theme.COLOR_INFORMATION_BAR))
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
remaining_size = self.width - x
self.addnstr(' '*remaining_size, remaining_size,
curses.color_pair(theme.COLOR_INFORMATION_BAR))
- self.win.refresh()
+ self._refresh()
class InfoWin(Win):
"""
@@ -227,11 +226,11 @@ class PrivateInfoWin(InfoWin):
if not self.visible:
return
with g_lock:
- self.win.erase()
+ self._win.erase()
self.write_room_name(room)
self.print_scroll_position(room)
self.finish_line(theme.COLOR_INFORMATION_BAR)
- self.win.refresh()
+ self._refresh()
def write_room_name(self, room):
(room_name, nick) = room.name.split('/', 1)
@@ -257,11 +256,11 @@ class ConversationInfoWin(InfoWin):
# from someone not in our roster. In this case, we display
# only the maximum information from the message we can get.
with g_lock:
- self.win.erase()
+ self._win.erase()
self.write_room_name(contact, room)
self.print_scroll_position(room)
self.finish_line(theme.COLOR_INFORMATION_BAR)
- self.win.refresh()
+ self._refresh()
def write_room_name(self, contact, room):
if not contact:
@@ -285,14 +284,14 @@ class MucInfoWin(InfoWin):
if not self.visible:
return
with g_lock:
- self.win.erase()
+ 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(theme.COLOR_INFORMATION_BAR)
- self.win.refresh()
+ self._refresh()
def write_room_name(self, room):
"""
@@ -414,7 +413,7 @@ class TextWin(Win):
if self.height <= 0:
return
with g_lock:
- self.win.erase()
+ self._win.erase()
lines = self.build_lines_from_messages(room.messages)
if room.pos + self.height > len(lines):
room.pos = len(lines) - self.height
@@ -426,7 +425,7 @@ class TextWin(Win):
lines = lines[-self.height:]
y = 0
for line in lines:
- self.win.move(y, 0)
+ self._win.move(y, 0)
if line == None:
self.write_line_separator()
y += 1
@@ -437,14 +436,14 @@ class TextWin(Win):
self.write_nickname(line.nickname, line.nickname_color)
self.write_text(y, line.text_offset, line.text, line.text_color, line.colorized)
y += 1
- self.win.refresh()
+ self._refresh()
def write_line_separator(self):
"""
"""
- self.win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR))
+ self._win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR))
self.addnstr('- '*(self.width//2), self.width)
- self.win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR))
+ self._win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR))
def write_text(self, y, x, txt, color, colorized):
"""
@@ -453,10 +452,10 @@ class TextWin(Win):
txt = txt
if not colorized:
if color:
- self.win.attron(curses.color_pair(color))
+ self._win.attron(curses.color_pair(color))
self.addstr(y, x, txt)
if color:
- self.win.attroff(curses.color_pair(color))
+ self._win.attroff(curses.color_pair(color))
else: # Special messages like join or quit
special_words = {
@@ -485,7 +484,7 @@ class TextWin(Win):
self.addstr(word[1:-1], curses.color_pair(theme.COLOR_BRACKETED_WORD))
else:
self.addstr(word, curses.color_pair(color))
- self.win.addch(' ')
+ self._win.addch(' ')
def write_nickname(self, nickname, color):
"""
@@ -493,10 +492,10 @@ class TextWin(Win):
and return the number of written characters
"""
if color:
- self.win.attron(curses.color_pair(color))
+ self._win.attron(curses.color_pair(color))
self.addstr(nickname)
if color:
- self.win.attroff(curses.color_pair(color))
+ self._win.attroff(curses.color_pair(color))
self.addstr("> ")
def write_time(self, time):
@@ -569,7 +568,7 @@ class Input(Win):
if not visible:
return
self._resize(height, width, y, x, stdscr, visible)
- self.win.clear()
+ self._win.erase()
self.addnstr(0, 0, self.text, self.width-1)
def jump_word_left(self):
@@ -657,7 +656,7 @@ class Input(Win):
"""
if not len(self.history):
return
- self.win.erase()
+ self._win.erase()
if self.histo_pos >= 0:
self.histo_pos -= 1
self.text = self.history[self.histo_pos+1]
@@ -709,7 +708,7 @@ class Input(Win):
Move the cursor one char to the left
"""
self.reset_completion()
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
if self.pos == self.width-1 and self.line_pos > 0:
self.line_pos -= 1
elif self.pos >= 1:
@@ -721,7 +720,7 @@ class Input(Win):
Move the cursor one char to the right
"""
self.reset_completion()
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
if self.pos == self.width-1:
if self.line_pos + self.width-1 < len(self.text):
self.line_pos += 1
@@ -734,7 +733,7 @@ class Input(Win):
Delete the char just before the cursor
"""
self.reset_completion()
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
if self.pos == 0:
return
self.text = self.text[:self.pos+self.line_pos-1]+self.text[self.pos+self.line_pos:]
@@ -771,7 +770,7 @@ class Input(Win):
#if " " in self.text.strip() and (not self.last_completion or ' ' in self.last_completion):
else:
after = " " # don't put the "," if it's not the begining of the sentence
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
if not self.last_completion:
# begin is the begining of the nick we want to complete
if self.text.strip() != '':
@@ -804,7 +803,7 @@ class Input(Win):
after = " " # don't put the "," if it's not the begining of the sentence
else:
after = config.get('after_completion', ',')+" "
- (y, x) = self.win.getyx()
+ (y, x) = self._win.getyx()
if self.text != '':
begin = self.text.split()[-1].lower()
else:
@@ -848,7 +847,7 @@ class Input(Win):
# 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()
+ (y, x) = self._win.getyx()
if x == self.width-1:
self.line_pos += 1
else:
@@ -878,7 +877,7 @@ class Input(Win):
self.clear_text()
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
# self.win.chgat(0, self.pos, 1, curses.A_REVERSE)
- self.win.refresh()
+ self._refresh()
def refresh(self):
if not self.visible:
@@ -886,7 +885,15 @@ class Input(Win):
self.rewrite_text()
def clear_text(self):
- self.win.erase()
+ self._win.erase()
+
+ def move_cursor_to_pos(self):
+ """
+ move the cursor at the current pos
+ """
+ from common import debug
+ debug('ALLO')
+ self._win.move(0, self.pos)
class VerticalSeparator(Win):
"""
@@ -899,8 +906,8 @@ class VerticalSeparator(Win):
def rewrite_line(self):
with g_lock:
- self.win.vline(0, 0, curses.ACS_VLINE, self.height, curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR))
- self.win.refresh()
+ self._win.vline(0, 0, curses.ACS_VLINE, self.height, curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR))
+ self._refresh()
def resize(self, height, width, y, x, stdscr, visible):
self.visible = visible
@@ -962,7 +969,7 @@ class RosterWin(Win):
return
with g_lock:
self.roster_len = len(roster)
- self.win.erase()
+ self._win.erase()
self.draw_roster_information(roster)
y = 1
for group in roster.get_groups():
@@ -987,19 +994,19 @@ class RosterWin(Win):
self.draw_plus(1)
if self.start_pos + self.height-2 < self.roster_len:
self.draw_plus(self.height-1)
- self.win.refresh()
+ self._refresh()
def draw_plus(self, y):
"""
Draw the indicator that shows that
the list is longer that what is displayed
"""
- self.win.addstr(y, self.width-5, '++++', curses.color_pair(42))
+ self.addstr(y, self.width-5, '++++', curses.color_pair(42))
def draw_roster_information(self, roster):
"""
"""
- self.win.addstr('%s contacts' % roster.get_contact_len(), curses.color_pair(12))
+ self.addstr('%s contacts' % roster.get_contact_len(), curses.color_pair(12))
self.finish_line(12)
def draw_group(self, y, group, colored):
@@ -1007,14 +1014,14 @@ class RosterWin(Win):
Draw a groupname on a line
"""
if colored:
- self.win.attron(curses.color_pair(14))
+ self._win.attron(curses.color_pair(14))
if group.folded:
self.addstr(y, 0, '[+] ')
else:
self.addstr(y, 0, '[-] ')
self.addstr(y, 4, group.name)
if colored:
- self.win.attroff(curses.color_pair(14))
+ self._win.attroff(curses.color_pair(14))
def draw_contact_line(self, y, contact, colored):
"""
@@ -1028,11 +1035,11 @@ class RosterWin(Win):
contact.get_jid().bare)
else:
display_name = '%s' % (contact.get_jid().bare,)
- self.win.addstr(y, 1, " ", curses.color_pair(color))
+ self.addstr(y, 1, " ", curses.color_pair(color))
if colored:
- self.win.addstr(y, 4, display_name, curses.color_pair(14))
+ self.addstr(y, 4, display_name, curses.color_pair(14))
else:
- self.win.addstr(y, 4, display_name)
+ self.addstr(y, 4, display_name)
def get_selected_row(self):
return self.selected_row
@@ -1065,9 +1072,9 @@ class ContactInfoWin(Win):
if not self.visible:
return
with g_lock:
- self.win.erase()
+ 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)
- self.win.refresh()
+ self._refresh()