Utiliser l’API Google Street View Image

L’API Google Street View Image permet de télécharger une image statique par l’envoi d’une requête HTTP standard.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TImage, un TEdit, un TTrackBar et quatre contrôles TButton. Je vous propose de placer les composants dans la fenêtre de la manière suivante:

Google Street View Image API

Le TEdit servira à entrer les coordonnées de latitude et longitude. La barre graduée permet d’agrandir et de réduire l’affichage de l’image. Un nombre plus petit signifie un plus grand niveau de zoom. Les boutons servent à déplacer la caméra dans différentes directions. Le résultat sera évidemment affiché dans le composant TImage.

Voici les attributs et la méthode à ajouter à votre fichier .h:

int FHeading;
int FFieldOfView;
int FPitch;

void __fastcall UpdateImage();

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

//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    // On fait croire à Google que l'on est Firefox 16.0
    IdHTTP1->Request->UserAgent =
        "Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0";

    // Valeur par défaut
    FHeading = 0;
    FFieldOfView = 90;
    FPitch = 0;

    TrackBar1->Tracking = false;
    TrackBar1->Value = FFieldOfView;
    TrackBar1->Min = 10;
    TrackBar1->Max = 120;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::UpdateImage()
{
    System::Classes::TMemoryStream* ResponseContent = new System::Classes::TMemoryStream;

    try
    {
        String URL = Format("http://maps.googleapis.com/maps/api/streetview?size=%dx%d&location=%s&sensor=false&heading=%d&fov=%d&pitch=%d",
            ARRAYOFCONST((
            (int)Image1->Width, // Image Width
            (int)Image1->Height, // Image Height
            EditLocation->Text, // Location
            FHeading, // Heading (0 to 360)
            FFieldOfView, // Field of view
            FPitch // Pitch
            )));

        IdHTTP1->Get(URL, ResponseContent); // Téléchargement de l'image

        Image1->Bitmap = new Fmx::Types::TBitmap(ResponseContent);
    }
    catch(...)
    {
    }

    delete ResponseContent;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::EditLocationKeyUp(TObject *Sender, WORD &Key,
          System::WideChar &KeyChar, TShiftState Shift)
{
    if(Key == vkReturn)
    {   // La touche Entrée a été appuyée, on met à jour l'image
        UpdateImage();
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
    FFieldOfView = TrackBar1->Value;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonLeftClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la gauche
    FHeading = (FHeading - 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonRightClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la droite
    FHeading = (FHeading + 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonTopClick(TObject *Sender)
{
    if((FPitch += 10) > 90)
    {   // La caméra est complètement vers le haut
        FPitch = 90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonBottomClick(TObject *Sender)
{
    if((FPitch -= 10) < -90)
    {   // La caméra est complètement vers le bas
        FPitch = -90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------

Le code qui est le plus important se trouve dans la méthode UpdateImage. Tous les évènements servent uniquement à modifier l’un des paramètres du URL.

J’aurais bien aimé utiliser le composant TLocationSensor disponible dans C++Builder XE3 pour aller chercher mes coordonnées de latitude et longitude, mais je n’ai pas le matériel nécessaire pour le tester. C’est dommage car ça me semble facile à utiliser.

Utiliser l’API de VirusTotal avec C++Builder

VirusTotal est un service gratuit qui permet la détection de virus, vers, chevaux de Troie ou tout autre type de programme malveillant. En visitant le site web de cette application, il est possible de choisir un fichier sur votre disque dur et de le soumettre à une analyse. Par la suite, plusieurs antivirus vérifieront si les données comportent des partie suspectes. Le résultat sera ensuite disponible en ligne.

Cet outil web est vraiment génial, mais ce qui l’est encore plus c’est qu’il existe une interface API publique que l’on peut utiliser avec des requêtes POST par HTTP. Les réponses seront en format JSON (JavaScript Object Notation).

Avant de commencer, il faudra vous inscrire pour obtenir une clef d’API. Elle sera affichée dans la section API de votre profil. Il est à noter qu’avec cette clef vous ne pourrez effectuer que quatre requêtes par minute.

Maintenant, on peut commencer à coder. 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 TOpenDialog, un contrôle TEdit deux TButton et un TMemo.

Je vous propose de placer les composants dans la fenêtre de la manière suivante:
VirusTotal API

Vous pourrez aussi utiliser des textes similaires à ceux de l’image pour les différents contrôles.

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

#define APIKEY "g9898984d0909534e1230958567f098090928712a8034cb0809786201854f678"
#include <Data.DBXJSON.hpp>
#include <Idmultipartformdata.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    OpenDialog1->Filter = "Application (*.exe)|*.EXE|Tous les fichiers|*.*";
    OpenDialog1->Options = System::Uitypes::TOpenOptions()
        << TOpenOption::ofHideReadOnly << TOpenOption::ofEnableSizing <<
        TOpenOption::ofFileMustExist;

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

La prochaine étape est d’ajouter le code dans l’événement Button1Click.

    if(OpenDialog1->Execute() == false)
    {
        return;
    }

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFile("file", OpenDialog1->FileName, "application/octet-stream");

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/scan", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair;
        TJSONString* Answer;
        if((Pair = Obj->Get("response_code")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            if(Answer->ToString() == "1")
            {
                if((Pair = Obj->Get("resource")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Edit1->Text = AnsiDequotedStr(Answer->ToString(), '\"');
                }
            }
            else
            {
                Edit1->Text = "Erreur";
            }
        }
    }

    delete Params;

Ce code est utilisé pour la soumission du fichier choisi avec le TOpenDialog vers le serveur de VirusTotal. Si le code de la réponse est 1, ce qui signifie donc que la requête à réussi, la ressource sera affichée dans le TEdit.

Finalement, il faut ajouter ce code dans l’événement Button2Click.

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFormField("resource", Edit1->Text);

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/report", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair = Obj->Get("scans");;
        if(Pair)
        {
            TJSONObject* Scans = static_cast<TJSONObject*>(Pair->JsonValue);
            TJSONPairEnumerator* Enumerator = Scans->GetEnumerator();
            while(Enumerator->MoveNext())
            {
                String AntivirusName =
                    AnsiDequotedStr(Enumerator->Current->JsonString->ToString(), '\"');
                Memo1->Lines->Add(AntivirusName);

                TJSONObject* AntivirusData =
                    static_cast<TJSONObject*>(Enumerator->Current->JsonValue);

                String Value;
                TJSONString* Answer;
                if((Pair = AntivirusData->Get("detected")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Detected: " + Value);
                }
                if((Pair = AntivirusData->Get("version")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Version: " + Value);
                }
                if((Pair = AntivirusData->Get("result")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Result: " + Value);
                }
                if((Pair = AntivirusData->Get("update")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Update: " + Value);
                }
            }
        }
    }
    delete Params;

Cette méthode servira à aller chercher le résultat d’une analyse pour la ressource entrée dans le TEdit. Le tout sera affiché dans le contrôle TMemo.

Il est important de savoir que les fichiers soumis par l’API ont la priorité d’analyse la plus basse. Dépendant de la charge de travail de VirusTotal, il pourrait être possible d’attendre plusieurs heures avant que le fichier soit analysé. Il est donc important d’appeler ce code à intervalle régulier pour s’assurer que tous les résultats sont disponibles.

La réponse de la requête POST ressemble à ceci:

{
   "scans":{
      "AVG":{
         "detected":false,
         "version":"10.0.0.1190",
         "result":null,
         "update":"20120826"
      },
      "Panda":{
         "detected":false,
         "version":"10.0.3.5",
         "result":null,
         "update":"20120826"
      }
   },
   "scan_id":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813-1346036118",
   "sha1":"189f6287cd36c2f49bc828a556a70d960b7547b3",
   "resource":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "response_code":1,
   "scan_date":"2012-08-27 02:55:18",
   "permalink":"https://www.virustotal.com/file/51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813/analysis/1346036118/",
   "verbose_msg":"Scan finished, scan information embedded in this object",
   "total":2,
   "positives":0,
   "sha256":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "md5":"463b0fe60365c7cc84325646aabeb907"
}

Pour plus de détail sur l’API vous pouvez consulter la page web suivante: https://www.virustotal.com/documentation/public-api

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-&gt;Kind = TBrushKind::bkSolid;
    Fill-&gt;Color = claBeige;

    Viewport3D1-&gt;Align = TAlignLayout::alClient;
    Viewport3D1-&gt;Color = claNull;

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

    TrackBar2-&gt;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.