Towel release 1.1

34 respostas
M

Nova versão do Towel está pronta e disponivel para Download.

Os highlights dessa versão:

JImagePanel agora tem suporte a sequencia de imagens.
Um novo componente foi adicionado no projeto, o JTableView, é uma JTable com Footer.
ObjectTableModel foi melhorado e agora implementa Iterable apra ser facil trata-lo como uma coleção.
Um pacote para suporte de sons que foi migrado do JGF do ViniGodoy.
DynamicFormatter é um formatter dinamico criado pelo Felipe Priuli.

Agradeço principalmente ao Felipe Priuli e o Marco Biscaro pela colaboração com os Java Docs, que agora estão quase completos.
Ainda não foi hospedado em nenhum lugar mas já é possivel navegar pelas classes no github e ler os docs nas próprias classes.

Veja todas as novidades dessa versão aqui.
Download disponivel pelo github.

[]'s

34 Respostas

A

Legal. Achei interessante o recurso do bind. :smiley:

[]'s

D

Hehe

Não conhecia o projeto Towel ainda… achei legal o motivo do nome, que vi na descrição :smiley: To até pensando em ler aquele livro

E

Boooa.

Já está baixado.

Valeu…

K

Que legal, boa notícia.

D

Show de bola Marky!

O ObjectTableModel (o melhor model de JTable que já vi) ainda melhor!!! :smiley: :smiley: :smiley:

Parabéns!

D

Marky, eu estava testando a nova versão do Binder, e há algumas alterações que precisam ser feitas quanto ao exemplo de utilização do Binder.
São alguns trechos onde é preciso atualizar a sintaxe na nomenclatura de pacotes e classes.

Como testei agora o Binder, já efetuei as correções. Segue o código para você atualizar a página: http://markytechs.wordpress.com/2010/03/03/binder-2-0-agora-com-annotations/:

import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
import com.towel.bind.Binder;
import com.towel.bind.annotation.*;


@Form(Pessoa.class)
// Form para Pessoa
public class PessoaForm extends JFrame {
	@Bindable(field = "nome")
	private JTextField nome;
	@Bindable(field = "idade", formatter = IntFormatter.class)
	private JTextField idade;
	@Bindable(field = "vivo")
	// o Binder assume que o JCheckBox trata-se de um atributo boolean
	private JCheckBox vivo;
	private Binder binder;
	public PessoaForm() {
		super("PessoaForm");
		nome = new JTextField(20);
		idade = new JTextField(20);
		vivo = new JCheckBox("Vivo?");
		JButton add = new JButton("Add");
		JButton load = new JButton("Load");
		setLayout(new GridLayout(4, 2));
		add(new JLabel("Nome:"));
		add(nome);
		add(new JLabel("Idade:"));
		add(idade);
		add(new JLabel());// Por causa do GridLayout
		add(vivo);
		add(add);
		add(load);
		load.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				Pessoa pessoa = new Pessoa();
				pessoa.setNome("Marky");
				pessoa.setIdade(18);
				pessoa.setVivo(true);// Claro
				binder.updateView(pessoa);
			}
		});
		add.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				Pessoa pessoa = new Pessoa();
				binder.updateModel(pessoa);
				pessoa.printAttrs();
			}
		});
		pack();
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setLocationRelativeTo(null);
		binder = new com.towel.bind.annotation.AnnotatedBinder(this);
	}
	public static void main(String[] args) {
		new PessoaForm().setVisible(true);
	}
	// IntFormatter sera usado para transformar a String em numero.
	public static class IntFormatter implements com.towel.bean.Formatter {
		public Object format(Object obj) {
			Integer d = (Integer) obj;
			return d.toString();
		}
		public Object parse(Object obj) {
			return Integer.valueOf(Integer.parseInt((String) obj));
		}
		public String getName() {
			return "int";
		}
	}

        


}
M

Opa… obrigado por avisar, corrigido.

R

Pow, não consigo executar… quando dou 2 cliques mostra isso (anexo)…
tô fazendo alguma besteira? :S

M

Towel é um framework, não um executavel.

Voce precisa adicionar ao class-path como os jars do hibernate por exemplo e utilizar os recursos.

M

Artigo sobre JImagePanel publicado.

R

Confundi com tower defense =P

M

Heheh… realmente os nomes parecem um pouco. :stuck_out_tongue:

Mas também pretendo atualizar o Tower Defense.

P

Tai um jogo dificil e viciante… rss

J

Muito bom… :smiley:
Ainda não estou tendo tempo para poder contribuir (trabalhando em 2 projetos!), mas está ficando muito bom mesmo!

I

Marky, parabéns pelo release!

Marky, o Towel já tem algum formatador de Célula da JTable padrão?

Uma coisa que é sempre um saco é formatar/criar um bando de formatadores para cada célula em uma JTable.

Imagine o seguinte:

Você um campo valor que é um BigDecimal(salvo na base de dados como Decimal), mas que para o usuário, sempre aparecerá

formatado, e ele mesmo editará o valor formatado.

Penso que poderia haver algo dessa forma:

CellFormatter(BigDecimal inputValue,String pattern)

mandando algo do tipo:

CellFormatter(11901.99,"###,###,##0.00")

e poder editar e ainda pegar de volta assim:

e cf.getOriginalContent();
//pega o texto modificado no valor do tipo de origem(no caso BigDecimal passado a CellFormatter): 11901.95

poderia haver construtores dessa forma:
CellFormatter(BigDecimal inputValue,String pattern,Font font,Color color)
para decorar um dado campo.Podia talvez passar um Locale ou um DecimalFormat(ou até um JFormattedTextField) ao construtor.
Imagine isso para CEP,CPF… ia eliminar muito retrabalho do programador. :smiley:

M

Hmm… ainda não tem não, mas sua idéia está anotada. Vou ver se faço isso para o proximo release.

Obrigado!

M

Novo artigo sobre outra novidade, o DynamicFormatter, desenvolvido por Felipe Priuli.

M

Ultimo artigo do release publicado, sobre a JTableView.

V
Lendo o artigo da JTableView, vi que podemos criar AggregateFunctions. Ao criar uma tabela, precisei de uma função similar ou CONT.VALORES do Excel. Então fiz assim (muito simples por sinal):
public class FuncCountValues implements AggregateFunc<String> {
    private int total;

    @Override
    public String getResult() {
	return String.valueOf(total);
    }

    @Override
    public void init() {
	total = 0;
    }

    @Override
    public void update(String obj) {
	if (obj != null && !obj.isEmpty())
	    total++;
    }

}
Vale lembrar que, como no Excel, ela conta células que não estão vazias. Marky pode usá-lo se quiser. :wink:
M

Opa, vou usar sim. Vai estar no proximo release, obrigado.

Quem tiver mais AggregateFunctions interessantes pode enviar também :smiley:

D

Olá Marky,

Duvida simples , eu tenho um codigo para carregar uma tabela e eu estou querendo que apenas uma unica coluna possa ser editada, mas acontece um erro de null.

...
public LayoutFilesTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(LayoutFilesTopComponent.class, "CTL_LayoutFilesTopComponent"));
        setToolTipText(NbBundle.getMessage(LayoutFilesTopComponent.class, "HINT_LayoutFilesTopComponent"));        
        FieldResolverFactory tab = new FieldResolverFactory(TbParlayout.class);
        FieldResolver ord = tab.createResolver("ordem", "Ordem");
        FieldResolver ativo = tab.createResolver("ativo", "Ativo");
        FieldResolver alias = tab.createResolver("alias", "Alias");        
        ativo.setFormatter(new FormatAtivo());
        base = new ObjectTableModel<TbParlayout>(new FieldResolver[]{ativo, ord, alias});        
    }

...
private void jComboBox1ItemStateChanged(java.awt.event.ItemEvent evt) {
        base.setData(dados.getResult((String) jComboBox1.getSelectedItem()));      
        eTable1.setModel(base);        
        base.setColEditable(2, true);      
        
    }
...

onde estou errando ?

D

hello !!

Acho que isso pode ajudar, eu geralmente uso fieldresolver porque assim eu não preciso mexer nas minhas classes de entidades.
Então comecei a investigar o erro e achei a diferença e acredito que seja um possível bug.

O setColEditable não funciona quando vc usa o fieldresolver apenas quando se utiliza o annotations.

Será que essa é a unica solução ?!

abraços
Daniel

D

Achei o Erro,

Como eu falei, o erro não ocorre quando se utiliza Annotations.
Tomei a liberdade de fazer um download do codigo e achei o erro !!!

Existe uma diferença no construtor da classe ObjectTableModel, quando se usa o construtor com AnnotationResolver ele inicializa o array editableCol, bom direto no codigo vejam :

Código Original

...
public ObjectTableModel(AnnotationResolver resolver, String cols) {
		data = new ArrayList<T>();
		this.fields = (FieldResolver[]) resolver.resolve(cols).clone();
		editDefault = false;
		editableCol = new Boolean[fields.length];
	}
...
public ObjectTableModel(FieldResolver fields[]) {
		data = new ArrayList<T>();
		this.fields = (FieldResolver[]) fields.clone();
		editDefault = false;               
	}
...

Agora apenas o construtor alterado

...
public ObjectTableModel(FieldResolver fields[]) {
		data = new ArrayList<T>();
		this.fields = (FieldResolver[]) fields.clone();
		editDefault = false; 
                editableCol = new Boolean[fields.length]; // Aqui está a diferença !!
	}
...

é isso ai, valeu !

abcs
Daniel

M

Nossa… nunca tinha reparado.

Obrigado, já vou atualizar no projeto.

D

Tarde!!!

Mark, é possível fazer um format de uma coluna para que seja um combo ?

abcs !

Daniel

D

olá amigos !! bom dia novamente !!

Pessoal realmente não sei mais oq fazer:
tenho um combo box que quando tento dar um refresh os dados ficam em branco.

vou colocar uma parte do codigo para ilustrar melhor :

//var
 private ObjectComboBoxModel<TbLayout> combolayout;
 private ConsParLay daolayout;
...
//inicialização 
 daolayout = Lookup.getDefault().lookup(ConsParLay.class); // query na base de dados
 combolayout = new ObjectComboBoxModel<TbLayout>();
 combolayout.setFormatter(new layoutFormat());
 jComboBox1.setModel(combolayout);

// momento quando a janela é aberta
@Override
    public void componentOpened() {
        loadCombo1();        
    }
public void loadCombo1(){
        combolayout.clear();
        combolayout.setData(daolayout.getLayout());
        combolayout.setSelectedItem(null);        
    }

//formatter
class layoutFormat implements Formatter {
        @Override
        public String format(Object arg0) {
            if(arg0==null)
                return null;
            TbLayout a = (TbLayout) arg0;           
            return a.getLayout();
        }
        @Override
        public String getName() {
            return "Layout";
        }
        @Override
        public Object parse(Object arg0) {
            return null;//Never get invoked, JComboBox cannot be editable
        }
    }    

//novo layout

 private void jButton11ActionPerformed(java.awt.event.ActionEvent evt) {                                          
        
        TbLayout n = new TbLayout();
        n.setLayout(jTextField2.getText());       
        daolayout.Save(n);
        daolayout.Commit();
        loadCombo1();
        
        jTextField2.setText("");
        jTextField2.setEditable(false);
        
        
    }

Sinceramente não sei mais oq fazer para arrumar, faço isso em outras coisas e funciona perfeitamente.

abraços
Daniel

M

Voce consegue reproduzir de uma maneira mais simples para isolarmos o problema?

É só criar um código simples com objetos fakes e invés de um DAO voce usa alguma List.

D

ok, fiz outra tela apenas com um combo e um botao, mas da o mesmo problema.

segue :

public final class tesTopComponent extends TopComponent {
List<TbLayout> l = new ArrayList<TbLayout>();
    private ObjectComboBoxModel<TbLayout> combolayout;

    public tesTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(tesTopComponent.class, "CTL_tesTopComponent"));
        setToolTipText(NbBundle.getMessage(tesTopComponent.class, "HINT_tesTopComponent"));
        combolayout = new ObjectComboBoxModel<TbLayout>();
        combolayout.setFormatter(new layoutFormat());
        jComboBox1.setModel(combolayout);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jComboBox1 = new javax.swing.JComboBox();
        jButton1 = new javax.swing.JButton();

        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(tesTopComponent.class, "tesTopComponent.jButton1.text")); // NOI18N
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(107, 107, 107)
                        .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, 182, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(162, 162, 162)
                        .addComponent(jButton1)))
                .addContainerGap(111, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(41, 41, 41)
                .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addComponent(jButton1)
                .addContainerGap(198, Short.MAX_VALUE))
        );
    }// </editor-fold>

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
 TbLayout c=new TbLayout(System.currentTimeMillis());
        c.setLayout("teste1"+System.currentTimeMillis());
        l.add(c);
        //combolayout.setData(daolayout.getLayout());
        combolayout.setData(l);        // TODO add your handling code here:
    }

    // Variables declaration - do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JComboBox jComboBox1;
    // End of variables declaration
    @Override
    public void componentOpened() {
        TbLayout c=new TbLayout(System.currentTimeMillis());
        c.setLayout("teste1"+System.currentTimeMillis());
        l.add(c);
        //combolayout.setData(daolayout.getLayout());
        combolayout.setData(l);
        combolayout.setSelectedItem(null);  
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }
    
    class layoutFormat implements Formatter {
        @Override
        public String format(Object arg0) {
            if(arg0==null)
                return null;
            TbLayout a = (TbLayout) arg0;           
            return a.getLayout();
        }
        @Override
        public String getName() {
            return "Layout";
        }
        @Override
        public Object parse(Object arg0) {
            return null;//Never get invoked, JComboBox cannot be editable
        }
    }
D

Tarde !!!

Marky, depois de muito quebrar a cabeça fiz alguns testes e que aqui funcionou, agora eu não sei te falar se o problema acontece apenas para quem desenvolve com netbeans ( que é o meu caso ), acabei fazendo mais um teste com uma jframe bem simples fora de um netbeans module, e também ocorre o problema…

…depois de pesquisar em alguns forum e conversar com um colega aqui do banco, que me perguntou sobre os eventos de atualização ( fireContentsChanged,fireIntervalAdded ) resolvir fazer alguns alterações que funcionaram, ok até aqui resolvido !!!
Mas com isso apareceu um outro problema, quando vc usa apenas o add os itens são duplicados e ainda não achei uma solução para isso…

Enfim fica aqui o workaround !!!

abraços !

segue o codigo alterado :

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.think.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.ComboBoxModel;

import com.towel.bean.DefaultFormatter;
import com.towel.bean.Formatter;
import javax.swing.AbstractListModel;




public class ObjectComboBoxModel<T> extends AbstractListModel implements ComboBoxModel {
	private List<T> data;
	private T selectedItem;
	private Formatter formatter;
	private Map<Object, T> maps = new HashMap<Object, T>();

	public ObjectComboBoxModel() {
		data = new ArrayList<T>();
		formatter = new DefaultFormatter();
                //
	}

	public void setFormatter(Formatter formatter) {
		if (formatter == null) {
			System.out.println("Formatter can't be null. A default one will be set.");
			formatter = new DefaultFormatter();
		}

		this.formatter = formatter;
		maps.clear();
		for (T t : data){
                   maps.put(formatter.format(t), t); 
                }
                
                
	}

//	public void add(T obj) {
//		data.add(obj);
//		maps.put(formatter.format(obj), obj);
//                
//	}

	public void clear() {
		data.clear();
		maps.clear();
	}

	public void setData(List<T> list) {
		data = list;
		maps.clear();
		for (T t : data){
                    maps.put(formatter.format(t), t);                   
                } 
             fireContentsChanged(data, 0, data.size()-1);

	}

	public T getSelectedObject() {
		return selectedItem;
	}

	@Override
	public Object getSelectedItem() {
		return formatter.format(selectedItem);
	}

	@Override
	public void setSelectedItem(Object arg0) {
		selectedItem = maps.get(arg0);

	}

	public void setSelectedObject(T obj) {
		selectedItem = obj;
	}

	@Override
	public Object getElementAt(int arg0) {
		return formatter.format(data.get(arg0));
	}

	@Override
	public int getSize() {
		return data.size();
	}

//	@Override
//	public void addListDataListener(ListDataListener l) {
//	}
//
//	@Override
//	public void removeListDataListener(ListDataListener l) {
//	}
}
D

Bom dia !

Apenas para fazer uma correção sobre o código acima, não é preciso excluir o metodo add(obj).

O erro era do meu código de teste !

abraços
Daniel

M

O seu problema foi resolvido?

Precisou modficar algo do Towel?

D

Eu tive que alterar sim, eu fiz um extend do AbstractListModel para poder usar os eventos de fireXXXX e deste modo não precisa implementar os listeners addListDataListener e removeListDataListener.

Fiz vários testes e em qualquer situação o combo é atualizado, bom segue o codigo alterado !!

abraços
Daniel

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.ComboBoxModel;
import com.towel.bean.DefaultFormatter;
import com.towel.bean.Formatter;
import javax.swing.AbstractListModel;




public class ObjectComboBoxModel<T> extends AbstractListModel implements ComboBoxModel {
	private List<T> data;
	private T selectedItem;
	private Formatter formatter;
	private Map<Object, T> maps = new HashMap<Object, T>();

	public ObjectComboBoxModel() {
		data = new ArrayList<T>();
		formatter = new DefaultFormatter();                
	}

	public void setFormatter(Formatter formatter) {
		if (formatter == null) {
			System.out.println("Formatter can't be null. A default one will be set.");
			formatter = new DefaultFormatter();
		}

		this.formatter = formatter;
		maps.clear();
		for (T t : data){
                   maps.put(formatter.format(t), t); 
                }
                
                
	}

	public void add(T obj) {
		data.add(obj);
		maps.put(formatter.format(obj), obj);
                fireIntervalAdded(data, data.size()-1, data.size()-1);
	}

	public void clear() {
		data.clear();
		maps.clear();
	}

	public void setData(List<T> list) {
		data = list;
		maps.clear();
		for (T t : data){
                    maps.put(formatter.format(t), t);                   
                } 
             fireContentsChanged(data, 0, data.size()-1);

	}

	public T getSelectedObject() {
		return selectedItem;
	}

	@Override
	public Object getSelectedItem() {
		return formatter.format(selectedItem);
	}

	@Override
	public void setSelectedItem(Object arg0) {
		selectedItem = maps.get(arg0);

	}

	public void setSelectedObject(T obj) {
		selectedItem = obj;
	}

	@Override
	public Object getElementAt(int arg0) {
		return formatter.format(data.get(arg0));
	}

	@Override
	public int getSize() {
		return data.size();
	}

        
//	@Override
//	public void addListDataListener(ListDataListener l) {
//	}
//
//	@Override
//	public void removeListDataListener(ListDataListener l) {
//	}
}
M

Valeu, vou atualizar no projeto.

D

Fala Marky,

Pergunta: o Towel não implementa nada para utilizar o JList ?

ou você tem outra sugestão ?

abraços
Daniel

Criado 10 de fevereiro de 2011
Ultima resposta 8 de fev. de 2012
Respostas 34
Participantes 12