summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core.py25
-rw-r--r--src/poezio.py66
-rw-r--r--src/tabs.py78
-rw-r--r--src/windows.py43
4 files changed, 114 insertions, 98 deletions
diff --git a/src/core.py b/src/core.py
index e2edff8b..d2931933 100644
--- a/src/core.py
+++ b/src/core.py
@@ -26,6 +26,7 @@ import sys
import shlex
import curses
import threading
+import traceback
from datetime import datetime
@@ -79,20 +80,22 @@ class Core(object):
User interface using ncurses
"""
def __init__(self, xmpp):
+ # All uncaught exception are given to this callback, instead
+ # of being displayed on the screen and exiting the program.
+ sys.excepthook = self.on_exception
self.running = True
self.stdscr = curses.initscr()
self.init_curses(self.stdscr)
self.xmpp = xmpp
+ # a unique buffer used to store global informations
+ # that are displayed in almost all tabs, in an
+ # information window.
self.information_buffer = TextBuffer()
self.information_win_size = config.get('info_win_height', 2, 'var')
default_tab = tabs.InfoTab(self, "Info") if self.xmpp.anon\
else tabs.RosterInfoTab(self)
default_tab.on_gain_focus()
self.tabs = [default_tab]
- # a unique buffer used to store global informations
- # that are displayed in almost all tabs, in an
- # information window.
-
self.resize_timer = None
self.previous_tab_nb = 0
self.own_nick = config.get('own_nick', self.xmpp.boundjid.bare)
@@ -155,6 +158,20 @@ class Core(object):
self.xmpp.add_event_handler("roster_update", self.on_roster_update)
self.xmpp.add_event_handler("changed_status", self.on_presence)
+ def on_exception(self, typ, value, trace):
+ """
+ When an exception in raised, open a special tab
+ displaying the traceback and some instructions to
+ make a bug report.
+ """
+ try:
+ tb_tab = tabs.SimpleTextTab(self, "/!\ Oups, an error occured (this may not be fatal). /!\\\n\nPlease report this bug (by copying the present error message and explaining what you were doing) on the page http://codingteam.net/project/poezio/bugs/add\n\n%s\n\nIf Poezio does not respond anymore, kill it with Ctrl+\\, and sorry about that :(" % ''.join(traceback.format_exception(typ, value, trace)))
+ self.add_tab(tb_tab, focus=True)
+ except Exception: # If an exception is raised in this code, this is
+ # this is fatal, so we exit cleanly and display the traceback
+ curses.endwin()
+ raise
+
def grow_information_win(self):
"""
"""
diff --git a/src/poezio.py b/src/poezio.py
index 03b6d3e6..7989b4d5 100644
--- a/src/poezio.py
+++ b/src/poezio.py
@@ -20,70 +20,9 @@
Starting point of poezio. Launches both the Connection and Gui
"""
-import os
-import curses
import sys
-import traceback
-import threading
-
+import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-
-def installThreadExcepthook():
- """
- Workaround for sys.excepthook thread bug
- See http://bugs.python.org/issue1230540
- Python, you made me sad :(
- """
- init_old = threading.Thread.__init__
- def init(self, *args, **kwargs):
- init_old(self, *args, **kwargs)
- run_old = self.run
- def run_with_except_hook(*args, **kw):
- try:
- run_old(*args, **kw)
- except (KeyboardInterrupt, SystemExit):
- raise
- except:
- sys.excepthook(*sys.exc_info())
- self.run = run_with_except_hook
- threading.Thread.__init__ = init
-
-class MyStdErr(object):
- def __init__(self, fd):
- """
- Change sys.stderr to something like /dev/null
- to disable any printout on the screen that would
- mess everything
- """
- self.old_stderr = sys.stderr
- sys.stderr = fd
- def restore(self):
- """
- Restore the good ol' sys.stderr, because we need
- it in order to print the tracebacks
- """
- sys.stderr.close()
- sys.stderr = self.old_stderr
-
-# my_stderr = MyStdErr(open('/dev/null', 'a'))
-
-def exception_handler(type_, value, trace):
- """
- on any traceback: exit ncurses and print the traceback
- then exit the program
- """
- my_stderr.restore()
- try:
- curses.endwin()
- curses.echo()
- except: # if an exception is raised but initscr has never been called yet
- pass
- traceback.print_exception(type_, value, trace, None, sys.stderr)
- import os # used to quit the program even from a thread
- os.abort()
-
-# sys.excepthook = exception_handler
-
import signal
import logging
@@ -96,4 +35,7 @@ if __name__ == '__main__':
if options.debug:
logging.basicConfig(filename=options.debug,level=logging.DEBUG)
connection.start() # Connect to remote server
+ # Disable any display of non-wanted text on the terminal
+ # by redirecting stderr to /dev/null
+ # sys.stderr = open('/dev/null', 'a')
core.main_loop() # Refresh the screen, wait for user events etc
diff --git a/src/tabs.py b/src/tabs.py
index c9707996..8016f86e 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -110,19 +110,19 @@ class Tab(object):
"""
returns the color that should be used in the GlobalInfoBar
"""
- raise NotImplementedError
+ return theme.COLOR_TAB_NORMAL
def set_color_state(self, color):
"""
set the color state
"""
- raise NotImplementedError
+ pass
def get_name(self):
"""
get the name of the tab
"""
- raise NotImplementedError
+ return self.__class__.__name__
def get_text_window(self):
"""
@@ -1039,14 +1039,51 @@ class MucListTab(Tab):
self.core.execute(txt)
return self.reset_help_message()
+ def get_name(self):
+ return self.name
+
+ def on_input(self, key):
+ res = self.input.do_command(key)
+ if res:
+ return True
+ if key in self.key_func:
+ return self.key_func[key]()
+
+ def on_lose_focus(self):
+ self._color_state = theme.COLOR_TAB_NORMAL
+
+ def on_gain_focus(self):
+ self._color_state = theme.COLOR_TAB_CURRENT
+ curses.curs_set(0)
+
def get_color_state(self):
- return theme.COLOR_TAB_NORMAL
+ return self._color_state
- def set_color_state(self, color):
- pass
+class SimpleTextTab(Tab):
+ """
+ A very simple tab, with just a text displaying some
+ information or whatever.
+ For example used to display tracebacks
+ """
+ def __init__(self, core, text):
+ Tab.__init__(self, core)
+ self._color_state = theme.COLOR_TAB_NORMAL
+ self.text_win = windows.SimpleTextWin(text)
+ self.tab_win = windows.GlobalInfoBar()
+ self.default_help_message = windows.HelpText("“Ctrl+q”: close")
+ self.input = self.default_help_message
+ self.key_func['^T'] = self.close
+ self.key_func["/"] = self.on_slash
+ self.resize()
- def get_name(self):
- return self.name
+ def on_slash(self):
+ """
+ '/' is pressed, activate the input
+ """
+ curses.curs_set(1)
+ self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command)
+ self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
+ self.input.do_command("/") # we add the slash
def on_input(self, key):
res = self.input.do_command(key)
@@ -1055,6 +1092,19 @@ class MucListTab(Tab):
if key in self.key_func:
return self.key_func[key]()
+ def close(self):
+ self.core.close_tab()
+
+ def resize(self):
+ self.text_win.resize(self.height-2, self.width, 0, 0, self.core.stdscr)
+ self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
+ self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
+
+ def refresh(self, tabs, information, roster):
+ self.text_win.refresh()
+ self.tab_win.refresh(tabs, tabs[0])
+ self.input.refresh()
+
def on_lose_focus(self):
self._color_state = theme.COLOR_TAB_NORMAL
@@ -1065,18 +1115,6 @@ class MucListTab(Tab):
def get_color_state(self):
return self._color_state
-# class SimpleTextTab(Tab):
-# """
-# A very simple tab, with just a text displaying some
-# information or whatever
-# """
-# def __init__(self, core, text):
-# Tab.__init__(self, core)
-# self.text = text
-# self.text_win =
-
-# def resize(self):
-# pass
def diffmatch(search, string):
"""
Use difflib and a loop to check if search_pattern can
diff --git a/src/windows.py b/src/windows.py
index a7cee054..7161b0dd 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -49,7 +49,7 @@ import theme
g_lock = Lock()
-LINES_NB_LIMIT = 16384
+LINES_NB_LIMIT = 4096
class Win(object):
def __init__(self):
@@ -1425,16 +1425,35 @@ class ColumnHeaderWin(Win):
x += size
self._refresh()
-# class SimpleTextWin(Win):
-# def __init__(self, text):
-# self._text = text
-# self.built_lines = []
+class SimpleTextWin(Win):
+ def __init__(self, text):
+ self._text = text
+ self.built_lines = []
-# def resize(self, height, width, y, x, stdscr):
-# self._resize(height, width, y, x, stdscr)
-# self.rebuild_text()
+ def resize(self, height, width, y, x, stdscr):
+ self._resize(height, width, y, x, stdscr)
+ self.rebuild_text()
-# def rebuild_text(self):
-
-# def refresh(self):
-# pass
+ def rebuild_text(self):
+ """
+ Transform the text in lines than can then be
+ displayed without any calculation or anything
+ at refresh() time
+ It is basically called on each resize
+ """
+ self.built_lines = []
+ for line in self._text.split('\n'):
+ while len(line) >= self.width:
+ limit = line[:self.width].rfind(' ')
+ if limit <= 0:
+ limit = self.width
+ self.built_lines.append(line[:limit])
+ line = line[limit:]
+ self.built_lines.append(line)
+
+ def refresh(self):
+ with g_lock:
+ self._win.erase()
+ for y, line in enumerate(self.built_lines):
+ self.addstr(y, 0, line)
+ self._refresh()