742 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			742 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#holds basic GUI structures for use in curses (Dialog, button, editBox, readline, listbox and fileBrowser)
 | 
						|
# Taken and modified from http://bmcginty.us/clifox.git
 | 
						|
import curses, time, os, os.path, string, sys
 | 
						|
from curses import ascii
 | 
						|
 | 
						|
class GuiObject(object):
 | 
						|
	done = 0
 | 
						|
 | 
						|
	def beepIfNeeded(self):
 | 
						|
#		if self.base.config.beeps:
 | 
						|
		curses.beep()
 | 
						|
 | 
						|
	def setStatus(self,*a,**kw):
 | 
						|
		return self.base.setStatus(*a,**kw)
 | 
						|
 | 
						|
	def onFocus(self, *args, **kwargs): self.screen.move(self.y, self.x)
 | 
						|
 | 
						|
class Dialog(GuiObject):
 | 
						|
	"""control holder
 | 
						|
down and up arrows move through controls
 | 
						|
enter selects default button or displays error if not one
 | 
						|
"""
 | 
						|
	@property
 | 
						|
	def controlIndex(self):
 | 
						|
		return self._controlIndex
 | 
						|
 | 
						|
	@controlIndex.setter
 | 
						|
	def controlIndex(self, i):
 | 
						|
		self._controlIndex  =  i
 | 
						|
		self.controls[self._controlIndex].onFocus()
 | 
						|
		return i
 | 
						|
 | 
						|
	def __init__(self, screen = None, base = None, y = 0, x = 0, controls = [], can_go_back=True):
 | 
						|
		self.base = base
 | 
						|
		self.screen = screen
 | 
						|
		self.y, self.x = y, x
 | 
						|
		self._controls = controls
 | 
						|
		self.controls = []
 | 
						|
		self.can_go_back = can_go_back
 | 
						|
		self.initialDraw()
 | 
						|
		self.draw()
 | 
						|
 | 
						|
	def initialDraw(self):
 | 
						|
		for i in range(0, len(self._controls)):
 | 
						|
			co = self._controls[i]
 | 
						|
			c = co[0](screen=self.screen, base=self.base, y=i, can_go_back=self.can_go_back, **co[1])
 | 
						|
			self.controls.append(c)
 | 
						|
		self.controlIndex = 0
 | 
						|
 | 
						|
	def draw(self):
 | 
						|
		for i in self.controls:
 | 
						|
			i.draw()
 | 
						|
 | 
						|
	def handleKey(self, c):
 | 
						|
		ret = 1
 | 
						|
		if c == curses.KEY_DOWN:
 | 
						|
			if self.controlIndex >= len(self.controls) -1:
 | 
						|
				self.beepIfNeeded()
 | 
						|
				self.setStatus("No more controls in this dialog. Please up arrow to the first control.")
 | 
						|
				self.controlIndex = len(self.controls) -1
 | 
						|
			else:
 | 
						|
				self.controlIndex += 1
 | 
						|
		elif c == curses.KEY_UP:
 | 
						|
			if self.controlIndex <= 0:
 | 
						|
				self.beepIfNeeded()
 | 
						|
				self.setStatus("This is the first control in this dialog.")
 | 
						|
				self.controlIndex = 0
 | 
						|
			else:
 | 
						|
				self.controlIndex -= 1
 | 
						|
		else:
 | 
						|
			ret = self.controls[self.controlIndex].handleKey(c)
 | 
						|
		return ret
 | 
						|
 | 
						|
class Button(GuiObject):
 | 
						|
	def __init__(self, screen=None, base=None, y=1, x=0, can_go_back=True, prompt="Button", action="", help_string=""):
 | 
						|
		self.base = base
 | 
						|
		self.screen = screen
 | 
						|
		self.y, self.x = y, x
 | 
						|
		self.prompt = prompt
 | 
						|
		self.selected = 0
 | 
						|
		self.draw()
 | 
						|
		self.action  =  action
 | 
						|
		self.help_string = help_string
 | 
						|
		self.can_go_back = can_go_back
 | 
						|
 | 
						|
	def draw(self):
 | 
						|
		s =  self.prompt
 | 
						|
		self.screen.addstr(self.y, self.x, s)
 | 
						|
		self.screen.refresh()
 | 
						|
 | 
						|
	def handleKey(self, k):
 | 
						|
		if k == 10 or k == curses.KEY_RIGHT: # Enter key or right for easier use
 | 
						|
			self.done = 1
 | 
						|
			self.selected = 1
 | 
						|
			return 1
 | 
						|
		elif k == curses.KEY_F1:
 | 
						|
			if hasattr(self, "help_string"):
 | 
						|
				self.setStatus(self.help_string)
 | 
						|
				return 1
 | 
						|
		elif k == curses.KEY_BACKSPACE or k == curses.KEY_LEFT and self.can_go_back: # Let's go back
 | 
						|
			self.base.go_back()
 | 
						|
			return 1
 | 
						|
		return None
 | 
						|
 | 
						|
class Editbox(object):
 | 
						|
	"""Editing widget using the interior of a window object.
 | 
						|
		Supports the following Emacs-like key bindings:
 | 
						|
 Ctrl-A Go to left edge of window.
 | 
						|
 Ctrl-B Cursor left, wrapping to previous line if appropriate.
 | 
						|
 Ctrl-D Delete character under cursor.
 | 
						|
 Ctrl-E Go to right edge (stripspaces off) or end of line (stripspaces on).
 | 
						|
 Ctrl-F Cursor right, wrapping to next line when appropriate.
 | 
						|
 Ctrl-G Terminate, returning the window contents.
 | 
						|
 Ctrl-H Delete character backward.
 | 
						|
 Ctrl-J Terminate if the window is 1 line, otherwise insert newline.
 | 
						|
 Ctrl-K If line is blank, delete it, otherwise clear to end of line.
 | 
						|
 Ctrl-L Refresh screen.
 | 
						|
 Ctrl-N Cursor down; move down one line.
 | 
						|
 Ctrl-O Insert a blank line at cursor location.
 | 
						|
 Ctrl-P Cursor up; move up one line.
 | 
						|
 Move operations do nothing if the cursor is at an edge where the movement is not possible.
 | 
						|
The following synonyms are supported where possible:
 | 
						|
 KEY_LEFT  =  Ctrl-B, KEY_RIGHT  =  Ctrl-F, KEY_UP  =  Ctrl-P, KEY_DOWN  =  Ctrl-N, KEY_BACKSPACE  =  Ctrl-h
 | 
						|
 """
 | 
						|
	def __init__(self, screen=None, base=None, y=1, x=0, default="edit field"):
 | 
						|
		self.base = base
 | 
						|
		self.value = default
 | 
						|
		self.win = screen
 | 
						|
		self.loop = self.edit
 | 
						|
		(self.maxy, self.maxx)  =  self.win.getmaxyx()
 | 
						|
		self.maxy -=  2
 | 
						|
		self.maxx -=  1
 | 
						|
		self.stripspaces  =  1
 | 
						|
		self.lastcmd  =  None
 | 
						|
		self.text  =  [[] for y in xrange(self.maxy+1)]
 | 
						|
		self.win.keypad(1)
 | 
						|
		self.win.move(0,0)
 | 
						|
 | 
						|
	def text_insert(self, y, x, ch):
 | 
						|
		if len(self.text[y]) > x:
 | 
						|
			self.text[y].insert(x, ch)
 | 
						|
		else: # < =  x
 | 
						|
#			self.text[y] + =  [curses.ascii.SP] * (x - len(self.text[y]))
 | 
						|
			self.text[y].append(ch)
 | 
						|
 | 
						|
	def text_delete(self, y, x):
 | 
						|
		if y < 0 or x < 0 or y >= len(self.text) or x >=  len(self.text[y]): return
 | 
						|
		del self.text[y][x]
 | 
						|
 
 | 
						|
	def _end_of_line(self, y):
 | 
						|
		"""Go to the location of the first blank on the given line."""
 | 
						|
		last  =  self.maxx
 | 
						|
		while 1:
 | 
						|
			if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:
 | 
						|
				last  =  min(self.maxx, last+1)
 | 
						|
				break
 | 
						|
			elif last == 0:
 | 
						|
				break
 | 
						|
			last  = last - 1
 | 
						|
		return last
 | 
						|
 | 
						|
	def do_command(self, ch):
 | 
						|
		"Process a single editing command."
 | 
						|
		(y, x)  =  self.win.getyx()
 | 
						|
		self.lastcmd  =  ch
 | 
						|
		if ch == curses.ascii.SOH:									# ^a
 | 
						|
			x = 0
 | 
						|
			self.win.move(y, x)
 | 
						|
		elif ch in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS, curses.KEY_BACKSPACE,127):
 | 
						|
			if x > 0:
 | 
						|
				x -= 1
 | 
						|
				self.win.move(y, x)
 | 
						|
			elif y  == 0:
 | 
						|
				pass
 | 
						|
			else:
 | 
						|
				y -= 1
 | 
						|
				x = len(self.text[y])-1 #if len(self.text[y])<self.maxx else self.maxx
 | 
						|
				self.win.move(y, x)
 | 
						|
			if ch in (curses.ascii.BS, curses.KEY_BACKSPACE, 127):
 | 
						|
				self.win.delch()
 | 
						|
				y, x  =  self.win.getyx()
 | 
						|
				self.text_delete(y, x)
 | 
						|
		elif ch in (curses.ascii.EOT, curses.KEY_DC):									# ^d
 | 
						|
			self.win.delch()
 | 
						|
			self.text_delete(y, x)
 | 
						|
		elif ch  ==  curses.ascii.ENQ:									# ^e
 | 
						|
			x  =  len(self.text[y]) if len(self.text[y])<self.maxx else self.maxx
 | 
						|
			self.win.move(y, x)
 | 
						|
		elif ch in (curses.ascii.ACK, curses.KEY_RIGHT):				# ^f
 | 
						|
			if x < self.maxx and x < len(self.text[y]):
 | 
						|
				x += 1
 | 
						|
				self.win.move(y, x)
 | 
						|
			elif y == self.maxy:
 | 
						|
				pass
 | 
						|
			else:
 | 
						|
				y += 1
 | 
						|
				x = 0
 | 
						|
				self.win.move(y, x)
 | 
						|
		elif ch == curses.ascii.BEL:									# ^g
 | 
						|
			return True
 | 
						|
		elif ch in (10, 13):				# ^j ^m
 | 
						|
			if y < self.maxy:
 | 
						|
				y += 1
 | 
						|
				x = 0
 | 
						|
				self.win.move(y, x)
 | 
						|
		elif ch  ==  curses.ascii.VT:							# ^k
 | 
						|
			if x < len(self.text[y]):
 | 
						|
				self.win.clrtoeol()
 | 
						|
				del self.text[y][x:]
 | 
						|
			else:
 | 
						|
				self.win.deleteln()
 | 
						|
				del self.text[y]
 | 
						|
#			self.win.move(y, x)
 | 
						|
		elif ch  ==  curses.ascii.FF:							# ^l
 | 
						|
			self.win.refresh()
 | 
						|
		elif ch in (curses.ascii.SO, curses.KEY_DOWN):			# ^n
 | 
						|
			if y < self.maxy:
 | 
						|
				y += 1
 | 
						|
				x = len(self.text[y]) if x > len(self.text[y]) else x
 | 
						|
				self.win.move(y, x)
 | 
						|
			else:
 | 
						|
				pass
 | 
						|
		elif ch  ==  curses.ascii.SI:							# ^o
 | 
						|
			self.win.insertln()
 | 
						|
			self.text.insert(y, [])
 | 
						|
		elif ch in (curses.ascii.DLE, curses.KEY_UP):				# ^p
 | 
						|
			if y > 0:
 | 
						|
				y -= 1
 | 
						|
				x = len(self.text[y]) if x > len(self.text[y]) else x
 | 
						|
				self.win.move(y, x)
 | 
						|
			else:
 | 
						|
				pass
 | 
						|
		elif ch  ==  curses.KEY_HOME:
 | 
						|
			y = 0
 | 
						|
			x = len(self.text[y]) if x > len(self.text[y]) else x
 | 
						|
			self.win.move(y, x)
 | 
						|
		elif ch  ==  curses.KEY_END:
 | 
						|
			y = len(self.text)
 | 
						|
#			x = len(self.text[y]) if x > len(self.text[y]) else x
 | 
						|
			self.win.move(y,x)
 | 
						|
		elif ch == curses.KEY_F2:
 | 
						|
			if self.externalEdit()  ==  None:
 | 
						|
				self.setStatus("No external editor found.")
 | 
						|
			else:
 | 
						|
				return True				
 | 
						|
		elif ch>31 and ch<256:
 | 
						|
			ch  =  self.getunicode(ch)
 | 
						|
			if y < self.maxy or x < self.maxx:
 | 
						|
				self.text_insert(y, x, ch)
 | 
						|
				self.win.addstr(y, 0, ''.join(self.text[y]))
 | 
						|
				if x<self.maxx:
 | 
						|
					x += 1
 | 
						|
				else:
 | 
						|
					x = 0
 | 
						|
					y += 1
 | 
						|
				self.win.move(y, x)
 | 
						|
		self.draw()
 | 
						|
		return False
 | 
						|
 | 
						|
	def gather(self):
 | 
						|
		tmp  =  [''] * len(self.text)
 | 
						|
		for y in xrange(len(self.text)):
 | 
						|
			tmp[y]  =  ''.join(self.text[y])
 | 
						|
			tmp[y]  =  tmp[y].rstrip()
 | 
						|
		return '\n'.join(tmp).strip()
 | 
						|
 | 
						|
	def clear(self):
 | 
						|
		self.text  =  [[] for y in xrange(self.maxy+1)]
 | 
						|
		for y in xrange(self.maxy+1):
 | 
						|
			self.win.move(y, 0)
 | 
						|
			self.win.clrtoeol()
 | 
						|
		self.win.move(0, 0)
 | 
						|
 
 | 
						|
	def draw(self):
 | 
						|
		y, x = self.win.getyx()
 | 
						|
		l  =  len(self.text)
 | 
						|
		if l < self.maxy:
 | 
						|
			for i in range(l):
 | 
						|
				self.win.move(i, 0)
 | 
						|
				self.win.clrtoeol()
 | 
						|
				self.win.addstr(i, 0, ''.join(self.text[i]))
 | 
						|
			self.win.move(y, x)
 | 
						|
		else:
 | 
						|
			for i in xrange(l-self.maxy):
 | 
						|
				self.win.move(i, 0)
 | 
						|
				self.win.clrtoeol()
 | 
						|
				self.win.addstr(i, 0, ''.join(self.text[i]))
 | 
						|
			self.win.move(y, x)
 | 
						|
		self.win.refresh()
 | 
						|
 | 
						|
	def externalEdit(self):
 | 
						|
#		if self.base.config.editor:
 | 
						|
#			e = self.base.config.editor
 | 
						|
		if os.environment.get("nano"):
 | 
						|
			e = os.environment.get("EDITOR")
 | 
						|
		else:
 | 
						|
			return None
 | 
						|
		tempfile = "/tmp/squigglitz"
 | 
						|
		open(tempfile,"wb").write(self.gather())
 | 
						|
		cmd = "%s %s" % (e,tempfile)
 | 
						|
		os.system(cmd)
 | 
						|
		fh = open(tempfile,"rb")
 | 
						|
		self.text = fh.readlines()
 | 
						|
		fh.close()
 | 
						|
		os.remove(tempfile)
 | 
						|
		return self.text
 | 
						|
 
 | 
						|
	def getunicode(self, c):
 | 
						|
		tc  =  u' '
 | 
						|
		buf  =  ''
 | 
						|
		done  =  False
 | 
						|
		nc  =  chr(c)
 | 
						|
		buf += nc
 | 
						|
		if ord(nc) in (194, 195):
 | 
						|
			nc  =  chr(self.win.getch())
 | 
						|
			buf += nc
 | 
						|
		try:
 | 
						|
			tc  =  buf.decode()
 | 
						|
			done  =  True
 | 
						|
		except:
 | 
						|
			pass
 | 
						|
		return tc
 | 
						|
 | 
						|
	def edit(self):
 | 
						|
		self.win.clear()
 | 
						|
		text = list(self.value)
 | 
						|
		while 1:
 | 
						|
			if text != None and len(text)>0:
 | 
						|
				ch = ord(text.pop(0))
 | 
						|
				if len(text) == 0:
 | 
						|
					text = -1
 | 
						|
			else:
 | 
						|
				ch  =  self.win.getch()
 | 
						|
				if ch == -1:
 | 
						|
					time.sleep(0.02)
 | 
						|
					continue
 | 
						|
			o_ch  =  ch
 | 
						|
			if self.do_command(ch):
 | 
						|
				break
 | 
						|
			if text == -1:
 | 
						|
				self.win.move(0,0)
 | 
						|
				self.win.refresh()
 | 
						|
				text = None
 | 
						|
		return self.gather()
 | 
						|
 | 
						|
class Readline(GuiObject):
 | 
						|
	"""
 | 
						|
prompt for user input, with bindings to that of the default readline implementation
 | 
						|
prompt: prompt displayed before the users text
 | 
						|
history: a list of strings which constitutes the previously entered set of strings given to the caller of this function during previous calls
 | 
						|
text: the default text, entered as if the user had typed it directly
 | 
						|
echo: acts as a mask for passwords (set to ' ' in order to not echo any visible character for passwords)
 | 
						|
length: the maximum length for this text entry
 | 
						|
delimiter: the delimiter between prompt and text
 | 
						|
readonly: whether to accept new text
 | 
						|
"""
 | 
						|
	def __init__(self, screen=None, base=None, y=0, x=0, history=[], prompt=u"input", default=u"", echo=False, maxLength=0, delimiter=u": ", readonly=0, action=""):
 | 
						|
		self.value = default
 | 
						|
		self.done = 0
 | 
						|
		self.base = base
 | 
						|
		self.screen = screen
 | 
						|
		self.y, self.x = y, x
 | 
						|
		self.history = history
 | 
						|
		self.historyPos = len(self.history) if self.history else 0
 | 
						|
		self.prompt = prompt
 | 
						|
		self.delimiter = delimiter
 | 
						|
		self.echo = echo
 | 
						|
		self.readonly = readonly
 | 
						|
		self.maxLength = maxLength
 | 
						|
#prompt and delimiter
 | 
						|
		self.s = u"%s%s" % (self.prompt,self.delimiter,) if self.prompt else ""
 | 
						|
#position in the currently-being-editted text
 | 
						|
		self.ptr = 0
 | 
						|
#start of text entry "on-screen", should be greater than self.ptr unless there is absolutely no prompt, (in other words, a completely blank line)
 | 
						|
#if there's a prompt, startX should be right after the prompt and the delimiter
 | 
						|
#if not, startX is going to be wherever self.x is, as that's where our text is going to appear
 | 
						|
		self.startX = len(self.s) if self.s else self.x
 | 
						|
#put ptr at the end of the current bit of text
 | 
						|
		self.currentLine = self.value
 | 
						|
		self.ptr = len(self.currentLine)
 | 
						|
		self.insertMode = True
 | 
						|
		self.lastDraw = None
 | 
						|
		self.draw()
 | 
						|
		self.action = action
 | 
						|
 | 
						|
	def externalEdit(self): return None
 | 
						|
 
 | 
						|
	def getunicode(self, c):
 | 
						|
		return chr(c)
 | 
						|
 | 
						|
	def draw(self):
 | 
						|
		d = self.ptr, self.currentLine
 | 
						|
		if self.lastDraw and d == self.lastDraw:
 | 
						|
			return
 | 
						|
		loc = self.x
 | 
						|
		t = self.s
 | 
						|
		self.screen.move(self.y, self.x)
 | 
						|
		self.screen.clrtoeol()
 | 
						|
		if self.s:
 | 
						|
			self.screen.addstr(self.y, self.x, self.s)
 | 
						|
		t = self.currentLine
 | 
						|
		cnt = 0
 | 
						|
		if self.echo:
 | 
						|
			t = str(self.echo)[:1]*len(t)
 | 
						|
		self.screen.addstr(self.y, self.startX, "".join(t))
 | 
						|
		self.screen.move(self.y, self.startX+self.ptr)
 | 
						|
		self.screen.refresh()
 | 
						|
		self.lastDraw = self.ptr, self.currentLine
 | 
						|
 | 
						|
	def handleKey(self, c):
 | 
						|
			if c == -1:
 | 
						|
				return None
 | 
						|
			if c  ==  3:		# ^C
 | 
						|
				self.setStatus("Input aborted!")
 | 
						|
				self.currentLine = u''
 | 
						|
			elif c  ==  10:		# ^J newline
 | 
						|
				if self.history != None and self.currentLine:
 | 
						|
					self.history.append(self.currentLine)
 | 
						|
				self.done = 1
 | 
						|
			elif c in (1, 262):		# ^A, Home key
 | 
						|
				self.ptr = 0
 | 
						|
			elif c in (5, 360):		# ^E, End key
 | 
						|
				self.ptr = len(self.currentLine)
 | 
						|
			elif c in (2, 260):		# ^B, left arrow
 | 
						|
				if self.ptr>0:
 | 
						|
					self.ptr -= 1
 | 
						|
				else:
 | 
						|
					self.beepIfNeeded()
 | 
						|
				return 1
 | 
						|
			elif c in (6, 261):		# ^f, right arrow
 | 
						|
				if self.ptr<len(self.currentLine):
 | 
						|
					self.ptr += 1
 | 
						|
				else:
 | 
						|
					self.beepIfNeeded()
 | 
						|
					self.ptr = len(self.currentLine)
 | 
						|
				return 1
 | 
						|
			elif c  ==  259:		# Up arrow
 | 
						|
				if not self.history or self.historyPos == 0: #history will return non-zero if it has content
 | 
						|
					self.beepIfNeeded()
 | 
						|
					msg = "No history to move up through." if not self.history else "No previous history to move up through."
 | 
						|
					self.setStatus(msg)
 | 
						|
				elif self.history and self.historyPos>0:
 | 
						|
					self.tempLine = self.currentLine
 | 
						|
					self.historyPos -= 1
 | 
						|
					self.currentLine = self.history[self.historyPos]
 | 
						|
					self.ptr = len(self.currentLine)
 | 
						|
				else:
 | 
						|
					self.setStatus("Something odd occured, readLine, up arrow")
 | 
						|
			elif c  ==  258:		# Down arrow
 | 
						|
#if there is no history, or we're off the end of the history list (therefore using tempLine), show an error
 | 
						|
				if not self.history or self.historyPos>= len(self.history):
 | 
						|
					self.beepIfNeeded()
 | 
						|
					msg = "No history to move down through." if not self.history else "No more history to move down through."
 | 
						|
					self.setStatus(msg)
 | 
						|
#otherwise, we've got more history, or tempLine left to view
 | 
						|
				elif self.history:
 | 
						|
#go ahead and move down
 | 
						|
					self.historyPos += 1
 | 
						|
#if we're now off the end of the history, pull up tempLine
 | 
						|
#maybe user thought they'd typed something and they hadn't, so they can get back to their pre-history command
 | 
						|
					if self.historyPos == len(self.history):
 | 
						|
						self.currentLine = self.tempLine
 | 
						|
#normal history item
 | 
						|
					else:
 | 
						|
						self.currentLine = self.history[self.historyPos]
 | 
						|
#move to the end of this line, history or tempLine
 | 
						|
					self.ptr = len(self.currentLine)
 | 
						|
				else:
 | 
						|
					self.setStatus("Something odd occured, readLine, down arrow")
 | 
						|
			elif c in (8, curses.KEY_BACKSPACE, 263):		# ^H, backSpace
 | 
						|
				if self.ptr>0:
 | 
						|
					self.currentLine = u"%s%s" % (self.currentLine[:self.ptr-1],self.currentLine[self.ptr:])
 | 
						|
					self.ptr -= 1
 | 
						|
				else:
 | 
						|
					self.beepIfNeeded()
 | 
						|
			elif c in (4, 330):		# ^D, delete
 | 
						|
				if self.ptr<len(self.currentLine):
 | 
						|
					self.currentLine = u"%s%s" % (self.currentLine[:self.ptr],self.currentLine[self.ptr+1:])
 | 
						|
				else:
 | 
						|
					self.beepIfNeeded()
 | 
						|
			elif c  ==  331:		# insert
 | 
						|
				self.insertMode = False if self.insertMode == True else False
 | 
						|
				self.setStatus("insert mode "+"on" if self.insertMode else "off")
 | 
						|
			elif c  ==  21:		# ^U
 | 
						|
				self.ptr = 0
 | 
						|
				self.currentLine = u''
 | 
						|
			elif c  ==  11:		# ^K
 | 
						|
				self.currentLine = self.currentLine[:self.ptr]
 | 
						|
			else:
 | 
						|
				if self.readonly:
 | 
						|
					self.beepIfNeeded()
 | 
						|
					self.setStatus("This is a read only line. Text can not be modified.")
 | 
						|
				else:
 | 
						|
					uchar = self.getunicode(c)
 | 
						|
					if not self.insertMode:
 | 
						|
						self.currentLine[self.ptr] = uchar
 | 
						|
					else:
 | 
						|
						self.currentLine = "%s%s%s" % (self.currentLine[:self.ptr],uchar,self.currentLine[self.ptr:])
 | 
						|
						self.ptr += 1
 | 
						|
						if self.maxLength>0 and self.ptr >=  self.maxLength:
 | 
						|
							if self.history != None and self.currentLine:
 | 
						|
								self.history.append(self.currentLine)
 | 
						|
							self.done = True
 | 
						|
							self.setStatus("Maximum field length reached.")
 | 
						|
						#handled keystroke
 | 
						|
			self.draw()
 | 
						|
			return 1
 | 
						|
 | 
						|
class Listbox(GuiObject):
 | 
						|
	"""Listbox
 | 
						|
render a listbox to the screen in `title \n separator \n items` format
 | 
						|
y,x: y and x coordinates where to draw this window on the screen
 | 
						|
height: maximum height of this window on the screen, including title and separator (defaults to the length of the list, or the height of the window)
 | 
						|
base: base clifox object for accessing settings and other clifox state
 | 
						|
default: the index of the currently selected item (or a list of selected items, if multiple is true)
 | 
						|
title: the title of this select box (might be taken from the element on the webpage)
 | 
						|
keysWaitTime: maximum amount of time the system will consider a consecutive set of key-presses as a single search
 | 
						|
items: a list of options in string form, or a list of (id,option) tuples
 | 
						|
multiple: whether to allow selecting multiple options
 | 
						|
"""
 | 
						|
	def __init__(self, screen=None, base=None, y=0, x=0, height=None, title=None, items=[], keysWaitTime=0.4, default=0, multiple=False):
 | 
						|
		self.screen = screen
 | 
						|
		self.base = base
 | 
						|
		self.multiple = multiple
 | 
						|
		self.y, self.x = y, x
 | 
						|
		self.title = title
 | 
						|
#if we've got a list of strings or a list of non-list objects, turn them into itemIndex,item
 | 
						|
#so ["a","b","c"] would become [[0,"a"],[1,"b"],[2,"c"]]
 | 
						|
		if items and type(items[0]) not in (tuple, list):
 | 
						|
			items = zip(range(len(items)),items)
 | 
						|
		else:
 | 
						|
			items = items
 | 
						|
		items = [(i,str(j)) for i, j in items]
 | 
						|
		self.items = items
 | 
						|
		self.keys = []
 | 
						|
		self.lastKeyTime = -1
 | 
						|
		self.keysWaitTime = keysWaitTime
 | 
						|
		if height == None:
 | 
						|
			height = (len(self.items)+2) if (len(self.items)+2)<self.base.maxy-2 else self.base.maxy-2
 | 
						|
		self.height = height
 | 
						|
		if type(default) not in (list, tuple):
 | 
						|
			default = [default]
 | 
						|
		self.selections = default
 | 
						|
		self.pos = self.selections[0]
 | 
						|
		self.lastDraw = None
 | 
						|
		self.draw()
 | 
						|
 | 
						|
	def draw(self):
 | 
						|
		title = self.title
 | 
						|
		windowY = self.y
 | 
						|
		listHeight = self.height-2
 | 
						|
		start = self.pos//listHeight
 | 
						|
		startL = (start*listHeight)
 | 
						|
#if startL%lsitHiehgt =  = 0 then we can clear the screen
 | 
						|
		endL = (start*listHeight)+listHeight
 | 
						|
		sw = windowY+2
 | 
						|
		if self.lastDraw != (startL, endL,[i for i in self.selections]):
 | 
						|
			show = self.items[startL:endL]
 | 
						|
			self.screen.move(windowY, 0)
 | 
						|
			self.screen.clrtoeol()
 | 
						|
			self.screen.addstr(windowY, 0, title)
 | 
						|
			for idx, itm in enumerate(show):
 | 
						|
				self.screen.move(idx+sw, 0)
 | 
						|
				self.screen.clrtoeol()
 | 
						|
				if self.multiple:
 | 
						|
					s = "[%s] %s" % (("+" if startL+idx in self.selections else "-"),itm[1],)
 | 
						|
				else:
 | 
						|
					s = "%s" % (itm[1],)
 | 
						|
				self.screen.addstr(idx+sw, 0, s)
 | 
						|
		self.lastDraw = (startL, endL,[i for i in self.selections])
 | 
						|
		self.screen.move(sw+(self.pos-startL), 0)
 | 
						|
#		self.setStatus("height = %d:startL = %d:endL = %d:pos = %d" % (self.height,startL,endL,sw+self.pos-startL))
 | 
						|
		self.screen.refresh()
 | 
						|
 | 
						|
	def search(self, key):
 | 
						|
		t = time.time()
 | 
						|
		if key in string.printable and (self.keys and t-self.lastKeyTime<self.keysWaitTime):
 | 
						|
			self.keys.append(key)
 | 
						|
			self.lastKeyTime = t
 | 
						|
		else:
 | 
						|
			self.keys = [key]
 | 
						|
		keys = "".join(self.keys)
 | 
						|
		keys = keys.lower()
 | 
						|
		j = -1
 | 
						|
		for i in self.items:
 | 
						|
			j += 1
 | 
						|
			if str(i[1]).lower().startswith(keys):
 | 
						|
				self.pos = j
 | 
						|
				break
 | 
						|
 | 
						|
	def handleKey(self, c):
 | 
						|
		if c == -1:
 | 
						|
			return None
 | 
						|
		if self.multiple and c == 32:
 | 
						|
			if self.pos in self.selections:
 | 
						|
				self.selections.remove(self.pos)
 | 
						|
			else:
 | 
						|
				self.selections.append(self.pos)
 | 
						|
		elif curses.ascii.isprint(c):
 | 
						|
			self.search(chr(c))
 | 
						|
		elif c == curses.KEY_UP:
 | 
						|
			if self.pos == 0:
 | 
						|
#we don't want to wrap around to the top
 | 
						|
				pass #self.pos = len(self.items)-1
 | 
						|
				self.beepIfNeeded()
 | 
						|
			else:
 | 
						|
				self.pos -= 1
 | 
						|
		elif c == curses.KEY_DOWN:
 | 
						|
			if self.pos == len(self.items)-1:
 | 
						|
				pass #self.pos = 0
 | 
						|
				self.beepIfNeeded()
 | 
						|
			else:
 | 
						|
				self.pos += 1
 | 
						|
		elif c in (10, 261): # newline or right arrow
 | 
						|
			self.done = 1
 | 
						|
			return self.selections if self.multiple else self.pos
 | 
						|
		elif c == 260: # left arrow quietly back out
 | 
						|
			self.done = 1
 | 
						|
			return self.selections if self.multiple else self.pos
 | 
						|
		self.draw()
 | 
						|
 | 
						|
 | 
						|
class question(Listbox):
 | 
						|
 | 
						|
	def handleKey(self, c):
 | 
						|
		if c == -1:
 | 
						|
			return None
 | 
						|
		if c == curses.KEY_UP:
 | 
						|
			if self.pos == 0:
 | 
						|
#we don't want to wrap around to the top
 | 
						|
				self.base.setStatus(self.title)
 | 
						|
				pass #self.pos = len(self.items)-1
 | 
						|
#				return 1
 | 
						|
			else:
 | 
						|
				self.pos -= 1
 | 
						|
		elif c == curses.KEY_DOWN:
 | 
						|
			if self.pos == len(self.items)-1:
 | 
						|
				self.setStatus(self.title) #self.pos = 0
 | 
						|
#				return 1
 | 
						|
			else:
 | 
						|
				self.pos += 1
 | 
						|
		elif c in (10, 261): # newline or right arrow
 | 
						|
			self.done = 1
 | 
						|
			return self.selections if self.multiple else self.pos
 | 
						|
		elif c == 260: # left arrow quietly back out
 | 
						|
			self.done = -1
 | 
						|
			return self.selections if self.multiple else self.pos
 | 
						|
		self.draw()
 | 
						|
 | 
						|
class fileBrowser(Listbox):
 | 
						|
 | 
						|
	def __init__(self, dir="./", select_type="file", action="", prev_items=[], extensions=None, *args, **kwargs):
 | 
						|
		self.select_type = select_type
 | 
						|
		self.selected_action = action
 | 
						|
		if dir:
 | 
						|
			self.dir = dir if dir else os.environ.get("HOME","/")
 | 
						|
		self.prev_items = prev_items
 | 
						|
		self.extensions = extensions
 | 
						|
		items = self.make_list()
 | 
						|
		super(fileBrowser, self).__init__(items=items, *args, **kwargs)
 | 
						|
 | 
						|
	def make_list(self):
 | 
						|
		self.pos = 0
 | 
						|
		files = []
 | 
						|
		folders = []
 | 
						|
		os.chdir(self.dir)
 | 
						|
		self.back_directory = os.path.abspath("..")
 | 
						|
		for i in self.prev_items:
 | 
						|
			folders.append((i, i))
 | 
						|
		for i in sorted(os.listdir(self.dir)):
 | 
						|
			if os.path.isdir(i):
 | 
						|
				folders.append((os.path.abspath(i), i))
 | 
						|
			else:
 | 
						|
				if self.extensions != None:
 | 
						|
					ext = i.split(".")[-1]
 | 
						|
					if ext not in self.extensions:
 | 
						|
						continue
 | 
						|
				files.append((os.path.abspath(i), i))
 | 
						|
		folders.extend(files)
 | 
						|
		return folders
 | 
						|
 | 
						|
	def getDir(self):
 | 
						|
		return self.items[self.pos][0]
 | 
						|
 | 
						|
	def expand(self):
 | 
						|
		self.dir = self.getDir()
 | 
						|
		self.items = self.make_list()
 | 
						|
		self.lastDraw = None
 | 
						|
		self.draw()
 | 
						|
 | 
						|
	def collapse(self):
 | 
						|
		self.dir = self.back_directory
 | 
						|
		self.items = self.make_list()
 | 
						|
		self.lastDraw = None
 | 
						|
		self.draw()
 | 
						|
 | 
						|
	def handleKey(self, c):
 | 
						|
		if c == -1:
 | 
						|
			return None
 | 
						|
		if self.multiple and c == 32:
 | 
						|
			if self.pos in self.selections:
 | 
						|
				self.selections.remove(self.pos)
 | 
						|
			else:
 | 
						|
				self.selections.append(self.pos)
 | 
						|
			return 1
 | 
						|
		elif curses.ascii.isprint(c):
 | 
						|
			self.search(chr(c))
 | 
						|
			return 1
 | 
						|
		elif c == curses.KEY_UP:
 | 
						|
			if self.pos == 0:
 | 
						|
#we don't want to wrap around to the top
 | 
						|
				pass #self.pos = len(self.items)-1
 | 
						|
				self.beepIfNeeded()
 | 
						|
			else:
 | 
						|
				self.pos -= 1
 | 
						|
		elif c == curses.KEY_DOWN:
 | 
						|
			if self.pos == len(self.items)-1:
 | 
						|
				pass #self.pos = 0
 | 
						|
				self.beepIfNeeded()
 | 
						|
			else:
 | 
						|
				self.pos += 1
 | 
						|
		elif c in (10, 261, curses.KEY_RIGHT): # newline or right arrow
 | 
						|
			if self.getDir() in self.prev_items:
 | 
						|
				self.done = 1
 | 
						|
				self.dir = self.getDir()
 | 
						|
				return 1
 | 
						|
			elif os.path.isfile(self.getDir()) and self.select_type == "file":
 | 
						|
				self.done = 1
 | 
						|
				self.dir = self.getDir()
 | 
						|
				self.setStatus(self.dir)
 | 
						|
				return 1
 | 
						|
			if os.path.isdir(self.getDir()) and self.select_type != "file":
 | 
						|
				self.done = 1
 | 
						|
				return 1
 | 
						|
			self.expand()
 | 
						|
			return 1
 | 
						|
		elif c == curses.KEY_LEFT or c == curses.KEY_BACKSPACE: # left arrow quietly back out
 | 
						|
			self.collapse()
 | 
						|
			return 1
 | 
						|
		self.draw()
 | 
						|
 |