diff options
| author | Mattias Andrée <maandree@operamail.com> | 2013-09-20 10:24:10 +0200 |
|---|---|---|
| committer | Mattias Andrée <maandree@operamail.com> | 2013-09-20 10:24:10 +0200 |
| commit | c0e5250c0721c82a075c2341d1872ef19f31be0d (patch) | |
| tree | 217e990ef75d3fa6f86cb7de4936ebe1a8d83b5b | |
| parent | m (diff) | |
| download | pytagomacs-c0e5250c0721c82a075c2341d1872ef19f31be0d.tar.gz pytagomacs-c0e5250c0721c82a075c2341d1872ef19f31be0d.tar.bz2 pytagomacs-c0e5250c0721c82a075c2341d1872ef19f31be0d.tar.xz | |
split up textarea and line into two files
Signed-off-by: Mattias Andrée <maandree@operamail.com>
| -rw-r--r-- | src/editor.py | 272 | ||||
| -rw-r--r-- | src/line.py | 288 |
2 files changed, 292 insertions, 268 deletions
diff --git a/src/editor.py b/src/editor.py index 25776a2..33ab321 100644 --- a/src/editor.py +++ b/src/editor.py @@ -29,6 +29,7 @@ _ = gettext.gettext from killring import * from editring import * +from line import * @@ -78,6 +79,8 @@ backspace = lambda x : (ord(x) == 127) or (ord(x) == 8) Check if a key stroke is a backspace key stroke ''' + + class Jump(): ''' Create a cursor jump that can either be included in a print statement @@ -132,7 +135,7 @@ class TextArea(): self.innerleft = len(max(self.fields, key = len)) + 3 self.killring, self.editring = Killring(), Editring() data = lambda field : datamap[field] if field in datamap else '' - self.lines = [TextArea.Line(self, self.fields[y], data(self.fields[y]), y) for y in range(len(self.fields))] + self.lines = [Line(self, self.fields[y], data(self.fields[y]), y) for y in range(len(self.fields))] self.areawidth = self.width - self.innerleft self.y, self.offy, self.x, self.offx, self.mark = 0, 0, 0, 0, None self.last_alert, self.last_status, self.alerted = None, None, False @@ -181,273 +184,6 @@ class TextArea(): return (a, b) - class Line(): - ''' - A line in the text area - ''' - - def __init__(self, area, name, text, y): - ''' - Constructor - - @param area:TextArea The text area - @param name:str The name of the, displayed at the left side - @param text:str The text in the line - @param y:int The y position of the line - - ''' - self.area, self.name, self.text, self.y = area, name, text, y - self.killring = self.area.killring - self.jump = lambda x : Jump(self.area.top + self.y - self.area.offy, self.area.left + self.area.innerleft + x) - - - def is_active(self): - ''' - Checks if the line is the focused line - - @return :bool Whether the line is the focused line - ''' - return self.area.y == self.y - - - def has_selection(self): - ''' - Checks if there is any text selected, assuming the line is focused - - @param :bool Whether there is any text selected - ''' - return atleast(self.area.mark, 0) and (self.area.mark != self.area.x) - - - def draw(self): - ''' - Redraw the line - ''' - if 0 <= self.y - self.area.offy < self.area.height - 2: - leftside = ACTIVE_COLOUR if self.is_active() else INACTIVE_COLOUR - if leftside is not None: - leftside = '%s\033[%sm%s:\033[00m' % (self.jump(-(self.area.innerleft)), leftside, self.name) - else: - leftside = '%s%s:' % (self.jump(-(self.area.innerleft)), self.name) - text = (self.text[self.area.offx if self.is_active() else 0:] + ' ' * self.area.areawidth)[:self.area.areawidth] - if self.is_active() and atleast(self.area.mark, 0): - (a, b) = self.area.get_selection(True) - if a != b: - if SELECTED_COLOUR is not None: - text = text[:a] + ('\033[%sm%s\033[00m' % (SELECTED_COLOUR, text[a : b])) + text[b:] - print('%s%s%s' % (leftside, self.jump(0), text), end='') - if self.is_active(): - self.jump(self.area.x - self.area.offx)() - - - def copy(self): - ''' - Copy the selected text - - @return :bool Whether any text select, and therefore copied - ''' - if self.has_selection(): - (a, b) = self.area.get_selection() - self.killring.add(self.text[a : b]) - (a, b) = self.area.get_selection(True) - text = self.text[self.area.offx:][:self.area.areawidth][a : b] - print('%s%s' % (self.jump(a), text), end='') - self.area.mark = None - return True - return False - - - def cut(self): - ''' - Cut the selected text - - @return :bool Whether any text select, and therefore cut - ''' - mark, x = self.area.mark, self.area.x - if self.copy(): - self.area.mark, self.area.x = mark, x - self.delete() - return True - return False - - - def kill(self): - ''' - Cut all text on the same line after the position of the point - - @return :bool Whether the point was not at the end of the line, and therefore a cut was made - ''' - if self.area.x < len(self.text): - self.area.mark = len(self.text) - self.cut() - return True - return False - - - def delete(self): - ''' - Delete the selected text or, if none, the character at the position of the point - - @return :bool The point was not at the end of the line or something was selected, and therefore a deletion was made - ''' - removed = 0 - if self.has_selection(): - (a, b) = self.area.get_selection() - self.text = self.text[:a] + self.text[b:] - self.area.x = a - if self.area.offx > len(self.text): - self.area.offx = max(len(self.text) - self.area.areawidth, 0) - self.area.mark = None - print('%s%s' % (self.jump(0), ' ' * self.area.areawidth), end='') - self.draw() - return True - removed = b - a - self.area.mark = None - if removed == 0: - if self.area.x == len(self.text): - return False - removed = 1 - self.text = self.text[:self.area.x] + self.text[self.area.x + 1:] - text = self.text[self.area.offx:][:self.area.areawidth] - a = limit(0, self.area.x - self.area.offx, self.area.areawidth) - print('%s%s%s' % (self.jump(a), text[a:] + ' ' * removed, self.jump(a)), end='') - return True - - - def erase(self): - ''' - Select the selected text or the character directly before the position of the point - - @return :bool Whether point as at the beginning of the line or any text was selected, and therefore an erasure was made - ''' - if not self.has_selection(): - self.area.mark = None - if self.area.x == 0: - return False - self.area.x -= 1 - if self.area.x < self.area.offx: - self.area.offx = max(self.area.offx - self.area.areawidth, 0) - self.draw() - self.jump(self.area.x - self.area.offx)() - self.delete() - return True - - - def yank(self): - ''' - Yank the text from the top of the killring - - @return :bool Whether the killring was not empty, and therefor a yank was made - ''' - if self.killring.is_empty(): - return False - self.area.mark = None - yanked = self.killring.get() - self.text = self.text[:self.area.x] + yanked + self.text[self.area.x:] - self.area.x += len(yanked) - if self.area.x > self.area.offx + self.area.areawidth: - self.area.offx = len(self.text) - self.area.areawidth - print('%s%s' % (self.jump(0), ' ' * self.area.areawidth), end='') - self.draw() - self.jump(self.area.x - self.area.offx)() - return True - - - def yank_cycle(self): - ''' - Replace the recently yank text with the next in the killring - - @return :bool False on failure, which happens if the killring is empty or if the text before the point is not the yanked text - ''' - if self.killring.is_empty(): - return False - yanked = self.killring.get() - if self.text[max(self.area.x - len(yanked), 0) : self.area.x] != yanked: - return False - self.area.mark = self.area.x - len(yanked) - self.delete() - self.killring.next() - self.yank() - return True - - - def move_point(self, delta): - ''' - Move the the point - - @param delta:int The number of steps to move the point to the right - @return :bool Whether the point has been moved - ''' - x = self.area.x + delta - if 0 <= x <= len(self.text): - self.area.x = x - if delta < 0: - if self.area.offx > self.area.x: - self.area.offx = max(self.area.x - 3 * self.area.areawidth // 4, 0) - self.draw() - else: - print('\033[%iD' % -delta, end='') - elif delta > 0: - if self.area.x - self.area.offx > self.area.areawidth: - self.area.offx = self.area.x - self.area.areawidth // 4 - self.draw() - else: - print('\033[%iC' % delta, end='') - return delta != 0 - return False - - - def swap_mark(self): - ''' - Swap the position of the mark and the position of the point - - @return :bool Whether the mark was set, and therefore as swap was made - ''' - if atleast(self.area.mark, 0): - self.area.mark, self.area.x = self.area.x, self.area.mark - return True - return False - - - def override(self, insert, override = True): - ''' - Insert a text (by default) by overriding the existing text at the position of the point - - @param insert:str The text to insert - @param override:bool Whether to override - ''' - if atleast(self.area.mark, 0): - self.area.mark = ~(self.area.mark) - if len(insert) == 0: - return - a, b = self.area.x, self.area.x - if override: - b = min(self.area.x + len(insert), len(self.text)) - self.text = self.text[:a] + insert + self.text[b:] - oldx = self.area.x - self.area.x += len(insert) - if self.area.x - self.area.offx < self.area.areawidth: - if not override: - print('%s\033[%iP' % (self.jump(self.area.areawidth - len(insert)), len(insert)), end='') - print('%s\033[%i@' % (self.jump(oldx - self.area.offx), len(insert)), end='') - print(insert, end='') - else: - self.area.offx = self.area.x - self.area.areawidth // 4 - self.jump(0)() - print(' ' * self.area.areawidth, end='') - self.draw() - - - def insert(self, insert): - ''' - Insert a text at the position of the point - - @param insert:str The text to insert - ''' - self.override(insert, False) - - - def limit_text(self, text): ''' Limit a text to fit the width of the screen diff --git a/src/line.py b/src/line.py new file mode 100644 index 0000000..843e913 --- /dev/null +++ b/src/line.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +''' +pytagomacs – An Emacs like key–value editor library for Python + +Copyright © 2013 Mattias Andrée (maandree@member.fsf.org) + +This program 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. + +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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +''' +from editor import * + + +class Line(): + ''' + A line in the text area + ''' + + def __init__(self, area, name, text, y): + ''' + Constructor + + @param area:TextArea The text area + @param name:str The name of the, displayed at the left side + @param text:str The text in the line + @param y:int The y position of the line + + ''' + self.area, self.name, self.text, self.y = area, name, text, y + self.killring = self.area.killring + self.jump = lambda x : Jump(self.area.top + self.y - self.area.offy, self.area.left + self.area.innerleft + x) + + + def is_active(self): + ''' + Checks if the line is the focused line + + @return :bool Whether the line is the focused line + ''' + return self.area.y == self.y + + + def has_selection(self): + ''' + Checks if there is any text selected, assuming the line is focused + + @param :bool Whether there is any text selected + ''' + return atleast(self.area.mark, 0) and (self.area.mark != self.area.x) + + + def draw(self): + ''' + Redraw the line + ''' + if 0 <= self.y - self.area.offy < self.area.height - 2: + leftside = ACTIVE_COLOUR if self.is_active() else INACTIVE_COLOUR + if leftside is not None: + leftside = '%s\033[%sm%s:\033[00m' % (self.jump(-(self.area.innerleft)), leftside, self.name) + else: + leftside = '%s%s:' % (self.jump(-(self.area.innerleft)), self.name) + text = (self.text[self.area.offx if self.is_active() else 0:] + ' ' * self.area.areawidth)[:self.area.areawidth] + if self.is_active() and atleast(self.area.mark, 0): + (a, b) = self.area.get_selection(True) + if a != b: + if SELECTED_COLOUR is not None: + text = text[:a] + ('\033[%sm%s\033[00m' % (SELECTED_COLOUR, text[a : b])) + text[b:] + print('%s%s%s' % (leftside, self.jump(0), text), end='') + if self.is_active(): + self.jump(self.area.x - self.area.offx)() + + + def copy(self): + ''' + Copy the selected text + + @return :bool Whether any text select, and therefore copied + ''' + if self.has_selection(): + (a, b) = self.area.get_selection() + self.killring.add(self.text[a : b]) + (a, b) = self.area.get_selection(True) + text = self.text[self.area.offx:][:self.area.areawidth][a : b] + print('%s%s' % (self.jump(a), text), end='') + self.area.mark = None + return True + return False + + + def cut(self): + ''' + Cut the selected text + + @return :bool Whether any text select, and therefore cut + ''' + mark, x = self.area.mark, self.area.x + if self.copy(): + self.area.mark, self.area.x = mark, x + self.delete() + return True + return False + + + def kill(self): + ''' + Cut all text on the same line after the position of the point + + @return :bool Whether the point was not at the end of the line, and therefore a cut was made + ''' + if self.area.x < len(self.text): + self.area.mark = len(self.text) + self.cut() + return True + return False + + + def delete(self): + ''' + Delete the selected text or, if none, the character at the position of the point + + @return :bool The point was not at the end of the line or something was selected, and therefore a deletion was made + ''' + removed = 0 + if self.has_selection(): + (a, b) = self.area.get_selection() + self.text = self.text[:a] + self.text[b:] + self.area.x = a + if self.area.offx > len(self.text): + self.area.offx = max(len(self.text) - self.area.areawidth, 0) + self.area.mark = None + print('%s%s' % (self.jump(0), ' ' * self.area.areawidth), end='') + self.draw() + return True + removed = b - a + self.area.mark = None + if removed == 0: + if self.area.x == len(self.text): + return False + removed = 1 + self.text = self.text[:self.area.x] + self.text[self.area.x + 1:] + text = self.text[self.area.offx:][:self.area.areawidth] + a = limit(0, self.area.x - self.area.offx, self.area.areawidth) + print('%s%s%s' % (self.jump(a), text[a:] + ' ' * removed, self.jump(a)), end='') + return True + + + def erase(self): + ''' + Select the selected text or the character directly before the position of the point + + @return :bool Whether point as at the beginning of the line or any text was selected, and therefore an erasure was made + ''' + if not self.has_selection(): + self.area.mark = None + if self.area.x == 0: + return False + self.area.x -= 1 + if self.area.x < self.area.offx: + self.area.offx = max(self.area.offx - self.area.areawidth, 0) + self.draw() + self.jump(self.area.x - self.area.offx)() + self.delete() + return True + + + def yank(self): + ''' + Yank the text from the top of the killring + + @return :bool Whether the killring was not empty, and therefor a yank was made + ''' + if self.killring.is_empty(): + return False + self.area.mark = None + yanked = self.killring.get() + self.text = self.text[:self.area.x] + yanked + self.text[self.area.x:] + self.area.x += len(yanked) + if self.area.x > self.area.offx + self.area.areawidth: + self.area.offx = len(self.text) - self.area.areawidth + print('%s%s' % (self.jump(0), ' ' * self.area.areawidth), end='') + self.draw() + self.jump(self.area.x - self.area.offx)() + return True + + + def yank_cycle(self): + ''' + Replace the recently yank text with the next in the killring + + @return :bool False on failure, which happens if the killring is empty or if the text before the point is not the yanked text + ''' + if self.killring.is_empty(): + return False + yanked = self.killring.get() + if self.text[max(self.area.x - len(yanked), 0) : self.area.x] != yanked: + return False + self.area.mark = self.area.x - len(yanked) + self.delete() + self.killring.next() + self.yank() + return True + + + def move_point(self, delta): + ''' + Move the the point + + @param delta:int The number of steps to move the point to the right + @return :bool Whether the point has been moved + ''' + x = self.area.x + delta + if 0 <= x <= len(self.text): + self.area.x = x + if delta < 0: + if self.area.offx > self.area.x: + self.area.offx = max(self.area.x - 3 * self.area.areawidth // 4, 0) + self.draw() + else: + print('\033[%iD' % -delta, end='') + elif delta > 0: + if self.area.x - self.area.offx > self.area.areawidth: + self.area.offx = self.area.x - self.area.areawidth // 4 + self.draw() + else: + print('\033[%iC' % delta, end='') + return delta != 0 + return False + + + def swap_mark(self): + ''' + Swap the position of the mark and the position of the point + + @return :bool Whether the mark was set, and therefore as swap was made + ''' + if atleast(self.area.mark, 0): + self.area.mark, self.area.x = self.area.x, self.area.mark + return True + return False + + + def override(self, insert, override = True): + ''' + Insert a text (by default) by overriding the existing text at the position of the point + + @param insert:str The text to insert + @param override:bool Whether to override + ''' + if atleast(self.area.mark, 0): + self.area.mark = ~(self.area.mark) + if len(insert) == 0: + return + a, b = self.area.x, self.area.x + if override: + b = min(self.area.x + len(insert), len(self.text)) + self.text = self.text[:a] + insert + self.text[b:] + oldx = self.area.x + self.area.x += len(insert) + if self.area.x - self.area.offx < self.area.areawidth: + if not override: + print('%s\033[%iP' % (self.jump(self.area.areawidth - len(insert)), len(insert)), end='') + print('%s\033[%i@' % (self.jump(oldx - self.area.offx), len(insert)), end='') + print(insert, end='') + else: + self.area.offx = self.area.x - self.area.areawidth // 4 + self.jump(0)() + print(' ' * self.area.areawidth, end='') + self.draw() + + + def insert(self, insert): + ''' + Insert a text at the position of the point + + @param insert:str The text to insert + ''' + self.override(insert, False) + |
