Inputs are more modulable (they also have a common history and clipboard). Search is now fully functional, and some other stuff
This commit is contained in:
parent
8bdab49145
commit
de7c007a22
3 changed files with 257 additions and 204 deletions
42
src/core.py
42
src/core.py
|
@ -155,7 +155,6 @@ class Core(object):
|
|||
self.xmpp.add_event_handler("roster_update", self.on_roster_update)
|
||||
self.xmpp.add_event_handler("changed_status", self.on_presence)
|
||||
|
||||
# self.__debug_fill_roster()
|
||||
|
||||
def grow_information_win(self):
|
||||
"""
|
||||
|
@ -486,13 +485,10 @@ class Core(object):
|
|||
"""
|
||||
"""
|
||||
jid = presence['from']
|
||||
log.debug('Presence Received: %s\n' % presence)
|
||||
contact = roster.get_contact_by_jid(jid.bare)
|
||||
log.debug('Contact: %s\n' % contact)
|
||||
if not contact:
|
||||
return
|
||||
resource = contact.get_resource_by_fulljid(jid.full)
|
||||
log.debug('Resource: %s\n' % resource)
|
||||
if not resource:
|
||||
return
|
||||
status = presence['type']
|
||||
|
@ -504,34 +500,6 @@ class Core(object):
|
|||
if isinstance(self.current_tab(), RosterInfoTab):
|
||||
self.refresh_window()
|
||||
|
||||
def __debug_fill_roster(self):
|
||||
for i in range(10):
|
||||
jid = 'contact%s@fion%s.org'%(i,i)
|
||||
contact = Contact(jid)
|
||||
contact.set_ask('wat')
|
||||
contact.set_subscription('both')
|
||||
roster.add_contact(contact, jid)
|
||||
contact.set_name('%s %s fion'%(i,i))
|
||||
roster.edit_groups_of_contact(contact, ['hello'])
|
||||
for i in range(10):
|
||||
jid = 'test%s@bernard%s.org'%(i,i)
|
||||
contact = Contact(jid)
|
||||
contact.set_ask('wat')
|
||||
contact.set_subscription('both')
|
||||
roster.add_contact(contact, jid)
|
||||
contact.set_name('%s test'%(i))
|
||||
roster.edit_groups_of_contact(contact, ['hello'])
|
||||
for i in range(10):
|
||||
jid = 'pouet@top%s.org'%(i)
|
||||
contact = Contact(jid)
|
||||
contact.set_ask('wat')
|
||||
contact.set_subscription('both')
|
||||
roster.add_contact(contact, jid)
|
||||
contact.set_name('%s oula'%(i))
|
||||
roster.edit_groups_of_contact(contact, ['hello'])
|
||||
if isinstance(self.current_tab(), RosterInfoTab):
|
||||
self.refresh_window()
|
||||
|
||||
def on_roster_update(self, iq):
|
||||
"""
|
||||
A subscription changed, or we received a roster item
|
||||
|
@ -1374,20 +1342,13 @@ class Core(object):
|
|||
if not key:
|
||||
return
|
||||
res = self.current_tab().on_input(key)
|
||||
if not res:
|
||||
return
|
||||
if key in ('^J', '\n') and isinstance(res, str):
|
||||
self.execute(res)
|
||||
else :
|
||||
# we did "enter" with an empty input in the roster
|
||||
self.on_roster_enter_key(res)
|
||||
self.refresh_window()
|
||||
|
||||
def on_roster_enter_key(self, roster_row):
|
||||
"""
|
||||
when enter is pressed on the roster window
|
||||
"""
|
||||
if isinstance(roster_row, Contact):
|
||||
# roster_row.toggle_folded()
|
||||
if not self.get_conversation_by_jid(roster_row.get_bare_jid()):
|
||||
self.open_conversation_window(roster_row.get_bare_jid())
|
||||
else:
|
||||
|
@ -1427,7 +1388,6 @@ class Core(object):
|
|||
muc.send_private_message(self.xmpp, self.current_tab().get_name(), line)
|
||||
if isinstance(self.current_tab(), PrivateTab) or\
|
||||
isinstance(self.current_tab(), ConversationTab):
|
||||
log.debug('ALLO ICI\n\n')
|
||||
self.add_message_to_text_buffer(self.current_tab().get_room(), line, None, self.own_nick)
|
||||
elif isinstance(self.current_tab(), MucTab):
|
||||
muc.send_groupchat_message(self.xmpp, self.current_tab().get_name(), line)
|
||||
|
|
119
src/tab.py
119
src/tab.py
|
@ -216,7 +216,7 @@ class MucTab(Tab):
|
|||
self.info_header = window.MucInfoWin(1, (self.width//10)*9, self.height-3-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.info_win = window.TextWin(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible)
|
||||
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
|
||||
def resize(self, stdscr):
|
||||
"""
|
||||
|
@ -244,14 +244,17 @@ class MucTab(Tab):
|
|||
self.input.refresh()
|
||||
|
||||
def on_input(self, key):
|
||||
self.key_func = {
|
||||
key_func = {
|
||||
"\t": self.completion,
|
||||
"^I": self.completion,
|
||||
"M-i": self.completion,
|
||||
"KEY_BTAB": self.last_words_completion,
|
||||
"^J": self.on_enter,
|
||||
"^M": self.on_enter,
|
||||
"\n": self.on_enter
|
||||
}
|
||||
if key in self.key_func:
|
||||
return self.key_func[key]()
|
||||
if key in key_func:
|
||||
return key_func[key]()
|
||||
return self.input.do_command(key)
|
||||
|
||||
def completion(self):
|
||||
|
@ -271,7 +274,6 @@ class MucTab(Tab):
|
|||
for msg in self._room.messages[:-40:-1]:
|
||||
if not msg:
|
||||
continue
|
||||
log.debug('line: %s\n'%msg)
|
||||
for char in char_we_dont_want:
|
||||
msg.txt = msg.txt.replace(char, ' ')
|
||||
for word in msg.txt.split():
|
||||
|
@ -279,6 +281,12 @@ class MucTab(Tab):
|
|||
words.append(word)
|
||||
self.input.auto_completion(words, False)
|
||||
|
||||
def on_enter(self):
|
||||
"""
|
||||
When enter is pressed, send the message to the Muc
|
||||
"""
|
||||
self.core.execute(self.input.key_enter())
|
||||
|
||||
def get_color_state(self):
|
||||
return self._room.color_state
|
||||
|
||||
|
@ -313,7 +321,7 @@ class MucTab(Tab):
|
|||
self.info_win.resize(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
|
||||
def just_before_refresh(self):
|
||||
self.input.move_cursor_to_pos()
|
||||
return
|
||||
|
||||
def on_close(self):
|
||||
return
|
||||
|
@ -329,7 +337,7 @@ class PrivateTab(Tab):
|
|||
self.info_header = window.PrivateInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.info_win = window.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible)
|
||||
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
|
||||
def resize(self, stdscr):
|
||||
Tab.resize(self, stdscr)
|
||||
|
@ -359,8 +367,21 @@ class PrivateTab(Tab):
|
|||
return self._room.name
|
||||
|
||||
def on_input(self, key):
|
||||
key_func = {
|
||||
"^J": self.on_enter,
|
||||
"^M": self.on_enter,
|
||||
"\n": self.on_enter
|
||||
}
|
||||
if key in key_func:
|
||||
return key_func[key]()
|
||||
return self.input.do_command(key)
|
||||
|
||||
def on_enter(self):
|
||||
"""
|
||||
When enter is pressed, send the message to the Muc
|
||||
"""
|
||||
self.core.execute(self.input.key_enter())
|
||||
|
||||
def on_lose_focus(self):
|
||||
self._room.set_color_state(theme.COLOR_TAB_NORMAL)
|
||||
self._room.remove_line_separator()
|
||||
|
@ -395,17 +416,6 @@ class RosterInfoTab(Tab):
|
|||
A tab, splitted in two, containing the roster and infos
|
||||
"""
|
||||
def __init__(self, stdscr, core):
|
||||
self.single_key_commands = {
|
||||
"^J": self.on_enter,
|
||||
"^M": self.on_enter,
|
||||
"\n": self.on_enter,
|
||||
' ': self.on_space,
|
||||
"/": self.on_slash,
|
||||
"KEY_UP": self.move_cursor_up,
|
||||
"KEY_DOWN": self.move_cursor_down,
|
||||
"o": self.toggle_offline_show,
|
||||
"^F": self.start_search,
|
||||
}
|
||||
Tab.__init__(self, stdscr, core)
|
||||
self.name = "Roster"
|
||||
roster_width = self.width//2
|
||||
|
@ -415,7 +425,8 @@ class RosterInfoTab(Tab):
|
|||
self.info_win = window.TextWin(self.height-2, info_width, 0, roster_width+1, stdscr, self.visible)
|
||||
self.roster_win = window.RosterWin(self.height-2-3, roster_width, 0, 0, stdscr, self.visible)
|
||||
self.contact_info_win = window.ContactInfoWin(3, roster_width, self.height-2-3, 0, stdscr, self.visible)
|
||||
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible, False, "Enter commands with “/”. “o”: toggle offline show")
|
||||
self.default_help_message = window.HelpText(1, self.width, self.height-1, 0, stdscr, self.visible, "Enter commands with “/”. “o”: toggle offline show")
|
||||
self.input = self.default_help_message
|
||||
self.set_color_state(theme.COLOR_TAB_NORMAL)
|
||||
|
||||
def resize(self, stdscr):
|
||||
|
@ -447,19 +458,22 @@ class RosterInfoTab(Tab):
|
|||
self._color_state = color
|
||||
|
||||
def on_input(self, key):
|
||||
if self.input.input_mode:
|
||||
ret = self.input.do_command(key)
|
||||
roster._contact_filter = (jid_and_name_match, self.input.text)
|
||||
# if the input is empty, go back to command mode
|
||||
if self.input.is_empty() and not self.input._instructions:
|
||||
self.input.input_mode = False
|
||||
curses.curs_set(0)
|
||||
self.input.rewrite_text()
|
||||
if self.input._instructions:
|
||||
return True
|
||||
return ret
|
||||
if key in self.single_key_commands:
|
||||
return self.single_key_commands[key]()
|
||||
key_commands = {
|
||||
"^J": self.on_enter,
|
||||
"^M": self.on_enter,
|
||||
"\n": self.on_enter,
|
||||
' ': self.on_space,
|
||||
"/": self.on_slash,
|
||||
"KEY_UP": self.move_cursor_up,
|
||||
"KEY_DOWN": self.move_cursor_down,
|
||||
"o": self.toggle_offline_show,
|
||||
"^F": self.start_search,
|
||||
}
|
||||
res = self.input.do_command(key)
|
||||
if res:
|
||||
return res
|
||||
if key in key_commands:
|
||||
return key_commands[key]()
|
||||
|
||||
def toggle_offline_show(self):
|
||||
"""
|
||||
|
@ -476,9 +490,19 @@ class RosterInfoTab(Tab):
|
|||
"""
|
||||
'/' is pressed, we enter "input mode"
|
||||
"""
|
||||
self.input.input_mode = True
|
||||
curses.curs_set(1)
|
||||
self.on_input("/") # we add the slash
|
||||
self.input = window.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "", self.reset_help_message, self.execute_slash_command)
|
||||
self.input.do_command("/") # we add the slash
|
||||
|
||||
def reset_help_message(self, _=None):
|
||||
curses.curs_set(0)
|
||||
self.input = self.default_help_message
|
||||
return True
|
||||
|
||||
def execute_slash_command(self, txt):
|
||||
if txt.startswith('/'):
|
||||
self.core.execute(txt)
|
||||
return self.reset_help_message()
|
||||
|
||||
def on_lose_focus(self):
|
||||
self._color_state = theme.COLOR_TAB_NORMAL
|
||||
|
@ -518,6 +542,7 @@ class RosterInfoTab(Tab):
|
|||
|
||||
def on_enter(self):
|
||||
selected_row = self.roster_win.get_selected_row()
|
||||
self.core.on_roster_enter_key(selected_row)
|
||||
return selected_row
|
||||
|
||||
def start_search(self):
|
||||
|
@ -526,15 +551,17 @@ class RosterInfoTab(Tab):
|
|||
in it.
|
||||
"""
|
||||
curses.curs_set(1)
|
||||
roster._contact_filter = (jid_and_name_match, self.input.text)
|
||||
self.input.input_mode = True
|
||||
self.input.start_command(self.on_search_terminate, self.on_search_terminate, '[search]')
|
||||
self.input = window.CommandInput(1, self.width, self.height-1, 0, self.default_help_message, self.visible, "[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter)
|
||||
return True
|
||||
|
||||
def set_roster_filter(self, txt):
|
||||
roster._contact_filter = (jid_and_name_match, txt)
|
||||
|
||||
def on_search_terminate(self, txt):
|
||||
curses.curs_set(0)
|
||||
roster._contact_filter = None
|
||||
return True
|
||||
self.reset_help_message()
|
||||
return False
|
||||
|
||||
def just_before_refresh(self):
|
||||
return
|
||||
|
@ -556,7 +583,7 @@ class ConversationTab(Tab):
|
|||
self.info_header = window.ConversationInfoWin(1, self.width, self.height-3-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.info_win = window.TextWin(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, stdscr, self.visible)
|
||||
self.tab_win = window.GlobalInfoBar(1, self.width, self.height-2, 0, stdscr, self.visible)
|
||||
self.input = window.Input(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
self.input = window.MessageInput(1, self.width, self.height-1, 0, stdscr, self.visible)
|
||||
|
||||
def resize(self, stdscr):
|
||||
Tab.resize(self, stdscr)
|
||||
|
@ -588,8 +615,22 @@ class ConversationTab(Tab):
|
|||
return self._name
|
||||
|
||||
def on_input(self, key):
|
||||
key_func = {
|
||||
"^J": self.on_enter,
|
||||
"^M": self.on_enter,
|
||||
"\n": self.on_enter
|
||||
}
|
||||
if key in key_func:
|
||||
return key_func[key]()
|
||||
return self.input.do_command(key)
|
||||
|
||||
|
||||
def on_enter(self):
|
||||
"""
|
||||
When enter is pressed, send the message to the Muc
|
||||
"""
|
||||
self.core.execute(self.input.key_enter())
|
||||
|
||||
def on_lose_focus(self):
|
||||
self.set_color_state(theme.COLOR_TAB_NORMAL)
|
||||
self._text_buffer.remove_line_separator()
|
||||
|
|
300
src/window.py
300
src/window.py
|
@ -598,25 +598,54 @@ class TextWin(Win):
|
|||
self.visible = visible
|
||||
self._resize(height, width, y, x, stdscr, visible)
|
||||
|
||||
class HelpText(Win):
|
||||
"""
|
||||
A buffer just displaying a read-only message.
|
||||
Usually used to replace an Input when the tab is in
|
||||
command mode.
|
||||
"""
|
||||
def __init__(self, height, width, y, x, parent_win, visible, text=''):
|
||||
self.visible = visible
|
||||
Win.__init__(self, height, width, y, x, parent_win)
|
||||
self.txt = text
|
||||
|
||||
def resize(self, height, width, y, x, stdscr, visible):
|
||||
self._resize(height, width, y, x, stdscr, visible)
|
||||
|
||||
def refresh(self):
|
||||
if not self.visible:
|
||||
return
|
||||
with g_lock:
|
||||
self._win.erase()
|
||||
self.addstr(0, 0, self.txt[:self.width-1], curses.color_pair(theme.COLOR_INFORMATION_BAR))
|
||||
self.finish_line(theme.COLOR_INFORMATION_BAR)
|
||||
self._refresh()
|
||||
|
||||
def do_command(self, key):
|
||||
return False
|
||||
|
||||
class Input(Win):
|
||||
"""
|
||||
The line where text is entered
|
||||
It can be in input mode or in commmand mode.
|
||||
Command mode means that single_key_commands can be entered, handled
|
||||
by the Tab object, while this input just displays an help text.
|
||||
The simplest Input possible, provides just a way to edit a single line
|
||||
of text. It also has a clipboard, common to all Inputs.
|
||||
Doesn't have any history.
|
||||
It doesn't do anything when enter is pressed either.
|
||||
This should be herited for all kinds of Inputs, for example MessageInput
|
||||
or the little inputs in dataforms, etc, adding specific features (completion etc)
|
||||
It features two kinds of completion, but they have to be called from outside (the Tab),
|
||||
passing the list of items that can be used to complete. The completion can be used
|
||||
in a very flexible way.
|
||||
"""
|
||||
def __init__(self, height, width, y, x, stdscr, visible, input_mode=True, help_text=''):
|
||||
clipboard = '' # A common clipboard for all the inputs, this makes
|
||||
# it easy cut and paste text between various input
|
||||
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,
|
||||
|
@ -629,69 +658,13 @@ class Input(Win):
|
|||
'M-f': self.jump_word_right,
|
||||
"KEY_BACKSPACE": self.key_backspace,
|
||||
'^?': self.key_backspace,
|
||||
'^J': self.on_enter,
|
||||
'\n': self.on_enter,
|
||||
}
|
||||
|
||||
Win.__init__(self, height, width, y, x, stdscr)
|
||||
self.input_mode = input_mode
|
||||
self.help_text = help_text # the text displayed in command_mode
|
||||
self.visible = visible
|
||||
self.history = []
|
||||
self.text = ''
|
||||
self.clipboard = None
|
||||
self.pos = 0 # cursor position
|
||||
self.line_pos = 0 # position (in self.text) of
|
||||
# the first char to display on the screen
|
||||
self.histo_pos = 0
|
||||
self.hit_list = [] # current possible completion (normal)
|
||||
self.last_completion = None # Contains the last nickname completed,
|
||||
# if last key was a tab
|
||||
# These are used when the user is entering a comand
|
||||
self._on_cancel = None
|
||||
self._on_terminate = None
|
||||
self._instructions = "" # a string displayed before the input, read-only
|
||||
|
||||
def on_enter(self):
|
||||
"""
|
||||
Called when Enter is pressed
|
||||
"""
|
||||
if not self._instructions:
|
||||
return self.get_text()
|
||||
self.on_terminate()
|
||||
return True
|
||||
|
||||
def start_command(self, on_cancel, on_terminate, instructions):
|
||||
"""
|
||||
Start a command, with an instruction, and two callbacks.
|
||||
on_terminate is called when the command is successfull
|
||||
on_cancel is called when the command is canceled
|
||||
"""
|
||||
assert isinstance(instructions, str)
|
||||
self._on_cancel = on_cancel
|
||||
self._on_terminate = on_terminate
|
||||
self._instructions = instructions
|
||||
|
||||
def cancel_command(self):
|
||||
"""
|
||||
Call it to cancel the current command
|
||||
"""
|
||||
self._on_cancel()
|
||||
self._on_cancel = None
|
||||
self._on_terminate = None
|
||||
self._instructions = ''
|
||||
return self.get_text()
|
||||
|
||||
def on_terminate(self):
|
||||
"""
|
||||
Call it to terminate the command. Returns the content of the input
|
||||
"""
|
||||
txt = self.get_text()
|
||||
self._on_terminate(txt)
|
||||
self._on_terminate = None
|
||||
self._on_cancel = None
|
||||
self._instructions = ''
|
||||
return txt
|
||||
|
||||
def is_empty(self):
|
||||
return len(self.text) == 0
|
||||
|
@ -716,6 +689,7 @@ class Input(Win):
|
|||
diff = self.pos+self.line_pos-previous_space
|
||||
for i in range(diff):
|
||||
self.key_left()
|
||||
return True
|
||||
|
||||
def jump_word_right(self):
|
||||
"""
|
||||
|
@ -729,6 +703,7 @@ class Input(Win):
|
|||
diff = next_space - (self.pos+self.line_pos)
|
||||
for i in range(diff):
|
||||
self.key_right()
|
||||
return True
|
||||
|
||||
def delete_word(self):
|
||||
"""
|
||||
|
@ -743,6 +718,7 @@ class Input(Win):
|
|||
for i in range(diff):
|
||||
self.key_backspace(False)
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def delete_end_of_line(self):
|
||||
"""
|
||||
|
@ -750,9 +726,10 @@ class Input(Win):
|
|||
"""
|
||||
if len(self.text) == self.pos+self.line_pos:
|
||||
return # nothing to cut
|
||||
self.clipboard = self.text[self.pos+self.line_pos:]
|
||||
Input.clipboard = self.text[self.pos+self.line_pos:]
|
||||
self.text = self.text[:self.pos+self.line_pos]
|
||||
self.key_end()
|
||||
return True
|
||||
|
||||
def delete_begining_of_line(self):
|
||||
"""
|
||||
|
@ -760,18 +737,20 @@ class Input(Win):
|
|||
"""
|
||||
if self.pos+self.line_pos == 0:
|
||||
return
|
||||
self.clipboard = self.text[:self.pos+self.line_pos]
|
||||
Input.clipboard = self.text[:self.pos+self.line_pos]
|
||||
self.text = self.text[self.pos+self.line_pos:]
|
||||
self.key_home()
|
||||
return True
|
||||
|
||||
def paste_clipboard(self):
|
||||
"""
|
||||
Insert what is in the clipboard at the cursor position
|
||||
"""
|
||||
if not self.clipboard or len(self.clipboard) == 0:
|
||||
if not Input.clipboard or len(Input.clipboard) == 0:
|
||||
return
|
||||
for letter in self.clipboard:
|
||||
for letter in Input.clipboard:
|
||||
self.do_command(letter)
|
||||
return True
|
||||
|
||||
def key_dc(self):
|
||||
"""
|
||||
|
@ -782,36 +761,7 @@ class Input(Win):
|
|||
return # end of line, nothing to delete
|
||||
self.text = self.text[:self.pos+self.line_pos]+self.text[self.pos+self.line_pos+1:]
|
||||
self.rewrite_text()
|
||||
|
||||
def key_up(self):
|
||||
"""
|
||||
Get the previous line in the history
|
||||
"""
|
||||
if not len(self.history):
|
||||
return
|
||||
self._win.erase()
|
||||
if self.histo_pos >= 0:
|
||||
self.histo_pos -= 1
|
||||
self.text = self.history[self.histo_pos+1]
|
||||
self.key_end()
|
||||
|
||||
def key_down(self):
|
||||
"""
|
||||
Get the next line in the history
|
||||
"""
|
||||
if not len(self.history):
|
||||
return
|
||||
self.reset_completion()
|
||||
if self.histo_pos < len(self.history)-1:
|
||||
self.histo_pos += 1
|
||||
self.text = self.history[self.histo_pos]
|
||||
self.key_end()
|
||||
else:
|
||||
self.histo_pos = len(self.history)-1
|
||||
self.text = ''
|
||||
self.pos = 0
|
||||
self.line_pos = 0
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def key_home(self):
|
||||
"""
|
||||
|
@ -821,6 +771,7 @@ class Input(Win):
|
|||
self.pos = 0
|
||||
self.line_pos = 0
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def key_end(self, reset=False):
|
||||
"""
|
||||
|
@ -835,6 +786,7 @@ class Input(Win):
|
|||
self.pos = len(self.text)
|
||||
self.line_pos = 0
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def key_left(self):
|
||||
"""
|
||||
|
@ -847,6 +799,7 @@ class Input(Win):
|
|||
elif self.pos >= 1:
|
||||
self.pos -= 1
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def key_right(self):
|
||||
"""
|
||||
|
@ -860,6 +813,7 @@ class Input(Win):
|
|||
elif self.pos < len(self.text):
|
||||
self.pos += 1
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def key_backspace(self, reset=True):
|
||||
"""
|
||||
|
@ -873,6 +827,7 @@ class Input(Win):
|
|||
self.key_left()
|
||||
if reset:
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def auto_completion(self, user_list, add_after=True):
|
||||
"""
|
||||
|
@ -885,6 +840,7 @@ class Input(Win):
|
|||
self.shell_completion(user_list, add_after)
|
||||
else:
|
||||
self.normal_completion(user_list, add_after)
|
||||
return True
|
||||
|
||||
def reset_completion(self):
|
||||
"""
|
||||
|
@ -977,7 +933,7 @@ class Input(Win):
|
|||
if key in self.key_func:
|
||||
return self.key_func[key]()
|
||||
if not key or len(key) > 1:
|
||||
return # ignore non-handled keyboard shortcuts
|
||||
return False # 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()
|
||||
|
@ -987,38 +943,22 @@ class Input(Win):
|
|||
self.pos += 1
|
||||
if reset:
|
||||
self.rewrite_text()
|
||||
return True
|
||||
|
||||
def get_text(self):
|
||||
"""
|
||||
Clear the input and return the text entered so far
|
||||
"""
|
||||
txt = self.text
|
||||
self.text = ''
|
||||
self.pos = 0
|
||||
self.line_pos = 0
|
||||
if len(txt) != 0:
|
||||
self.history.append(txt)
|
||||
self.histo_pos = len(self.history)-1
|
||||
self.rewrite_text()
|
||||
return txt
|
||||
return self.text
|
||||
|
||||
def rewrite_text(self):
|
||||
"""
|
||||
Refresh the line onscreen, from the pos and pos_line
|
||||
"""
|
||||
with g_lock:
|
||||
self.clear_text()
|
||||
if self.input_mode:
|
||||
self.addstr(self._instructions, curses.color_pair(theme.COLOR_INFORMATION_BAR))
|
||||
if self._instructions:
|
||||
self.addstr(' ')
|
||||
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
|
||||
else:
|
||||
self.addstr(self.help_text, curses.color_pair(theme.COLOR_INFORMATION_BAR))
|
||||
self.finish_line(theme.COLOR_INFORMATION_BAR)
|
||||
self._win.erase()
|
||||
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
|
||||
cursor_pos = self.pos
|
||||
if self._instructions:
|
||||
cursor_pos += 1 + len(self._instructions)
|
||||
self.addstr(0, cursor_pos, '') # WTF, this works but .move() doesn't…
|
||||
self._refresh()
|
||||
|
||||
|
@ -1028,13 +968,125 @@ class Input(Win):
|
|||
self.rewrite_text()
|
||||
|
||||
def clear_text(self):
|
||||
self._win.erase()
|
||||
self.text = ''
|
||||
self.pos = 0
|
||||
self.line_pos = 0
|
||||
self.rewrite_text()
|
||||
|
||||
def move_cursor_to_pos(self):
|
||||
class MessageInput(Input):
|
||||
"""
|
||||
The input featuring history and that is being used in
|
||||
Conversation, Muc and Private tabs
|
||||
"""
|
||||
history = list() # The history is common to all MessageInput
|
||||
|
||||
def __init__(self, height, width, y, x, stdscr, visible):
|
||||
Input.__init__(self, height, width, y, x, stdscr, visible)
|
||||
self.histo_pos = 0
|
||||
self.key_func["KEY_UP"] = self.key_up
|
||||
self.key_func["M-A"] = self.key_up
|
||||
self.key_func["KEY_DOWN"] = self.key_down
|
||||
self.key_func["M-B"] = self.key_down
|
||||
|
||||
def key_up(self):
|
||||
"""
|
||||
move the cursor at the current pos
|
||||
Get the previous line in the history
|
||||
"""
|
||||
return
|
||||
if not len(MessageInput.history):
|
||||
return
|
||||
self.reset_completion()
|
||||
self._win.erase()
|
||||
if self.histo_pos >= 0:
|
||||
self.histo_pos -= 1
|
||||
self.text = MessageInput.history[self.histo_pos+1]
|
||||
self.key_end()
|
||||
|
||||
def key_down(self):
|
||||
"""
|
||||
Get the next line in the history
|
||||
"""
|
||||
if not len(MessageInput.history):
|
||||
return
|
||||
self.reset_completion()
|
||||
if self.histo_pos < len(MessageInput.history)-1:
|
||||
self.histo_pos += 1
|
||||
self.text = self.history[self.histo_pos]
|
||||
self.key_end()
|
||||
else:
|
||||
self.histo_pos = len(MessageInput.history)-1
|
||||
self.text = ''
|
||||
self.pos = 0
|
||||
self.line_pos = 0
|
||||
self.rewrite_text()
|
||||
|
||||
def key_enter(self):
|
||||
txt = self.get_text()
|
||||
if len(txt) != 0:
|
||||
self.history.append(txt)
|
||||
self.histo_pos = len(self.history)-1
|
||||
self.clear_text()
|
||||
return txt
|
||||
|
||||
class CommandInput(Input):
|
||||
"""
|
||||
An input with an help message in the left, with three given callbacks:
|
||||
one when when successfully 'execute' the command and when we abort it.
|
||||
The last callback is optional and is called on any input key
|
||||
This input is used, for example, in the RosterTab when, to replace the
|
||||
HelpMessage when a command is started
|
||||
The on_input callback
|
||||
"""
|
||||
def __init__(self, height, width, y, x, stdscr, visible,
|
||||
help_message, on_abort, on_success, on_input=None):
|
||||
Input.__init__(self, height, width, y, x, stdscr, visible)
|
||||
self.on_abort = on_abort
|
||||
self.on_success = on_success
|
||||
self.on_input = on_input
|
||||
self.help_message = help_message
|
||||
self.key_func['^J'] = self.success
|
||||
self.key_func['^M'] = self.success
|
||||
self.key_func['\n'] = self.success
|
||||
self.key_func['^G'] = self.abort
|
||||
|
||||
def do_command(self, key):
|
||||
res = Input.do_command(self, key)
|
||||
if self.on_input:
|
||||
self.on_input(self.get_text())
|
||||
log.debug('do_command returns : %s\n' % res)
|
||||
return res
|
||||
|
||||
def success(self):
|
||||
"""
|
||||
call the success callback, passing the text as argument
|
||||
"""
|
||||
self.on_input = None
|
||||
log.debug('before on_success')
|
||||
res = self.on_success(self.get_text())
|
||||
log.debug('after on_success, res: %s'%res)
|
||||
return res
|
||||
|
||||
def abort(self):
|
||||
"""
|
||||
Call the abort callback, passing the text as argument
|
||||
"""
|
||||
self.on_input = None
|
||||
return self.on_abort(self.get_text())
|
||||
|
||||
def rewrite_text(self):
|
||||
"""
|
||||
Rewrite the text just like a normal input, but with the instruction
|
||||
on the left
|
||||
"""
|
||||
with g_lock:
|
||||
self._win.erase()
|
||||
self.addstr(self.help_message, curses.color_pair(theme.COLOR_INFORMATION_BAR))
|
||||
cursor_pos = self.pos + len(self.help_message)
|
||||
if len(self.help_message):
|
||||
self.addstr(' ')
|
||||
cursor_pos += 1
|
||||
self.addstr(self.text[self.line_pos:self.line_pos+self.width-1])
|
||||
self.addstr(0, cursor_pos, '') # WTF, this works but .move() doesn't…
|
||||
self._refresh()
|
||||
|
||||
class VerticalSeparator(Win):
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue