python 3 only. Fixes all EncodingError bugs :))))

parent 43654505
#!/usr/bin/env sh
cd src/ && python poezio.py "$@"
cd src/ && python3 poezio.py "$@"
# -*- coding: utf-8 -*-
# some functions coming from gajim sources (thanks)
## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
......@@ -188,7 +186,7 @@ def temp_failure_retry(func, *args, **kwargs):
while True:
try:
return func(*args, **kwargs)
except (os.error, IOError, select.error), ex:
except (os.error, IOError, select.error) as ex:
if ex.errno == errno.EINTR:
continue
else:
......
# -*- coding:utf-8 -*-
#
# Copyright 2009 chickenzilla
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
......@@ -22,7 +20,7 @@ Defines the global config instance, used to get or set (and save) values
from/to the config file
"""
from ConfigParser import RawConfigParser, NoOptionError
from configparser import RawConfigParser, NoOptionError
from os import environ, makedirs, path
from shutil import copy2
try:
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -78,7 +76,7 @@ class Gui(object):
self.ignores = {}
self.commands = {
'help': (self.command_help, u'\_o< KOIN KOIN KOIN'),
'help': (self.command_help, '\_o< KOIN KOIN KOIN'),
'join': (self.command_join, _("Usage: /join [room_name][@server][/nick] [password]\nJoin: Join the specified room. You can specify a nickname after a slash (/). If no nickname is specified, you will use the default_nick in the configuration file. You can omit the room name: you will then join the room you\'re looking at (useful if you were kicked). You can also provide a room_name without specifying a server, the server of the room you're currently in will be used. You can also provide a password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/John\n/join room2\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / password")),
'quit': (self.command_quit, _("Usage: /quit\nQuit: Just disconnect from the server and exit poezio.")),
'exit': (self.command_quit, _("Usage: /exit\nExit: Just disconnect from the server and exit poezio.")),
......@@ -215,7 +213,7 @@ class Gui(object):
if from_nick not in [user.nick for user in room.users]:
new_user = User(from_nick, affiliation, show, status, role)
room.users.append(new_user)
if from_nick.encode('utf-8') == room.own_nick:
if from_nick == room.own_nick:
room.joined = True
new_user.color = theme.COLOR_OWN_NICK
self.add_message_to_room(room, _("Your nickname is %s") % (from_nick))
......@@ -333,7 +331,7 @@ class Gui(object):
msg += _('affiliation: %s,') % affiliation
if role != user.role:
msg += _('role: %s,') % role
if show != user.show and show in SHOW_NAME.keys():
if show != user.show and show in list(SHOW_NAME.keys()):
msg += _('show: %s,') % SHOW_NAME[show]
if status != user.status:
msg += _('status: %s,') % status
......@@ -378,7 +376,7 @@ class Gui(object):
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
room = self.open_private_window(room_from, nick_from.encode('utf-8'), False)
room = self.open_private_window(room_from, nick_from, False)
if not room:
return
body = message['body']
......@@ -407,16 +405,12 @@ class Gui(object):
while True:
doupdate()
char=read_char(self.stdscr)
try: # if this is not a valide utf-8 char, discard it
char.decode('utf-8')
except UnicodeDecodeError:
continue
# search for keyboard shortcut
if char in self.key_func.keys():
if char in list(self.key_func.keys()):
self.key_func[char]()
else:
if len(char.decode('utf-8')) > 1:
continue # ignore non-handled keyboard shortcuts
# if len(char) > 1:
# continue # ignore non-handled keyboard shortcuts
self.window.do_command(char)
def current_room(self):
......@@ -430,12 +424,8 @@ class Gui(object):
returns the room that has this name
"""
for room in self.rooms:
try:
if room.name.decode('utf-8') == name:
return room
except UnicodeEncodeError:
if room.name == name:
return room
if room.name == name:
return room
return None
def init_curses(self, stdscr):
......@@ -485,19 +475,20 @@ class Gui(object):
"""
Called when Tab is pressed, complete the nickname in the input
"""
def compare_users(a, b):
"""
Used to sort users by their last_talked
"""
if not a.last_talked and b.last_talked:
return 0
elif not b.last_talked and a.last_talked:
return 1
if a.last_talked < b.last_talked:
return 1
else:
return -1
self.window.input.auto_completion([user.nick for user in sorted(self.current_room().users, compare_users)])
# def compare_users(a, b):
# """
# Used to sort users by their last_talked
# """
# if not a.last_talked and b.last_talked:
# return 0
# elif not b.last_talked and a.last_talked:
# return 1
# if a.last_talked < b.last_talked:
# return 1
# else:
# return -1
compare_users = lambda x: x.last_talked
self.window.input.auto_completion([user.nick for user in sorted(self.current_room().users, key=compare_users)])
def last_words_completion(self):
"""
......@@ -513,7 +504,7 @@ class Gui(object):
for char in char_we_dont_want: # remove the chars we don't want
word = word.replace(char, '')
if len(word) > 5:
words.append(word.encode('utf-8'))
words.append(word)
self.window.input.auto_completion(words)
def go_to_important_room(self):
......@@ -576,7 +567,7 @@ class Gui(object):
code = error['error']['code']
body = error['error']['text']
if not body:
if code in ERROR_AND_STATUS_CODES.keys():
if code in list(ERROR_AND_STATUS_CODES.keys()):
body = ERROR_AND_STATUS_CODES[code]
else:
body = condition or _('Unknown error')
......@@ -596,7 +587,7 @@ class Gui(object):
self.refresh_window()
def open_private_window(self, room_name, user_nick, focus=True):
complete_jid = room_name.decode('utf-8')+'/'+user_nick
complete_jid = room_name+'/'+user_nick
for room in self.rooms: # if the room exists, focus it and return
if room.jid:
if room.jid == complete_jid:
......@@ -646,7 +637,7 @@ class Gui(object):
if nick_from == room_from:
nick_from = None
room = self.get_room_by_name(room_from)
if (self.ignores.has_key(room_from)) and (nick_from in self.ignores[room_from]):
if (room_from in self.ignores) and (nick_from in self.ignores[room_from]):
return
room = self.get_room_by_name(room_from)
if not room:
......@@ -659,7 +650,7 @@ class Gui(object):
self.add_message_to_room(room, _("%(nick)s changed the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=date)
else:
self.add_message_to_room(room, _("The subject is: %(subject)s") % {'subject':subject}, time=date)
room.topic = subject.encode('utf-8').replace('\n', '|')
room.topic = subject.replace('\n', '|')
if room == self.current_room():
self.window.topic_win.refresh(room.topic)
elif body:
......@@ -699,7 +690,7 @@ class Gui(object):
command = line.strip()[:].split()[0][1:]
arg = line[2+len(command):] # jump the '/' and the ' '
# example. on "/link 0 open", command = "link" and arg = "0 open"
if command in self.commands.keys():
if command in list(self.commands.keys()):
func = self.commands[command][0]
func(arg)
return
......@@ -708,7 +699,7 @@ class Gui(object):
elif self.current_room().name != 'Info':
if self.current_room().jid is not None:
muc.send_private_message(self.xmpp, self.current_room().name, line)
self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick.decode('utf-8'))
self.add_message_to_room(self.current_room(), line, None, self.current_room().own_nick)
else:
muc.send_groupchat_message(self.xmpp, self.current_room().name, line)
self.window.input.refresh()
......@@ -722,11 +713,11 @@ class Gui(object):
room = self.current_room()
if len(args) == 0:
msg = _('Available commands are: ')
for command in self.commands.keys():
for command in list(self.commands.keys()):
msg += "%s " % command
msg += _("\nType /help <command_name> to know what each command does")
if len(args) == 1:
if args[0] in self.commands.keys():
if args[0] in list(self.commands.keys()):
msg = self.commands[args[0]][1]
else:
msg = _('Unknown command: %s') % args[0]
......@@ -811,7 +802,7 @@ class Gui(object):
if self.current_room().name != 'Info':
if self.current_room().jid is not None:
muc.send_private_message(self.xmpp, self.current_room().name, line)
self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick)
self.add_message_to_room(self.current_room(), line, None, self.current_room().own_nick)
else:
muc.send_groupchat_message(self.xmpp, self.current_room().name, line)
self.window.input.refresh()
......@@ -942,7 +933,7 @@ class Gui(object):
}
if len(args) < 1:
return
if not args[0] in possible_show.keys():
if not args[0] in list(possible_show.keys()):
self.command_help('show')
return
show = possible_show[args[0]]
......@@ -966,7 +957,7 @@ class Gui(object):
return
roomname = self.current_room().name
nick = args[0]
if not self.ignores.has_key(roomname):
if roomname not in self.ignores:
self.ignores[roomname] = set() # no need for any order
if nick not in self.ignores[roomname]:
self.ignores[roomname].add(nick)
......@@ -986,7 +977,7 @@ class Gui(object):
return
roomname = self.current_room().name
nick = args[0]
if not self.ignores.has_key(roomname) or (nick not in self.ignores[roomname]):
if roomname not in self.ignores or (nick not in self.ignores[roomname]):
self.add_message_to_room(self.current_room(), _("%s was not ignored") % nick)
return
self.ignores[roomname].remove(nick)
......@@ -1052,11 +1043,11 @@ class Gui(object):
return
for user in room.users:
if user.nick == nick:
r = self.open_private_window(room.name, user.nick.decode('utf-8'))
r = self.open_private_window(room.name, user.nick)
if r and len(args) > 1:
msg = arg[len(nick)+1:]
muc.send_private_message(r.name, msg)
self.add_message_to_room(r, msg.decode('utf-8'), None, r.own_nick)
self.add_message_to_room(r, msg, None, r.own_nick)
def command_topic(self, arg):
"""
......@@ -1065,7 +1056,7 @@ class Gui(object):
args = arg.split()
room = self.current_room()
if len(args) == 0:
self.add_message_to_room(room, _("The subject of the room is: %s") % room.topic.decode('utf-8'))
self.add_message_to_room(room, _("The subject of the room is: %s") % room.topic)
return
subject = ' '.join(args)
if not room.joined or room.name == "Info":
......
# -*- coding: utf-8 -*-
# Copyright 2009, 2010 Erwan Briand
# Copyright 2010, Florent Le Coz <louizatakk@fedoraproject.org>
......@@ -80,6 +78,6 @@ class Handler(Singleton):
def emit(self, signal, **kwargs):
"""Emit a signal."""
if self.__signals__.has_key(signal):
if signal in self.__signals__:
for func in self.__signals__[signal]:
func(**kwargs)
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -26,6 +24,9 @@ shortcut, like ^A, M-a or KEY_RESIZE)
def get_next_byte(s):
"""
Read the next byte of the utf-8 char
ncurses seems to return a string of the byte
encoded in latin-1. So what we get is NOT what we typed
unless we do the conversion…
"""
try:
c = s.getkey()
......@@ -33,7 +34,7 @@ def get_next_byte(s):
return (None, "KEY_RESIZE")
if len(c) >= 4:
return (None, c)
return (ord(c), c)
return (ord(c), c.encode('latin-1')) # returns a number and a bytes object
def read_char(s):
"""
......@@ -47,10 +48,10 @@ def read_char(s):
return "KEY_BACKSPACE"
if first < 127: # ASCII char on one byte
if first <= 26: # transform Ctrl+* keys
char = "^"+chr(first + 64)
return "^"+chr(first + 64)
if first == 27:
(first, c) = get_next_byte(s)
char = "M-"+c
return "M-"+c
if 194 <= first:
(code, c) = get_next_byte(s) # 2 bytes char
char += c
......@@ -60,4 +61,11 @@ def read_char(s):
if 240 <= first:
(code, c) = get_next_byte(s) # 4 bytes char
char += c
return char
return char.decode('utf-8')# return all the concatened byte objets, decoded
if __name__ == '__main__':
import curses
s = curses.initscr()
curses.noecho()
while True:
s.addstr('%s\n' % read_char(s))
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -69,12 +67,9 @@ class Logger(object):
fd = open(dir+room, 'a')
except IOError:
return
try:
msg = msg.encode('utf-8')
except:
pass
msg = msg
if nick:
fd.write(datetime.now().strftime('%d-%m-%y [%H:%M:%S] ')+nick.encode('utf-8')+': '+msg+'\n')
fd.write(datetime.now().strftime('%d-%m-%y [%H:%M:%S] ')+nick+': '+msg+'\n')
else:
fd.write(datetime.now().strftime('%d-%m-%y [%H:%M:%S] ')+'* '+msg+'\n')
fd.close()
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......
#!/usr/bin/python
# -*- coding:utf-8 -*-
#!/usr/bin/env python3
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
......@@ -42,21 +41,21 @@ class MyStdErr(object):
sys.stderr.close()
sys.stderr = self.old_stderr
my_stderr = MyStdErr(open('/dev/null', 'a'))
# 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.restaure()
curses.endwin()
curses.echo()
traceback.print_exception(type_, value, trace, None, sys.stderr)
import os # used to quit the program even from a thread
os.abort()
# def exception_handler(type_, value, trace):
# """
# on any traceback: exit ncurses and print the traceback
# then exit the program
# """
# my_stderr.restaure()
# curses.endwin()
# curses.echo()
# 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
# sys.excepthook = exception_handler
import signal
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -70,9 +68,9 @@ class Room(object):
Set the tab color and returns the txt color
"""
color = theme.COLOR_NORMAL_TEXT
if not time and nickname and nickname.encode('utf-8') != self.own_nick and self.joined: # do the highlight
if not time and nickname and nickname != self.own_nick and self.joined: # do the highlight
try:
if self.own_nick in txt.encode('utf-8'):
if self.own_nick in txt:
self.set_color_state(theme.COLOR_TAB_HIGHLIGHT)
color = theme.COLOR_HIGHLIGHT_TEXT
except UnicodeDecodeError:
......@@ -134,7 +132,7 @@ class Room(object):
def get_user_by_name(self, nick):
for user in self.users:
if user.nick == nick.encode('utf-8'):
if user.nick == nick:
return user
return None
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -154,7 +152,7 @@ if __name__ == '__main__':
s = curses.initscr()
curses.start_color()
curses.use_default_colors()
for i in xrange(80):
for i in range(80):
s.attron(curses.color_pair(i))
s.addstr(str(i))
s.attroff(curses.color_pair(i))
......
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -22,6 +20,12 @@ from datetime import timedelta, datetime
import curses
import theme
ROLE_DICT = {
'none':0,
'visitor':1,
'participant':2,
'moderator':3
}
class User(object):
"""
keep trace of an user in a Room
......@@ -29,7 +33,7 @@ class User(object):
def __init__(self, nick, affiliation, show, status, role):
from common import debug
debug('NEW USER: nick:%s, affiliation:%s, show:%s, status:%s, role:%s\n' % (nick, affiliation, show, status, role))
self.last_talked = None
self.last_talked = datetime(1, 1, 1) # The oldest possible time
self.update(affiliation, show, status, role)
self.change_nick(nick)
self.color = choice(theme.LIST_COLOR_NICKNAMES)
......@@ -41,7 +45,7 @@ class User(object):
self.role = role
def change_nick(self, nick):
self.nick = nick.encode('utf-8')
self.nick = nick
def set_last_talked(self, time):
"""
......@@ -63,3 +67,26 @@ class User(object):
def __repr__(self):
return ">%s<" % (self.nick.decode('utf-8'))
def __eq__(self, b):
return self.role == b.role and self.nick.lower() == b.nick.lower()
def __gt__(self, b):
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() > b.nick.lower()
return ROLE_DICT[self.role] < ROLE_DICT[b.role]
def __ge__(self, b):
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() >= b.nick.lower()
return ROLE_DICT[self.role] <= ROLE_DICT[b.role]
def __lt__(self, b):
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() < b.nick.lower()
return ROLE_DICT[self.role] > ROLE_DICT[b.role]
def __le__(self, b):
if ROLE_DICT[self.role] == ROLE_DICT[b.role]:
return self.nick.lower() <= b.nick.lower()
return ROLE_DICT[self.role] >= ROLE_DICT[b.role]
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
......@@ -63,52 +61,31 @@ class UserList(Win):
self.color_role = {'moderator': theme.COLOR_USER_MODERATOR,
'participant':theme.COLOR_USER_PARTICIPANT,
'visitor':theme.COLOR_USER_VISITOR,
'none':theme.COLOR_USER_NONE
'none':theme.COLOR_USER_NONE,
'':theme.COLOR_USER_NONE
}
self.color_show = {'xa':theme.COLOR_STATUS_XA,
'None':theme.COLOR_STATUS_NONE,
'none':theme.COLOR_STATUS_NONE,
'':theme.COLOR_STATUS_NONE,
'dnd':theme.COLOR_STATUS_DND,
'away':theme.COLOR_STATUS_AWAY,
'chat':theme.COLOR_STATUS_CHAT
}
def refresh(self, users):
def compare_user(a, b):
try:
arole = self.color_role[a.role]
except KeyError:
arole = 5
try:
brole = self.color_role[b.role]
except KeyError:
brole = 5
if arole == brole:
if a.nick.lower() < b.nick.lower():
return -1
return 1
return arole - brole
if not self.visible:
return
g_lock.acquire()
self.win.erase()
y = 0
for user in sorted(users, compare_user):
try:
role_col = self.color_role[user.role]
except KeyError:
role_col = theme.COLOR_USER_NONE
try:
show_col = self.color_show[user.show]
except KeyError:
show_col = theme.COLOR_STATUS_NONE
for user in sorted(users):#sorted(users, compare_user):
role_col = self.color_role[user.role]
show_col = self.color_show[user.show]
self.win.attron(curses.color_pair(show_col))
self.win.addnstr(y, 0, theme.CHAR_STATUS, 1)
self.win.attroff(curses.color_pair(show_col))
self.win.attron(curses.color_pair(role_col))
try:
self.win.addnstr(y, 1, user.nick, self.width-1)
except:
pass
self.win.addnstr(y, 1, user.nick, self.width-1)
self.win.attroff(curses.color_pair(role_col))
y += 1
if y == self.height:
......@@ -136,20 +113,17 @@ class Topic(Win):
g_lock.acquire()
self.win.erase()
if not jid:
try:
self.win.addstr(0, 0, topic, curses.color_pair(theme.COLOR_TOPIC_BAR))
while True:
try:
self.win.addch(' ', curses.color_pair(theme.COLOR_TOPIC_BAR))
except:
break
except:
pass
self.win.addnstr(0, 0, topic[:self.width], curses.color_pair(theme.COLOR_TOPIC_BAR))
while True:
try:
self.win.addch(' ', curses.color_pair(theme.COLOR_TOPIC_BAR))
except:
break
elif jid:
room = jid.split('/')[0]
nick = '/'.join(jid.split('/')[1:])
topic = _('%(nick)s from room %(room)s' % {'nick': nick, 'room':room})
self.win.addnstr(0, 0, topic.encode('utf-8') + " "*(self.width-len(topic)), self.width-1
self.win.addnstr(0, 0, topic + " "*(self.width-len(topic)), self.width-1
, curses.color_pair(theme.COLOR_PRIVATE_ROOM_BAR))
self.win.refresh()
......@@ -175,29 +149,24 @@ class RoomInfo(Win):
def refresh(self, rooms, current):
if not self.visible:
return
def compare_room(a, b):
return a.nb - b.nb
def compare_room(a):
# return a.nb - b.nb
return a.nb
comp = lambda x: x.nb
g_lock.acquire()
self.win.erase()
self.win.addnstr(0, 0, "[", self.width
,curses.color_pair(theme.COLOR_INFORMATION_BAR))
sorted_rooms = sorted(rooms, compare_room)
sorted_rooms = sorted(rooms, key=comp)
for room in sorted_rooms:
color = room.color_state
try:
self.win.addstr("%s" % str(room.nb), curses.color_pair(color))
self.win.addstr(u"|".encode('utf-8'), curses.color_pair(theme.COLOR_INFORMATION_BAR))
self.win.addstr("|", curses.color_pair(theme.COLOR_INFORMATION_BAR))
except: # end of line
break
(y, x) = self.win.getyx()
try:
self.win.addstr(y, x-1, '] '+ current.name, curses.color_pair(theme.COLOR_INFORMATION_BAR))