Source

BASIC-RoBots / src / terminal.py

Full commit
##
## terminal.py for BASIC-RoBots
##
## Copyright (C) 2012 Pierre Surply
## <pierre.surply@gmail.com>
##
## This file is part of BASIC-RoBots.
##
##    BASIC-RoBots is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    BASIC-RoBots 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 General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with BASIC-RoBots.  If not, see <http://www.gnu.org/licenses/>.
## 
## Started on  Wed May  9 18:57:29 2012 Pierre Surply
## Last update Sun Aug 19 18:25:54 2012 Pierre Surply
##

import time
from pygame.locals import *

class Info:
    def __init__(self):
        self.content = ""
        self.tps = 0

    def write_info(self, content, color = 1, tps = 3):
        self.content = content
        self.color = color
        self.tps = time.time() + tps

    def show(self):
        return self.tps > time.time()

class Terminal:
    def __init__(self, events):
        self.events = events

        self.set_title("Terminal")
        self.lines = [[]]
        
        self.hist = []
        self.id_hist = 0

        self.act_prompt = False
        self.buf = ""
        self.pos_curseur = 0
        self.char = ""

        self.info = Info()

    def set_title(self, s):
        self.org_title = self.str2charlist(s)
        self.title = self.org_title

    def str2charlist(self, s, m=0x00):
        return [ord(c) | (m << 8) for c in s]

    def write_line(self, s, m=0x00):
        self.write(s + "\n", m)
        
    def write(self, s, m=0x00):
        for i in s.split("\n"):
            self.lines[-1].extend(self.str2charlist(i, m))
            self.lines.append([])
        del self.lines[-1]
        
    def clear(self):
        self.lines = [[]]

    def display(self, display, (org_x, org_y, w, h), info_bottom = None, info_top=None):
        display.set_tile()
        display.draw_frame((org_x, org_y, w, h), 177)
        self.display_line(display, self.title, (org_x+2, 0))
        if self.info.show():
          self.display_line(display, self.str2charlist(self.info.content, self.info.color), (org_x, h))  
        elif info_bottom != None:
            x = 0
            for i in sorted(info_bottom[0].keys()):
                if isinstance(info_bottom[0][i], int):
                    val = hex(info_bottom[0][i])
                else:
                    val = str(info_bottom[0][i])
                self.display_line(display, \
                                      self.str2charlist(i+":"+val, 1), (org_x+x, h))
                x += info_bottom[1]
        if info_top != None:
            x = w-16
            for i in info_top[0].keys():
                self.display_line(display, \
                                      self.str2charlist(i+":"+hex(info_top[0][i]), 1), (org_x+x, 0))
                x += info_top[1]
        self.display_lines(display, (org_x+1, org_y+1, w-1, h-2))

    def display_lines(self, display, (org_x, org_y, w, h)):
        y = 0
        for line in reversed(self.lines):
            y = self.cut_line(display, line, (org_x, org_y, w-1, h), y, True) + 1

    def display_line(self, display, line, (x, y)):
        for c in line:
            display.print_char((x, y), c)
            x += 1

    def cut_line(self, display, line, (org_x, org_y, w, h), y, first):
        if y <= h:
            if not first:
                display.print_char((org_x+w, org_y+h-y), ord('\\'))
            if len(line) > w:
                middle = len(line)%w
                if middle == 0:
                    middle = w
                self.display_line(display, line[-middle:], (org_x, org_y+h-y))
                return self.cut_line(display, line[:-middle], (org_x, org_y, w, h), y+1, False)
            else:
                self.display_line(display, line, (org_x, org_y+h-y))
                return y
        else:
            return y

    def start_prompt(self, char):
        self.act_prompt = True
        self.events.rec_char = True
        self.char = char
        self.write(char + " ")
        self.id_hist = 0
        self.title = self.org_title

    def end_prompt(self, events):
        self.act_prompt = False
        events.rec_char = False
        self.lines[-1] = self.str2charlist(self.char + " "+ self.buf)
        self.write_line("")
        buf = self.buf
        self.buf = ""
        if len(buf) > 0:
            self.hist.append(buf)
        return buf
        
    def prompt(self):
        up = self.events.key[K_UP]
        down = self.events.key[K_DOWN]
        right = self.events.key[K_RIGHT]
        left = self.events.key[K_LEFT]
        if (up or down) and len(self.hist) > 0:
            if up:
                self.id_hist += 1
                self.events.key[K_UP] = False
            else:
                self.id_hist -= 1
                self.events.key[K_DOWN] = False
            self.id_hist %= len(self.hist)
            self.buf = self.hist[-self.id_hist]
            self.pos_curseur = len(self.buf)

        if self.events.get_key_once(K_RIGHT) and\
                self.pos_curseur < len(self.buf):
            self.pos_curseur += 1
        elif self.events.get_key_once(K_LEFT) and\
                self.pos_curseur > 0:
            self.pos_curseur -= 1
            
        if (self.events.key[K_RCTRL] or \
                self.events.key[K_LCTRL]):
            if self.events.get_key_once(K_u):
                self.buf = self.buf[self.pos_curseur:]
                self.pos_curseur = 0
            if self.events.get_key_once(K_k):
                self.buf = self.buf[:self.pos_curseur]
            elif self.events.get_key_once(K_l):
                self.clear()
            elif self.events.get_key_once(K_a):
                self.pos_curseur = 0
            elif self.events.get_key_once(K_e):
                self.pos_curseur = len(self.buf)
        if len(self.events.char_stack) > 0:
            c = self.events.char_stack.popleft()
            if len(c) == 1:
                c = ord(c)
                if 32 <= c <= 126:
                    self.buf = self.buf[:self.pos_curseur] + chr(c) + self.buf[self.pos_curseur:]
                    self.pos_curseur += 1
                elif c == 8 and len(self.buf) > 0:
                    if self.pos_curseur > 0:
                        self.buf = self.buf[:self.pos_curseur-1] + self.buf[self.pos_curseur:]
                        self.pos_curseur -= 1
                elif c == 13:
                    return self.end_prompt(self.events)
        self.lines[-1] = self.str2charlist(self.char + " " + self.buf)
        if len(self.buf) < self.pos_curseur:
            self.pos_curseur = len(self.buf)
        if len(self.lines[-1]) < len(self.char) + 2 + self.pos_curseur:
            self.lines[-1].append(ord(" "))
        self.lines[-1][len(self.char) + 1 + self.pos_curseur] |= (1 << 8)
        for i in range(len(self.char) - 1):
            self.lines[-1][i] |= (4 << 8)

    def start_edit(self, path, f):
        self.clear()
        self.edit_lines = f.split("\n")
        self.pos_curseur_edit = [0,0]
        self.events.rec_char = True
        self.begin = 0
        self.path_edit = path
        self.title = self.str2charlist("Edit \""+ self.path_edit +"\"")
        
    def stop_edit(self):
        self.events.rec_char = False
        self.clear()

    def info_cursor_moved_edit(self):
        self.info.write_info("Cursor moved to (" + \
                                 str(self.pos_curseur_edit[0]) + ", " +\
                                 str(self.pos_curseur_edit[1]) + ")")
        
    def edit(self, o, h):
        h = h-2
        self.clear()
        self.lines = [""] * h

        if self.events.get_key_once(K_RIGHT) and\
                self.pos_curseur_edit[0] < len(self.edit_lines[self.pos_curseur_edit[1]]):
            self.pos_curseur_edit[0] += 1
            self.info_cursor_moved_edit()
        elif self.events.get_key_once(K_LEFT) and\
                self.pos_curseur_edit[0] > 0:
            self.pos_curseur_edit[0] -= 1
            self.info_cursor_moved_edit()
        elif self.events.get_key_once(K_DOWN) and\
                self.pos_curseur_edit[1] < len(self.edit_lines)-1:
            self.pos_curseur_edit[1] += 1
            self.info_cursor_moved_edit()
        elif self.events.get_key_once(K_UP) and\
                self.pos_curseur_edit[1] > 0:
            self.pos_curseur_edit[1] -= 1
            self.info_cursor_moved_edit()
        if (self.events.key[K_RCTRL] or \
                self.events.key[K_LCTRL]):
            if self.events.get_key_once(K_k):
                self.edit_lines[self.pos_curseur_edit[1]] = \
                    self.edit_lines[self.pos_curseur_edit[1]][:self.pos_curseur_edit[0]]
            elif self.events.get_key_once(K_a):
                self.pos_curseur_edit[0] = 0
            elif self.events.get_key_once(K_e):
                self.pos_curseur_edit[0] = len(self.edit_lines[self.pos_curseur_edit[1]])
            elif self.events.get_key_once(K_s):
                o.save_file(self.path_edit, self.edit_lines)
                self.info.write_info("Wrote " + self.path_edit)
            
        if self.pos_curseur_edit[1] < self.begin:
            self.begin = self.pos_curseur_edit[1]
        elif self.pos_curseur_edit[1] > self.begin+h-2:
            self.begin = self.pos_curseur_edit[1]-h+2

        if len(self.events.char_stack) > 0:
            self.show_cursor = True
            c = self.events.char_stack.popleft()
            if len(c) == 1:
                c = ord(c)
                if 32 <= c <= 126:
                    self.edit_lines[self.pos_curseur_edit[1]] = \
                        self.edit_lines[self.pos_curseur_edit[1]][:self.pos_curseur_edit[0]]\
                        + chr(c) \
                        + self.edit_lines[self.pos_curseur_edit[1]][self.pos_curseur_edit[0]:]
                    self.pos_curseur_edit[0] += 1
                elif c == 9:
                    self.edit_lines[self.pos_curseur_edit[1]] = \
                        self.edit_lines[self.pos_curseur_edit[1]][:self.pos_curseur_edit[0]]\
                        + "   " \
                        + self.edit_lines[self.pos_curseur_edit[1]][self.pos_curseur_edit[0]:]
                    self.pos_curseur_edit[0] += 3
                elif c == 8:
                    if self.pos_curseur_edit[0] > 0:
                        self.edit_lines[self.pos_curseur_edit[1]] = \
                            self.edit_lines[self.pos_curseur_edit[1]][:self.pos_curseur_edit[0]-1] + \
                            self.edit_lines[self.pos_curseur_edit[1]][self.pos_curseur_edit[0]:]
                        self.pos_curseur_edit[0] -= 1
                    elif self.pos_curseur_edit[1] > 0:
                        self.pos_curseur_edit[0] = \
                            len(self.edit_lines[self.pos_curseur_edit[1]-1])
                        self.edit_lines[self.pos_curseur_edit[1]-1] += \
                            self.edit_lines[self.pos_curseur_edit[1]]
                        del self.edit_lines[self.pos_curseur_edit[1]]
                        self.pos_curseur_edit[1] -= 1
                elif c == 13:
                    self.edit_lines.insert(self.pos_curseur_edit[1]+1,
                                           self.edit_lines[self.pos_curseur_edit[1]][self.pos_curseur_edit[0]:])
                    self.edit_lines[self.pos_curseur_edit[1]] = \
                        self.edit_lines[self.pos_curseur_edit[1]][:self.pos_curseur_edit[0]]
                    self.pos_curseur_edit[1] +=1
                    self.pos_curseur_edit[0] = 0
        i=0
        while i < h and i < len(self.edit_lines)-self.begin:
            self.lines[i] = self.str2charlist(":" + self.edit_lines[i+self.begin])
            i += 1

        cur_line = self.lines[self.pos_curseur_edit[1]-self.begin]
        if len(cur_line)-1 <= self.pos_curseur_edit[0]:
            cur_line.append(ord(" "))
            self.pos_curseur_edit[0] = len(cur_line)-2
        cur_line[self.pos_curseur_edit[0]+1] |= (1 << 8)
        self.lines[self.pos_curseur_edit[1]-self.begin] = cur_line
        if self.events.key[K_LCTRL] and self.events.get_key_once(K_c):
            self.stop_edit()
            return False
        else:
            return True