Aller au contenu | Aller au menu | Aller à la recherche

mardi 5 septembre 2006

Amélioration des ComboBox Flash

une autre petite amélioration des combo box de flash qui consiste à aligner la liste déroulante à droite du composant lorsque celui-ci se situe à droite. C'est-à-dire que cela permet d'éviter qu'une liste déroulante très large sorte de la zone d'affichage.

l'exemple ci-dessous sera sans doute plus clair que ma tentative d'explication ;)

Les sources sont téléchargeables ici :

mercredi 16 août 2006

Spam

Nouvellement victime de spams, j'ai mis en place un système anti-spam.

j'ai préféré un procédé un peu radical aux filtres anti-spam.
c'est un plugin pour dotclear qui oblige chaque personne voulant poster un commentaire à prouver son humanité...
A mort le spam !!!

jeudi 13 avril 2006

Affichage des caractères invisibles dans un JTextPane

Dans Word, il y a une fonctionnalité qui permet d'afficher les caractères invisibles : espaces insécables, retour chariot, espaces, etc.

J'ai essayé de reproduire cela en java avec le composant JTextPane qui sert de base à tout les éditeurs de texte un peu avancées.
Pour cela il faut sous-classer la classe LabelView qui est chargé du rendu des fragments de textes à l'écran. L'éditorKit qui permet d'utiliser cette nouvelle classe :

package org.bigcrunch.text;
 
import java.awt.Container;
 
import javax.swing.text.AbstractDocument;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.LabelView;
import javax.swing.text.Segment;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
 
public class ExtendedEditorKit extends StyledEditorKit {
    
    public final static String DISPLAY_INVISIBLE_CHARS = "invisibleChars";
    
    protected static ViewFactory defaultFactory;
 
    /**
     * @return the view factory
     */
    public ViewFactory getViewFactory() {
        if(defaultFactory==null)
            defaultFactory = new ExtendedViewFactory(super.getViewFactory());
        return defaultFactory;
    }
    /**
     * @return a instance of ExtendedDocument()
     */
    @Override
    public Document createDefaultDocument() {
        return new ExtendedDocument();
    }
    
    public static class ExtendedViewFactory implements ViewFactory{
        private ViewFactory parentViewFactory;
        public ExtendedViewFactory(ViewFactory parentViewFactory){
            super();
            this.parentViewFactory = parentViewFactory;
        }
        public View create(Element elem) {
            if(AbstractDocument.ContentElementName.equals(elem.getName()))
                return new ExtendedLabelView(elem);
            return parentViewFactory.create(elem);
        }
    }
 
    public static class ExtendedLabelView extends LabelView{
 
        public ExtendedLabelView(Element elem) {
            super(elem);
        }
 
        /**
         * Fetch a reference to the text that occupies
         * the given range.  This is normally used by
         * the GlyphPainter to determine what characters
         * it should render glyphs for.
         *
         * @param p0  the starting document offset >= 0
         * @param p1  the ending document offset >= p0
         * @return    the <code>Segment</code> containing the text
         */
        public Segment getText(int p0, int p1) {
            Segment text = super.getText(p0,p1);
            Container c = this.getContainer();
            if(c instanceof JTextComponent){
                if(Boolean.TRUE.equals(((JTextComponent)c).getClientProperty(DISPLAY_INVISIBLE_CHARS))){
                    char array[] = new char[text.array.length];
                    System.arraycopy(text.array,0,array,0,text.array.length);
                    for(int i = 0;i<text.array.length;i++){
                        switch(text.array[i]){
                        case ' ':           array[i] = '\u00B7';break;
                        case '\u00A0':  array[i] = '\u00B0';break;
                        case '\n':         array[i] = '\u00B6';break;
                        }
                    }
                    text.array = array;
                }
            }
            return text;
        }
    }
}

On voit que la méthode getText() de la classe LabelView a été modifié afin de replacer les caractères visibles par des caractères invisibles à l'affichage et seulement à l'affichage.

Cet EditorKit utilise une sous-classe de DefaultStyleDocument qui permet d'ajouter automatiquement des espaces insécables avant les caractères de punctuations (:, ?, !, etc)

/*
 * Created on 12 avr. 2006 by JO
 */
package org.bigcrunch.text;
 
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
 
public class ExtendedDocument extends DefaultStyledDocument {
    
    protected final static char punctuationCharacters[] = {'!','?',':',';',')'};
    
    @Override
    public void insertString(int offset,String text,AttributeSet attr) throws BadLocationException{
        
        //if it's a punctuation character, add a insecable space before
        if(offset>0&&text.length()==1){
            char previousChar = this.getText(offset-1,1).charAt(0);
            char currentChar = text.charAt(0);
            
            for(int i = 0;i<punctuationCharacters.length;i++)
                if(currentChar==punctuationCharacters[i]&&previousChar==' '){
                    offset--;
                    text = "\u00A0"+text;
                    replace(offset,1,text,attr);
                    return;
            }
        }
        super.insertString(offset, addInsecableSpaces(text),attr);
    }
    
    @Override
    public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
 
        //if it's a punctuation character, add a insecable space before
        if(offset>0&&text.length()==1){
            char previousChar = this.getText(offset-1,1).charAt(0);
            char currentChar = text.charAt(0);
 
            for(int i = 0;i<punctuationCharacters.length;i++)
                if(currentChar==punctuationCharacters[i]&&previousChar==' '){
                    offset--;
                    length++;
                    text = "\u00A0"+text;
            }
        }
 
        super.replace(offset, length, addInsecableSpaces(text), attrs);
    }
    
    public static String addInsecableSpaces(String theText){
        char c[] = theText.toCharArray();
        for(int i = 1;i<c.length;i++){
            if(Character.getType(c[i])==Character.OTHER_PUNCTUATION
                    &&Character.getType(c[i-1])==Character.SPACE_SEPARATOR
            ){
                c[i-1]='\u00A0';
            }
        }
        return new String(c);
    }
}

L'utilisation est très simple et permet de changer de mode d'affichage à n'importe quel moment:

final JTextPane editor = new JTextPane();
editor.setEditorKit(new ExtendedEditorKit());
editor.putClientProperty( ExtendedEditorKit.DISPLAY_INVISIBLE_CHARS, Boolean.TRUE);

La prochaine étape sera d'afficher les tabulations.

samedi 25 mars 2006

Synchronisation d'ascenceurs

Parfois on aimerait bien que 2 ascenseurs soient liés. Par exemple sur 2 listes qui présentent des éléments liés. Le modèle sous-jacent des JScrollBar implémente l'interface BoundedRangeModel. Dans le cas où les 2 listes ont le même nombre d'éléments, le plus simple est alors que les deux ascenseurs utilisent le même modèle.

jScrollBar1.setModel(jScrollBar2.getModel());

L'inconvénient c'est que cela ne fonctionne que lorsque les modèles possédent les mêmes limites. J'ai donc développé une classe utilitaire qui permet de synchroniser plusieurs modèles et cela de façons proportionnelles.

package org.bigcrunch;
 
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoundedRangeModel;
import javax.swing.JScrollBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 
public class ModelSynchronizer implements ChangeListener{
 
    protected BoundedRangeModel models[];
    
    public ModelSynchronizer(BoundedRangeModel... models){
        super();
        this.models = models;
        for(int i = 0;i<models.length;i++)
            models[i].addChangeListener(this);
    }
 
    public void stateChanged(ChangeEvent e) {
        BoundedRangeModel source = (BoundedRangeModel)e.getSource();
        for(BoundedRangeModel model:models){
            if(model == null ||model.equals(source))continue;
            if(source.getMaximum()-source.getExtent()>0){
                int value = Math.round(source.getValue() 
                    *((float)(model.getMaximum()-model.getExtent())
                    /(float)(source.getMaximum()-source.getExtent()))
                  );
                model.setValue(value);
            }
        }
    }
    
    public static void install(JScrollBar... scrollbars){
        List<BoundedRangeModel> m = new ArrayList<BoundedRangeModel>();
        for(JScrollBar bar:scrollbars){
            BoundedRangeModel model= bar.getModel();
            if(model != null){
                m.add(model);
            }
        }
        install(m.toArray(new BoundedRangeModel[m.size()]));
    }
    
    public static void install(BoundedRangeModel...m){
        new ModelSynchronizer(m);
    }
}
 

Vous pouvez télécharger les fichiers via le lien ci-dessous:

jeudi 19 janvier 2006

Ouvrir les liens dans une nouvelle fenêtre en XHTML

la norme XHTML 1.0 strict interdit l'utilisation de l'attribut 'target' dans les liens. Ce qui n'est pas très agréable par exemple sur un blog comme dotclear car en ouvrant un lien externe, on quitte le site courant.

Le javascript suivant ajoute dynamiquement un attribut target égale à "_blank" sur tout les liens externes de la page. Un lien externe étant un lien ne contenant pas le nom de domaine du site contenant la page (ici bigcrunch.free.fr).

 
<script type="text/javascript">
    <!--
        function checkTargetAttribute(){
            var url= top.location.href;
            url = url.split("/")[url.indexOf("://")<0?0:2];
            for(var i=0; i < document.links.length; ++i)
                if(document.links[i].href.indexOf(url)<0)
                    document.links[i].target = "_blank";
        };
        -->
</script>
 

il suffit d'appeler ce script au chargement de la page pour modifier tout les liens externe au site en écrivant par exemple ceci:

 
<body onload="javascript:checkTargetAttribute(); return false;">
 

Ce script est actif sur cette page pour la démonstration ;)

dimanche 15 janvier 2006

JSwiff: fabriquer des SWF en Java

Je viens de découvrir une librarie open-source qui permet de manipuler des fichiers swf au format flash7. Elle est écrite à 100% en java et est disponible en licence GPL ou Commercial.

Elle permet de construire completement une animation ce qui peut être interressant par exemple sur un serveur.

www.jswiff.com

vendredi 4 novembre 2005

ToolTip

Il est toujours pratique de pouvoir afficher des infobulles dans une application. La plupart du temps, ce sont des fonctionnalités built-in mais Flash ne la posséde pas. Chacun crée la sienne plus ou moins bien, je vous propose donc ma version qui a le mérite d'être très simple à intégrer. J'ai trouvé le code de base sur theFlashBlog() que j'ai amélioré pour en facilité l'usage. La classe fonctionne maintenant uniquement via des appels à des méthodes statiques.

Le cas le plus simple est l'utilisation de la méthode install. Par exemple :

         ToolTip.install(myButton,"my tooltip",250);

Cette méthode prend 3 paramètres:

  • le MovieClip ou le composant sur lequel vous voulez avoir une infobulle
  • le texte de l'infobulle
  • le délai d'apparition de l'infobulle

démo:

Les sources sont téléchargeables ici :

jeudi 3 novembre 2005

Mouse Gestures en Java

Dans une application, les mouse gestures permettent de détecter certains mouvements de la souris et de les interpréter pour déclencher une action.

J'ai eu l'idée d'implémenter ma solution Java après avoir utilisé le plugin All-in-One Gestures pour FireFox. Ce plugin permet d'utiliser les Mouse Gestures pour utiliser les fonctionnalités du navigateur (on dessine un L pour fermer la fenêtre par exemple).

Voici ce que cela donne au final:

Pour arriver à ce résultat, je remplace le GlassPane existant (si cela est possible) par un nouveau qui me permet de dessiner la trace de la souris au dessus de tout les composants de la fenêtre. Ce nouveau GlassPane est visible que lorsque l'utilisateur maintient le bouton droit (ou un autre) de la souris appuyé.

La classe MouseGestures est chargé de la détection des mouvements de la souris. Pour faire simple, on détecte les mouvements haut, bas, gauche et droite (T,D,L,R). Tant que l'on en relache pas le bouton de la souris, on empille les mouvements pour donner une séquence. Par exemple, on aura DR pour les mouvements bas-droite.

Lorsque l'on relâche le bouton de la souris, un evénement de la classe MouseGestureEvent est envoyé à tous les écouteurs. Cet événement donne deux informations importantes:

  1. des mouvements ont ils été détectés ou non
  2. la séquence des mouvements

Il ne reste plus qu'a tester si la séquence correspond à quelque chose de traiter par votre programme. Par exemple, on peut avoir :

public void processMouseGesture(MouseGestureEvent gesture) {
            if(gesture.isRecognized() && gesture.is("DR")){
                    System.exit(0);
            }
}

Parmis les choses qui sont paramétrables :

  • l'affichage ou non du GlassPane
  • la largeur du trait
  • la précision de la détection

Pour utiliser la démo, il suffit de deplacer la souris en maintenant le bouton droit appuyé. Les mouvements détectés sont affichés dans la liste. la séquence DR (Down-Right : le dessin d'un L) ferme la fenêtre.

Vous pouvez télécharger les fichiers via les liens ci-dessous:

mercredi 2 novembre 2005

Amélioration ComboBox de Flash 7/8

Le ComboBox de Macromedia Flash MX possède un léger défaut si on l'utilise en mode éditable. Lorsque l'on clic sur un des éléments de la liste, le texte de la zone de saisie du composant est remplacé par l'élément sélectionné et le focus lui est donné. C'est � ce moment que le FocusManager commet une erreur, en redonnant le focus au champ de texte, il en sélectionne le texte en lui réappliquant les informations qu'il avait gardé en mémoire... et c'est � ce niveau que se trouve l'erreur. Le texte a la plupart du temps changé. Cela ne pose aucun problème lorsque l'on passe d'un item long � un court mais dans le cas contraire, seulement une partie du texte se retrouve sélectionné correspondant � la longueur de l'élément précédent.

Le code suivant permet de redéfinir la méthode du FocusManager qui pose problème:

 mx.managers.FocusManager.prototype.oldRestoreFocus = mx.managers.FocusManager.prototype.restoreFocus;
 mx.managers.FocusManager.prototype.restoreFocus = function():Void{
 		this.oldRestoreFocus();
 		if(this.lastFocus instanceof mx.controls.ComboBase){
 			var cb:mx.controls.ComboBase = mx.controls.ComboBase(this.lastFocus);
 			Selection.setSelection(0, cb.textField.text.length);
 		}
 
 	}

Et voici le résultat :

Un autre comportement que l'on pourrait souhaiter est que le texte ne soit pas sélectionner lorsque l'on sélectionne un élément de la liste. Pour cela il suffit de remplacer Selection.setSelection(0, cb.textField.text.length); par Selection.setSelection(0,0);. Cela a pour effet de placer le curseur avant le premier caractère et donc de ne pas sélectionner le texte. Cela corrige un autre "bug" du ComboBox qui fait que l'on ne voit que la fin des éléments très longs... car ils sont sélectionnés.

En espérant que cela sera utile � quelqu'un ;)

mardi 1 novembre 2005

Jung

Je viens de découvrir Jung, une libraire java qui permet de dessiner et de manipuler des graphes assez facilement. A voir les applets de démonstration, il y a beaucoup de possibilités... Affaire � suivre.

lundi 31 octobre 2005

Initialisation

Aujourd'hui, c'est décidé, je me remets à écrire sur la toile. Je compte partager via ce blog quelques uns de mes projets. Cela sera principalement en Java et en Flash, les deux technologies avec lesquelles je travaille en ce moment au quotidien.
Ce qui est prévu au programme :

  • Java : une petite collection de composant Swing. Il s'agit d'une sorte de compilation d'astuces récoltées ici et là, avec quelques améliorations de mon cru. Quelques informations sur les composants texte de Swing. etc.
  • Flash : quelques corrections/améliorations que j'ai fait sur certains composants Flash, un Logger flash/java qui vient avec un plugin eclipse permettant d'appliquer des filtres, afficher des messages xml, et encore bien d'autres choses. ça change la vie!!