Saturday, June 17, 2006

Bug no JPopupMenu

Recentemente um bug com JPopupMenu foi detectado no projeto do qual faço parte. Não foram realizados testes no Windows, apenas no Linux (Suse 10).
O bug se dá quando abrimos o popup menu e logo em seguida sobrepomos a janela de uma outra aplicação. O resultado é que o popup menu continua aberto sobre essa outra janela, podendo até ter seus ítens selecionados.
Esse bug consta no site da sun (bug id: 5014550) e muitas soluções foram postadas pois ela alegou que a correção estará incorporada apenas na versão 6 (Mustang), claro que ninguém achou graça nessa notícia, mas paciência. Lógico que para quem não tem paciência ou realmente não pode esperar devido aos prazos de seus projetos (meu caso), segue uma solução "paleativa": Adicionar um WindowListener a janela que receberá o popup menu para quando ela for desativada, ou seja, perder o foco para outra aplicação, o menu seja fechado. Notem que o post passado é bem útil para quem não sabe muito de listeners e tiver alguma dúvida com o código abaixo.

public class MyFrame extends JFrame{
JPopupMenu menu = new JPopupMenu();

/*Window listener para fechar o popup menu quando o frame perder o foco*/
WindowListener windowListener = new WindowAdapter(){
public void windowDeactivated(WindowEvent e)(
if (menu != null || menu.isVisible()){
System.out.println("Frame desativado, o menu será fechado!");
menu.setVisible(false);
}
}
}

/*Mouse listener para abrir o popup menu*/
MouseListener mouseListener = new MouseAdapter(){
public void mousePressed(MouseEvenet e){
if (e.isPopupTrigger()){
menu.setVisible(true);
}
}
}

/*Construtor*/
public MyFrame(){
super("Teste JPopupMenu");
this.addWindowListener(this.windowListener);
this.getContentPane().addMouseListener(this.mouseListener);
}

public static void main(String[] args){
MyFrame frame = new MyFrame();
frame.setVisible(true);
}
}

Wednesday, June 07, 2006

Curiosidade sobre Listeners

Se você precisa usar listeners do tipo KeyListener, MouseListener, FocusListener e sempre odiou ter que implementar todos os métodos (claro, lembrando que quando implementamos uma interface é obrigatório a implementação de todos os métodos que a compõe, mesmo que o corpo esteja vazio), aqui vai uma pequena dica que me ajudou bastante:

Situação: Eu preciso de um MouseListener em que apenas do método mousePressed será utilizado, o que fazer?
Solução : Ao invés de implementar a interface MouseListener, extenda a classe MouseAdapter e sobrescreva apenas o mousePressed. Exemplo:

MouseListener myMouseListener = new MouseAdapter(){
public void mousePressed(MouseEvent e){
System.out.println("Pressionou o mouse!");
}
};

**Outros listeners também possuem seu proprio adapter:

FocusListener = FocusAdapter
WindowListener = WindowAdapter
KeyListener = KeyAdapter


Concluindo, a classe MouseAdapter simplesmente implementa a inteface MouseListener e seus metodos respectivamente, deixando-os com o corpo vazio e dessa forma a implementação dos metodos fica ao gosto do freguês.

Customizando Fontes Em Aplicações Java

Ás vezes distribuimos aplicações desktop que necessitam de fontes que em alguns casos não encontram-se instaladas no SO. Pode ser o caso de fontes orientais como chinês, japonês, koreano e etc. Essa solução elimina eventuais problemas de caracteres inválidos aparecendo no meio de um texto (aqueles quadradinhos), pois a aplicação não mais dependerá apenas de fontes previamente instaladas no ambiente gráfico. Essas situações são comumente observadas no linux, já que no Windows existem ótimas fontes orientais e em outros sistemas operacionais não fiz a constatação.

O UIManager gerencia (como diz o proprio nome) as propriedades de cada componente swing tais como: fonte, cor de fundo, borda. Essas configurações são refletidas ao se instanciar um componente swing, ou seja, se você deseja que todos os JLabels, por padrão, possuam a fonte "Comic sans" faça o seguinte:

UIManager.getDefaults().put("Label.font", new Font("Comic Sans",Font.PLAIN,12));

Sendo assim, segue abaixo um método que lê uma fonte dinamicamente a partir de um path informado e altera, para essa fonte, as respectivas propriedades no UIManager para cada componente:

public void setAppFonts(String path)
throws FontFormatException, IOException {
File file = new File(path);
FileInputStream in = new FileInputStream(file);
/* According to javadoc only true type fonts are recognized for while */
Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, in);
/* Changing all UIManager fonts to a derived font */
Hashtable defaults = UIManager.getDefaults();
Enumeration keys = defaults.keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if ((key instanceof String)
&& (((String) key).toLowerCase().endsWith("font"))) {
if (defaults.get(key) instanceof Font) {
Font currentFont = UIManager.getFont((String) key);
Font derivedFont = dynamicFont.deriveFont(new Float(
currentFont.getSize()).floatValue());
defaults.put(key, derivedFont);
}
}
}
}