removed deprecated and really old Sortilege frontend, today Primitivus is the console frontend
This commit is contained in:
parent
7f45c519f3
commit
97b8554124
7 changed files with 0 additions and 1026 deletions
|
|
@ -1,75 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# sortilege: a SAT frontend
|
||||
# Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from window import Window
|
||||
import os,pdb
|
||||
|
||||
class BoxSizer(object):
|
||||
"""This class manage the position of the window like boxes."""
|
||||
|
||||
|
||||
|
||||
def __init__(self, parent):
|
||||
self.__parent=parent
|
||||
self.boxes=[]
|
||||
|
||||
|
||||
|
||||
def appendRow(self, win):
|
||||
self.boxes.append([win])
|
||||
|
||||
def appendColum(self, index, win):
|
||||
if len(self.boxes)<=index:
|
||||
#TODO: throw an error here
|
||||
return
|
||||
self.boxes[index].append(win)
|
||||
|
||||
def update(self):
|
||||
"""Resize boxes"""
|
||||
oriY=0
|
||||
visible_row=[]
|
||||
for row in self.boxes:
|
||||
current_row=[]
|
||||
oriX=0
|
||||
for win in row:
|
||||
x=win.getOriX()
|
||||
y=win.getOriY()
|
||||
w=win.getOriWidth()
|
||||
h=win.getOriHeight()
|
||||
if win.isHidden():
|
||||
if len(current_row)>1 and win is row[-1]:
|
||||
#if the last win is hidden, we expand previous visible one
|
||||
current_row[-1][2] = current_row[-1][2] + (win.getX() - oriX)+win.getWidth()
|
||||
else:
|
||||
current_row.append([win, h+y-oriY, w+x-oriX, oriY, oriX])
|
||||
oriX=oriX+w
|
||||
|
||||
if oriX!=0:
|
||||
oriY=oriY+h
|
||||
visible_row.append(current_row)
|
||||
elif visible_row:
|
||||
#if all the row is empty, we take the space
|
||||
for box in visible_row[-1]:
|
||||
box[1]=box[1]+h
|
||||
oriY=oriY+h #this only happen if it's not the first visible row
|
||||
|
||||
for row in visible_row:
|
||||
for win in row:
|
||||
win[0].resize(win[1], win[2], win[3], win[4])
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# sortilege: a SAT frontend
|
||||
# Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import os.path
|
||||
import pdb
|
||||
from logging import debug, info, error
|
||||
from window import Window
|
||||
import os
|
||||
from time import time, localtime, strftime
|
||||
import curses
|
||||
import curses.ascii as ascii
|
||||
from tools.jid import JID
|
||||
from quick_frontend.quick_chat import QuickChat
|
||||
|
||||
|
||||
def C(k):
|
||||
"""return the value of Ctrl+key"""
|
||||
return ord(ascii.ctrl(k))
|
||||
|
||||
class Chat(Window, QuickChat):
|
||||
|
||||
def __init__(self, to_id, host):
|
||||
QuickChat.__init__(self, to_id, host)
|
||||
self.__parent=host.stdscr
|
||||
self.to_id=JID(to_id)
|
||||
self.content=[]
|
||||
self.__scollIdx=0
|
||||
Window.__init__(self, self.__parent, self.__parent.getmaxyx()[0]-2, self.__parent.getmaxyx()[1]-30, 0,30, code=host.code)
|
||||
|
||||
#history
|
||||
self.historyPrint(50, True, profile=self.host.profile)
|
||||
|
||||
self.hide()
|
||||
|
||||
def resize(self, height, width, y, x):
|
||||
Window.resize(self, height, width, y, x)
|
||||
self.update()
|
||||
|
||||
def resizeAdapt(self):
|
||||
"""Adapt window size to self.__parent size.
|
||||
Must be called when self.__parent is resized."""
|
||||
self.resize(self.__parent.getmaxyx()[0]-2, self.__parent.getmaxyx()[1]-30, 0,30)
|
||||
self.update()
|
||||
|
||||
def __getHeader(self, line):
|
||||
"""Return the header of a line (eg: "[12:34] <toto> ")."""
|
||||
header=''
|
||||
if self.host.chatParams["timestamp"]:
|
||||
header = header + '[%s] ' % strftime("%H:%M", localtime(float(line[0])))
|
||||
if self.host.chatParams['short_nick']:
|
||||
header = header + ('> ' if line[1]==self.host.profiles[self.host.profile]['whoami'] else '** ')
|
||||
else:
|
||||
header = header + '<%s> ' % line[1]
|
||||
return header
|
||||
|
||||
def update(self):
|
||||
if self.isHidden():
|
||||
return
|
||||
Window.update(self)
|
||||
content=[] #what is really printed
|
||||
irange=range(len(self.content))
|
||||
irange.reverse() #we print the text upward
|
||||
for idx in irange:
|
||||
header=self.__getHeader(self.content[idx])
|
||||
msg=self.content[idx][2]
|
||||
part=0 #part of the text
|
||||
if JID(self.content[idx][1]).bare==self.host.profiles[self.host.profile]['whoami'].bare:
|
||||
att_header=curses.color_pair(1)
|
||||
else:
|
||||
att_header=curses.color_pair(2)
|
||||
|
||||
while (msg):
|
||||
if part==0:
|
||||
hd=header
|
||||
att=att_header
|
||||
max=self.rWidth-len(header)
|
||||
else:
|
||||
hd=""
|
||||
att=0
|
||||
max=self.rWidth
|
||||
|
||||
LF = msg.find('\n') #we look for Line Feed
|
||||
if LF != -1 and LF < max:
|
||||
max = LF
|
||||
next = max + 1 #we skip the LF
|
||||
else:
|
||||
next = max
|
||||
|
||||
content.insert(part,[att,hd, msg[:max]])
|
||||
msg=msg[next:] #we erase treated part
|
||||
part=part+1
|
||||
|
||||
if len(content)>=self.rHeight+self.__scollIdx:
|
||||
#all the screen is filled, we can continue
|
||||
break
|
||||
|
||||
if self.__scollIdx>0 and len(content)<self.rHeight+self.__scollIdx:
|
||||
self.__scollIdx=abs(len(content)-self.rHeight) #all the log fit on the screen, we must stop here
|
||||
|
||||
idx=0
|
||||
for line in content[-self.rHeight-self.__scollIdx : -self.__scollIdx or None]:
|
||||
self.addYXStr(idx, 0, line[1], line[0])
|
||||
self.addYXStr(idx, len(line[1]), line[2])
|
||||
idx=idx+1
|
||||
|
||||
self.noutrefresh()
|
||||
|
||||
def scrollIdxUp(self):
|
||||
"""increment scroll index"""
|
||||
self.__scollIdx = self.__scollIdx + 1
|
||||
self.update()
|
||||
|
||||
def scrollIdxDown(self):
|
||||
"""decrement scroll index"""
|
||||
if self.__scollIdx > 0:
|
||||
self.__scollIdx = self.__scollIdx - 1
|
||||
self.update()
|
||||
|
||||
def printMessage(self, jid, msg, profile, timestamp=""):
|
||||
if timestamp=="":
|
||||
current_time=time()
|
||||
timestamp=str(current_time)
|
||||
if self.last_history and current_time - float(self.last_history) < 5: #FIXME: Q&D fix to avoid double print on new chat window
|
||||
return
|
||||
self.content.append([timestamp,jid.bare,msg])
|
||||
self.update()
|
||||
|
||||
def handleKey(self, k):
|
||||
if k == C('p'):
|
||||
self.scrollIdxUp()
|
||||
elif k == C('n'):
|
||||
self.scrollIdxDown()
|
||||
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# sortilege: a SAT frontend
|
||||
# Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import curses
|
||||
from curses import ascii
|
||||
from window import Window
|
||||
|
||||
def C(k):
|
||||
"""return the value of Ctrl+key"""
|
||||
return ord(ascii.ctrl(k))
|
||||
|
||||
def A(k):
|
||||
"""return the value of Alt+key"""
|
||||
return ord(ascii.alt(k))
|
||||
|
||||
class EditBox(Window):
|
||||
"""This class manage the edition of text"""
|
||||
|
||||
def __init__(self, parent, header, code="utf-8"):
|
||||
self.__header=header
|
||||
self.__text = unicode()
|
||||
self.__curs_pos=0
|
||||
self.__buffer=str()
|
||||
self.__replace_mode=False
|
||||
self.__parent=parent
|
||||
self.__code=code
|
||||
|
||||
Window.__init__(self, self.__parent, 1, self.__parent.getmaxyx()[1], self.__parent.getmaxyx()[0]-1,0, code=code)
|
||||
self.update()
|
||||
|
||||
def registerEnterCB(self, CB):
|
||||
self.__enterCB=CB
|
||||
|
||||
def resizeAdapt(self):
|
||||
"""Adapt window size to self.__parent size.
|
||||
Must be called when self.__parent is resized."""
|
||||
self.resize(1, self.__parent.getmaxyx()[1], self.__parent.getmaxyx()[0]-1,0)
|
||||
self.update()
|
||||
|
||||
def __getTextToPrint(self):
|
||||
"""return the text printed on the edit line"""
|
||||
width = self.rWidth - len(self.__header) -1
|
||||
if self.__curs_pos<width:
|
||||
begin = 0
|
||||
end = width
|
||||
else:
|
||||
begin = self.__curs_pos-width
|
||||
end = self.__curs_pos
|
||||
return self.__header+self.__text[begin:end]
|
||||
|
||||
def update(self):
|
||||
Window.update(self)
|
||||
text = self.__getTextToPrint()
|
||||
self.addYXStr(0, 0, text, limit=self.rWidth)
|
||||
|
||||
self.noutrefresh()
|
||||
|
||||
def __dec_cur(self):
|
||||
"""move cursor on the left"""
|
||||
if self.__curs_pos>0:
|
||||
self.__curs_pos = self.__curs_pos - 1
|
||||
|
||||
def __inc_cur(self):
|
||||
"""move cursor on the right"""
|
||||
if self.__curs_pos<len(self.__text):
|
||||
self.__curs_pos = self.__curs_pos + 1
|
||||
|
||||
def move_cur(self, x):
|
||||
pos = x+len(self.__header)
|
||||
if pos>=self.rWidth:
|
||||
pos=self.rWidth-1
|
||||
self.move(0, pos)
|
||||
|
||||
def clear_text(self):
|
||||
"""Clear the text of the edit box"""
|
||||
self.__text=""
|
||||
self.__curs_pos=0
|
||||
|
||||
def replace_cur(self):
|
||||
"""must be called earch time the cursor is moved"""
|
||||
self.move_cur(self.__curs_pos)
|
||||
self.noutrefresh()
|
||||
|
||||
def activate(self, state=True):
|
||||
cursor_mode = 1 if state else 0
|
||||
curses.curs_set(cursor_mode)
|
||||
Window.activate(self,state)
|
||||
|
||||
def handleKey(self, k):
|
||||
if ascii.isgraph(k) or ascii.isblank(k):
|
||||
pacman = 0 if not self.__replace_mode else 1
|
||||
self.__text = self.__text[:self.__curs_pos] + chr(k) + self.__text[self.__curs_pos + pacman:]
|
||||
self.__curs_pos = self.__curs_pos + 1
|
||||
|
||||
elif k==ascii.NL:
|
||||
try:
|
||||
self.__enterCB(self.__text)
|
||||
except NameError:
|
||||
pass # TODO: thrown an error here
|
||||
self.clear_text()
|
||||
|
||||
elif k==curses.KEY_BACKSPACE:
|
||||
self.__text = self.__text[:self.__curs_pos-1]+self.__text[self.__curs_pos:]
|
||||
self.__dec_cur()
|
||||
|
||||
elif k==curses.KEY_DC:
|
||||
self.__text = self.__text[:self.__curs_pos]+self.__text[self.__curs_pos+1:]
|
||||
|
||||
elif k==curses.KEY_IC:
|
||||
self.__replace_mode = not self.__replace_mode
|
||||
|
||||
elif k==curses.KEY_LEFT:
|
||||
self.__dec_cur()
|
||||
|
||||
elif k==curses.KEY_RIGHT:
|
||||
self.__inc_cur()
|
||||
|
||||
elif k==curses.KEY_HOME or k==C('a'):
|
||||
self.__curs_pos=0
|
||||
|
||||
elif k==curses.KEY_END or k==C('e'):
|
||||
self.__curs_pos=len(self.__text)
|
||||
|
||||
elif k==C('k'):
|
||||
self.__text = self.__text[:self.__curs_pos]
|
||||
|
||||
elif k==C('w'):
|
||||
before = self.__text[:self.__curs_pos]
|
||||
pos = before.rstrip().rfind(" ")+1
|
||||
self.__text = before[:pos] + self.__text[self.__curs_pos:]
|
||||
self.__curs_pos = pos
|
||||
|
||||
elif k>255:
|
||||
self.__buffer=""
|
||||
|
||||
else: ## FIXME: dangerous code, must be checked ! (specialy buffer overflow) ##
|
||||
#We now manage unicode
|
||||
self.__buffer = self.__buffer+chr(k)
|
||||
decoded=unicode()
|
||||
if len(self.__buffer)>4:
|
||||
self.__buffer=""
|
||||
return
|
||||
try:
|
||||
decoded = self.__buffer.decode(self.__code)
|
||||
except UnicodeDecodeError, e:
|
||||
if e.reason!="unexpected end of data":
|
||||
self.__buffer=""
|
||||
return
|
||||
if len(self.__buffer)==1: ## FIXME: awful ! only for test !
|
||||
self.__buffer=""
|
||||
return
|
||||
self.__text = self.__text + decoded
|
||||
self.__curs_pos = self.__curs_pos + 1
|
||||
self.__buffer=""
|
||||
|
||||
self.update()
|
||||
|
||||
|
|
@ -1,400 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
sortilege: a SAT frontend
|
||||
Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
from quick_frontend.quick_app import QuickApp
|
||||
from quick_frontend.quick_chat_list import QuickChatList
|
||||
from quick_frontend.quick_contact_list import QuickContactList
|
||||
from quick_frontend.quick_contact_management import QuickContactManagement
|
||||
import curses
|
||||
import pdb
|
||||
from window import Window
|
||||
from editbox import EditBox
|
||||
from statusbar import StatusBar
|
||||
from chat import Chat
|
||||
from tools.jid import JID
|
||||
import logging
|
||||
from logging import debug, info, error
|
||||
import locale
|
||||
import sys, os
|
||||
import gobject
|
||||
import time
|
||||
from curses import ascii
|
||||
import locale
|
||||
from signal import signal, SIGWINCH
|
||||
import fcntl
|
||||
import struct
|
||||
import termios
|
||||
from boxsizer import BoxSizer
|
||||
|
||||
|
||||
### logging configuration FIXME: put this elsewhere ###
|
||||
logging.basicConfig(level=logging.CRITICAL, #TODO: configure it top put messages in a log file
|
||||
format='%(message)s')
|
||||
###
|
||||
|
||||
const_APP_NAME = "Sortilège"
|
||||
const_CONTACT_WIDTH = 30
|
||||
|
||||
def ttysize():
|
||||
"""This function return term size.
|
||||
Comes from Donn Cave from python list mailing list"""
|
||||
buf = 'abcdefgh'
|
||||
buf = fcntl.ioctl(0, termios.TIOCGWINSZ, buf)
|
||||
row, col, rpx, cpx = struct.unpack('hhhh', buf)
|
||||
return row, col
|
||||
|
||||
def C(k):
|
||||
"""return the value of Ctrl+key"""
|
||||
return ord(ascii.ctrl(k))
|
||||
|
||||
class ChatList(QuickChatList):
|
||||
"""This class manage the list of chat windows"""
|
||||
|
||||
def __init__(self, host):
|
||||
QuickChatList.__init__(self, host)
|
||||
self.sizer=host.sizer
|
||||
|
||||
def createChat(self, name):
|
||||
chat = Chat(name, self.host)
|
||||
self.sizer.appendColum(0,chat)
|
||||
self.sizer.update()
|
||||
return chat
|
||||
|
||||
class ContactList(Window, QuickContactList):
|
||||
|
||||
def __init__(self, host, CM):
|
||||
QuickContactList.__init__(self, CM)
|
||||
self.host = host
|
||||
self.jid_list = []
|
||||
self.__index=0 #indicate which contact is selected (ie: where we are)
|
||||
Window.__init__(self, stdscr, stdscr.getmaxyx()[0]-2,const_CONTACT_WIDTH,0,0, True, _("Contact List"), code=code)
|
||||
|
||||
def resize(self, height, width, y, x):
|
||||
Window.resize(self, height, width, y, x)
|
||||
self.update()
|
||||
|
||||
def resizeAdapt(self):
|
||||
"""Adapt window size to stdscr size.
|
||||
Must be called when stdscr is resized."""
|
||||
self.resize(stdscr.getmaxyx()[0]-2,const_CONTACT_WIDTH,0,0)
|
||||
self.update()
|
||||
|
||||
def registerEnterCB(self, CB):
|
||||
self.__enterCB=CB
|
||||
|
||||
def clear_contacts(self):
|
||||
"""clear all the contact list"""
|
||||
del self.jid_list[:]
|
||||
self.__index = 0
|
||||
self.update() #FIXME: window is not updated correctly (contacts are still here until C-L)
|
||||
|
||||
def replace(self, jid, groups=None):
|
||||
"""add a contact to the list"""
|
||||
name = self.CM.getAttr(jid,'name')
|
||||
self.jid_list.append(jid.bare)
|
||||
self.update()
|
||||
|
||||
def indexUp(self):
|
||||
"""increment select contact index"""
|
||||
if self.__index < len(self.jid_list)-1: #we dont want to select a missing contact
|
||||
self.__index = self.__index + 1
|
||||
self.update()
|
||||
|
||||
def indexDown(self):
|
||||
"""decrement select contact index"""
|
||||
if self.__index > 0:
|
||||
self.__index = self.__index - 1
|
||||
self.update()
|
||||
|
||||
def disconnect(self, jid):
|
||||
"""for now, we just remove the contact"""
|
||||
self.remove(jid)
|
||||
|
||||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
self.jid_list.remove(jid.bare)
|
||||
if self.__index >= len(self.jid_list) and self.__index > 0: #if select index is out of border, we put it on the last contact
|
||||
self.__index = len(self.jid_list)-1
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
"""redraw all the window"""
|
||||
if self.isHidden():
|
||||
return
|
||||
Window.update(self)
|
||||
self.jid_list.sort()
|
||||
begin=0 if self.__index<self.rHeight else self.__index-self.rHeight+1
|
||||
idx=0
|
||||
for item in self.jid_list[begin:self.rHeight+begin]:
|
||||
attr = curses.A_REVERSE if ( self.isActive() and (idx+begin) == self.__index ) else 0
|
||||
centered = item.center(self.rWidth) ## it's nicer in the center :)
|
||||
self.addYXStr(idx, 0, centered, attr)
|
||||
idx = idx + 1
|
||||
|
||||
self.noutrefresh()
|
||||
|
||||
def handleKey(self, k):
|
||||
if k == curses.KEY_UP:
|
||||
self.indexDown()
|
||||
elif k == curses.KEY_DOWN:
|
||||
self.indexUp()
|
||||
elif k == ascii.NL:
|
||||
if not self.jid_list:
|
||||
return
|
||||
try:
|
||||
self.__enterCB(self.jid_list[self.__index])
|
||||
except NameError:
|
||||
pass # TODO: thrown an error here
|
||||
|
||||
class SortilegeApp(QuickApp):
|
||||
|
||||
def __init__(self):
|
||||
#debug(const_APP_NAME+" init...")
|
||||
|
||||
## unicode support ##
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
global code
|
||||
code = locale.getpreferredencoding()
|
||||
self.code=code
|
||||
|
||||
## main loop setup ##
|
||||
self.loop=gobject.MainLoop()
|
||||
gobject.io_add_watch(0, gobject.IO_IN, self.loopCB)
|
||||
|
||||
## misc init stuff ##
|
||||
self.CM = QuickContactManagement()
|
||||
self.listWins=[]
|
||||
self.chatParams={'timestamp':True,
|
||||
'color':True,
|
||||
'short_nick':False}
|
||||
|
||||
def start(self):
|
||||
curses.wrapper(self.start_curses)
|
||||
|
||||
def start_curses(self, win):
|
||||
global stdscr
|
||||
stdscr = win
|
||||
self.stdscr = stdscr
|
||||
curses.raw() #we handle everything ourself
|
||||
curses.curs_set(False)
|
||||
stdscr.nodelay(True)
|
||||
|
||||
## colours ##
|
||||
self.color(True)
|
||||
|
||||
## windows ##
|
||||
self.contactList = ContactList(self, self.CM)
|
||||
self.editBar = EditBox(stdscr, "> ", self.code)
|
||||
self.editBar.activate(False)
|
||||
self.statusBar = StatusBar(stdscr, self.code)
|
||||
self.statusBar.hide(True)
|
||||
self.addWin(self.contactList)
|
||||
self.addWin(self.editBar)
|
||||
self.addWin(self.statusBar)
|
||||
self.sizer=BoxSizer(stdscr)
|
||||
self.sizer.appendRow(self.contactList)
|
||||
self.sizer.appendRow(self.statusBar)
|
||||
self.sizer.appendRow(self.editBar)
|
||||
self.currentChat=None
|
||||
|
||||
self.contactList.registerEnterCB(self.onContactChoosed)
|
||||
self.editBar.registerEnterCB(self.onTextEntered)
|
||||
|
||||
self.chat_wins=ChatList(self)
|
||||
|
||||
QuickApp.__init__(self) #XXX: yes it's an unusual place for the constructor of a parent class, but the init order is important
|
||||
self.plug_profile()
|
||||
|
||||
signal (SIGWINCH, self.onResize) #we manage SIGWINCH ourselves, because the loop is not called otherwise
|
||||
|
||||
#last but not least, we adapt windows' sizes
|
||||
self.sizer.update()
|
||||
self.editBar.replace_cur()
|
||||
curses.doupdate()
|
||||
|
||||
self.loop.run()
|
||||
|
||||
def addWin(self, win):
|
||||
self.listWins.append(win)
|
||||
|
||||
def color(self, activate=True):
|
||||
if activate:
|
||||
debug (_("Activating colors"))
|
||||
curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_BLACK)
|
||||
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||
else:
|
||||
debug (_("Deactivating colors"))
|
||||
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||
|
||||
|
||||
def showChat(self, chat):
|
||||
debug (_("show chat"))
|
||||
if self.currentChat:
|
||||
debug (_("hiding %s"), self.currentChat)
|
||||
self.chat_wins[self.currentChat].hide()
|
||||
self.currentChat=chat
|
||||
debug (_("showing %s"), self.currentChat)
|
||||
self.chat_wins[self.currentChat].show()
|
||||
self.chat_wins[self.currentChat].update()
|
||||
|
||||
|
||||
### EVENTS ###
|
||||
|
||||
def onContactChoosed(self, jid_txt):
|
||||
"""Called when a contact is selected in contact list."""
|
||||
jid=JID(jid_txt)
|
||||
debug (_("contact choosed: %s"), jid)
|
||||
self.showChat(jid.bare)
|
||||
self.statusBar.remove_item(jid.bare)
|
||||
if len(self.statusBar)==0:
|
||||
self.statusBar.hide()
|
||||
self.sizer.update()
|
||||
|
||||
|
||||
def onTextEntered(self, text):
|
||||
jid=JID(self.profiles[self.profile]['whoami'])
|
||||
self.bridge.sendMessage(self.currentChat, text, profile_key=self.profile)
|
||||
|
||||
def showDialog(self, message, title, type="info"):
|
||||
if type==question:
|
||||
raise NotImplementedError
|
||||
pass
|
||||
|
||||
|
||||
def presenceUpdate(self, jabber_id, show, priority, statuses, profile):
|
||||
QuickApp.presenceUpdate(self, jabber_id, show, priority, statuses, profile)
|
||||
self.editBar.replace_cur()
|
||||
curses.doupdate()
|
||||
|
||||
def askConfirmation(self, type, id, data):
|
||||
#FIXME
|
||||
info (_("FIXME: askConfirmation not implemented"))
|
||||
|
||||
def actionResult(self, type, id, data):
|
||||
#FIXME
|
||||
info (_("FIXME: actionResult not implemented"))
|
||||
|
||||
def newMessage(self, from_jid, msg, type, to_jid, profile):
|
||||
QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile)
|
||||
sender=JID(from_jid)
|
||||
addr=JID(to_jid)
|
||||
win = addr if sender.bare == self.whoami.bare else sender #FIXME: duplicate code with QuickApp
|
||||
if (self.currentChat==None):
|
||||
self.currentChat=win.bare
|
||||
self.showChat(win.bare)
|
||||
|
||||
# we show the window in the status bar
|
||||
if not self.currentChat == win.bare:
|
||||
self.statusBar.add_item(win.bare)
|
||||
self.statusBar.show()
|
||||
self.sizer.update()
|
||||
self.statusBar.update()
|
||||
|
||||
self.editBar.replace_cur()
|
||||
curses.doupdate()
|
||||
|
||||
def onResize(self, sig, stack):
|
||||
"""Called on SIGWINCH.
|
||||
resize the screen and launch the loop"""
|
||||
height, width = ttysize()
|
||||
curses.resizeterm(height, width)
|
||||
gobject.idle_add(self.callOnceLoop)
|
||||
|
||||
def callOnceLoop(self):
|
||||
"""Call the loop and return false (for not being called again by gobject mainloop).
|
||||
Usefull for calling loop when there is no input in stdin"""
|
||||
self.loopCB()
|
||||
return False
|
||||
|
||||
def __key_handling(self, k):
|
||||
"""Handle key and transmit to active window."""
|
||||
|
||||
### General keys, handled _everytime_ ###
|
||||
if k == C('x'):
|
||||
if os.getenv('TERM')=='screen':
|
||||
os.system('screen -X remove')
|
||||
else:
|
||||
self.loop.quit()
|
||||
|
||||
## windows navigation
|
||||
elif k == C('l') and not self.contactList.isHidden():
|
||||
"""We go to the contact list"""
|
||||
self.contactList.activate(not self.contactList.isActive())
|
||||
if self.currentChat:
|
||||
self.editBar.activate(not self.contactList.isActive())
|
||||
|
||||
elif k == curses.KEY_F2:
|
||||
self.contactList.hide(not self.contactList.isHidden())
|
||||
if self.contactList.isHidden():
|
||||
self.contactList.activate(False) #TODO: auto deactivation when hiding ?
|
||||
if self.currentChat:
|
||||
self.editBar.activate(True)
|
||||
self.sizer.update()
|
||||
|
||||
## Chat Params ##
|
||||
elif k == C('c'):
|
||||
self.chatParams["color"] = not self.chatParams["color"]
|
||||
self.color(self.chatParams["color"])
|
||||
elif k == C('t'):
|
||||
self.chatParams["timestamp"] = not self.chatParams["timestamp"]
|
||||
self.chat_wins[self.currentChat].update()
|
||||
elif k == C('s'):
|
||||
self.chatParams["short_nick"] = not self.chatParams["short_nick"]
|
||||
self.chat_wins[self.currentChat].update()
|
||||
|
||||
## misc ##
|
||||
elif k == curses.KEY_RESIZE:
|
||||
stdscr.erase()
|
||||
height, width = stdscr.getmaxyx()
|
||||
if height<5 and width<35:
|
||||
stdscr.addstr(_("Pleeeeasse, I can't even breathe !"))
|
||||
else:
|
||||
for win in self.listWins:
|
||||
win.resizeAdapt()
|
||||
for win in self.chat_wins.keys():
|
||||
self.chat_wins[win].resizeAdapt()
|
||||
self.sizer.update() # FIXME: everything need to be managed by the sizer
|
||||
|
||||
## we now throw the key to win handlers ##
|
||||
else:
|
||||
for win in self.listWins:
|
||||
if win.isActive():
|
||||
win.handleKey(k)
|
||||
if self.currentChat:
|
||||
self.chat_wins[self.currentChat].handleKey(k)
|
||||
|
||||
def loopCB(self, source="", cb_condition=""):
|
||||
"""This callback is called by the main loop"""
|
||||
#pressed = self.contactList.window.getch()
|
||||
pressed = stdscr.getch()
|
||||
if pressed != curses.ERR:
|
||||
self.__key_handling(pressed)
|
||||
self.editBar.replace_cur()
|
||||
curses.doupdate()
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
sat = SortilegeApp()
|
||||
sat.start()
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# sortilege: a SAT frontend
|
||||
# Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import curses
|
||||
from window import Window
|
||||
import os
|
||||
|
||||
class StatusBar(Window):
|
||||
"""This class manage the edition of text"""
|
||||
|
||||
def __init__(self, parent, code="utf-8"):
|
||||
self.__parent=parent
|
||||
self.__code=code
|
||||
self.__items=set()
|
||||
|
||||
Window.__init__(self, self.__parent, 1, self.__parent.getmaxyx()[1], self.__parent.getmaxyx()[0]-2,0, code=code)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.__items)
|
||||
|
||||
def resizeAdapt(self):
|
||||
"""Adapt window size to self.__parent size.
|
||||
Must be called when self.__parent is resized."""
|
||||
self.resize(1, self.__parent.getmaxyx()[1], self.__parent.getmaxyx()[0]-2,0)
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
if self.isHidden():
|
||||
return
|
||||
Window.update(self)
|
||||
x=0
|
||||
for item in self.__items:
|
||||
pitem="[%s] " % item
|
||||
self.addYXStr(0, x, pitem, curses.A_REVERSE)
|
||||
x = x + len(pitem)
|
||||
if x>=self.rWidth:
|
||||
break
|
||||
self.addYXStr(0, x, (self.rWidth-x)*" ", curses.A_REVERSE)
|
||||
self.noutrefresh()
|
||||
|
||||
def clear_text(self):
|
||||
"""Clear the text of the edit box"""
|
||||
del(self.__items[:])
|
||||
|
||||
def add_item(self, item):
|
||||
self.__items.add(item)
|
||||
self.update()
|
||||
|
||||
def remove_item(self, item):
|
||||
if item in self.__items:
|
||||
self.__items.remove(item)
|
||||
self.update()
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# sortilege: a SAT frontend
|
||||
# Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import curses
|
||||
import os
|
||||
import pdb
|
||||
|
||||
|
||||
class Window(object):
|
||||
def __init__(self, parent, height, width, y, x, border=False, title="", code="utf-8"):
|
||||
self.__border=border
|
||||
self.__title=title
|
||||
self.__active=False
|
||||
self.__parent=parent
|
||||
self.__code=code
|
||||
self.__hide=False
|
||||
|
||||
self.resize(height, width, y, x)
|
||||
self.oriCoords=self.__coords #FIXME: tres moche, a faire en mieux
|
||||
|
||||
def hide(self, hide=True):
|
||||
self.__hide=hide
|
||||
|
||||
def show(self):
|
||||
self.__hide=False
|
||||
|
||||
def isHidden(self):
|
||||
return self.__hide
|
||||
|
||||
def getY(self):
|
||||
return self.__coords[2]
|
||||
|
||||
def getX(self):
|
||||
return self.__coords[3]
|
||||
|
||||
def getHeight(self):
|
||||
return self.__coords[0]
|
||||
|
||||
def getWidth(self):
|
||||
return self.__coords[1]
|
||||
|
||||
|
||||
#FIXME: tres moche, a faire en plus joli
|
||||
def getOriY(self):
|
||||
return self.oriCoords[2]
|
||||
|
||||
def getOriX(self):
|
||||
return self.oriCoords[3]
|
||||
|
||||
def getOriHeight(self):
|
||||
return self.oriCoords[0]
|
||||
|
||||
def getOriWidth(self):
|
||||
return self.oriCoords[1]
|
||||
|
||||
def defInsideCoord(self):
|
||||
"""define the inside coordinates (win coordinates minus decorations)"""
|
||||
height,width,y,x=self.__coords
|
||||
self.oriX = x if not self.__border else x+1
|
||||
self.oriY = y if not self.__border else y+1
|
||||
self.endX = x+width if not self.__border else x+width-2
|
||||
self.endY = y+height if not self.__border else y+height-2
|
||||
self.rWidth = width if not self.__border else width-2
|
||||
self.rHeight = height if not self.__border else height-2
|
||||
|
||||
def resize(self, height, width, y, x):
|
||||
self.__coords=[height, width, y, x]
|
||||
|
||||
# we check that coordinates are under limits
|
||||
self.__coordAdjust(self.__coords)
|
||||
height,width,y,x=self.__coords
|
||||
|
||||
self.window = self.__parent.subwin(height, width, y, x)
|
||||
self.defInsideCoord()
|
||||
|
||||
def __coordAdjust(self, coords):
|
||||
"""Check that coordinates are under limits, adjust them else otherwise"""
|
||||
height,width,y,x=coords
|
||||
parentY, parentX = self.__parent.getbegyx()
|
||||
parentHeight, parentWidth = self.__parent.getmaxyx()
|
||||
|
||||
if y < parentY:
|
||||
y = parentY
|
||||
if x < parentX:
|
||||
x = parentX
|
||||
if height > parentHeight - y:
|
||||
height = parentHeight - y
|
||||
if width > parentWidth - x:
|
||||
width = parentWidth - x
|
||||
coords[0], coords[1], coords[2], coords[3] = [height, width, y, x]
|
||||
|
||||
|
||||
def activate(self,state=True):
|
||||
"""Declare this window as current active one"""
|
||||
self.__active=state
|
||||
self.update()
|
||||
|
||||
def isActive(self):
|
||||
return self.__active
|
||||
|
||||
def addYXStr(self, y, x, text, attr = 0, limit=0):
|
||||
if self.__border:
|
||||
x=x+1
|
||||
y=y+1
|
||||
n = self.rWidth-x if not limit else limit
|
||||
encoded = text.encode(self.__code)
|
||||
adjust = len(encoded) - len(text) # hack because addnstr doesn't manage unicode
|
||||
try:
|
||||
self.window.addnstr(y, x, encoded, n + adjust, attr)
|
||||
except:
|
||||
#We have to catch error to write on last line last col FIXME: is there a better way ?
|
||||
pass
|
||||
|
||||
def move(self, y, x):
|
||||
self.window.move(y,x)
|
||||
|
||||
def noutrefresh(self):
|
||||
self.window.noutrefresh()
|
||||
|
||||
def update(self):
|
||||
"""redraw all the window"""
|
||||
if self.__hide:
|
||||
return
|
||||
self.clear()
|
||||
|
||||
def border(self):
|
||||
"""redraw the border and title"""
|
||||
y,x = self.window.getbegyx()
|
||||
width = self.window.getmaxyx()[1]
|
||||
if self.__border:
|
||||
self.window.border()
|
||||
if self.__title:
|
||||
if len(self.__title)>width:
|
||||
self.__title=""
|
||||
else:
|
||||
self.window.addstr(y,x+(width-len(self.__title))/2, self.__title)
|
||||
|
||||
def clear(self):
|
||||
self.window.clear()
|
||||
self.border()
|
||||
Loading…
Reference in a new issue