martes, agosto 29, 2006

Obtener backtraces de KDE en Gentoo Linux

Más de una vez he necesitado obtener un backtrace de una aplicación de KDE tras un crash de la misma, para crear enviar un informe de fallo a los desarrolladores.

Cuando el crash ocurre en una aplicación, KDE muestra un diálogo desde el que se puede seleccionar el backtrace. Desafortunadamente, el código que se lista puede ser totalmente inútil si la aplicación y librerías no se han compilado en modo debug, y con las opciones correctas para el compilador. Es fácil de saber si la traza es inútil, ya que estará llena de symbol not found o cosas parecidas por todos los sitios.

En Gentoo, cuando queremos depurar un paquete, tendríamos primeramente que cambiar un par de cosas en el fichero /etc/make.conf.

- En las opciones de CFLAGS tenemos que asegurarnos que no aparezca la opción -fomit-frame-pointer.

Además, la variable de entorno FEATURES debe contener al menos la opción nostrip.

Ya tenemos preparadas las opciones de compilación. Ahora hay que recompilar los paquetes necesarios. Las kdelibs siempre va a ser necesaria de compilarla en este modo. Y luego las aplicaciones o paquetes que sean. Eso sí, incluyendo la USE="debug" como opción para el emerge.

Por ejemplo, para obtener una traza de kmail yo he tenido que hacer:

USE="debug" emerge kdelibs kdepim

No olvidéis eliminar los cambios del make.conf cuando ya no queráis compilar paquetes en modo debug, ya que compilar de ese modo hace que las aplicaciones funcionen más lentas. Una vez eliminado del make.conf y tras obtener todas las trazas necesarias podemos volver al modo original recompilando otra vez kdelibs y el resto de paquetes.

Java: personalizando JFileChooser

Recientemente, programando en java, he necesitado modificar un JFileChooser para poder introducir un componente adicional en la misma línea de los botones de control (abrir y cancelar).

El JFileChooser es una clase un poco especial, ya que usa bastantes componentes nativas en el UI. Buscando por internet he encontrado algunas ideas que me han sido útiles, pero nada concreto que me resolviese el problema del todo.

Quería agregar un JCheckBox con una opción, para que el fichero abriese una sesión en la misma ventana de una aplicación o en una nueva, y pensé que el JFileChooser era un lugar apropiado para ofrecer la opción.

El truco está en sustituir la parte baja del panel con uno nuevo, que incluya dos botones que emulen a los anteriores, y que contenga además el resto de componentes que necesitemos. Otro truco está en hacer que con un PropertyListener se agregue el nuevo panel en el momento adecuado, ya que si no se hace así no funciona.

El resultado sería algo así:


Y el código fuente sería:

------------------------------------------------

import java.awt.BorderLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JPanel;

/**
* Ejemplo de personalización de JFileChooser
* @author airon
* Publicado en http://webpacifica.blogspot.com/
*/
public class CustomJFileChooser extends JFileChooser
{
public CustomJFileChooser (JComponent component)
{
super();
this.setControlButtonsAreShown(false);

JButton acceptB = new JButton("Open");
JButton cancelB = new JButton("Cancel");

final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS));
panel.add(component);
panel.add(Box.createHorizontalGlue());
panel.add(acceptB);
panel.add(Box.createHorizontalStrut(4));
panel.add(cancelB);

Insets insets = this.getInsets();

panel.setBorder(BorderFactory.createEmptyBorder(
insets.top,
((insets.left>=2)?insets.left-2:insets.left),
insets.bottom,insets.right));

acceptB.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent arg0) {
approveSelection();
}

});

cancelB.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent arg0) {
cancelSelection();
}

});

// Tricky way to add an extra component to the dialog
addPropertyChangeListener("ancestor",
new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent arg0)
{
getParent().add(panel,BorderLayout.SOUTH);
}
});
}


public static void main (String args[])
{
JCheckBox cb = new JCheckBox("Hello!",false);
JFileChooser jfc = new CustomJFileChooser(cb);
int returnVal = jfc.showOpenDialog(null);
System.out.println("Checkbox selected: "+cb.isSelected());

System.exit(0);
}
}