<filename>picotui/editor.py
#
# Simple VT100 terminal text editor widget
# Copyright (c) 2015 <NAME>
# Distributed under MIT License
#
import sys
import os

from .basewidget import Widget
from .defs import *


class Editor(Widget):

    def __init__(self, x=0, y=0, width=80, height=24):
        Widget.__init__(self)
        self.top_line = 0
        self.cur_line = 0
        self.row = 0
        self.col = 0
        self.x = x
        self.y = y
        self.height = height
        self.width = width
        self.margin = 0

    def set_cursor(self):
        self.goto(self.col + self.x, self.row + self.y)
        self.cursor(True)

    def adjust_cursor_eol(self):
        # Returns True if entire window needs redraw
        val = 0
        if self.content:
            val = self.col + self.margin
            val = min(val, len(self.content[self.cur_line]))
        if val > self.width - 1:
            self.margin = val - (self.width - 1)
            self.col = self.width - 1
            return True
        else:
            self.col = val - self.margin
            return False

    def set_lines(self, lines):
        self.content = lines
        self.total_lines = len(lines)

    def redraw(self):
        self.cursor(False)
        i = self.top_line
        r = self.y
        for c in range(self.height):
            self.goto(self.x, r)
            if i == self.total_lines:
                self.show_line("", -1)
            else:
                self.show_line(self.content[i], i)
                i += 1
            r += 1
        self.set_cursor()

    def update_line(self):
        self.cursor(False)
        self.goto(self.x, self.row + self.y)
        self.show_line(self.content[self.cur_line], self.cur_line)
        self.set_cursor()

    def show_line(self, l, i):
        l = l[self.margin:]
        l = l[:self.width]
        self.wr(l)
        self.clear_num_pos(self.width - len(l))

    def next_line(self):
        if self.row + 1 == self.height:
            self.top_line += 1
            return True
            self.redraw()
        else:
            self.row += 1
            return False
            self.set_cursor()

    def handle_cursor_keys(self, key):
        if not self.total_lines:
            return
        if key == KEY_DOWN:
            if self.cur_line + 1 != self.total_lines:
                self.cur_line += 1
                redraw = self.adjust_cursor_eol()
                if self.next_line() or redraw:
                    self.redraw()
                else:
                    self.set_cursor()
        elif key == KEY_UP:
            if self.cur_line > 0:
                self.cur_line -= 1
                redraw = self.adjust_cursor_eol()
                if self.row == 0:
                    if self.top_line > 0:
                        self.top_line -= 1
                        self.redraw()
                else:
                    self.row -= 1
                    if redraw:
                        self.redraw()
                    else:
                        self.set_cursor()
        elif key == KEY_LEFT:
            if self.col > 0:
                self.col -= 1
                self.set_cursor()
            elif self.margin > 0:
                self.margin -= 1
                self.redraw()
        elif key == KEY_RIGHT:
            self.col += 1
            if self.adjust_cursor_eol():
                self.redraw()
            else:
                self.set_cursor()
        elif key == KEY_HOME:
            self.col = 0
            if self.margin > 0:
                self.margin = 0
                self.redraw()
            else:
                self.set_cursor()
        elif key == KEY_END:
            self.col = len(self.content[self.cur_line])
            if self.adjust_cursor_eol():
                self.redraw()
            else:
                self.set_cursor()
        elif key == KEY_PGUP:
            self.cur_line -= self.height
            self.top_line -= self.height
            if self.top_line < 0:
                self.top_line = 0
                self.cur_line = 0
                self.row = 0
            elif self.cur_line < 0:
                self.cur_line = 0
                self.row = 0
            self.adjust_cursor_eol()
            self.redraw()
        elif key == KEY_PGDN:
            self.cur_line += self.height
            self.top_line += self.height
            if self.cur_line >= self.total_lines:
                self.top_line = self.total_lines - self.height
                self.cur_line = self.total_lines - 1
                if self.top_line >= 0:
                    self.row = self.height - 1
                else:
                    self.top_line = 0
                    self.row = self.cur_line
            self.adjust_cursor_eol()
            self.redraw()
        else:
            return False
        return True

    def handle_mouse(self, col, row):
        row -= self.y
        col -= self.x
        if 0 <= row < self.height and 0 <= col < self.width:
            cur_line = self.top_line + row
            if cur_line < self.total_lines:
                self.row = row
                self.col = col
                self.cur_line = cur_line
                self.adjust_cursor_eol()
                self.set_cursor()
                return True

    def handle_key(self, key):
        if key == KEY_QUIT:
            return key
        if self.handle_cursor_keys(key):
            return
        return self.handle_edit_key(key)

    def handle_edit_key(self, key):
            l = self.content[self.cur_line]
            if key == KEY_ENTER:
                self.content[self.cur_line] = l[:self.col + self.margin]
                self.cur_line += 1
                self.content[self.cur_line:self.cur_line] = [l[self.col + self.margin:]]
                self.total_lines += 1
                self.col = 0
                self.margin = 0
                self.next_line()
                self.redraw()
            elif key == KEY_BACKSPACE:
                if self.col + self.margin:
                    if self.col:
                        self.col -= 1
                    else:
                        self.margin -= 1
                    l = l[:self.col + self.margin] + l[self.col + self.margin + 1:]
                    self.content[self.cur_line] = l
                    self.update_line()
            elif key == KEY_DELETE:
                l = l[:self.col + self.margin] + l[self.col + self.margin + 1:]
                self.content[self.cur_line] = l
                self.update_line()
            else:
                l = l[:self.col + self.margin] + str(key, "utf-8") + l[self.col + self.margin:]
                self.content[self.cur_line] = l
                self.col += 1
                self.adjust_cursor_eol()
                self.update_line()

    def deinit_tty(self):
        # Don't leave cursor in the middle of screen
        self.goto(0, self.height)
        super().deinit_tty()


if __name__ == "__main__":
    with open(sys.argv[1]) as f:
        content = f.read().splitlines()
        #content = f.readlines()


#os.write(1, b"\x1b[18t")
#key = os.read(0, 32)
#print(repr(key))

#key = os.read(0, 32)
#print(repr(key))

    e = Editor()
    e.init_tty()
    e.enable_mouse()
    e.set_lines(content)
    e.loop()
    e.deinit_tty()
