Laboratório de programação 2º Trabalho Março de 2016
Mini editor de texto: Funcionalidade base: 1 Estrutura de dados para buffer (1ª fase) 2 Editor interativo de ecrân (2ª fase) : edição de múltiplos ficheiros cortar, copiar e colar texto desfazer edições ( undo )
Funcionalidade base
Estrutura Buffer Funcionalidade base Atributos: lista de linhas lógicas (comprimento arbitrário) posição do cursor (linha, coluna) Métodos: inserir um carater apagar um carater mover o cursor obter número de linhas obter a n-ésima linha
Invariantes do Buffer 1 A lista de linhas nunca é vazia há sempre pelo menos uma linha vazia 2 A posição do cursor (row, col) é sempre válida: 0 row < número total de linhas 0 col comprimento da linha do cursor 3 As linhas lógicas não contém mudanças de linha (\n) Todos métodos devem assumir estas condições à entrada e garantir que se continuam verificar à saida.
public class Buffer { private List<StringBuilder> linelist; private int cursorrow, cursorcol; // inicialização public Buffer() {... // inserir carater public void insert(char c) {... // apagar carater public void delete() {... // mover o cursor public void moveprev() {......
Gravar e escrever ficheiros Podemos acrescentar novos métodos à classe Buffer Alternativa: definir uma nova classe FileBuffer que: Atenção: extende a classe Buffer (herança) mantém um caminho para um ficheiro associado mantém o estado modified/unmodified anotações de throws para checked exceptions (I/O) overriding dos métodos modificadores de Buffer
import java.nio.file.path; public class FileBuffer extends Buffer { private Path savepath; // null= não definido private boolean modified; // true= modificado; false= inalterado // gravar public void save() throws IOException {... public void saveas(path path) throws IOException {......
... // abrir public void open(path path) throws IOException {... @Override public void insert(char c) { super.insert(c); modified = true; // marcar modificação // análogo para outros modificadores...
Visualização Funcionalidade base Representar uma vista sobre o Buffer tamanho em carateres (largura, altura) linha lógica da primeira linha Cada linha lógica pode corresponder a várias linhas visuais O número de linhas lógicas visiveis pode ser menor que o de linhas visuais
3 linhas lógicas em 4 linhas visuais
Conversão de coordenadas Implemente um método auxiliar para converter coordenadas lógicas em visuais. Ideia do algoritmo 1 Começar na coluna 0 da primeira linha lógica visível (coordenadas visuais 0,0) 2 Para cada linha lógica: obter o seu comprimento distribuir por linhas visuais (função da largura da janela) avançar coordenadas visuais 3 Repetir até chegar às coordenadas lógicas desejadas
public class BufferView { private Buffer buffer; // ou FileBuffer private int width, height; private int startrow; // linha início... // converter uma posição lógica em visual // * `null' se a posição está fora da janela public Position viewpos(position logical) {... public class Position { // par de coordenadas linha, coluna...
Atualização do Ecrân Podemos desenhar a janela completa a cada modificação Desvantagem: ineficiente, flickering Alternativa melhor: manter uma lista dos índices das linhas lógicas alteradas re-desenhar apenas essas
public class BufferView {... private List<Integer> modifiedlines; // linhas alteradas public void redraw() { for (Integer line : modifiedlines) {... // desenhar a linha modifiedlines.clear(); // feito
Atualização de Ecrân (2) Não esquecer: acrescentar índices à lista sempre que há modificações! A ordem dos índices não é importante e não há repetidos Em vez de uma lista pode usar um conjunto
Copiar, Cortar e Colar Três fases: 1 marcar o início do texto (e.g. Ctrl-space); 2 copiar ou cortar (e.g. Ctrl-C ou Ctrl-X); 3 mover o cursor e colar (e.g. Ctrl-V).
Sugestões Funcionalidade base Modificar a classe Buffer: atributos para a marca de início atributo para o clipboard métodos para definir o início, copiar, cortar e colar
public class Buffer {... private int markrow, markcol; // linha e coluna de início private boolean marked; // está marcado? private StringBuilder clipboard; // carateres copiados public void setmark(int line, int col) {... public void unsetmark() {... public void copy() {... public void cut() {... public void paste() {...
Edição de múltiplos ficheiros Fácil se Buffer, FileBuffer e BufferView mantiverem estado localizado (e.g. atributos não static) Cada ficheiro fica associado ao seu BufferView e FileBuffer Sugestões: receber a lista de ficheiros da linha de comandos navegação usando uma lista circular (e.g. Ctrl-B troca para o próximo ficheiro) alternativa: abrir múltiplos terminais do Lanterna (mais trabalho)
Desfazer edições Funcionalidade base Primeira abordagem: fazer cópia de todo o Buffer antes de cada operação manter uma lista dos n últimos Buffer (número de undos permitido) Problemas: necessário copiar todas as sub-estruturas mutáveis (e.g. copiar todos os StringBuilder e não só a lista ligada) ineficiente: obriga a copiar todo o texto por cada modificação
Desfazer edições Funcionalidade base Segunda abordagem: uma estrutura de dados para representar operações de edições: inserir um carater, apagar um carater,... mantém informação necessária para fazer ou desfazer a operação guardar a lista das últimas n operações: acrescentamos um novo elemento sempre que fazemos uma edição removemos um elemento quando desfazemos uma edição
Operações de edição public class Edit { enum EditOp { INSERT, DELETE,... ; private EditOp op; private int cursorrow, cursorcol; // posição do cursor private char c; // outros argumentos necessários...
Desfazer edições Funcionalidade base public class Buffer { private List<Edit> undolist; // últimas edições efetuadas... // desfazer uma operação private void undo(edit ed) {... // desfazer a última operação public void undo() {... /* FALTA: modificar métodos insert e delete para acrescentar operações à undolist */