Archives pour la catégorie Divers

Un taquin avec des mots

Il y a quelques années, j’avais vu un taquin fait avec des mots, en anglais. Le voici, avec d’abord la position initiale, puis la position à atteindre.

dead      dead
pigs  =>  pigs
wont      fly
fly       town

A l’automne dernier (2012), j’ai cherché un équivalent en français. Je me suis adressé à Éric Angelini, qui s’intéresse à tous types de casse-têtes, que ce soit avec des lettres, des chiffres, ou aux échecs. Vous pouvez voir son site 50 signes. Il m’a instantanément redirigé vers la liste de diffusion Oulipo, où les participants s’intéressent à la création de textes sous contrainte. J’y ai donc posé ma question, mais aucune des propositions qui y ont été faites par les membres de la liste ne m’a réellement satisfaite.

J’avais plusieurs contraintes, dont l’une était que les 2 textes aient une signification. Je ne dirai rien sur les autres contraintes, plus technique, sinon je dévoilerai ce qui fait que ce taquin n’est pas tout à fait comme les autres. Le mot « lynx » est vite devenu, grâce à sa lettre « y », le mot central. Après quelques jours de recherche, j’ai proposé le taquin suivant :

doux      doux
lynx  =>  lynx
vira      est
est       ravi

Ce taquin en français a les mêmes contraintes que celui en anglais, et le même type de solution. J’ai créé une page interactive, qui vous permet de jouer avec ce taquin avec des mots. Lorsque vous l’aurez résolu, vous aurez découvert ce qui fait sa particularité, et donc son intérêt.

Extension Firefox pour les images des autres sites

Concernant l'affichage des images dans Firefox, il y a 2 paramètres par défaut que je modifie. Pour les images animées, je ne veux voir l'animation qu'une seule fois, et j'empêche l'affichage des images qui ne sont pas sur le même site que la page que je visualise.

Je modifie ces options à travers la page 'about:config', puisqu'elles ne sont pas accessibles à travers les préférences :
image.animation_mode : once
permissions.default.image : 3

Avec ces options, je suis moins déconcentré dans ma navigation quotidienne par certaines images qui sont essentiellement de la publicité. Bien sûr, j'utilise Adblock Plus, mais ces précautions complémentaires ne sont pas inutiles.

Pourtant, j'ai quelquefois un soucis, surtout avec la 2e option. Il y a des pages qui ont besoins des images situées sur d'autres sites pour être compréhensibles. Jusqu'à  présent, j'étais alors obligé de retourner sur la page de configuration, et de remettre la préférence permissions.default.image à 1, le temps de recharger la page en question.

Après avoir fait ces aller-retour entre les 2 valeurs un grand nombre de fois, je me suis dis que j'allais apprendre à créer une extension Firefox pour pouvoir basculer les valeurs très facilement.

Cette extension existe maintenant. Vous pouvez installer ImagesPermission si elle vous intéresse. (L'installation se fait en 2 temps : vous téléchargez le fichier sur votre disque dur, puis vous l'ouvrez avec Firefox. Ne m'attendant pas à ce que cette extension ait un grand succès, je n'ai fait aucun effort pour faciliter son installation.)

Elle ajoute dans la barre de statut, un texte 'on' ou bien 'off', qui indique si l'affichage des images externes est activé ou non. Cliquer sur le texte change l'état.

Si jamais vous trouvez cette extension utile, prévenez-moi.

Antialiasing en Java

Introduction

L'antialiasing concerne 2 catégories différentes : les dessins et le texte. Pour le paramétrer, on utilise dans les 2 cas la fonction setRenderingHint de la classe Graphics2D, mais les paramètres et les valeurs à positionner sont différents.

Les dessins

Pour les dessins (les traits, les cercles, etc…), il vaut mieux toujours activer l'antialiasing sur le Graphics2D qui est fournit dans la fonction paintComponent(Graphics g) :

((Graphics2D) g).setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON );

Le texte

Activer l'antialising ou pas pour le texte dépend de la plate-forme et de la fonte. Je vous conseille de ne pas changer les fontes que Java à définit pour votre interface graphique. Il vaut mieux ne pas activer systématique l'antialiasing, mais obéir aux paramètres définis par le système d'exploitation. Pour obtenir ces paramètres, je vous suggère d'obtenir ces paramètres dans le constructeur de votre classe (qui dérive probablement de JPanel) l'information et de la stocker dans une variable.

Map renderingHints = (Map)Toolkit.getDefaultToolkit()
            .getDesktopProperty("awt.font.desktophints");

Ensuite, il faut utiliser cette information dans paintComponent(Graphics g) :

((Graphics2D)g).setRenderingHints(renderingHints);

Remarque

Il faut bien positionner les paramètres d'antialiasing à chaque appel de la fonction paintComponent(Graphics g), parce qu'il n'est jamais garanti que l'objet Graphics que vous recevez en argument de la fonction est le même d'un appel à l'autre

Dessiner une partie d’image avec drawImage

Le besoin

3 cartes de tarot superposéesLa classe Graphics et sa fille Graphics2D offrent de nombreuses fonctions pour dessiner une image. Elles s'appellent toutes drawImage et prennent des arguments différents.

Je cherche aujourd’hui la bonne fonction pour dessiner une partie d'une image. J'en ai besoin dans le logiciel LeTarot, pour dessiner une carte à jouer qui est recouverte d'une autre carte, comme sur l'image ci-contre.

Pour fixer les idées, voici un exemple. J'ai une image de 50×50, et je veux afficher le morceau de cette image qui va de (0, 0) à (20, 20) à la position x=100, y=100 d'un JPanel. Quelle fonction faut-il utiliser ? Je vous laisse chercher un instant…

La mauvaise solution

Vous avez pensé à la fonction suivante ?

drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
          int sx1, int sy1, int sx2, int sy2, ImageObserver observer)

Vous avez raison, elle répond à la question. Voici comment l'utiliser pour l'exemple donné :

drawImage(carte, 100, 100, 120, 120, 0, 0, 20, 20, null)

Mais elle a un défaut : la performance. Cette fonction est capable de faire une mise à l'échelle. Or agrandir ou réduire une image prend du temps. De plus, cette image ne peut pas profiter de l'accélération de votre carte graphique. Dans notre cas, il n'y a aucun changement de taille, mais la fonction n'est peut-être pas assez maligne pour le détecter. Et de toute façon, l'accélération graphique est désactivée.

Use the clip, Luke, use the clip

Sans vous faire languir, voici la bonne fonction drawImage :

drawImage(Image img, int x, int y, ImageObserver observer)

Oui ! C'est la fonction de base, qui dessine l'image complète. Elle est aussi très rapide. Mais, me direz-vous, elle ne répond pas au besoin. En fait, il faut la combiner avec l'utilisation du clipping. Voici un extrait de code qui montre comment faire :

paintComponent(Graphics g) {
    Shape clip = g.getClip();
    g.clipRect(destX, destY, width, height);
    g.drawImage(img, destX, destY, null);
    g.setClip(clip);
}

Le clipping sert en général à ne dessiner que ce qui a changé depuis le dernier affichage. Mais il peut aussi servir à afficher efficacement une partie d'image.

Mes plugins Eclipse

Voici une petite description des plugins Eclipse que j'utilise. Ils sont tous librement utilisable.

Subclispe

Un plugin indispensable est celui qui permet d'accéder à votre gestionnaire de versions. J'ai choisi Subversion pour gérer mes sources, et pris le plugin Subclipse sans vraiment me poser de question.

Checkstyle

Checkstyle est un logiciel qui permet de vérifier le style de votre code : placement des accolades, indentation, bloc vide, etc… Un fichier source étant lu bien plus souvent qu'il n'est modifié, avoir un style uniforme en facilite la compréhension.

Checkstyle est entièrement paramétrable, il a une bonne centaine d'options. Par défaut, il vérifie les règles édictées par Sun.

Le plugin Checkstyle vérifie ces règles en tache de fond, à chaque écriture d'un source, et indique les problèmes comme un problème de compilation classique, dans la vue « Problems ». Il met aussi une icône (une loupe) dans la marge des lignes concernées.

Mylar

Mylar est un plugin qui ajoute une vue permettant de gérer des tâches. Le grand intérêt est qu'à chaque tâche les fichiers que vous utilisez, lorsque vous travaillez en ayant activé une tâche Mylar, sont mémorisés. Lorsque vous passez d'une tâche à une autre, vous retrouvez donc le contexte dans lequel vous avez travaillé précédemment. A l'usage, c'est très intéressant, je n'ai plus que très peu de fichiers ouverts simultanément.

Editor Enhencement

Utilisant Emacs depuis plus de 15 ans, j'ai évidemment choisi le schéma Emacs pour définir les raccourcis clavier. Mais Emacs est bien plus riche qu'Eclipse en tant qu'éditeur de texte, il y a donc des fonctionnalités qui me manquent. Lunar Eclipse est composé de 3 plugins, et celui intitulé Editor Enhencement me semble très utile. Ce plugin ajoute la complétion à la mode Emacs, qu'à l'usage je trouve au moins aussi utile que celle d'Eclipse, et aussi la possibilité de travailler sur des rectangles.

Conclusion

Je ne m'étends pas sur l'utilité (la nécessité ?!) d'utiliser un gestionnaire de versions, ou bien d'avoir un style cohérent pour tous les sources d'un projet. J'espère que vous en êtes autant convaincu que moi.

S'il y a des plugins que je devrais utiliser, n'hésitez-pas à écrire un commentaire ou bien à m'envoyez un courriel.

Implémentation d’une MRU dans une application Eclipse RCP

Introduction

Extrait de menu avec MRUVous développez une application utilisant le framework Eclipse RCP, vous avez créé votre propre éditeur et vous voulez implémenter une MRU. Si votre classe ne dérive pas de AbstractTextEditor, qui implémente ce mécanisme par défaut, voici un mode d'emploi.

Pour rappel, MRU signifie Most Recent Used, et est la liste des fichiers récemment utilisés, qui est généralement accessible dans le menu Fichiers.

Voici les étapes nécessaires pour l'implémenter. Je vous parle d'expérience, l'ayant mis en œuvre dans mon logiciel iNatch. J'ai trouvé la plupart des informations dans une liste de diffusion à l'adresse suivante :
http://dev.eclipse.org/newslists/news.e … 08987.html.

Première étape

Vous l'avez probablement déjà mis en œuvre, elle consiste à faire que votre application sauvegarde d'une session à l'autre un ensemble d'informations. Il faut ajouter la ligne configurer.setSaveAndRestore(true); dans la méthode initialize(IWorkbenchConfigurer configurer) de votre classe WorkbenchAdvisor :

public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
    ...
    @Override
    public void initialize(IWorkbenchConfigurer configurer) {
	configurer.setSaveAndRestore(true);
    }
}

Avec ce paramètre mis à true, Eclipse sauvegarde automatiquement dans un fichier XML, dont nous n'avons pas à nous préoccuper, les informations concernant la position et la taille de la fenêtre (ou des fenêtres si vous avez permis d'en ouvrir plusieurs), la disposition des vues et des éditeurs à l'intérieur de la fenêtre. Ces informations prennent alors le pas sur la disposition que vous avez définies dans la fonction createInitialLayout(IPageLayout layout) de la classe Perspective.

Cette option intègre le fait de sauvegarder la liste des éditeurs qui sont présent au moment de quitter votre application. Mais pour l'instant, le framework n'a aucun moyen de savoir ce qu'il faut sauver à propos de votre éditeur, ni comment utiliser cette information au moment du redémarrage.

Deuxième étape

Elle consiste à implémenter IPersistableElement dans votre classe IEditorInput. La fonction getPersistable renvoie alors this si l'éditeur a été sauvegardé, null sinon. Pour pouvoir faire ce test, l'un des attributs de la classe peut-être de type File, mis à jour au moment de la sauvegarde sur disque de l'éditeur (par les options Enregistrer ou Enregistrer sous).

public class GameEditorInput implements IEditorInput, IPersistableElement {
    ...
    public IPersistableElement getPersistable() {
	return file_ == null ? null : this;
    }
    public String getFactoryId() {
	return GameEditorInputFactory.ID;
    }
    public void saveState(IMemento memento) {
	GameEditorInputFactory.saveState(memento, file_);
    }
    ...
    private File file_;
}

Il faut maintenant créer la classe EditorInputFactory. Il faut d'abord y définir les éléments que nous utilisons dans EditorInput, l'identifiant unique ID, et la fonction saveState, qui se contente de stocker le chemin vers le fichier dans un attribut.

public class GameEditorInputFactory implements IElementFactory {
    ...
    private static final String TAG_PATH = "path"; //$NON-NLS-1$
    ...
    public static void saveState(IMemento memento, File file) {
	if (file != null) {
	    memento.putString(TAG_PATH, file.getPath().toString());
	}
    }

La sauvegarde des informations est maintenant complètes. Mais il faut encore écrire la partie qui relit cette information au moment du lancement de votre application. C'est la fonction createElement de la classe EditorInputFactory qui s'en charge. Cette partie est un peu dépendante de la manière que vous avez prévu pour créer un éditeur à partir d'un nom de fichier. Je n'ai pas eu de difficulté dans mon code, parce que j'avais déjà un constructeur qui crée un EditorInput à partir d'un nom de fichier. Voici comment j'ai agencé les choses dans mon code :

public class GameEditorInputFactory implements IElementFactory {
    ...
    public IAdaptable createElement(IMemento memento) {
	String fileName = memento.getString(TAG_PATH);
	if (fileName != null) {
	    try {
		GameEditorInput gei = new GameEditorInput(new File(fileName));
		return gei;
	    } catch (PartInitException e) {
		// Nothing to do
	    }
	}
	return null;
    }

Troisième étape

La dernière partie consiste à intégrer la classe EditorInputFactory dans l'infrastructure de Eclipse. Cela se fait en étendant l'extension point elementFactories, en lui donnant en paramètre votre classe EditorInputFactory.

Conclusion

Il n'est pas très difficile d'implémenter une MRU dans une application Eclipse RCP. De plus, il y a un effet de bord intéressant, c'est que tout éditeur ouvert à l'arrêt précédent de votre application est automatiquement rouvert au prochain lancement.