[Nym3-commit] r150 - trunk/nym3/Client

jr at komite.net jr at komite.net
Sun Jan 2 00:19:38 CET 2005


Author: jr
Date: 2005-01-02 00:19:37 +0100 (Sun, 02 Jan 2005)
New Revision: 150

Modified:
   trunk/nym3/Client/testcurses.py
Log:
- added a pager mode to view messages (supports resize, lines break,
moves in the message, moves between message)
- changed the behaviour of the tagprefix : only lasts for one command
- display of the tag flags


Modified: trunk/nym3/Client/testcurses.py
===================================================================
--- trunk/nym3/Client/testcurses.py	2004-12-29 12:04:07 UTC (rev 149)
+++ trunk/nym3/Client/testcurses.py	2005-01-01 23:19:37 UTC (rev 150)
@@ -78,7 +78,7 @@
 	"""Display the requested synopsis"""
 	# TODO : this needs a PAGER view.
 	self.synlist[self.ctx.index]['read_status'] = 'read'
-	pass
+	return self.synlist[self.ctx.index]['synopsis']
 
     def mark_unread(self):
 	for i in self.ctx.get_targets():
@@ -97,7 +97,8 @@
 	return ret
 
     def line(self, i):
-	"""Ugly print current entry as a line"""
+	"""Ugly print current entry as a line
+	also returns the index where the tag flag can be inserted"""
 	# TODO: we need to draw this better.
 	# Allow the user to specify some kind of format
 	assert(i < len(self.synlist))
@@ -105,7 +106,9 @@
 	sfrom, sdate, subject = self.extract(syn, 'From', 'Date', 'Subject')
 	sfrom = self.format(sfrom, 10)
 	sdate = self.format(sdate, 20)
-	subject = self.format(subject, 20)
+	#the returned line need not to be of fixed length or < than a value
+	#the line is cut when it is displayed
+	#subject = self.format(subject, 20)
 	if syn['read_status'] == 'unread': flags = 'N'
 	else: flags = " "
 	if syn['fetch_status'] == 'fetch': flags += 'F'
@@ -115,10 +118,11 @@
 	else: flags += ' '
 
 	numdigits = int(math.ceil(math.log10(len(self.synlist))))
+	format = "%" + `numdigits` + "d %s %s %s %s"
+	tup = (i + 1, flags, sdate,sfrom, subject)
+	return format % tup, numdigits + 1 + len(flags)
+		
 
-	return ("%" + `numdigits` + "d %s %s %s %s") % (i + 1, flags, sdate,
-						      sfrom, subject)
-
     def extract(self, syn, *h):
 	"""Get most important header of a mail"""
 	#TODO: this is not RFC2822-compliant
@@ -156,15 +160,182 @@
 	deleteC.fromData(delete_list)
 	return str(getC) + str(deleteC)
 
+class Pager:
+    """ this class takes care of the display of a message in a pager.
+    It does the approptiate display in a provided window"""
+    
+    def __init__(self, fscreen, breakcolor):
+	#self.set_win(win)
+	#self.set_msg(message)
+	#self.buffer = curses.newpad(2, self.maxcol)
+	self.color = breakcolor
+	self.win = None
+	self.fscreen = fscreen
+	self.table = None
+	
+    def set_win(self, win):
+	if self.win != None:
+	    del (self.win)
+	self.win = win
+	if win != None:
+	    (self.maxline, self.maxcol) = win.getmaxyx()
+	    self.buffer = curses.newpad(2, self.maxcol)
+	    if self.table != None:
+		self.parse_message()
+	    
+    def set_msg(self, msg):
+	self.table = msg.split('\n')
+	self.table = [self.table[i].rstrip() for i in range(len(self.table))]
+	self.line = 0
+	if self.win != None:
+	    self.fstchunk = 0
+	    self.index = [[] for i in range(len(self.table))]
+	    self.parse_message()
+	
+    def parse_message(self):
+	for i in range(len(self.table)):
+	    self.parse_line(i)
+	#in order to put in self.nextline and self.nextfstchunk the
+	#good values
+	self.display_from_start()
+	
+    def parse_line(self, idx):
+	i = 0
+	buf = self.buffer
+	line_string = self.table[idx]
+		
+	buf.move(0,0)
+	self.index[idx] = [0]
+	tab = self.index[idx]
+	n = len(line_string)
+	while i < n:
+	    buf.addch(ord(line_string[i]))
+	    (y, x) = buf.getyx()
+	    if y == 0:
+		i = i + 1
+		continue
+	    else:
+		if x == 0:
+		    #the caracter i fits in the line
+		    i = i + 1
+		tab.append(i)
+		#if we are unable to display anything new on a line
+		#we stop to avoid infinite loops
+		if tab[-1] == tab[-2]:
+		    break
+		try:
+		    buf.move(0,1)
+		except curses.error:
+		    tab.append(i)
+		    break
+	#at the end, if everything was displayable we add the length of the
+	#line
+	if len(tab) == 1 or (tab[-1] != tab[-2] and tab[-1] != n):
+	    tab.append(n)
+	
+    def previous_chunk(self, l, k):
+	if k == 0:
+	    if l == 0:
+		return (l, k)
+	    else:
+		return (l - 1, len (self.index[l - 1]) - 2)
+	else:
+	    return (l, k -1)
+
+    def next_chunk(self, l, k):
+	if l == len(self.table):
+	    return (l, 0)
+	else:
+	    if k == len(self.index[l]) -2:
+		return (l + 1, 0)
+	    else:
+		return (l, k + 1)
+ 
+    def display_from_start(self):
+	idx = self.index[self.line]
+	k = self.fstchunk
+	l = self.line
+	i = 0
+	while i < self.maxline:
+	    self.win.move(i, 0)
+	    if l == len(self.table):
+		pass
+	    else:
+		if k != 0:
+		    try:
+			self.win.addch(ord('+'), self.color)
+		    except curses.error:
+			pass
+		src = self.table[l][idx[k]: idx[k + 1]]
+		if src != "":
+		    try:
+			self.win.addstr(src)
+		    except curses.error:
+			pass
+		    (l, k) = self.next_chunk(l, k)
+		    if l != len(self.table):
+			idx = self.index[l]
+	    self.win.clrtoeol()
+	    i = i + 1
+	self.nextline = l
+	self.nextfstchunk = k
+	self.fscreen.touchwin()
+
+    def get_start_from_end(self):
+	l = self.line
+	k = self.fstchunk
+	idx = self.index[l]
+	i = self.maxline - 1
+	#at the beginning of the loop i, (l, k) is the first chunk
+	#on line i + 1
+	while i >= 0:
+	    if l == 0 and k == 0:
+		break
+	    else:
+		(l, k) = self.previous_chunk(l, k)
+		idx = self.index[l]
+		if idx[k] == idx[k + 1]:
+		    if i == self.maxline - 1:
+			break
+		    else:
+			(l, k) = self.next_chunk(l, k)
+			break
+	    i = i - 1
+	self.line = l
+	self.fstchunk = k
+
+    #def on_resize(self, new_win):
+#	self.set_win(new_win)
+#	self.parse_message()
+
+    def refresh(self):
+	self.win.refresh(0, 0, 1, 0, 1 + self.maxline, self.maxcol)
+
+    def on_page_down(self):
+	if self.win != None and self.nextline != len(self.table):
+	    self.line = self.nextline
+	    self.fstchunk = self.nextfstchunk
+	    self.display_from_start()
+
+    def on_page_up(self):
+	if self.win != None and self.line != 0:
+	    self.get_start_from_end()
+	    self.display_from_start()
+
 class Screen_ctx:
     """ this class holds all the information relative to the content and the
     way a screen is drawn"""
-
+    
+    MODE_MENU = 0
+    MODE_PAGER = 1
+    MODE_HELP = 2
+    
     def __init__(self, screen, tab):
 	"""initialize the structure from a screen and a displayable list"""
 	self.NORMAL = curses.color_pair(0)
 	self.HIGHLIGHT = curses.color_pair(1)
 	self.STATUS = curses.color_pair(2)
+	self.BROKENLINE = curses.color_pair(3)
 	# the screen itself
 	self.screen = screen
 	#the table, in fact a class with a method size() and line(i), and
@@ -177,50 +348,93 @@
 	self.fstpos = 1 #index of the line on the screen where the first
 	#line of the table is displayed
 	self.listrange = max (0, self.maxline - 3)
+	self.pager = Pager(screen, self.BROKENLINE)
 	self.action = {
 		ord('q') : self.quit, 
 		ord('t') : self.tag,
 		ord(';') : self.settagprefix,
 		curses.KEY_DOWN : self.on_K_down,
 		curses.KEY_UP : self.on_K_up,
-		curses.KEY_RESIZE : self.on_resize
+		curses.KEY_RESIZE : self.on_resize,
+		ord('\n') : self.view,
+		ord(' ') : self.view
 		}
+
+	self.pager_action = {
+		ord('q') : self.pager_quit,
+		curses.KEY_DOWN : self.pager_on_K_down,
+		curses.KEY_UP : self.pager_on_K_up,
+		curses.KEY_RESIZE : self.pager_on_resize,
+		curses.KEY_NPAGE : self.pager_on_K_npage,
+		curses.KEY_PPAGE : self.pager_on_K_ppage
+		}
+	self.mode = Screen_ctx.MODE_MENU
+	self.nd = 0
+
 	shortcuts = tab.get_actions()
 	for i in shortcuts.keys():
 	    self.action[i] = shortcuts[i]
-	self.tagprefix = None
+	self.tagprefix = False
+	self.tagged = {}
+	self.bufferline= ""
 	self.draw_picture()
-	self.tagged = {}
 
+
     def message(self):
-	return "q:Quitter"
+	rl, rc = self.screen.getmaxyx()
+	s = "%d lr: %d mc: %d ml: %d rc: %d rl: %d" % (self.nd, self.listrange,
+					    self.maxcol, self.maxline, rc, rl)
+	return s
+	#return "q:Quitter"
 
     def status(self):
 	return "NymClient"
 	
-    def buffer(self):
-	return "%d %d %d %d %d" % (self.listrange, self.index, self.maxline,
-				self.tab.size(), self.fstindex)
-    
+    def	buffer(self):
+	if self.bufferline == "":
+	    rl, rc = self.screen.getmaxyx()
+	    s = "lr: %d mc: %d ml: %d rc: %d rl: %d" % (self.listrange,
+					    self.maxcol, self.maxline, rc, rl)
+	    if self.pager.win != None:
+		s = s + (" pmc: %d pml: %d" % (self.pager.maxcol, 
+						self.pager.maxline))
+	    return s
+	else:
+	    return self.bufferline 
+   
+    def draw_menu(self):
+	for i in range(0, self.listrange):
+	    if self.fstindex + i < self.tab.size(): 
+		if (self.fstindex + i == self.index):
+		    j = self.HIGHLIGHT
+		else:
+		    j = self.NORMAL
+		self.draw_line(self.fstindex + i, self.fstpos + i, j)
+	    else:
+		self.draw_empty_line(self.fstpos + i)
+
+   
     def draw_picture(self):
 	"""Displays the screen"""
+	self.nd = self.nd + 1
+	self.screen.touchwin()
 	if self.maxline > 2:
 	    self.draw_string(self.message(), 0, self.STATUS)
 	if self.maxline == 1:
 	    self.draw_string(self.status(), 0, self.STATUS)
 	else:
 	    self.draw_string(self.buffer(), self.maxline - 1, self.NORMAL)
+	    self.bufferline= ""
 	    self.draw_string(self.status(), self.maxline - 2, self.STATUS)
-	    for i in range(0, self.listrange):
-		if self.fstindex + i < self.tab.size(): 
-		    if (self.fstindex + i == self.index):
-			j = self.HIGHLIGHT
-		    else:
-			j = self.NORMAL
-		    self.draw_line(self.fstindex + i, self.fstpos + i, j)
-		else:
-		    self.draw_empty_line(self.fstpos + i)
-	    self.screen.refresh()
+	    if self.mode == Screen_ctx.MODE_MENU:
+		self.draw_menu()
+	    elif self.mode == Screen_ctx.MODE_PAGER:
+		pass
+	    elif self.mode == Screen_ctx.MODE_HELP:
+		pass
+	self.screen.refresh()
+	if self.mode == Screen_ctx.MODE_PAGER:
+	    self.pager.refresh()
 
     def draw_empty_line(self, i):
 	"""draws an empty line at line i of the screen"""
@@ -231,7 +445,12 @@
 	"""draws line idx of tab at the line at the position pos on the screen
 	with color color"""
 	self.screen.move(pos,0)
-	src = self.tab.line(idx)
+	(src, tgidx) = self.tab.line(idx)
+	if self.tagged.has_key(idx):
+	    tag = '*'
+	else:
+	    tag = ' '
+	src = src[0: tgidx] + tag + src[tgidx:]
 	self.screen.addstr(src[0: self.maxcol], color)
 	self.screen.bkgdset(ord(' '), color)
 	self.screen.clrtoeol()
@@ -239,8 +458,17 @@
 	
     def draw_string(self, s, pos, color):
 	"""draws string s at the position pos with color color"""
+	#if displaying the last character of the last line
+	#the cursor is moved to a line not in the screen
+	#causing an error
+	limit = self.maxcol
+	#if pos == self.maxline - 1:
+	#    limit = limit - 1
 	self.screen.move(pos,0)
-	self.screen.addstr(s[0: self.maxcol], color)
+	try:
+	    self.screen.addstr(s[0: limit], color)
+	except curses.error:
+	    pass
 	self.screen.bkgdset(ord(' '),color)
 	self.screen.clrtoeol()
 	self.screen.bkgdset(ord(' '),self.NORMAL)
@@ -249,13 +477,25 @@
 	self.active = True
 	while(self.active):
 	    c = self.screen.getch()
-	    if self.action.has_key(c):
-		self.action[c]()
+	    if self.mode == Screen_ctx.MODE_MENU:
+		t = self.tagprefix
+		if self.action.has_key(c):
+		    self.action[c]()
+		if t:
+		    self.tagprefix = False
+	    elif self.mode == Screen_ctx.MODE_PAGER:
+		if self.pager_action.has_key(c):
+		    self.pager_action[c]()
 	    self.draw_picture()
 	    
     def quit(self):
 	self.active = False
 
+    def view(self):
+	self.mode = Screen_ctx.MODE_PAGER
+	self.pager_give_win()
+	self.pager.set_msg(self.tab.view())
+
     def on_K_down(self):
 	if self.index < self.tab.size() - 1:
 	    self.index = self.index + 1
@@ -286,12 +526,52 @@
 	    self.tagged[self.index] = True
 
     def settagprefix(self):
-	self.tagprefix = True
-    
+	if not self.tagprefix:
+	    self.tagprefix = True
+	    self.bufferline = "tag-"
+
     def get_targets(self):
 	if self.tagprefix and self.tagged: return self.tagged.keys()
 	else: return [self.index]
+    
+    #action in pager mode
+    def pager_quit(self):
+	self.mode = Screen_ctx.MODE_MENU
+	self.pager.set_win(None)
+	
+    def pager_on_K_down(self):
+	i = self.index
+	self.on_K_down()
+	if i == self.index:
+	    self.pager_quit()
+	else:
+	    self.pager.set_msg(self.tab.view())
 	    
+    def pager_on_K_up(self):
+    	i = self.index
+	self.on_K_up()
+	if i == self.index:
+	    self.pager_quit()
+	else:
+	    self.pager.set_msg(self.tab.view())
+	
+    def pager_on_resize(self):
+	self.on_resize()
+	self.pager_give_win()
+	
+    def pager_on_K_npage(self):
+	self.pager.on_page_down()
+	
+    def pager_on_K_ppage(self):
+	self.pager.on_page_up()
+    
+    def pager_give_win(self):
+	if self.listrange != 0:
+	    sub = curses.newpad(self.listrange, self.maxcol)     
+	    self.pager.set_win(sub)
+	else:
+	    self.pager.set_win(None)
+    
 def func(stdscr):
     curses.curs_set(0)
     #define here our colors pair
@@ -299,6 +579,9 @@
     curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN)
     #status bar
     curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLUE)
+    #broken line indicator
+    curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
+    stdscr.scrollok(False)
     ctx = Screen_ctx(stdscr, Synbox_tab())
     ctx.execute()
 



More information about the Nym3-commit mailing list