Charger une image dans les ressources

Dans C++Builder il est très facile de charger un icône ou même une image Bitmap situé dans les ressources d’une application. La méthode LoadFromResourceName sera alors utilisée.
Voici un exemple pour un fichier Bitmap que l’on veut mettre dans un TImage:

Image1->Picture->Bitmap->LoadFromResourceName((unsigned)HInstance, "BMP_BACKGROUND");

Maintenant, voici un exemple pour un icône que l’on veut utiliser pour un TForm:

Form1->Icon->LoadFromResourceName((unsigned)HInstance, "ICO_DELETE");

Une seule ligne de code est nécessaire pour ces deux types. Par contre, les autres types d’image utilisent le type RC DATA, donc c’est un peu plus compliqué.

Voyons tout d’abord comment ajouter un fichier de type image dans un projet. C’est assez simple car les dernières versions de l’IDE mettent à notre disposition une fenêtre pour exécuter ces tâches. Le fichier .rc sera créé automatiquement par l’interface et il n’aura pas à être modifié manuellement. Pour y accéder, il faut aller dans le menu Project et après dans Resources. Ensuite, il suffit de cliquer le bouton Add pour ajouter un fichier. Par la suite, il est préférable de lui donner un nom approprié.
Ressources sous C++Builder
Pour utiliser la classe TPngImage il faut inclure l’en-tête suivant:

#include <pngimage.hpp>

Voici maintenant le code que j’utilise pour charger l’image PNG et la mettre dans un TImage:

    TPngImage *PngImage = new TPngImage;
    PngImage->LoadFromResourceName((unsigned)HInstance, "PNG_LOGO");
    Image1->Picture->Bitmap->Assign(PngImage);
    delete PngImage;

Le code est assez simple, mais il est quand même un peu plus long que les deux précédents. On doit créer l’objet PngImage pour y charger temporairement la ressource. Pour assigner le PNG à l’objet Image1, on utilise Assign.

Une même méthode similaire pourrait être appliquée avec les classes TGIFImage et TJPEGImage. Voici un exemple pour une image de type JPEG:

    TJPEGImage *JPEGImage = new TJPEGImage;
    TResourceStream *Res = new TResourceStream((unsigned)HInstance, "JPEG_PHOTO", (System::WideChar *)RT_RCDATA);
    JPEGImage->LoadFromStream(Res);
    Image1->Picture->Bitmap->Assign(JPEGImage);
    delete Res;
    delete JPEGImage;

La classe TResourceStream permet de lire les ressources d’une application et l’enregistrer dans un flux mémoire. Ensuite, on charge ce flux dans l’objet JPEGImage à l’aide de la méthode LoadFromStream.

Je crois maintenant vous entendre penser: « Quoi, il n’existe pas de façon plus générale de charger n’importe quel type d’image dans un TImage? ». La réponse est oui, bien sûr, si vous possédez C++Builder 2010. La classe TWICImage peut être utilisée à cet fin. Il s’agit d’une encapsulation de Windows Imaging Component qui permet le chargement d’une grande variété de formats d’images. Voici l’enum des formats supportés:

enum TWICImageFormat { wifBmp, wifPng, wifJpeg, wifGif, wifTiff, wifWMPhoto, wifOther };

TWICImage est défini dans Graphics.hpp et on l’utilise comme suit:

    TWICImage *WicImg = new TWICImage();
    TResourceStream *Res = new TResourceStream((unsigned)HInstance, "PNG_LOGO", (System::WideChar *)RT_RCDATA);
    WicImg->LoadFromStream(Res);
    Image1->Picture->Graphic = WicImg;
    delete Res;
    delete WicImg;

TWICImage est un descendant de TGraphic tout comme TJPEGImage et TGIFImage. C’est pourquoi le code est pratiquement le même et que je ne le commenterai pas. Il est à noter cependant que TWICImage est disponible sur Windows XP SP3 ou une version supérieure.

À partir de maintenant, vous n’aurez plus d’excuse à utiliser des Bitmaps en ressources. Il est désormais plus simple que jamais d’utiliser n’importe quel autre format. Encore une fois, j’espère que cela vous sera utile.

Texte dans une liste vide

Voici un article qui explique comment ajouter un texte dans la liste vide d’un de vos programmes à l’aide de C++Builder.

Dans certaines applications, quand il y a une liste vide (ce qui pourrait laisser croire à une erreur), on ajoute un message au lieu de la laisser ainsi. C’est le cas d’Outlook Express, on peut le voir sur la capture d’écran qui suit.
Outlook Express avec une liste vide
Il existe plusieurs façons d’imiter ce comportement. Mais tout comme Outlook, je voulais que mon texte soit centré et que le redimensionnement des colonnes n’ait aucune influence sur la position du texte.

La première étape est d’ajouter un TListView à votre fenêtre principale. Utilisez le style vsReport pour que l’on puisse voir des colonnes. Pour tester le redimensionnement de la liste, vous pouvez mettre la propriété Align à alClient. Pour tester le redimensionnement des colonnes, il serait préférable d’en ajouter quelques-unes.

Pour capturer les messages qui sont envoyés au contrôle, il faut rediriger la propriété WindowProc vers votre code. Tous les objets qui héritent de TControl possèdent cette propriété. Dans votre fichier d’en-tête, ajoutez les déclarations suivantes dans la section private:

    void __fastcall ListViewWndProc(Messages::TMessage &Message);
    Classes::TWndMethod OldListViewProc;

Ensuite, on met ce code dans le constructeur de la Form:

    OldListViewProc = ListView1->WindowProc;
    ListView1->WindowProc = ListViewWndProc;

Il ne reste plus que le gros morceau maintenant:

void __fastcall TForm1::ListViewWndProc(Messages::TMessage &Message)
{
    static int PrevWidth = 65535; // On met un gros nombre

    if(Message.Msg == WM_PAINT && ListView1->Items->Count <= 0)
    {
        PAINTSTRUCT PaintStruct;
        BeginPaint(ListView1->Handle, &PaintStruct);

        ListView1->Canvas->Font->Color = clWindowText;
        if(ListView1->Enabled)
            ListView1->Canvas->Brush->Color = clWindow;
        else
            ListView1->Canvas->Brush->Color = clBtnFace;
        ListView1->Canvas->FillRect(ListView1->ClientRect);

        RECT rcH;
        int HeaderHeight;
        if(GetWindowRect(ListView_GetHeader(ListView1->Handle), &rcH))
        {    // On ajoute un petit décalage à la hauteur de l'entête
            HeaderHeight = (rcH.bottom - rcH.top) + 10;
        }

        String Temp = "La liste est vide, faites quelque chose pour la remplir.";
        TRect MyRect = ListView1->ClientRect;
        MyRect.Top = HeaderHeight;
        ListView1->Canvas->TextRect(MyRect, Temp, Graphics::TTextFormat() <<
            tfCenter << tfWordBreak << tfNoPrefix << tfNoClip);

        EndPaint(ListView1->Handle, &PaintStruct);
        Message.Result = 0; // On retourne 0 car WM_PAINT à été traité
        return;
    }
    else if(Message.Msg == WM_SIZE && ListView1->Items->Count <= 0)
    {
        if(Message.LParamLo < PrevWidth)
        {   // Quand la largeur de la liste diminue WM_PAINT n'est pas appelé, donc on le fait
            ListView1->Invalidate();
        }
        PrevWidth = Message.LParamLo; // On enregistre la nouvelle largeur
        Message.Result = 0; // On retourne 0 car WM_SIZE à été traité
    }
    OldListViewProc(Message);
}

La première partie du code sert à intercepter le message WM_PAINT dans le but de dessiner la liste. WParam et LParam ne sont pas utilisés pour ce message.

Le texte sera toujours de la couleur clWindowText et l’arrière plan, un rectangle dessiné par FillRect, sera clWindow ou clBtnFace dépendant si le contrôle est activé ou désactivé. Servez-vous des fonctions GetWindowRect et ListView_GetHeader pour aller chercher la hauteur de l’en-tête de liste à laquelle il est préférable d’ajouter un décalage de 10 pixels pour rendre le texte plus apparent. Ensuite, on dessine le texte avec TextRect qui permet de centrer le texte dans le rectangle qui correspond à la taille de la zone client du contrôle.

Dans la deuxième partie, on intercepte le message WM_SIZE car  il faut redessiner le contrôle lorsqu’il rapetisse. Le mot inférieur de LParam, LParamLo, correspond à la largeur de la zone client du contrôle. LParamHi contient la hauteur mais vous n’en aurez pas besoin.

Vous pouvez tenter de compiler le code sans cette section et diminuer la largeur de la liste. Vous remarquerez que le message WM_PAINT n’est pas appelé et donc le texte ne se recentre pas. Je me sers d’une variable statique nommée PrevWidth pour enregistrer la dernière largeur de la liste. Si on avait voulu centrer le texte de façon verticale, il aurait aussi fallu gérer le redimensionnement de la hauteur.

Une des parties les plus importante du code est la ligne 43 où l’on transmet tous les messages non traités vers la procédure d’origine. Sans cette ligne de code, l’application ne fonctionnera pas.

J’espère que vous avez encore appris quelque chose à l’aide de cet article.

Configurer Programmer’s Notepad pour lancer Dolphin

Programmer’s Notepad est un éditeur de texte conçu pour travailler avec du code source. Il est inclus avec devkitPro, la fameuse suite d’outils qui permet la programmation d’homebrew pour la Wii. Présentement, la version 2.0.8 est distribué avec celui-ci. Par contre, la version 2.0.10.1010 est disponible depuis le mois d’août 2009 et peut être téléchargée sur le site officiel du logiciel. La version 2.1.2 qui est encore en développement est disponible dans la section des téléchargements du projet sur Google Code. Cette version permettra la traduction de l’interface dans d’autres langues, par exemple le français.

Dolphin est un émulateur qui permet de jouer à des jeux GameCube et Wii sur votre ordinateur. Il permet aussi de pouvoir tester vos homebrew avec certaines limites. Par exemple, je n’ai jamais réussi à faire fonctionner le son avec un homebrew. En plus, l’infrarouge de la Wii Remote ne fonctionne pas lorsqu’on utilise un clavier et une souris. C’est pour cette raison que j’enlève toujours les trois derniers caractères de WPAD_FMT_BTNS_ACC_IR lorsque j’appelle WPAD_SetDataFormat. Sans cette modification, il vous sera impossible d’émuler les boutons de la Wii Remote. Même avec ces défauts, il est préférable d’utiliser ce programme plutôt que d’envoyer le fichier elf à la Wii et ensuite de marcher jusqu’à la télévision pour voir le résultat. Ce n’est pas que je suis paresseux mais dans mon cas, la console de jeu n’est pas sur le même étage que mon PC, donc c’est du sport de faire du débogage de cette manière.

Maintenant que les logiciels ont été introduits, on peut se poser la question: pourquoi voudrait-on démarrer Dolphin à partir de Programmer’s Notepad? Tout simplement parce que ce serait très long à chaque fois qu’on compile et qu’on veut voir le résultat de faire les manipulations suivantes:

  • Ouvrir Dolphin en double cliquant sur l’exécutable ou le raccourci
  • Cliquer sur Open dans la barre de d’outil
  • Sélectionner le bon fichier
  • Cliquer sur Open dans la boîte de dialogue de sélection de fichier

Peut-être trouvez-vous que ce n’est pas si terrible que ça, mais pourquoi se compliquer la vie? Voici donc comment faire:

  • Ouvrir Programmer’s Notepad
  • Ouvrir la fenêtre d’options en allant dans Tools / Options
  • Sélectionner Tools dans la structure arborescente
  • Sélectionner (None – Global Tools) dans la liste déroulante Scheme
  • Cliquer sur le bouton Add
  • Entrer les informations suivantes
    • Name: Dolphin
    • Command: D:\emulateur\dolphin\Dolphin.exe
    • Folder: D:\emulateur\dolphin\
    • Parameters: --elf=$(ProjectPath)$(ProjectName).elf
    • Shortcut: Alt + D
    • Save: None
    • Décocher This tool will modify the current file.
  • Cliquer sur OK pour fermer la fenêtre de propriété d’outils
  • Cliquer sur OK pour fermer la fenêtre d’options

Les paramètres Command et Folder doivent être modifiés pour représenter le chemin d’accès à Dolphin sur votre disque dur. Pour Shortcut, j’ai décidé d’utiliser Alt + D, D comme dans Dolphin ou Debug. Si vous être un habitué de la compilation avec F5, ne vous gênez pas pour mettre la touche de raccourci qui fait votre bonheur.
Programmer’s Notepad with Dolphin menu
Pour que cette configuration fonctionne, il va falloir renommer votre projet dans Programmer’s Notepad de la même manière que votre fichier elf. La raison de ceci est que Parameters utilise le symbole spécial $(ProjectName).

Il existe d’autres façons de pouvoir lancer Dolphin à partir de Programmer’s Notepad. Par exemple, il est possible de modifier le fichier Makefile de votre projet pour que le paramètre run démarre Dolphin. L’inconvénient est que vous allez devoir modifier tous les Makefile des projets que vous voulez tester sous Dolphin. Il est beaucoup plus simple de seulement modifier le nom du projet.

J’espère que ce texte vous sera utile.