La transparence avec FireMonkey

Dans un article précédent j’avais expliqué comment changer l’opacité d’un contrôle TPngImage à l’exécution. Bien sûr, cette tâche était effectuée avec la VCL. Maintenant que FireMonkey est disponible, il est désormais très facile de modifier en temps réel la transparence d’un contrôle. La classe Fmx::Types::TControl possède une propriété Opacity, donc c’est assez simple à modifier.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer deux contrôles TTrackBar. Le premier servira à modifier l’opacité de l’image et le second à effectuer une rotation de l’image. Il faudra aussi ajouter un TViewport3D dans lequel on insère un TImage3D.

Parce que nous allons tester la transparence, j’ai pensé qu’une image de fantôme serait une bonne idée. J’utiliserais l’image PNG suivante dans la propriété Bitmap du Image3D1:

Trois fantômes
Image téléchargée sur Open Clipart Library

Voici tout le code qui sera nécessaire pour l’application:

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    Fill->Kind = TBrushKind::bkSolid;
    Fill->Color = claBeige;

    Viewport3D1->Align = TAlignLayout::alClient;
    Viewport3D1->Color = claNull;

    TrackBar1->Align =
        TAlignLayout::alBottom; // Le composant est aligné au bas de la fenêtre
    TrackBar1->Min = 0;         // Le minimum est 0%
    TrackBar1->Max = 100;       // Le maximum est 100%
    TrackBar1->Value = 100;     // Position de départ à 100%
    TrackBar1->Frequency = 5;   // Marque de graduation à tout les 5 incréments
    TrackBar1->Padding->Rect =
        Classes::Rect(5, 5, 20, 5);// Spécifie le remplissage du contrôle

    TrackBar2->Orientation =
        TOrientation::orVertical;// Contrôle vertical
<span id="wcad2221314">Try a few, and see which antioxidant drinks work best for you and your <a href="http://www.wouroud.com/order-2586">purchase sildenafil</a>  lifestyle. The best advantage for the Kamagra pills is that it is design and developed to help you to safely overcome erection problems.Take Great Diet Research shows that heavy consumption of fatty, fried, canned and even processed foods may clog arteries and decrease blood flow in the body, which results in poor erections during intercourse. <a href="http://www.wouroud.com/bitem.php?ln=en">http://www.wouroud.com/bitem.php?ln=en</a> cialis 5 mg In order to accomplish the sexual thirst you can opt for ED pills such as Kamagra, <a href="http://www.wouroud.com/bitem.php?item=2">buy tadalafil no prescription</a> , Caverta and others. With the increasing height of stress and strain in today's busy life, age is no longer the only factor that gives the account of an entire  <a href="http://www.wouroud.com/index.php?ln=en">canadian sildenafil</a> personality. </span>    TrackBar2-&gt;Align =
        TAlignLayout::alRight;  // Le composant est aligné à droite de la fenêtre
    TrackBar2-&gt;Min = 0;         // Le minimum est 0 degré
    TrackBar2-&gt;Max = 360;       // Le maximum est 360 degrés
    TrackBar2-&gt;Value = 0;       // Position de départ à 0 degré
    TrackBar2-&gt;Frequency = 10;  // Marque de graduation à tout les 10 incréments
    TrackBar2-&gt;Padding-&gt;Rect =
        Classes::Rect(5, 5, 5, 5);// Spécifie le remplissage du contrôle
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
    // On change l'opacité
    Viewport3D1-&gt;Opacity = TrackBar1-&gt;Value / 100.0f;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrackBar2Change(TObject *Sender)
{
    // On fait une rotation de l'image
    Image3D1-&gt;RotationAngle-&gt;X = TrackBar2-&gt;Value;
}
//---------------------------------------------------------------------------

Comme vous pouvez le voir, ce n’est pas très compliqué de modifier l’opacité d’un contrôle.

Voici une capture d’écran du résultat final:
Capture d'écran de l'exemple de transparence avec FireMonkeyBonne programmation!

Se connecter à MantisBT par SOAP

MantisBT est une interface Web codée en PHP qui sert à la gestion de bogues. Cet outil à code source ouvert est principalement utilisé pour faire le suivi de défaillance logicielle et la gestion de projet. MantisBT met à notre disposition un service Web qui utilise le WSDL (Web Services Description Language) pour accéder à son API à l’aide du protocole SOAP. Le WSDL est un langage basé sur le XML qui définit comment utiliser les fonctionnalités offertes par le service Web. Il est utilisé en combinaison avec SOAP pour l’échange d’information avec le serveur.

La première étape est de créer un nouveau projet FireMonkey HD. Ensuite il va falloir utiliser l’importateur WSDL qui se trouve dans le menu Component.
Import WSDL
Cet expert vous aidera à importer le document WSDL décrivant le service Web de MantisBT. Le seul champ à remplir est celui qui se nomme Location of WSDL File or URL. Vous devez y ajouter l’URL de MantisBT suivi de /api/soap/mantisconnect.php?wsdl. Dans ma capture d’écran plus haut, j’utilise l’URL du site web démo de MantisBT (http://www.mantisbt.org/demo/api/soap/mantisconnect.php?wsdl). Pour toutes les autres options de la fenêtre Import WSDL il est possible de garder les valeurs par défaut. Il suffit donc de cliquer sur Next jusqu’à la fin. Les fichiers mantisconnect.cpp et mantisconnect.h seront ajoutés au projet.

Dans la fenêtre principale de l’application, ajoutez un contrôle TEdit. Celui-ci servira à afficher la version de MantiBT avec laquelle nous communiquons. Il faudra aussi ajouter un contrôle TMemo, nous allons y insérer quelques informations acquises sur le serveur.

Dans votre fichier cpp voici le fichier d’en-tête à ajouter:

#include "mantisconnect.h"

Voici le code à ajouter dans votre constructeur:

_di_MantisConnectPortType Mantis = NS_mantisconnect::GetMantisConnectPortType();
Edit1->Text = Mantis->mc_version(); // Nous donnes la version de MantisBT

La méthode mc_version est la seule qui peut être appelée sans avoir besoin de donner un nom d’utilisateur et un mot de passe. Donc, pour ne pas avoir à écrire ces informations pour tous les appels à l’API je vous conseille d’ajouter ces macros:

#define USER "username" // Nom d'utilisateur sur MantisBT
#define PASS "password" // Mot de passe sur MantiBT

Dans le constructeur ajoutez le code suivant à la suite du code déjà existant:

// On va checher les informations des états
NS_mantisconnect::ObjectRefArray Status = Mantis->mc_enum_status(USER, PASS);
for(int i = 0; i < Status.Length; ++i)
{
    Memo1->Lines->Add(String(Status[i]->id) + ": " + Status[i]->name);
}
Memo1->Lines->Add("");
// On va chercher les informations des résolutions
ObjectRefArray Resolutions = Mantis->mc_enum_resolutions(USER, PASS);
for(int i = 0; i < Resolutions.Length; ++i)
{
    Memo1->Lines->Add(String(Resolutions[i]->id) + ": " + Resolutions[i]->name);
}

Des informations similaires à celles-ci devraient apparaitre dans le TMemo à l’exécution du programme:

10: new
20: feedback
30: acknowledged
40: confirmed
50: assigned
80: resolved
90: closed

10: open
20: fixed
30: reopened
40: unable to reproduce
50: not fixable
60: duplicate
70: no change required
80: suspended
90: won't fix

Ces informations sont importantes, par exemple si vous désirez changer l’état et la résolution du bogue 27, alors voici le code qui sera nécessaire:

NS_mantisconnect::IssueData* Issue;
Issue = Mantis->mc_issue_get(USER, PASS, 27); // Prends les informations du bogue
Issue->status->id = 80; // L'état est mis à resolved
Issue->resolution->id = 20; // La résolution est mise à fixed
IssueNoteDataArray Notes; // Tableau de notes
Issue->notes = Notes; // Corrige un bogue dans MantisBT avec mc_issue_update
Mantis->mc_issue_update(USER, PASS, 27, Issue); // Mise à jour des valeurs

Avant de terminer, voilà un dernier bout de code qui sert à ajouter une note à un bogue.

NS_mantisconnect::IssueNoteData* Note = new NS_mantisconnect::IssueNoteData();
Note->text = "Ajout d'une note pour le bogue 27"; // Texte de la note
Mantis->mc_issue_note_add(USER, PASS, 27, Note); // Ajoute la note au bogue 27
delete Note;

J’espère que cette cet article vous aidera à créer de jolis logiciels pour manipuler MantisBT.

Utiliser l’API URL Shortener de Google avec C++Builder

Dans mon dernier article j’avais expliqué l’utilisation l’API PageSpeed de Google. Pour continuer dans cette direction, j’aborderai dans celui-ci l’API URL Shortener.

L’utilisation de cet API comporte beaucoup de similitude avec le précédent. Encore une fois l’information sera prise sur une page web utilisant SSL et la réponse sera dans le format JSON (JavaScript Object Notation). Par contre, au lieu d’utiliser la méthode GET, ce sera la méthode POST qui sera utilisée par le protocole HTTP.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, deux contrôles TEdit et un TButton. Vous pouvez donner comme texte à votre bouton le mot « Réduire ». Je vous propose de placer les composants dans la fenêtre de la manière suivante:
Réducteur d’URLLa capture d’écran a été faite sous MAC OS X. C’est effectivement l’un des avantages de FireMonkey, l’application pourra fonctionner avec un système d’exploitation Windows ou MAC OS.

Dans votre fichier cpp voici le fichier d’en-tête à ajouter:

#include <Data.DBXJSON.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    Edit2->ReadOnly = true; // L'URL réduit ne peut pas être changé

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    Edit2->Text = ""; // On efface le texte
    Application->ProcessMessages();

    System::Classes::TStringStream* SourceFile =
        new System::Classes::TStringStream("{\"longUrl\": \"" + Edit1->Text + "\"}");

    IdHTTP1->Request->ContentType = "application/json";
    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.googleapis.com/urlshortener/v1/url", SourceFile);

    TJSONObject* Obj = static_cast<TJSONObject*>(TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair = Obj->Get("id");
        if(Pair)
        {
            TJSONString* Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Edit2->Text = AnsiDequotedStr(Answer->ToString(), '\"');
        }
    }

    delete SourceFile;

La première ligne de code efface le texte dans le contrôle de saisie monoligne. Par contre cette action ne sera pas exécutée sur le champ parce que le reste du code est bloquant car une requête HTTP au serveur doit s’effectuer. Cette requête pourrait prendre quelques secondes donc à la deuxième ligne on laisse du temps pour exécuter l’action précédente qui est dans la file d’attente des messages. Le contrôle TEdit sera alors redessiné sans le texte.

La méthode Post sert à appeler l’API et mettre la réponse dans la variable Response, en voici un exemple:

{
 "kind": "urlshortener#url",
 "id": "http://goo.gl/bT3bc",
 "longUrl": "http://imgs.xkcd.com/comics/time_management.png"
}

Voilà, vous savez maintenant utiliser l’API URL Shortener avec C++Builder. Si vous préférez utiliser les services de TinyURL au lieu de Google, alors voici le code dont il faut vous servir dans l’événement OnClick du bouton:

    Edit2->Text = ""; // On efface le texte
    Application->ProcessMessages();

    Edit2->Text = IdHTTP1->Get("http://tinyurl.com/api-create.php?url=" + Edit1->Text);

Utiliser l’API PageSpeed de Google avec C++Builder

PageSpeed est un outil développé par Google qui sert à analyser une page Web. Dans cet article il sera utilisé pour obtenir le nombre d’octets de chaque élément qui composent une page web. Les résultats seront présentés dans un diagramme circulaire à l’aide du contrôle TChart.

Avant de débuter, il faut aller sur le site de Google pour activer l’API Page Speed Online dans la section Services. Il vous faudra créer une clef d’API qui sera utilisée à chaque requête. Il est à noter que Google vous donne le droit d’exécuter 2500 requêtes par jour pour cet API.

Page Speed Online API ON
Page Speed Online API Activé

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, un TChart et un TLayout dans lequel il y aura un TEdit un TButton. On aligne le contrôle TLayout à alBottom et le TChart à alClient. Vous pouvez donner comme texte à votre bouton le mot « Analyser ».

Avant même de voir le code, je vous présente le résultat final. Ceci vous permettra de mieux comprendre vers où nous nous dirigeons.
Analyseur de site web

Dans votre fichier cpp voici la liste de fichier d’en-tête à utiliser ainsi que la macro qui va contenir votre clef d’API. N’essayer pas d’utiliser celle-ci, il s’agit de caractères écrits de façon aléatoire.

#include <Data.DBXJSON.hpp>
#include <FMXTee.Series.hpp>
#define GOOGLEAPIKEY "dskk1j3sW4WBYdkjds8sSDSD" // Clef d'API

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    Chart1->Title->Text->Text = "Statistique de la page"; // Titre du diagramme
    Chart1->Legend->Title->Text->Text = "Ressource en octets"; // Titre de la légende
    Series::TPieSeries *Series = new Series::TPieSeries(this); // Diagramme circulaire
    Series->Marks->Visible = false; // Ce n'est pas nécessaire car on a déjà une légende
    Chart1->AddSeries(Series);

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Sans elle, une exception dans la classe EIdIOHandlerPropInvalid produira le message « IOHandler value is not valid ». Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

Les lignes 3 à 7 servent à ajouter des informations au graphique.

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    Chart1->Series[0]->Clear(); // Efface le contenu du diagramme

    String Response = IdHTTP1->Get(
        "https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=" +
        Edit1->Text + "&key=" GOOGLEAPIKEY);

    TJSONObject* Obj = static_cast<TJSONObject*>(TJSONObject::ParseJSONValue(Response));
    TJSONPair* Pair = Obj->Get("pageStats");
    if(Pair)
    {
        String Value;
        TJSONString* Answer;
        TJSONObject* PageStats = static_cast<TJSONObject*>(Pair->JsonValue);
        if((Pair = PageStats->Get("htmlResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Value = AnsiDequotedStr(Answer->ToString(), '\"');
            Chart1->Series[0]->Add(Value.ToIntDef(0), "HTML", claGreen);
        }
        if((Pair = PageStats->Get("cssResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Value = AnsiDequotedStr(Answer->ToString(), '\"');
            Chart1->Series[0]->Add(Value.ToIntDef(0), "CSS", claOrange);
        }
        if((Pair = PageStats->Get("imageResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Value = AnsiDequotedStr(Answer->ToString(), '\"');
            Chart1->Series[0]->Add(Value.ToIntDef(0), "Image", claYellow);
        }
        if((Pair = PageStats->Get("javascriptResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Value = AnsiDequotedStr(Answer->ToString(), '\"');
            Chart1->Series[0]->Add(Value.ToIntDef(0), "JavaScript", claRed);
        }
        if((Pair = PageStats->Get("otherResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            Value = AnsiDequotedStr(Answer->ToString(), '\"');
            Chart1->Series[0]->Add(Value.ToIntDef(0), "Autre", claBlue);
        }
    }

Les lignes 3 à 5 servent à appeler l’API et mettre la réponse dans la variable Response. Cette réponse utilise le format JSON (JavaScript Object Notation). Voici la section qui nous intéresse pour notre projet:

{
 "pageStats": {
  "numberResources": 89,
  "numberHosts": 11,
  "totalRequestBytes": "18788",
  "numberStaticResources": 72,
  "htmlResponseBytes": "511838",
  "cssResponseBytes": "127217",
  "imageResponseBytes": "399484",
  "javascriptResponseBytes": "396089",
  "otherResponseBytes": "4023",
  "numberJsResources": 19,
  "numberCssResources": 4
 }
}

Comme on peut le voir certaines valeurs numériques utilisent des guillemets doubles, c’est pour cette raison que nous utiliserons la fonction AnsiDequotedStr pour les enlever.

En terminant, je voulais juste vous faire une petite mise en garde. Faites attention de protéger votre clef d’API car il est très facile pour n’importe qui de la retrouver en regardant le contenu de votre fichier exécutable. Même pas besoin d’éditeur hexadécimal compliqué, il suffit du Bloc-notes comme démontré ici:
Fichier exécutable ouvert dans le Bloc-notes

Utiliser Subversion 1.7 avec C++Builder XE2

Ça faisait déjà plusieurs fois que j’étais confronté à ce message d’erreur dans C++Builder XE2, sans toutefois comprendre pourquoi.
Subversion Upgrade Error in C++Builder XE2C’est pourtant simple, ma copie de travail utilise la plus récente version de Subversion et le client intégré dans l’IDE utilise la version 1.6. C’est pour cette raison que ça ne fonctionne pas et qu’une mise à jour du client s’impose.

Les fichiers à modifier sont ceux qui se trouvent dans le dossier $(BDS)\bin\subversion. Il s’agit des DLLs qu’utilise RAD Studio pour faire fonctionner son client svn. Dans ledit dossier on y trouve le fichier readme.txt suivant qui explique comment faire la mise à jour:

===============================================================================
Information about bin/subversion.
===============================================================================
This directory contains the Subversion .dll files used by the IDE’s Subversion
integration. These files can be upgraded by going to www.collab.net and
downloading the subversion client and extracting it to this location. Other
subversion installations will not be used be default. The IDE only looks in
this location, this behavior can be changed by setting the registry string
SvnDllDir under the Subversion key to the location of your Subversion
installation. This will not work with all Subversion installation because not
all installation use the same .dll names.

Pour télécharger sur le site Web de CollabNet il faut s’enregistrer, pour cette raison j’ai préféré prendre mes fichiers dans le projet Win32Svn qui se trouve sur SourceForge. Même si votre système est 64 bits il faut prendre une version 32 bits des fichiers car l’IDE est 32 bits.

Une fois l’archive enregistrée sur votre disque dur, il faut extraire tous les fichiers *.dll du dossier bin dans le dossier $(BDS)\bin\subversion. Si vous n’êtes pas certain de ce que vous faites n’hésitez pas à faire une copie de sauvegarde des fichiers utilisés par Embarcadero.

Si C++Builder était ouvert alors il faut le redémarrer. Maintenant vous devriez avoir accès aux options de révision de code et enfin pouvoir cliquer sur le bouton Commit à l’intérieur de votre IDE préféré.

Subversion Commit Completed in C++Builder XE2

Charger une image à partir des ressources avec FireMonkey

Dans un article précédent j’avais expliqué comment charger une image à partir des ressources en utilisant la VCL. Maintenant, voici l’équivalent mais cette fois-ci avec FireMonkey.

New FireMonkey HD Application

Une des première chose à connaître dans FireMonkey est les formats d’images supportés par la classe TBitmap.

Voici la liste des formats supportés sur toutes les plates-formes :

  • JPEG (.jpeg, .jpg)
  • TIFF (.tiff, .tif)
  • GIF
  • PNG
  • BMP

Voici la liste des formats supplémentaires avec Direct2D sur Windows :

  • JPEG XR (.hdp, .jxr, .wdp)
  • ICO

Voici la liste des formats supplémentaires avec GDI+ sur Windows :

  • WMF
  • ICO

Finalement, la liste des formats supplémentaires sur OS X :

  • JPEG 2000 (.jp2)
  • PSD
  • TGA
  • ICNS

Étant donné que TBitmap gère tous ces types d’images, cela simplifie beaucoup le code nécessaire pour charger une image des ressources. Avec ces trois lignes, vous allez pouvoir charger n’importe quelle image mentionnée plus haut.

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

Pour utiliser ce code il faut que toutes les images dans les ressources soient de type RCDATA. Faites attention dans l’IDE lorsque vous ajoutez une image Bitmap: le type par défaut est BITMAP, donc il faut le changer.

Bonne chance dans vos projets FireMonkey!

Compression de fichiers avec C++Builder XE2

Depuis le début du mois de septembre, j’évalue la version d’essai de C++Builder XE2. Une des fonctionnalités que j’apprécie particulièrement est de pouvoir compresser et décompresser des fichiers zip. C’est la nouvelle classe TZipFile qui se chargera de ce travail. Il existe déjà plusieurs composants et bibliothèques pour faire cela, mais je trouve ça intéressant de pouvoir le faire nativement. Pour ma part, j’utilisais ZipArchive Library de Artpol Software.

RAD Studio XE2 Splash Screen

Maintenant, voyons comment utiliser cette classe. Tout d’abord, voici l’entête qu’il faudra ajouter à votre projet.

#include <System.Zip.hpp>

Voici un exemple très simple de code pour effectuer la compression d’un fichier.

TZipFile *ZipFile = new TZipFile(); // Création de l'objet

ZipFile->Open("c:\\fichier.zip", zmWrite); // Ouverture du fichier en mode écriture

ZipFile->Add("c:\\test.xml", "test.xml"); // Ajout d'un fichier dans l'archive

ZipFile->Close(); // Fermeture du fichier

delete ZipFile; // Libération de la mémoire

Ce n’est pas plus compliqué que cela!

Détecter la fermeture d’un TPopupMenu

Le composant TPopupMenu possède peu d’évènements. Seulement OnChange et OnPopup sont disponibles. Dans certains cas, il pourrait être pratique de détecter la fermeture d’un menu contextuel. Malheureusement, cet évènement n’existe pas, mais il est par contre possible de le coder.

Lorsqu’un menu contextuel se ferme, l’évènement WM_MENUSELECT est envoyé avec certains paramètres. Le seul problème c’est que ce n’est pas la fenêtre qui reçoit l’évènement. C’est la classe TPopupList qui a cette responsabilité. Dans Menus.hpp on retrouve cette ligne de code:

extern PACKAGE TPopupList* PopupList;

L’objet PopupList est créé à l’initialisation, il va donc falloir capturer les messages qu’il reçoit pour appeler une méthode lors de la fermeture du menu.

Voici la liste des méthodes et attributs à ajouter a votre fichier .h:

private:	// User declarations
    void __fastcall PopupWndProc(Messages::TMessage &Message);

    WNDPROC OldPopupProc;
    TFNWndProc FPopupProcInst;
    TPopupMenu *FLastOpenPopupMenu;
protected:
    void __fastcall DoPopupExit(TPopupMenu *PopupMenu);

La première étape est de rediriger les messages vers la méthode PopupWndProc. Avant de faire cela on va d’abord enregistrer l’ancienne adresse de la procédure de fenêtre dans OldPopupProc. Voici le code à ajouter à votre constructeur:

    FPopupProcInst = MakeObjectInstance(PopupWndProc);
    OldPopupProc = (WNDPROC)SetWindowLongPtr(PopupList->Window, GWL_WNDPROC,
        (LONG_PTR)FPopupProcInst);

Dans le destructeur on va restaurer l’appel vers la méthode WndProc originale et libérer la mémoire allouée par la fonction MakeObjectInstance:

    if(FPopupProcInst != NULL)
    {
        SetWindowLong(PopupList->Window, GWL_WNDPROC, (LONG)OldPopupProc);
        FreeObjectInstance(FPopupProcInst);
    }

À la réception du message WM_MENUSELECT on regarde si MenuFlag est à 0xFFFF et Menu à NULL. Si c’est le cas, alors le menu est fermé et on appelle DoPopupExit. Cette méthode a pour paramètre un pointeur vers le TPopupMenu qui vient de se fermer. La variable FLastOpenPopupMenu contient cette information. On lui affecte une valeur à la réception du message WM_INITMENUPOPUP qui est appelé lorsqu’un TPopupMenu est sur le point d’être actif. À la place de ce message, l’évènement OnPopup aurait pu être utilisé, mais je l’ai fait de cette manière pour rendre le code plus général. Les lignes 25 et 26 servent à appeler la méthode WndProc originale.

void __fastcall TForm1::PopupWndProc(Messages::TMessage &Message)
{
    if(Message.Msg == WM_MENUSELECT)
    {
        TWMMenuSelect *MenuSelect = (TWMMenuSelect *)&Message;
        if(MenuSelect->MenuFlag == 0xFFFF && MenuSelect->Menu == NULL)
        {
            DoPopupExit(FLastOpenPopupMenu);
        }
    }
    else if(Message.Msg == WM_INITMENUPOPUP)
    {
        TWMInitMenuPopup *InitMenuPopup = (TWMInitMenuPopup *)&Message;
        const int ListCount = PopupList->Count;
        for(int i = 0; i < ListCount; ++i)
        {
            TPopupMenu *LPopupMenu = (TPopupMenu *)PopupList->Items[i];
            if(LPopupMenu->Handle == InitMenuPopup->MenuPopup)
            {
                FLastOpenPopupMenu = LPopupMenu;
                break;
            }
        }
    }
    Message.Result = CallWindowProc(OldPopupProc, PopupList->Window,
        Message.Msg, Message.WParam, Message.LParam);
}

La seule chose qui manque est de mettre votre code à l’intérieur de cette méthode.

void __fastcall TForm1::DoPopupExit(TPopupMenu *PopupMenu)
{
}

Avec le code plus haut, il serait très facile de faire un composant que l’on pourrait utiliser dans plusieurs projets qui nécessitent d’être notifiés lors de la fermeture d’un menu contextuel. L’ajout d’une propriété OnPopupExit serait simple à implémenter.

J’espère que cet article vous sera pratique.

Changer le texte des ressources à l’exécution

Dans cet article je vous expliquerai comment changer le texte contenu dans les ressources de C++Builder pendant l’exécution de votre programme. Si vous concevez une application multilingue, cette technique sera pratique.

Dans votre fichier d’en-tête, ajoutez la déclaration suivante:

    void __fastcall HookResourceString(TResStringRec& ResStringRec, Char* StrID);

Voici la méthode à ajouter à votre code:

void __fastcall TForm1::HookResourceString(TResStringRec& ResStringRec, Char* StrID)
{
    DWORD OldProtect;

    VirtualProtect(&ResStringRec, sizeof(ResStringRec), PAGE_EXECUTE_READWRITE, &OldProtect);
#if __BORLANDC__ >= 0x630
    // Pour C++Builder XE et plus
    ResStringRec.Identifier = Integer(StrID);
#else
    ResStringRec.ident = Integer(StrID);
#endif
    VirtualProtect(&ResStringRec, sizeof(ResStringRec), OldProtect, &OldProtect);
}

Le code est assez simple: on change la protection sur ResStringRec pour que l’on puisse y mettre un pointeur vers la chaîne de caractères StrID. Lorsque la modification est terminée, on remet la protection précédente.

Il est à noter que la structure TResStringRec n’est pas la même sur C++Builder 2010 et C++Builder XE. Étant donné que je n’ai pas d’autre version à ma disposition, je n’ai pas pu les vérifier.

Sur C++Builder 2010:

struct PACKAGE TResStringRec
{
    long *module;
    long ident;
};

Sur C++Builder XE:

struct DECLSPEC_DRECORD TResStringRec
{
public:
    unsigned *Module;
    int Identifier;
};

Voici un exemple d’utilisation avec la fonction MessageDlg:

HookResourceString(_SMsgDlgConfirm, L"Mon titre");
HookResourceString(_SMsgDlgYes, L"Absolument");
HookResourceString(_SMsgDlgNo, L"Pas du tout");
HookResourceString(_SMsgDlgCancel, L"Laisse faire");

MessageDlg("Veux-tu faire quelque chose?", mtConfirmation,
        TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0, mbCancel);

Le premier paramètre de la méthode est le nom de la ressource qui correspond aux noms que l’on retrouve dans le fichier Consts.hpp. Donc, il est important d’inclure ce fichier d’en-tête dans votre code. Si vous êtes intéressés à connaître le texte original, alors je vous conseil de jeter un coup d’œil au fichier Consts.pas.

Je vous laisse avec un capture d’écran du résultat.
Boite de dialogue personnalisée

La fonction GRRLIB_DrawImg

Dans cet article, je tenterai d’expliquer l’utilisation de certains paramètres de la fonction GRRLIB_DrawImg de la bibliothèque GRRLIB.

Tout d’abord, voici le prototype de la fonction qui sert à dessiner une texture à l’écran:

void GRRLIB_DrawImg (
	const f32  	xpos,			// Position de l'image sur l'axe horizontal
	const f32  	ypos,			// Position de l'image sur l'axe vertical
	const GRRLIB_texImg *  tex,	// La texture à dessiner
	const f32  	degrees,		// La rotation en degré
	const f32  	scaleX,			// Proportion sur l'axe horizontal
	const f32  	scaleY,			// Proportion sur l'axe vertical
	const u32  	color			// La couleur
)

Le dernier paramètre de la fonction, color, sert à changer la couleur d’une texture. Le format de la couleur est le codage RGBA, qui est codé sur 4 octets (32 bits). Le premier octet représente le rouge, le second le vert, le troisième le bleu et finalement le dernier octet est pour l’opacité. 0 veut dire que la couleur n’est pas présente, tandis que 255, FF en hexadécimal, signifie que l’intensité de la couleur est à son maximum. Pour le canal alpha, 0 veut dire que l’image sera complètement transparente, donc dans cet exemple, l’opacité sera fixée à 255.

À partir de l’image PNG suivante on va afficher à l’écran quatre fantômes de différentes couleurs.

Fantômes de Pac-Man
Fantômes de Pac-Man
Il suffit d’ajouter quelque lignes seulement pour faire un test. Les lignes surlignées sont celles ajoutées au code du template du dossier examples de GRRLIB.

#include <grrlib.h>

#include <stdlib.h>
#include <wiiuse/wpad.h>

#include "ghost.h"

int main(int argc, char **argv) {
    // Initialise the Graphics & Video subsystem
    GRRLIB_Init();

    // Initialise the Wiimotes
    WPAD_Init();

    GRRLIB_SetBackgroundColour(0xFE, 0xCF, 0x07, 0xFF);
    GRRLIB_texImg *Fantome = GRRLIB_LoadTexture(ghost);

    // Loop forever
    while(1) {

        WPAD_ScanPads();  // Scan the Wiimotes

        // If [HOME] was pressed on the first Wiimote, break out of the loop
        if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)  break;

        // -------------------------------------------------------------------
        // Place your drawing code here
        GRRLIB_DrawImg(50, 50, Fantome, 0, 1, 1, 0xEF1E23FF); // Blinky
        GRRLIB_DrawImg(50, 250, Fantome, 0, 1, 1, 0xEE5395FF); // Pinky
        GRRLIB_DrawImg(250, 50, Fantome, 0, 1, 1, 0x6ECEDEFF); // Inky
        GRRLIB_DrawImg(250, 250, Fantome, 0, 1, 1, 0xF67E1FFF); // Clyde
        // -------------------------------------------------------------------

        GRRLIB_Render();  // Render the frame buffer to the TV
    }

    GRRLIB_FreeTexture(Fantome);
    GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB

    exit(0);
}

Le code est assez simple: à l’aide de la fonction GRRLIB_SetBackgroundColour on choisit une couleur d’arrière plan. Une texture est créée à la ligne 16 qui sera utilisée pour dessiner les fantômes aux lignes 28 à 31. Finalement la texture est détruite à la ligne 37.

Voici une capture d’écran du résultat final sous l’émulateur Dolphin révision 6972.
Dolphin avec les fantômes de Pac-Man

Une autre petite astuce est d’utiliser les paramètres scaleX et scaleY pour faire pivoter respectivement la texture horizontalement et verticalement. Pour effectuer cela il suffit de mettre une valeur négative. J’ai fait un petit bout de code qui déplace un fantôme de gauche à droite. Lorsque le fantôme se dirige vers la gauche, il regarde dans cette direction, même si sur l’image originale il regarde vers la droite.

#include <grrlib.h>

#include <stdlib.h>
#include <wiiuse/wpad.h>

#include "ghost.h"

typedef enum DirectionX {dGauche, dDroite} DirectionX;

int main(int argc, char **argv) {
    // Initialise the Graphics & Video subsystem
    GRRLIB_Init();

    // Initialise the Wiimotes
    WPAD_Init();

    GRRLIB_SetBackgroundColour(0xFE, 0xCF, 0x07, 0xFF);
    GRRLIB_texImg *Fantome = GRRLIB_LoadTexture(ghost);
    GRRLIB_SetMidHandle(Fantome, true);

    f32 Xposition = 0.0f;   // Position de l'image sur l'axe horizontal
    f32 FlipImage = 1.0f;   // Retourne l'image horizontalement
    DirectionX Direction = dDroite; // Direction (gauche ou droite)

    // Loop forever
    while(1) {

        WPAD_ScanPads();  // Scan the Wiimotes

        // If [HOME] was pressed on the first Wiimote, break out of the loop
        if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME)  break;

        // --------------------------------------------------------------------
        if(Xposition < Fantome->w / 2.0f) {
            Xposition = Fantome->w / 2.0f;
            Direction = dDroite; // On change la direction
            FlipImage = 1.0f;    // Le fantôme regarde vers la droite
        }
        else if(Xposition > (rmode->fbWidth - (Fantome->w / 2.0f))) {
            Xposition = rmode->fbWidth - (Fantome->w / 2.0f);
            Direction = dGauche; // On change la direction
            FlipImage = -1.0f;   // Le fantôme regarde vers la gauche
        }
        if(Direction == dGauche)
            Xposition -= 1.5f; // On se déplace un peu plus vers la gauche
        else
            Xposition += 1.5f; // On se déplace un peu plus vers la droite
        GRRLIB_DrawImg(Xposition, 150, Fantome, 0, FlipImage, 1, 0xEF1E23FF);
        // --------------------------------------------------------------------

        GRRLIB_Render();  // Render the frame buffer to the TV
    }

    GRRLIB_FreeTexture(Fantome);
    GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB

    exit(0);
}

Lorsque le fantôme atteint la gauche de l’écran du téléviseur, on entre dans la condition à la ligne 34. Quand on arrive à la droite, c’est la condition de la ligne 39 qui est vraie. L’image devrait être dessinée 50 ou 60 fois par seconde dépendant si l’affichage de votre téléviseur est 50 ou 60 Hz. En anglais on parle de FPS (frame per second). La valeur 1.5 aux lignes 45 et 47 correspond à la vitesse de déplacement de l’image, elle est calculée en nombre de pixels par frame.

La fonction GRRLIB_DrawImg est la base de tout jeu 2D conçu avec GRRLIB, donc il est important de bien la comprendre. J’espère que cet article vous a aidé.