package tirateima.gui.arquivos;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import tirateima.IEstado;
import tirateima.gui.variaveis.VarChar;
import tirateima.gui.variaveis.VarInteger;
import tirateima.gui.variaveis.VarReal;
/**
* O HidhlightPainter para arquivos texto.
*
* @author Luciano Santos
*/
class VarTextHighlightPainter implements Highlighter.HighlightPainter {
Color color;
public VarTextHighlightPainter(Color c){
color = c;
}
public Color getColor(){
return color;
}
/**
* Pinta o highlight.
*/
public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
if(offs0 != offs1){
try {
offs1 = drawHighlight(g, offs0, offs1, c);
drawMarks(g, offs0, offs1, c);
} catch (BadLocationException e) {
}
}
}
private int drawHighlight(
Graphics g,
int offs0, int offs1,
JTextComponent c) throws BadLocationException {
g.setColor(getColor() == null ? c.getSelectionColor() : getColor());
TextUI mapper = c.getUI();
int i;
Rectangle p0 = null, p1 = null;
int newoffs1 = offs1;
while (offs0 < offs1) {
//vai andando nos offsets até mudar de linha ou acabar
p0 = mapper.modelToView(c, offs0);
for (i = offs0; i < offs1; i++) {
p1 = mapper.modelToView(c, i + 1);
if (p0.y != p1.y) //mudou de linha
break;
}
if (i < offs1) {
//corrige posição, ignorando marca de final de linha
newoffs1 = i - 1;
p1 = mapper.modelToView(c, newoffs1);
}
Rectangle r = p0.union(p1);
g.fillRect(r.x, r.y, r.width, r.height);
//prepara para a próxima linha
offs0 = i + 1;
}
return newoffs1;
}
private void drawMarks(
Graphics g,
int offs0, int offs1,
JTextComponent c) throws BadLocationException {
//determina as posições
TextUI mapper = c.getUI();
Rectangle p0 = mapper.modelToView(c, offs0);
Rectangle p1 = mapper.modelToView(c, offs1);
/* Desenha os marcadores */
int altura = p0.height/4;
int largura = (int) (altura * 0.85);
int xpoints[] = new int[3];
int ypoints[] = new int[3];
/* Primeiro */
/* Esquerdo. */
xpoints[0] = p0.x;
ypoints[0] = p0.y + p0.height;
/* Superior. */
xpoints[1] = p0.x;
ypoints[1] = p0.y + p0.height - altura;
/* Direito. */
xpoints[2] = p0.x + largura;
ypoints[2] = p0.y + p0.height;
g.setColor(new Color(255, 30, 150));
g.drawPolygon(new Polygon(xpoints, ypoints, 3));
g.fillPolygon(new Polygon(xpoints, ypoints, 3));
/* Segundo */
/* Canto esquerdo. */
xpoints[0] = p1.x + (p1.x == 0 ? 0 : -1) * largura;
ypoints[0] = p1.y + p1.height;
/* Ponto superior. */
xpoints[1] = p1.x;
ypoints[1] = p1.y + p1.height - altura;
/* Canto direito. */
xpoints[2] = p1.x;
ypoints[2] = p1.y + p1.height;
g.setColor(new Color(150, 30, 255));
g.drawPolygon(new Polygon(xpoints, ypoints, 3));
g.fillPolygon(new Polygon(xpoints, ypoints, 3));
}
}
public class VarText extends AbstractArquivo implements IEstado{
private class Linha{
/* Posições de início e fim de linha */
public int off1, off2;
public String valor = "";
public Linha(String valor, int off1, int off2){
this.off1 = off1;
this.off2 = off2;
this.valor = valor;
}
}
private class EstadoVarText {
public int lin_atual;
public int pos_ant_lin;
public int pos_atual_lin;
public int off1, off2;
public List<Linha> linhas;
public boolean is_opened, read_only;
public File file;
}
private JTextPane pPrincipal;
/** Número da linha atual. */
private int lin_atual = 0; //linha atual
/** Último caractere lido da linha atual. */
private int pos_ant_lin = 0;
/** Caractere a ser lido no momento. */
private int pos_atual_lin = 1;
/** Offsets para highlighting. */
private int off1 = 0, off2 = 0;
/** Lista das linhas nesse vartext. */
private List<Linha> linhas = new ArrayList<Linha>();
private StringBuffer texto = null;
private static final long serialVersionUID = 1L;
/**
* Cria uma nova variável do tipo arquivo texto de Pascal.
*
* @param nome Nome desta variável
*/
public VarText(String nome){
super(nome);
inicializar();
}
public void close() throws IOException {
linhas = null;
lin_atual = pos_atual_lin = pos_ant_lin = 0;
pPrincipal.setText("");
}
public boolean eof() {
if(linhas == null){
return true;
}else if(lin_atual == linhas.size()){
return true;
}else if(lin_atual == linhas.size() - 1){
return pos_atual_lin == linhas.get(lin_atual).valor.length();
}else
return false;
}
public Color getCorTitulo() {
return Color.green;
}
public Object read() throws IOException {
if(eof()){
throw new IOException("Fim de arquivo atingido!");
}
/*Lê o próximo caractere. */
Linha linha = linhas.get(lin_atual);
char c;
off1 = linha.off1 + pos_ant_lin;
/* Chegou ao final da linha ? */
if(pos_atual_lin > linha.valor.length()){
off2 = linha.off2;
pos_ant_lin = 0;
pos_atual_lin = 1;
lin_atual++;
c = '\n';
}else{
off2 = linha.off1 + pos_atual_lin;
c = linha.valor.charAt(pos_ant_lin);
pos_ant_lin = pos_atual_lin++;
}
atualizarHighlights();
return new Character(c);
}
public String readString(int tam) throws IOException {
if ((tam <= 0) || (eof()))
throw new IllegalArgumentException("Tamanho para leitura inválido!");
off1 = linhas.get(lin_atual).off1 + pos_ant_lin;
StringBuffer sb = new StringBuffer();
int tam_restante_a_ler = tam;
do {
if (lin_atual >= linhas.size()) {
throw new IllegalArgumentException("Fim de arquivo atingido!");
}
Linha linha = linhas.get(lin_atual);
//o que resta da linha atual
String resto_linha = linha.valor.substring(pos_ant_lin);
int tam_resto_linha = resto_linha.length();
if (tam_resto_linha >= tam_restante_a_ler) {
sb.append(resto_linha.substring(0, tam_restante_a_ler));
if (tam_resto_linha > tam_restante_a_ler)
pos_atual_lin = (pos_ant_lin += tam_restante_a_ler) + 1;
else {
pos_atual_lin = (pos_ant_lin = 0) + 1;
lin_atual++;
}
break;
} else {
sb.append(resto_linha.substring(0, tam_resto_linha));
tam_restante_a_ler -= tam_resto_linha;
pos_atual_lin = (pos_ant_lin = 0) + 1;
lin_atual++;
}
} while (true);
off2 = linhas.get(lin_atual).off1 + pos_ant_lin;
return sb.toString();
}
public Object readInt() throws IOException{
String bloco = procurarBlocoIsolado();
int result;
try {
result = Integer.parseInt(bloco);
} catch (NumberFormatException e) {
return null;
}
return result;
}
public Object readReal() throws IOException{
String bloco = procurarBlocoIsolado();
double result;
try {
result = Double.parseDouble(bloco);
} catch (NumberFormatException e) {
return null;
}
return result;
}
public String readLine() throws IOException {
if(eof()){
throw new IOException("Fim de arquivo atingido!");
}
Linha linha = linhas.get(lin_atual);
String retorno = "";
off1 = linha.off1 + pos_ant_lin;
off2 = linha.off2 - 1;
if(pos_atual_lin <= linha.valor.length()){
retorno = linha.valor.substring(pos_ant_lin);
}
lin_atual++;
pos_ant_lin = 0;
pos_atual_lin = 1;
atualizarHighlights();
return retorno;
}
public void readln() throws IOException {
if(eof()){
throw new IOException("Fim de arquivo atingido!");
}
lin_atual++;
pos_ant_lin = 0;
pos_atual_lin = 1;
}
public void reset() throws Exception {
open();
read_only = true;
}
public void rewrite() throws Exception {
is_opened = true;
read_only = false;
texto = new StringBuffer();
linhas = new ArrayList<Linha>();
off1 = off2 = 0;
lin_atual = 0;
pos_atual_lin = 1;
pos_ant_lin = 0;
}
public void writeln(String s) {
write(s + "\n");
}
public void writeln(VarChar var) {
writeln(((Character) var.getValor()).toString());
}
public void writeln(VarInteger var) {
writeln(((Integer) var.getValor()).toString());
}
public void writeln(VarReal var) {
writeln(((Double) var.getValor()).toString());
}
public void write(String s) {
if (!is_opened) {
throw new RuntimeException("Arquivo fechado!");
}
if(read_only){
throw new RuntimeException("Arquivo aberto em modo de leitura!");
}
try{
texto.append(s);
linhas = gerarLinhas(new StringReader(texto.toString()));
}catch(IOException e){
e.printStackTrace();
}
}
public void write(VarChar var) {
write(((Character) var.getValor()).toString());
}
public void write(VarInteger var) {
write(((Integer) var.getValor()).toString());
}
public void write(VarReal var) {
write(((Double) var.getValor()).toString());
}
private void inicializar(){
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = gbc.weighty = 1.0;
add(new JScrollPane(getPPrincipal()), gbc);
}
private JTextPane getPPrincipal(){
if(pPrincipal == null){
pPrincipal = new JTextPane();
pPrincipal.setFont(pPrincipal.getFont().deriveFont(30.0f));
//pPrincipal.setEditable(false);
criarEstilos();
}
return pPrincipal;
}
private void atualizarHighlights() {
DefaultHighlighter dh = (DefaultHighlighter) pPrincipal.getHighlighter();
dh.removeAllHighlights();
try{
dh.addHighlight(off1, off2, new VarTextHighlightPainter(Color.orange));
pPrincipal.repaint();
pPrincipal.setCaretPosition(off2 + 1);
}catch(Exception e){}
}
private void open() throws Exception{
if(file == null){
throw new IOException("Nenhum nome associado ao arquivo!");
}
if (is_opened) {
close();
}
//lê o texto
BufferedReader br = new BufferedReader(getReader());
texto = new StringBuffer();
String linha = br.readLine();
if (linha != null) {
do {
texto.append(linha);
if ((linha = br.readLine()) != null)
texto.append("\n");
else
break;
}while (true);
}
linhas = gerarLinhas(new StringReader(texto.toString()));
off1 = off2 = 0;
lin_atual = 0;
pos_atual_lin = 1;
pos_ant_lin = 0;
atualizarHighlights();
is_opened = true;
}
private void arrumarTexto(){
StyledDocument doc = pPrincipal.getStyledDocument();
int len = doc.getLength();
try{
doc.remove(0, len == 0 ? 0 : len);
if (linhas != null) {
for(Linha l:linhas){
doc.insertString(doc.getLength(), l.valor, doc.getStyle("normal"));
doc.insertString(doc.getLength(), "\u2605\n", doc.getStyle("eol"));
}
//marca de final de arquivo só no modo de leitura
if (read_only)
doc.insertString(doc.getLength(), "\u2588\n", doc.getStyle("eof"));
}
}catch(Exception e){
e.printStackTrace();
}
}
private void criarEstilos() {
StyledDocument doc = pPrincipal.getStyledDocument();
StyleContext sc = StyleContext.getDefaultStyleContext();
Font f = pPrincipal.getFont();
/* Estilo para texto qualquer...*/
Style normal = sc.new NamedStyle();
normal.addAttributes(StyleContext.getDefaultStyleContext().
getStyle(StyleContext.DEFAULT_STYLE).copyAttributes());
//StyleConstants.setFontSize(normal, 20);
StyleConstants.setFontFamily(normal, f.getFamily());
StyleConstants.setBold(normal, true);
doc.addStyle("normal", normal);
/* Estilo para caractere de fim de linha...*/
Style s = sc.new NamedStyle();
s.addAttributes(normal.copyAttributes());
StyleConstants.setForeground(s, Color.BLUE);
StyleConstants.setBold(s, false);
doc.addStyle("eol", s);
/* Estilo para caractere de fim de arquivo...*/
s = sc.new NamedStyle();
s.addAttributes(normal.copyAttributes());
StyleConstants.setForeground(s, Color.RED);
StyleConstants.setBold(s, false);
doc.addStyle("eof", s);
}
private List<Linha> gerarLinhas(Reader origem) throws IOException{
List<Linha> lista = new ArrayList<Linha>();
BufferedReader br = new BufferedReader(origem);
String linha = null;
int i = 0;
while ((linha = br.readLine()) != null) {
int off1 = i;
/* Tamanho da linha + tamanho do sinal de eoln */
i += linha.length() + 1;
lista.add(new Linha(linha, off1, i));
i++;
}
return lista;
}
//IEstado
public Object getEstado() {
EstadoVarText e = new EstadoVarText();
e.is_opened = is_opened;
e.file = file;
e.read_only = read_only;
if (linhas != null) {
e.linhas = new ArrayList<Linha>();
e.linhas.addAll(linhas);
} else {
e.linhas = null;
}
e.off1 = off1;
e.off2 = off2;
e.lin_atual = lin_atual;
e.pos_ant_lin = pos_ant_lin;
e.pos_atual_lin = pos_atual_lin;
return e;
}
public void setEstado(Object estado) {
if(estado == null) {
linhas = new ArrayList<Linha>();
assign(null);
off1 = off2 = 0;
lin_atual = 0;
pos_atual_lin = 1;
pos_ant_lin = 0;
}
else {
EstadoVarText e = (EstadoVarText) estado;
is_opened = e.is_opened;
assign(e.file == null ? null : e.file.getName());
read_only = e.read_only;
linhas = e.linhas;
off1 = e.off1;
off2 = e.off2;
lin_atual = e.lin_atual;
pos_ant_lin = e.pos_ant_lin;
pos_atual_lin = e.pos_atual_lin;
arrumarTexto();
atualizarHighlights();
}
validate();
repaint();
}
/**
* Procura um bloco isolado por espaços em branco.
*
* Consome todos os espaços em branco até achar um caractere que
* não seja espaço em branco. Em seguida, pega todos os caracteres
* até o próximo espaço em branco, sem incluí-lo.
*
* Para fins desse método, c é um espaço em branco se
* java.lang.Character.isWhiteSpace(c)
* for true, ou seja, ' ', '\t', '\n', dentre outros, são espaços
* em branco.
*/
private String procurarBlocoIsolado() throws IOException {
if (eof()) {
throw new IOException("Fim de arquivo atingido!");
}
// Pega todos os espaço brancos até achar um não espaço em branco
Linha linha = linhas.get(lin_atual);
char buffer[];
int i;
do {
//o que resta da linha atual
buffer = linha.valor.substring(pos_ant_lin).toCharArray();
for (i = 0; i < buffer.length && Character.isWhitespace(buffer[i]); i++);
//terminou a linha?
if (i == buffer.length) {
lin_atual++;
if (lin_atual >= linhas.size()) {
throw new IOException("Fim de arquivo atingido!");
}
linha = linhas.get(lin_atual);
pos_ant_lin = 0;
pos_atual_lin = 1;
} else {
break; //i marca o primeiro caractere não espaço branco
}
} while (true);
/* Pega caracteres a partir da posição atual até achar um espaço em branco. */
pos_ant_lin += i;
off1 = linha.off1 + pos_ant_lin;
StringBuffer sb = new StringBuffer();
for(; (i < buffer.length) && (!Character.isWhitespace(buffer[i])); i++)
sb.append(buffer[i]);
off2 = off1 + sb.length();
pos_ant_lin += sb.length();
pos_atual_lin = pos_ant_lin + 1;
return sb.toString();
}
}