English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Création d'une application interactive pour afficher les flux RSS dans MetaTrader 5

Création d'une application interactive pour afficher les flux RSS dans MetaTrader 5

MetaTrader 5Expert Advisors | 13 janvier 2022, 16:31
259 0
Francis Dube
Francis Dube

Contenu


Introduction

L'article «Lecture des fils d'actualité RSS au moyen de MQL4» décrit un script plutôt rudimentaire qui pourrait être utilisé pour afficher les flux RSS dans la console du terminal au moyen d'une simple bibliothèque qui a été initialement conçue pour analyser les documents HTML.

Avec l'avènement de MetaTrader 5 et du langage de programmation MQL5, j'ai pensé qu'il était possible de créer une application interactive capable de mieux afficher le contenu RSS. Cet article décrit comment produire cette application à l'aide de la vaste bibliothèque standard MQL5 et d'autres outils développés par les contributeurs de la communauté MQL5.


1. Documents RSS en général

Avant d'aborder les spécificités de l'application, je pense qu'il est nécessaire de donner un aperçu de la structure générale d'un document RSS.

Afin de comprendre la description qui suit, vous devez vous familiariser avec le langage de balisage extensible et les concepts associés. Veuillez vous référer à Xml Tutoriel XML si vous n'êtes pas familier avec les documents XML. Notez que dans cet article, nœud fait référence à une balise dans un document XML. Comme mentionné dans l'article MQL4 référencé ci-dessus, les fichiers RSS sont simplement des documents XML avec une structure de balise spécifique.

Chaque document RSS a un conteneur global, la balise RSS. Ceci est commun à tous les documents RSS. La balise de canal est toujours un descendant direct de la balise RSS. Il contient des informations sur le site Web décrit par le flux. À partir de là, les documents RSS peuvent varier en termes de balises spécifiques qu'ils contiennent, mais il existe des balises que tous les documents doivent contenir pour être vérifiés en tant que fichiers RSS.

Les balises requises sont :

  • titre - Le titre de la chaîne. Doit contenir le nom du site Web ;
  • lien - URL du site Web qui fournit ce canal ;
  • description - Résumé de l'objet du site Web ;
  • élément- une balise d'élément au moins, pour le contenu.

Les balises affichées ci-dessus doivent toutes être des nœuds enfants de la balise de canal. Le nœud de l'élément est celui qui contient les données relatives à un contenu spécifique.

Chaque nœud d'élément à son tour doit également contenir les balises suivantes :

  • titre - Titre du contenu ;
  • lien - Le lien URL vers le contenu ;
  • description - Résumé du contenu ;
  • date - Date à laquelle le contenu a été publié sur le site Web.

Tous les documents RSS contiennent les balises décrites et suivent la même structure.

Un exemple de document RSS complet est donné ci-dessous.

<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Xul.fr: Tutorials and Applications of the Web 2.0</title>
    <link>http://www.xul.fr/</link>
    <description>Ajax, JavaScript, XUL, RSS, PHP and all technologies of the Web 2.0. Building a CMS, tutorial and application.</description>
    <pubDate>Wed, 07 Feb 2007 14:20:24 GMT</pubDate>
    <item>
    <title>News on interfaces of the Web in 2010</title>
    <link>http://www.xul.fr/en/2010.php</link>
    <description>Steve Jobs explains why iPad does not support Adobe Flash:&lt;em&gt;At Adobe they are lazy. 
    They have the potential to make  interesting things, but they refuse to do so. 
    Apple does not support Flash because it is too buggy.
     Each time a Mac crashes, most often it is because of Flash. Nobody will use Flash. 
     The world is moving  to &lt;a href="http://www.xul.fr/en/html5/" target="_parent"&gt;HTML 5&lt;/a&gt;</description>
     <pubDate>Sat, 11 Dec 10 09:41:06 +0100</pubDate>
    </item>
    <item>
      <title>Textured Border in CSS</title>
      <link>http://www.xul.fr/en/css/textured-border.php</link>
      <description>   The border attribute of the style sheets can vary in color and width, but it was not expected to give it a texture. However, only a CSS rule is required to add this graphic effect...   The principle is to assign a texture to the whole &lt;em&gt;fieldset&lt;/em&gt; and insert into it another &lt;em&gt;fieldset&lt;/em&gt; (for rounded edges) or a &lt;em&gt;div&lt;/em&gt;, whose background is the same as that of the page</description>
      <pubDate>Wed, 29 Jul 09 15:56:54  0200</pubDate>
    </item>
    <item>
      <title>Create an RSS feed from SQL, example with Wordpress</title>
      <link>http://www.xul.fr/feed/rss-sql-wordpress.html</link>
      <description>Articles contain at least the following items: And possibly, author's name, or an image. This produces the following table: The returned value is true if the database is found, false otherwise. It remains to retrieve the data from the array</description>
      <pubDate>Wed, 29 Jul 09 15:56:50  0200</pubDate>
    </item>
    <item>
      <title>Firefox 3.5</title>
      <link>http://www.xul.fr/gecko/firefox35.php</link>
      <description>Les balises audio et vid&#xE9;o sont impl&#xE9;ment&#xE9;es. Le format de donn&#xE9;e JSON est reconnu nativement par Firefox. L'avantage est d'&#xE9;viter l'utilisation de la fonction eval() qui n'est pas s&#xFB;r, ou d'employer des librairies additionnelles, qui est nettement plus lent</description>
      <pubDate>Wed, 24 Jun 09 15:18:47  0200</pubDate>
    </item>
    <item>
      <title>Contestation about HTML 5</title>
      <link>http://www.xul.fr/en/html5/contestation.php</link>
      <description>  Nobody seemed to be worried so far, but the definition of HTML 5 that is intended to be the format of billions of Web pages in coming years, is conducted and decided by a single person! &lt;em&gt;Hey, wait! Pay no attention to the multi-billions dollar Internet corporation behind the curtain. It's me Ian Hickson! I am my own man</description>
      <pubDate>Wed, 24 Jun 09 15:18:29  0200</pubDate>
    </item>
    <item>
      <title>Form Objects in HTML 4</title>
      <link>http://www.xul.fr/javascript/form-objects.php</link>
      <description>   It is created by the HTML &lt;em&gt;form&lt;/em&gt; tag:   The name or id attribute can access by script to its content. It is best to use both attributes with the same identifier, for the sake of compatibility.   The &lt;em&gt;action&lt;/em&gt; attribute indicates the page to which send the form data. If this attribute is empty, the page that contains the form that will be charged the data as parameters</description>
      <pubDate>Wed, 24 Jun 09 15:17:49  0200</pubDate>
    </item>
    <item>
      <title>DOM Tutorial</title>
      <link>http://www.xul.fr/en/dom/</link>
      <description>  The Document Object Model describes the structure of an XML or HTML document, a web page and allows access to each individual element.</description>
      <pubDate>Wed, 06 May 2009 18:30:11 GMT</pubDate>
    </item>    
  </channel>
</rss>


2. Structure globale de l'application

Ici, je vais donner une description des informations que le lecteur RSS doit afficher et une vue d'ensemble de l'interface utilisateur graphique de l'application.

Le premier aspect que l'application doit afficher est le titre de la chaîne, qui est contenu dans la balise titre. Ces informations serviront d'indication du site Web auquel le flux fait référence.

L'application doit également afficher un instantané de tout le contenu décrit par le flux, cela concerne toutes les balises d'élément du document. Pour chaque balise d'élément, le titre du contenu sera affiché. Enfin, je souhaite que le lecteur RSS puisse afficher la description du contenu, ce seront les données contenues dans la balise de description de chaque nœud d'élément.


2.1. L'interface utilisateur

L'interface utilisateur est fonction des informations à afficher par l'application.

L'idée que j'ai eue d'une interface utilisateur est mieux illustrée par le schéma ci-dessous.


Croquis du dialogue d'application

Fig. 1. Croquis du dialogue de l'application


Le diagramme montre les différentes sections qui composent l'interface utilisateur.

  • La première est la barre de titre. C'est là que le titre de la chaîne sera affiché ;
  • Zone de saisie. C'est ici que les utilisateurs saisiront l'adresse Web d'un flux RSS ;
  • Zone de titre. Le titre du contenu spécifique sera affiché ici ;
  • Zone de texte. La description du contenu est affichée ici;
  • Zone d'affichage de la liste. Cette liste déroulante affichera les titres de tout le contenu que contient le flux ;
  • Le bouton de gauche réinitialise et efface le texte affiché dans les zones Titre, texte et liste ;
  • Le bouton Mettre à jour le flux actuel récupère les nouvelles mises à jour pour un flux actuellement chargé.

Le lecteur RSS fonctionnera de la manière suivante - lorsque le programme est chargé sur la carte, la boîte de dialogue d'application vide s'affiche, l'utilisateur doit alors saisir l'adresse Web d'un flux RSS souhaité dans la zone de saisie, puis appuyez sur Entrée. Cela chargera tous les titres de contenu, c'est-à-dire les valeurs de balise de titre pour chaque balise d'élément dans la zone d'affichage de la liste. La liste sera numérotée à partir de 1, ce qui représente le contenu le plus récemment publié.

Chaque élément de la liste sera cliquable, en cliquant sur un élément de la liste, il sera mis en surbrillance et la description correspondante du contenu du titre sera affichée dans la zone de texte. En même temps, le titre du contenu sera affiché plus clairement dans la section de la zone de titre. Si une erreur se produit lors du chargement du flux pour quelque raison que ce soit, un message d'erreur s'affichera dans la zone de texte.

Le bouton de réinitialisation peut ensuite être utilisé pour effacer n'importe quel texte dans la zone de texte, la zone d'affichage de liste, les sections de la zone de titre.

Mettre à jour le flux actuel vérifie simplement les mises à jour du flux actuel.


2.2. Implémentation du code

Le lecteur RSS sera implémenté en tant qu'Expert Advisor et la bibliothèque standard MQL5 sera utilisée.

Le code sera contenu dans une classe CRssReader qui sera un descendant de la classe CAppDialog. La classe CAppDialog, donnée dans le fichier d'inclusion Dialog.mqh, permettra la mise en œuvre du dialogue d'application, qui fournit des fonctionnalités pour une barre de titre, et des contrôles d'application pour minimiser, maximiser et fermer. Ce sera la base de l'interface utilisateur, sur laquelle d'autres sections seront ajoutées. Pour les sections à ajouter, à savoir : la zone de titre, la zone de texte, la zone d'affichage de liste et les boutons. Chacun sera un contrôle. Les boutons seront implémentés en tant que contrôle de bouton décrit dans le fichier d'inclusion Button.mqh.

Le contrôle d'affichage de liste défini dans le fichier d'inclusion ListViewArea.mqh sera utilisé pour construire la section de zone d'affichage de liste du lecteur RSS.

Le champ de saisie suffira bien évidemment à construire la zone de saisie, ce champ est défini dans le fichier Edit.mqh.

Les sections de zone de titre et de zone de texte constituent un défi unique en ce qui concerne leur mise en œuvre. Le problème est que les deux doivent avoir un support pour le texte qui peut être affiché sur plusieurs lignes. Les objets texte dans MQL5 ne reconnaissent pas les nouveaux caractères de saut de ligne. Un autre problème est que seul un nombre limité de caractères de chaîne peut être affiché sur une ligne d'un objet texte. Cela signifie que si vous créez un objet texte avec une description suffisamment longue, l'objet sera affiché avec le texte coupé, seul un certain nombre de caractères sera affiché. Par essais et erreurs, j'ai découvert que la limite de caractères est de 63, espaces et signes de ponctuation compris.

Afin de surmonter ces problèmes, j'ai décidé d'implémenter les deux sections en tant que contrôles de vue de liste modifiés. Pour la section de zone de titre, le contrôle de vue de liste modifié ne pourra pas faire défiler et aura un nombre fixe d'éléments de liste (2). Chaque élément de liste ne sera pas cliquable ou sélectionnable et l'apparence physique du contrôle sera telle qu'il ne ressemblera pas à une liste. Ces 2 éléments de liste représenteront deux lignes de texte. Si le texte est trop long pour tenir sur une ligne, il sera divisé en conséquence et affiché sur 2 lignes de texte. Le contrôle de la section de zone de titre sera défini dans le fichier TitleArea.mqh.

Pour la section de zone de texte, une approche similaire sera appliquée, mais cette fois le nombre d'éléments de liste sera dynamique et le contrôle de vue de liste modifié défilera verticalement. Ce contrôle sera donné dans le fichier TextArea.mqh.

Les bibliothèques mentionnées jusqu'à présent s'occupent de l'interface utilisateur. Il y a encore une bibliothèque plus importante pour cette application qui doit être discutée. Il s'agit de la bibliothèque utilisée pour analyser les documents XML.


2.3. L'analyseur XML simple

Un document RSS étant un fichier XML, la bibliothèque EasyXML - XML Parser développée par liquinaut et présente dans la Base de Code est appliquée.

La bibliothèque est assez étendue et contient presque toutes les fonctionnalités nécessaires à notre lecteur RSS. J'ai apporté quelques modifications à la bibliothèque d'origine pour ajouter des fonctionnalités supplémentaires que je jugeais nécessaires.

Il s'agissait d'ajouts mineurs. Le premier était l'ajout d'une méthode supplémentaire appelée loadXmlFromUrlWebReq(). Cette méthode fournit simplement une alternative à l'utilisation de loadXmlFromUrl(), qui repose sur la bibliothèque WinInet pour le traitement des requêtes Web, loadXmlFromUrlWebReq() utilise la fonction WebRequest() intégrée pour permettre les téléchargements depuis Internet.

//+------------------------------------------------------------------+
//| load xml by given url using MQL5 webrequest function             |
//+------------------------------------------------------------------+
bool CEasyXml::loadXmlFromUrlWebReq(string pUrl)
  {
//---
   string cookie=NULL,headers;
   char post[],result[];
   int res;
//---
   string _url=pUrl;
   string sStream;
   res=WebRequest("GET",_url,cookie,NULL,5000,post,0,result,headers);
//--- check error
   if(res==-1)
     {
      Err=EASYXML_ERR_WEBREQUEST_URL;
      return(Error());
     }
//---success downloading file
   sStream=CharArrayToString(result,0,-1,CP_UTF8);
//---set up cach file
   if(blSaveToCache)
     {
      bool bResult=writeStreamToCacheFile(sStream);
      if(!bResult) Error(-1,false);
     }
//---
   return(loadXmlFromString(sStream));
  }

Le deuxième ajout était la méthode GetErrorMsg(), cela permet de récupérer le message d'erreur généré par l'analyseur chaque fois qu'une erreur se produit.

string            GetErrorMsg(void){   return(ErrMsg);}

Le dernier ajout a été fait pour corriger un défaut assez sérieux que j'ai trouvé lors du test de l'analyseur easyxml.

J'ai découvert que la bibliothèque n'était pas capable de reconnaître les déclarations de feuille de style XML. Le code confond une déclaration de feuille de style avec un attribut. Cela a bloqué le programme dans une boucle infinie, car le code recherchait en permanence la valeur d'attribut correspondante, qui n'a jamais existé.

Cela a été facilement rectifié, avec une petite modification de la méthode skipProlog().

//+------------------------------------------------------------------+
//| skip xml prolog                                                  |
//+------------------------------------------------------------------+
bool CEasyXml::skipProlog(string &pText,int &pPos)
  {
//--- skip xml declaration
   if(StringCompare(EASYXML_PROLOG_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_PROLOG_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_PROLOG_CLOSE,pPos+StringLen(EASYXML_PROLOG_OPEN));

      if(blDebug) Print("### Prolog ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_PROLOG_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_PROLOG_CLOSE);
           } else {
         Err=EASYXML_INVALID_PROLOG;
         return(false);
        }
     }
//--- skip stylesheet declarations
   if(StringCompare(EASYXML_STYLESHEET_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_STYLESHEET_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_STYLESHEET_CLOSE,pPos+StringLen(EASYXML_STYLESHEET_OPEN));

      if(blDebug) Print("### Prolog ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_STYLESHEET_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_STYLESHEET_CLOSE);
           } else {
         Err=EASYXML_INVALID_PROLOG;
         return(false);
        }
     }
//--- skip comments
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

//--- skip doctype
   if(StringCompare(EASYXML_DOCTYPE_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_DOCTYPE_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_DOCTYPE_CLOSE,pPos+StringLen(EASYXML_DOCTYPE_OPEN));

      if(blDebug) Print("### DOCTYPE ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_DOCTYPE_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_DOCTYPE_CLOSE);
           } else {
         Err=EASYXML_INVALID_DOCTYPE;
         return(false);
        }
     }

//--- skip comments
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

   return(true);
  }

Malgré ce problème, n'enlevez rien à liquinaut, car easyxml.mqh est un excellent outil.


2.4. Code d'Expert Advisor

Maintenant que toutes les bibliothèques nécessaires à l'application ont été décrites, il est temps de rassembler ces composants pour définir la classe CRssReader.

Veuillez noter que le code Lecteur RSS d’Expert Advisor commencera par la définition de la classe CRssReader.

//+------------------------------------------------------------------+
//|                                                    RssReader.mq5 |
//|                                                          Ufranco |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Controls\Dialog.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Button.mqh>
#include <TitleArea.mqh>
#include <TextArea.mqh>
#include <ListViewArea.mqh>
#include <easyxml.mqh>
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define INDENT_RIGHT                        (11)      // indent from right (with allowance for border width)
#define INDENT_BOTTOM                       (11)      // indent from bottom (with allowance for border width)
#define CONTROLS_GAP_X                      (5)       // gap by X coordinate
#define CONTROLS_GAP_Y                      (5)       // gap by Y coordinate

#define EDIT_HEIGHT                         (20)      // size by Y coordinate
#define BUTTON_WIDTH                        (150)     // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
#define TEXTAREA_HEIGHT                     (131)     // size by Y coordinate
#define LIST_HEIGHT                         (93)      // size by Y coordinate

Nous commençons par inclure les fichiers nécessaires. Les directives définir sont utilisées pour définir les paramètres physiques des contrôles en pixels.

INDENT_LEFT, INDENT_RIGHT, INDENT_TOP et INDENT_DOWN définissent la distance entre un contrôle et le bord du dialogue de l'application.

  • CONTROLS_GAP_Y est la distance verticale entre deux contrôles ;
  • EDIT_HEIGHT définit la hauteur du champ Editer qui constitue la zone de saisie ;
  • BUTTON_WIDTH et BUTTON_HEIGHT définissent la largeur et la hauteur de tous les contrôles de bouton ;
  • TEXTAREA_HEIGHT est la hauteur de la section de la zone de texte ;
  • LIST_HEIGHT définit la hauteur du contrôle d'affichage de liste.

Après cette définition, nous définissons la classe CRssReader.

//+------------------------------------------------------------------+
//| Class CRssReader                                                 |
//| Usage: main class for the RSS application                        |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                    // index of first item tag
   string            m_rssurl;                   // copy of web address of last feed 
   string            m_textareaoutput[];         // array of strings prepared for output to the text area panel
   string            m_titleareaoutput[];        // array of strings prepared for output to title area panel
   CButton           m_button1;                  // the button object
   CButton           m_button2;                  // the button object      
   CEdit             m_edit;                     // input panel
   CTitleArea        m_titleview;                // the display field object
   CListViewArea     m_listview;                 // the list object
   CTextArea         m_textview;                 // text area object
   CEasyXml          m_xmldocument;              // xml document object
   CEasyXmlNode     *RssNode;                    // root node object
   CEasyXmlNode     *ChannelNode;                // channel node object
   CEasyXmlNode     *ChannelChildNodes[];        // array of channel child node objects

Comme mentionné précédemment, CRssReader hérite de la classe CAppDialog.

La classe a plusieurs propriétés privées indiquées ci-dessous :

  • m_shift - cette variable de type entier stocke l'index du premier nœud d'élément, dans le tableau ChannelChildnodes ;
  • m_rssurl - est une valeur de chaîne qui conserve une copie de la dernière URL saisie ;
  • m_textareaoutput[] -est un tableau de chaînes, chaque élément correspond à une ligne de texte avec un certain nombre de caractères ;
  • m_titleareaoutput[] - il s'agit également d'un tableau de chaînes ayant le même objectif que le tableau de chaînes précédent ;
  • m_button1 et m_button2 sont des objets de type CButton;
  • m_listview est un objet représentant un contrôle de liste ;
  • propriété m_edit un objet CEdit et implémente la zone de saisie ;
  • m_titleview est un objet pour le champ d'affichage de la zone de titre ;
  • m_textview - l'objet de la section de zone de texte ;
  • m_xmldocument est un objet document XML ;
  • RssNode est l'objet nœud racine ;
  • ChannelNode est l'objet du nœud de canal ;
  • Le tableau ChannelChildNodes est un ensemble de pointeurs vers les descendants directs de la balise Chaine.

Notre classe n'aura que deux méthodes exposées publiquement.

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

La première méthode Create() définit la taille et la position initiale du dialogue de l'application.

Il initialise également tous les contrôles de l'application de lecteur RSS (rappelez-vous que notre classe hérite de la classe CAppDialog et donc les méthodes publiques de la classe parent et ses ancêtres peuvent être appelées par des instances de CRssReader).

//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CRssReader::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- succeed
   return(true);
  }

La seconde est la méthode OnEvent(), la fonction permet l'interactivité en attribuant des événements spécifiques à un contrôle correspondant et à une fonction de gestionnaire.

//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRssReader)
ON_EVENT(ON_CHANGE,m_listview,OnChangeListView)
ON_EVENT(ON_END_EDIT,m_edit,OnObjectEdit)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)


2.5. Méthodes d'initialisation des champs

Les méthodes protégées CreateEdit(), CreateButton1() ,CreateButton2(), CreateTitleView(), CreateListView() et CreateTextView() sont appelées par la fonction principale Create() pour initialiser un contrôle correspondant.

protected:
   // --- creating controls
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);

C'est dans chacune de ces fonctions que la taille, la position et les propriétés (c'est-à-dire la police, la taille de la police, la couleur, la couleur de bordure, le type de bordure) d'un contrôle sont définies.

//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- create
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_edit.Text("Please enter the web address of an Rss feed"))
      return(false);
   if(!m_edit.ReadOnly(false))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button 1                                                  |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- create
   if(!m_button1.Create(m_chart_id,m_name+"Button1",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Reset"))
      return(false);
   if(!m_button1.Font("Comic Sans MS"))
      return(false);
   if(!m_button1.FontSize(8))
      return(false);
   if(!m_button1.Color(clrWhite))
      return(false);
   if(!m_button1.ColorBackground(clrBlack))
      return(false);
   if(!m_button1.ColorBorder(clrBlack))
      return(false);
   if(!m_button1.Pressed(true))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button 2                                                  |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- coordinates
   int x1=(ClientAreaWidth()-INDENT_RIGHT)-BUTTON_WIDTH;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+BUTTON_HEIGHT;
//--- create
   if(!m_button2.Create(m_chart_id,m_name+"Button2",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Update current feed"))
      return(false);
   if(!m_button2.Font("Comic Sans MS"))
      return(false);
   if(!m_button2.FontSize(8))
      return(false);
   if(!m_button2.Color(clrWhite))
      return(false);
   if(!m_button2.ColorBackground(clrBlack))
      return(false);
   if(!m_button2.ColorBorder(clrBlack))
      return(false);
   if(!m_button2.Pressed(true))
      return(false);
   if(!Add(m_button2))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y)+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+(EDIT_HEIGHT*2);
   m_titleview.Current();
//--- create 
   if(!m_titleview.Create(m_chart_id,m_name+"TitleView",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating title view");
      return(false);
     }
   else
     {
      for(int i=0;i<2;i++)
        {
         m_titleview.AddItem(" ");
        }
     }
   if(!Add(m_titleview))
     {
      Print("error adding title view");
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "ListView" element                                    |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+TEXTAREA_HEIGHT+CONTROLS_GAP_Y+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+LIST_HEIGHT;
//--- create
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- fill out with strings
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+TEXTAREA_HEIGHT;
   m_textview.Current();
//--- create 
   if(!m_textview.Create(m_chart_id,m_name+"TextArea",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating text area view");
      return(false);
     }
   else
     {
      for(int i=0;i<1;i++)
        {
         m_textview.AddItem(" ");
        }
      m_textview.VScrolled(true);
      ChartRedraw();
     }
   if(!Add(m_textview))
     {
      Print("error adding text area view");
      return(false);
     }
//----success      
   return(true);
  }


2.6. Méthodes de traitement des documents RSS

// --- rss document processing
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);

2.6.1. LoadDocument()

Cette fonction a plusieurs rôles importants à jouer. Le principal étant le traitement des requêtes web. Le loadXmlFromUrlWebReq() est appelé pour télécharger le fichier RSS.

Si cela est terminé avec succès, la fonction passe à sa deuxième tâche d'initialiser les pointeurs RssNode, ChannelNode et également de remplir le tableau ChannelChildnodes. C'est ici que les propriétés m_rssurl et m_shift sont définies. Une fois tout cela fait, la fonction retourne vraie.

Si le fichier RSS ne peut pas être téléchargé, la zone de titre, la zone d'affichage de liste et les sections de zone de texte sont effacées de tout texte et un message d'état s'affiche dans la barre de titre. Ceci est suivi de la sortie d'un message d'erreur dans la section de zone de texte. Ensuite, la fonction renvoie faux.

//+------------------------------------------------------------------+
//|   load document                                                  |
//+------------------------------------------------------------------+
bool CRssReader::LoadDocument(string filename)
  {
   if(!m_xmldocument.loadXmlFromUrlWebReq(filename))
     {
      m_textview.ItemsClear();
      m_listview.ItemsClear();
      m_titleview.ItemsClear();
      CDialog::Caption("Failed to load Feed");
      if(!m_textview.AddItem(m_xmldocument.GetErrorMsg()))
         Print("error displaying error message");
      return(false);
     }
   else
     {
      m_rssurl=filename;
      RssNode=m_xmldocument.getDocumentRoot();
      ChannelNode=RssNode.FirstChild();
      if(CheckPointer(RssNode)==POINTER_INVALID || CheckPointer(ChannelNode)==POINTER_INVALID)
         return(false);
     }
   ArrayResize(ChannelChildNodes,ChannelNode.Children().Total());
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      ChannelChildNodes[i]=ChannelNode.Children().At(i);
     }
   m_shift=ChannelNode.Children().Total()-ItemNodesTotal();
   return(true);
  }


2.6.2. ItemNodesTotal()

Cette fonction d'assistance est utilisée dans la méthode LoadDocument(). Il renvoie une valeur entière qui correspond au nombre de nœuds d'élément descendants de la balise de canal.

S'il n'y a pas de nœuds d'élément, le document sera un document RSS non valide et la fonction renverra 0.

//+------------------------------------------------------------------+
//| function counts the number of item tags in document              |
//+------------------------------------------------------------------+
int CRssReader::ItemNodesTotal(void)
  {
   int t=0;
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      if(ChannelChildNodes[i].getName()=="item")
        {
         t++;
        }
      else continue;
     }
   return(t);
  }

2.6.3. FreeDocumentTree()

Cette fonction réinitialise tous les pointeurs CEasyXmlNode.

Tout d'abord, les éléments du tableau ChannelChildnodes sont supprimés en appelant la méthode Shutdown() de la classe CArrayObj. Le tableau est ensuite libéré avec un seul appel de ArrayFree().

Ensuite, le pointeur vers le nœud de canal est supprimé et l'arborescence de documents de l'analyseur easyxml est effacée. Ces actions font que les pointeurs RssNode et ChannelNode deviennent de mauvais pointeurs, c'est pourquoi ils reçoivent tous deux la valeur NULL.

//+------------------------------------------------------------------+
//| free document tree and reset pointer values                      |
//+------------------------------------------------------------------+ 
void CRssReader::FreeDocumentTree(void)
  {
   ChannelNode.Children().Shutdown();
   ArrayFree(ChannelChildNodes);
   RssNode.Children().Shutdown();
   m_xmldocument.Clear();
   m_shift=0;
   RssNode=NULL;
   ChannelNode=NULL;
  }


2.7. Méthodes d'extraction d'informations de l'arborescence du document

Ces fonctions permettent d'obtenir du texte à partir d'un document RSS.

//--- getters
   string            getChannelTitle(void);
   string            getTitle(CEasyXmlNode *Node);
   string            getDescription(CEasyXmlNode *Node);
   string            getDate(CEasyXmlNode *Node);

2.7.1. getChannelTitle()

Cette fonction récupère le titre de canal actuel du document RSS.

Il commence par vérifier la validité du pointeur de nœud de canal. Si le pointeur est valide, il parcourt tous les descendants directs du nœud de canal à la recherche de la balise titre.

La boucle for utilise la propriété m_shift pour limiter le nombre de descendants de nœuds de canal à rechercher. Si la fonction échoue, elle renvoie NULL.

//+------------------------------------------------------------------+
//| get channel title                                                |
//+------------------------------------------------------------------+
string CRssReader::getChannelTitle(void)
  {
   string ret=NULL;
   if(!CheckPointer(ChannelNode)==POINTER_INVALID)
     {
      for(int i=0;i<m_shift;i++)
        {
         if(ChannelChildNodes[i].getName()=="title")
           {
            ret=ChannelChildNodes[i].getValue();
            break;
           }
         else continue;
        }
     }
//---return value
   return(ret);
  }


2.7.2. getTitle()

La fonction prend en entrée un pointeur vers une balise d'élément, parcourt les descendants de cette balise à la recherche d'une balise de titre et renvoie sa valeur.

Les fonctions getDescription() et getDate() suivent le même format et fonctionnent de manière similaire. Un appel réussi de la fonction renvoie une valeur de chaîne, sinon NULL est renvoyé en sortie.

//+------------------------------------------------------------------+
//| display title                                                    |
//+------------------------------------------------------------------+
string CRssReader::getTitle(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="title")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| display description                                              |
//+------------------------------------------------------------------+
string CRssReader::getDescription(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="description")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| display date                                                     |
//+------------------------------------------------------------------+ 
string CRssReader::getDate(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="pubDate")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }


2.8. Méthodes de mise en forme du texte

Ces fonctions servent à préparer le texte pour la sortie en tant qu'objets texte afin de surmonter certaines des limitations des objets texte.

 //--- text formating 
   bool              FormatString(string v,string &array[],int n);
   string            removeTags(string _string);
   string            removeSpecialCharacters(string s_tring);
   int               tagPosition(string _string,int w);

2.8.1. FormatString()

Il s'agit de la fonction principale qui prépare le texte extrait d'un document RSS pour la sortie vers l'application.

Fondamentalement, il prend une valeur d'entrée de chaîne et divise le texte en lignes de "n" caractères. "n" étant une valeur entière du nombre de caractères dans une seule ligne de texte. Après chaque «n» caractères dans le texte, le code recherche un endroit approprié pour insérer un nouveau caractère de saut de ligne. Ensuite, toute la valeur de la chaîne est traitée et les nouveaux caractères de saut de ligne sont insérés dans le texte d'origine.

La fonction StringSplit() est utilisée pour créer un tableau de chaînes, chacune ne dépassant pas «n» caractères. La fonction renvoie une valeur booléenne ainsi qu'un tableau de valeurs de chaîne prêtes à être sorties.

//+------------------------------------------------------------------+
//| format string for output to text area panel                      |
//+------------------------------------------------------------------+
bool CRssReader::FormatString(string v,string &array[],int n)
  {
   ushort ch[],space,fullstop,comma,semicolon,newlinefeed;
   string _s,_k;
   space=StringGetCharacter(" ",0);
   fullstop=StringGetCharacter(".",0);
   comma=StringGetCharacter(",",0);
   semicolon=StringGetCharacter(";",0);
   newlinefeed=StringGetCharacter("\n",0);
   _k=removeTags(v);
   _s=removeSpecialCharacters(_k);
   int p=StringLen(_s);
   ArrayResize(ch,p+1);
   int d=StringToShortArray(_s,ch,0,-1);
   for(int i=1;i<d;i++)
     {
      int t=i%n;
      if(!t== 0)continue;
      else 
        {
         if(ch[(i/n)*n]==fullstop || ch[(i/n)*n]==semicolon || ch[(i/n)*n]==comma)
           {
            ArrayFill(ch,((i/n)*n)+1,1,newlinefeed);
           }
         else
           {
            for(int k=i;k>=0;k--)
              {
               if(ch[k]==space)
                 {
                  ArrayFill(ch,k,1,newlinefeed);
                  break;
                 }
               else continue;
              }
           }
        }
     }
   _s=ShortArrayToString(ch,0,-1);
   int s=StringSplit(_s,newlinefeed,array);
   if(!s>0)
     {return(false);}
// success 
   return(true);
  }


2.8.2. removeTags()

Cette fonction est devenue une nécessité après avoir remarqué qu'un bon nombre de documents RSS contiennent des balises HTML dans les nœuds XML.

Certains documents RSS sont publiés de cette manière, car de nombreuses applications d'agrégation RSS fonctionnent dans le navigateur.

La fonction prend une valeur de chaîne et recherche les balises dans le texte. Si des balises sont trouvées, la fonction parcourt chaque caractère du texte et stocke la position de chaque caractère de balise d'ouverture et de fermeture dans le tableau à 2 dimensions a[][]. Ce tableau est utilisé pour extraire le texte entre les balises et la chaîne extraite est renvoyée. Si aucune balise n'est trouvée, la chaîne d'entrée est renvoyée telle quelle.

//+------------------------------------------------------------------+
//| remove tags                                                      |
//+------------------------------------------------------------------+
string CRssReader::removeTags(string _string)
  {
   string now=NULL;
   if(StringFind(_string,"<",0)>-1)
     {
      int v=0,a[][2];
      ArrayResize(a,2024);
      for(int i=0;i<StringLen(_string);i++)
        {
         int t=tagPosition(_string,i);
         if(t>0)
           {
            v++;
            a[v-1][0]=i;
            a[v-1][1]=t;
           }
         else continue;
        }
      ArrayResize(a,v);
      for(int i=0;i<v-1;i++)
        {
         now+=StringSubstr(_string,(a[i][1]+1),(a[i+1][0]-(a[i][1]+1)));
        }
     }
   else
     {
      now=_string;
     }
   return(now);
  }

Un exemple partiel d'un tel document est présenté ci-dessous.

<item>            
    <title>GIGABYTE X99-Gaming G1 WIFI Motherboard Review</title>
    <author>Ian Cutress</author>
    <description><![CDATA[ <p>The gaming motherboard range from a manufacturer is one with a lot of focus in terms of design and function due to the increase in gaming related PC sales. On the Haswell-E side of gaming, GIGABYTE is putting forward the X99-Gaming G1 WIFI at the top of its stack, and this is what we are reviewing today.&nbsp;</p>
<p align="center"><a href='http://dynamic1.anandtech.com/www/delivery/ck.php?n=a1f2f01f&amp;cb=582254849' target='_blank'><img src='http://dynamic1.anandtech.com/www/delivery/avw.php?zoneid=24&amp;cb=582254849&amp;n=a1f2f01f' border='0' alt='' /></a><img src="http://toptenreviews.122.2o7.net/b/ss/tmn-test/1/H.27.3--NS/0" height="1" width="1" border="0" alt="" /></p>]]></description>
    <link>http://www.anandtech.com/show/8788/gigabyte-x99-gaming-g1-wifi-motherboard-review</link>
        <pubDate>Thu, 18 Dec 2014 10:00:00 EDT</pubDate>
        <guid isPermaLink="false">tag:www.anandtech.com,8788:news</guid>
        <category><![CDATA[ Motherboards]]></category>                               
</item>  


2.8.3. removeSpecialCharacters()

Cette fonction remplace simplement certaines constantes de chaîne par le caractère correct.

Par exemple, le caractère esperluette dans certains documents XML peut être représenté par «&amp». Cette fonction utilise la fonction StringReplace() intégrée pour remplacer ces types d'occurrences.

//+------------------------------------------------------------------+
//| remove special characters                                        |
//+------------------------------------------------------------------+ 
string CRssReader::removeSpecialCharacters(string s_tring)
  {
   string n=s_tring;
   StringReplace(n,"&amp;","&");
   StringReplace(n,"&#39;","'");
   StringReplace(n,"&nbsp;"," ");
   StringReplace(n,"&ldquo;","\'");
   StringReplace(n,"&rdquo;","\'");
   StringReplace(n,"&quot;","\"");
   StringReplace(n,"&ndash;","-");
   StringReplace(n,"&rsquo;","'");
   StringReplace(n,"&gt;","");
   return(n);
  }


2.8.4. tagPosition()

Il s'agit d'une fonction d'assistance appelée dans la fonction removeTags(). Il prend en entrée une chaîne et une valeur entière.

La valeur entière d'entrée représente la position d'un caractère dans la chaîne, à partir de laquelle la fonction commencera à rechercher un caractère de balise ouvrante, c'est-à-dire «<». Si une balise ouvrante est trouvée, la fonction commence à rechercher une balise fermante et renvoie en sortie la position de la balise de fermeture correspondante «>». Si aucune balise n'est trouvée, la fonction renvoie -1.

//+------------------------------------------------------------------+
//| tag positions                                                    |
//+------------------------------------------------------------------+
int CRssReader::tagPosition(string _string,int w)
  {
   int iClose=-1;
   if(StringCompare("<",StringSubstr(_string,w,StringLen("<")))==0)
     {
      iClose=StringFind(_string,">",w+StringLen("<"));
     }

   return(iClose);
  }


2.9. Méthodes de gestion des événements de contrôles indépendants

Ces fonctions gèrent les événements capturés d'un contrôle spécifique.

//--- handlers of the dependent controls events
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };

2.9.1. OnChangeListView()

Il s'agit d'une fonction de gestionnaire d'événements qui est appelée à chaque fois que l'on clique sur l'un des éléments de liste dans la section de zone d'affichage de liste de l'application.

La fonction est chargée de permettre la visualisation du résumé de description de certains contenus référencés dans le document RSS.

La fonction efface les sections de zone de texte et de zone de titre de tout texte, récupère les nouvelles données de l'arborescence du document et les prépare pour la sortie. Tout cela ne se produit que si le tableau ChannelChildnodes n'est pas vide.

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CRssReader::OnChangeListView(void)
  {
   int a=0,k=0,l=0;
   a=m_listview.Current()+m_shift;
   if(ArraySize(ChannelChildNodes)>a)
     {
      if(m_titleview.ItemsClear())
        {
         if(!FormatString(getTitle(ChannelChildNodes[a]),m_titleareaoutput,55))
           {
            return;
           }
         else
         if(ArraySize(m_titleareaoutput)>0)
           {
            for(l=0;l<ArraySize(m_titleareaoutput);l++)
              {
               m_titleview.AddItem(removeSpecialCharacters(m_titleareaoutput[l]));
              }
           }
        }
      if(m_textview.ItemsClear())
        {
         if(!FormatString(getDescription(ChannelChildNodes[a]),m_textareaoutput,35))
            return;
         else
         if(ArraySize(m_textareaoutput)>0)
           {
            for(k=0;k<ArraySize(m_textareaoutput);k++)
              {
               m_textview.AddItem(m_textareaoutput[k]);
              }
            m_textview.AddItem(" ");
            m_textview.AddItem("Date|"+getDate(ChannelChildNodes[a]));
           }
         else return;
        }
     }
  }


2.9.2. OnObjectEdit()

La fonction de gestion est appelée chaque fois qu'un utilisateur termine de saisir du texte dans la zone de saisie.

La fonction appelle la méthode LoadDocument(). Si le téléchargement réussit, le texte est effacé de l'ensemble de l'application. Ensuite, la légende est modifiée et le nouveau contenu est affiché dans la section de zone d'affichage de liste.

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CRssReader::OnObjectEdit(void)
  {
   string f=m_edit.Text();
   if(StringLen(f)>0)
     {
      if(ArraySize(ChannelChildNodes)<1)
        {
         CDialog::Caption("Loading...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
      else
        {
         FreeDocumentTree();
         CDialog::Caption("Loading new RSS Feed...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
     }
   else return;
  }


2.9.3. OnClickButton1/2()

Ces gestionnaires sont appelés chaque fois qu'un utilisateur clique sur les boutons de réinitialisation ou de recherche de mises à jour de flux.

Cliquer sur le bouton de réinitialisation actualise la boîte de dialogue de l'application à l'état dans lequel elle se trouvait lors du premier lancement de l'Expert advisor.

Cliquer sur le bouton «vérifier la mise à jour du flux» provoque un rappel de la méthode charger LoadDocument() et les données du flux RSS seront téléchargées, actualisant la section de la zone d'affichage de la liste.

//+------------------------------------------------------------------+
//| Event handler  refresh the app dialogue                          |
//+------------------------------------------------------------------+   
void CRssReader::OnClickButton1(void)
  {
   if(ArraySize(ChannelChildNodes)<1)
     {
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
   else
     {
      FreeDocumentTree();
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| Event handler  update current feed                               |
//+------------------------------------------------------------------+ 
void CRssReader::OnClickButton2(void)
  {
   string f=m_rssurl;
   if(ArraySize(ChannelChildNodes)<1)
      return;
   else
     {
      FreeDocumentTree();
      CDialog::Caption("Checking for RSS Feed update...");
      if(LoadDocument(f))
        {
         if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
            Print("error changing caption");
         if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
           {
            for(int i=0;i<ItemNodesTotal()-1;i++)
              {
               if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                 {
                  Print("can not add item to listview area");
                  return;
                 }
              }
           }
         else
           {
            Print("text area/listview area not cleared");
            return;
           }
        }
      else return;
     }
  }

Cela conclut la définition de la classe CRssReader.


2.10. L'implémentation de la classe CRssReader

//+------------------------------------------------------------------+
//| Class CRssReader                                                 |
//| Usage: main class for the RSS application                        |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                   // index of first item tag
   string            m_rssurl;                  // copy of web address of last feed 
   string            m_textareaoutput[];        // array of strings prepared for output to the text area panel
   string            m_titleareaoutput[];       // array of strings prepared for output to title area panel
   CButton           m_button1;                 // the button object
   CButton           m_button2;                 // the button object      
   CEdit             m_edit;                    // input panel
   CTitleArea        m_titleview;               // the display field object
   CListViewArea     m_listview;                // the list object
   CTextArea         m_textview;                // text area object
   CEasyXml          m_xmldocument;             // xml document object
   CEasyXmlNode     *RssNode;                   // root node object
   CEasyXmlNode     *ChannelNode;               // channel node object
   CEasyXmlNode     *ChannelChildNodes[];       // array of channel child node objects

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   // --- creating controls
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);
   // --- rss document processing
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);
   //--- getters
   string            getChannelTitle(void);
   string            getTitle(CEasyXmlNode *Node);
   string            getDescription(CEasyXmlNode *Node);
   string            getDate(CEasyXmlNode *Node);
   //--- text formating 
   bool              FormatString(string v,string &array[],int n);
   string            removeTags(string _string);
   string            removeSpecialCharacters(string s_tring);
   int               tagPosition(string _string,int w);
   //--- handlers of the dependent controls events
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRssReader)
ON_EVENT(ON_CHANGE,m_listview,OnChangeListView)
ON_EVENT(ON_END_EDIT,m_edit,OnObjectEdit)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRssReader::CRssReader(void)
  {

  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CRssReader::~CRssReader(void)
  {
  }
//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CRssReader::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- create
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_edit.Text("Please enter the web address of an Rss feed"))
      return(false);
   if(!m_edit.ReadOnly(false))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button 1                                                  |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- create
   if(!m_button1.Create(m_chart_id,m_name+"Button1",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Reset"))
      return(false);
   if(!m_button1.Font("Comic Sans MS"))
      return(false);
   if(!m_button1.FontSize(8))
      return(false);
   if(!m_button1.Color(clrWhite))
      return(false);
   if(!m_button1.ColorBackground(clrBlack))
      return(false);
   if(!m_button1.ColorBorder(clrBlack))
      return(false);
   if(!m_button1.Pressed(true))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button 2                                                  |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- coordinates
   int x1=(ClientAreaWidth()-INDENT_RIGHT)-BUTTON_WIDTH;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+BUTTON_HEIGHT;
//--- create
   if(!m_button2.Create(m_chart_id,m_name+"Button2",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Update current feed"))
      return(false);
   if(!m_button2.Font("Comic Sans MS"))
      return(false);
   if(!m_button2.FontSize(8))
      return(false);
   if(!m_button2.Color(clrWhite))
      return(false);
   if(!m_button2.ColorBackground(clrBlack))
      return(false);
   if(!m_button2.ColorBorder(clrBlack))
      return(false);
   if(!m_button2.Pressed(true))
      return(false);
   if(!Add(m_button2))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y)+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+(EDIT_HEIGHT*2);
   m_titleview.Current();
//--- create 
   if(!m_titleview.Create(m_chart_id,m_name+"TitleView",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating title view");
      return(false);
     }
   else
     {
      for(int i=0;i<2;i++)
        {
         m_titleview.AddItem(" ");
        }
     }
   if(!Add(m_titleview))
     {
      Print("error adding title view");
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "ListView" element                                    |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+TEXTAREA_HEIGHT+CONTROLS_GAP_Y+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+LIST_HEIGHT;
//--- create
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- fill out with strings
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the display field                                         |
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+TEXTAREA_HEIGHT;
   m_textview.Current();
//--- create 
   if(!m_textview.Create(m_chart_id,m_name+"TextArea",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating text area view");
      return(false);
     }
   else
     {
      for(int i=0;i<1;i++)
        {
         m_textview.AddItem(" ");
        }
      m_textview.VScrolled(true);
      ChartRedraw();
     }
   if(!Add(m_textview))
     {
      Print("error adding text area view");
      return(false);
     }
//----success      
   return(true);
  }
//+------------------------------------------------------------------+
//|   load document                                                  |
//+------------------------------------------------------------------+
bool CRssReader::LoadDocument(string filename)
  {
   if(!m_xmldocument.loadXmlFromUrlWebReq(filename))
     {
      m_textview.ItemsClear();
      m_listview.ItemsClear();
      m_titleview.ItemsClear();
      CDialog::Caption("Failed to load Feed");
      if(!m_textview.AddItem(m_xmldocument.GetErrorMsg()))
         Print("error displaying error message");
      return(false);
     }
   else
     {
      m_rssurl=filename;
      RssNode=m_xmldocument.getDocumentRoot();
      ChannelNode=RssNode.FirstChild();
      if(CheckPointer(RssNode)==POINTER_INVALID || CheckPointer(ChannelNode)==POINTER_INVALID)
         return(false);
     }
   ArrayResize(ChannelChildNodes,ChannelNode.Children().Total());
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      ChannelChildNodes[i]=ChannelNode.Children().At(i);
     }
   m_shift=ChannelNode.Children().Total()-ItemNodesTotal();
   return(true);
  }
//+------------------------------------------------------------------+
//| function counts the number of item tags in document              |
//+------------------------------------------------------------------+
int CRssReader::ItemNodesTotal(void)
  {
   int t=0;
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      if(ChannelChildNodes[i].getName()=="item")
        {
         t++;
        }
      else continue;
     }
   return(t);
  }
//+------------------------------------------------------------------+
//| free document tree and reset pointer values                      |
//+------------------------------------------------------------------+ 
void CRssReader::FreeDocumentTree(void)
  {
   ChannelNode.Children().Shutdown();
   ArrayFree(ChannelChildNodes);
   RssNode.Children().Shutdown();
   m_xmldocument.Clear();
   m_shift=0;
   RssNode=NULL;
   ChannelNode=NULL;
  }
//+------------------------------------------------------------------+
//| get channel title                                                |
//+------------------------------------------------------------------+
string CRssReader::getChannelTitle(void)
  {
   string ret=NULL;
   if(!CheckPointer(ChannelNode)==POINTER_INVALID)
     {
      for(int i=0;i<m_shift;i++)
        {
         if(ChannelChildNodes[i].getName()=="title")
           {
            ret=ChannelChildNodes[i].getValue();
            break;
           }
         else continue;
        }
     }
//---return value
   return(ret);
  }
//+------------------------------------------------------------------+
//| display title                                                    |
//+------------------------------------------------------------------+
string CRssReader::getTitle(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="title")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| display description                                              |
//+------------------------------------------------------------------+
string CRssReader::getDescription(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="description")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| display date                                                     |
//+------------------------------------------------------------------+ 
string CRssReader::getDate(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="pubDate")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| format string for output to text area panel                      |
//+------------------------------------------------------------------+
bool CRssReader::FormatString(string v,string &array[],int n)
  {
   ushort ch[],space,fullstop,comma,semicolon,newlinefeed;
   string _s,_k;
   space=StringGetCharacter(" ",0);
   fullstop=StringGetCharacter(".",0);
   comma=StringGetCharacter(",",0);
   semicolon=StringGetCharacter(";",0);
   newlinefeed=StringGetCharacter("\n",0);
   _k=removeTags(v);
   _s=removeSpecialCharacters(_k);
   int p=StringLen(_s);
   ArrayResize(ch,p+1);
   int d=StringToShortArray(_s,ch,0,-1);
   for(int i=1;i<d;i++)
     {
      int t=i%n;
      if(!t== 0)continue;
      else 
        {
         if(ch[(i/n)*n]==fullstop || ch[(i/n)*n]==semicolon || ch[(i/n)*n]==comma)
           {
            ArrayFill(ch,((i/n)*n)+1,1,newlinefeed);
           }
         else
           {
            for(int k=i;k>=0;k--)
              {
               if(ch[k]==space)
                 {
                  ArrayFill(ch,k,1,newlinefeed);
                  break;
                 }
               else continue;
              }
           }
        }
     }
   _s=ShortArrayToString(ch,0,-1);
   int s=StringSplit(_s,newlinefeed,array);
   if(!s>0)
     {return(false);}
// success 
   return(true);
  }
//+------------------------------------------------------------------+
//| remove special characters                                        |
//+------------------------------------------------------------------+ 
string CRssReader::removeSpecialCharacters(string s_tring)
  {
   string n=s_tring;
   StringReplace(n,"&amp;","&");
   StringReplace(n,"&#39;","'");
   StringReplace(n,"&nbsp;"," ");
   StringReplace(n,"&ldquo;","\'");
   StringReplace(n,"&rdquo;","\'");
   StringReplace(n,"&quot;","\"");
   StringReplace(n,"&ndash;","-");
   StringReplace(n,"&rsquo;","'");
   StringReplace(n,"&gt;","");
   return(n);
  }
//+------------------------------------------------------------------+
//| remove tags                                                      |
//+------------------------------------------------------------------+
string CRssReader::removeTags(string _string)
  {
   string now=NULL;
   if(StringFind(_string,"<",0)>-1)
     {
      int v=0,a[][2];
      ArrayResize(a,2024);
      for(int i=0;i<StringLen(_string);i++)
        {
         int t=tagPosition(_string,i);
         if(t>0)
           {
            v++;
            a[v-1][0]=i;
            a[v-1][1]=t;
           }
         else continue;
        }
      ArrayResize(a,v);
      for(int i=0;i<v-1;i++)
        {
         now+=StringSubstr(_string,(a[i][1]+1),(a[i+1][0]-(a[i][1]+1)));
        }
     }
   else
     {
      now=_string;
     }
   return(now);
  }
//+------------------------------------------------------------------+
//| tag positions                                                    |
//+------------------------------------------------------------------+
int CRssReader::tagPosition(string _string,int w)
  {
   int iClose=-1;
   if(StringCompare("<",StringSubstr(_string,w,StringLen("<")))==0)
     {
      iClose=StringFind(_string,">",w+StringLen("<"));
     }

   return(iClose);
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CRssReader::OnChangeListView(void)
  {
   int a=0,k=0,l=0;
   a=m_listview.Current()+m_shift;
   if(ArraySize(ChannelChildNodes)>a)
     {
      if(m_titleview.ItemsClear())
        {
         if(!FormatString(getTitle(ChannelChildNodes[a]),m_titleareaoutput,55))
           {
            return;
           }
         else
         if(ArraySize(m_titleareaoutput)>0)
           {
            for(l=0;l<ArraySize(m_titleareaoutput);l++)
              {
               m_titleview.AddItem(removeSpecialCharacters(m_titleareaoutput[l]));
              }
           }
        }
      if(m_textview.ItemsClear())
        {
         if(!FormatString(getDescription(ChannelChildNodes[a]),m_textareaoutput,35))
            return;
         else
         if(ArraySize(m_textareaoutput)>0)
           {
            for(k=0;k<ArraySize(m_textareaoutput);k++)
              {
               m_textview.AddItem(m_textareaoutput[k]);
              }
            m_textview.AddItem(" ");
            m_textview.AddItem("Date|"+getDate(ChannelChildNodes[a]));
           }
         else return;
        }
     }
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CRssReader::OnObjectEdit(void)
  {
   string f=m_edit.Text();
   if(StringLen(f)>0)
     {
      if(ArraySize(ChannelChildNodes)<1)
        {
         CDialog::Caption("Loading...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
      else
        {
         FreeDocumentTree();
         CDialog::Caption("Loading new RSS Feed...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
     }
   else return;
  }
//+------------------------------------------------------------------+
//| Event handler  refresh the app dialogue                          |
//+------------------------------------------------------------------+   
void CRssReader::OnClickButton1(void)
  {
   if(ArraySize(ChannelChildNodes)<1)
     {
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
   else
     {
      FreeDocumentTree();
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| Event handler  update current feed                               |
//+------------------------------------------------------------------+ 
void CRssReader::OnClickButton2(void)
  {
   string f=m_rssurl;
   if(ArraySize(ChannelChildNodes)<1)
      return;
   else
     {
      FreeDocumentTree();
      CDialog::Caption("Checking for RSS Feed update...");
      if(LoadDocument(f))
        {
         if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
            Print("error changing caption");
         if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
           {
            for(int i=0;i<ItemNodesTotal()-1;i++)
              {
               if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                 {
                  Print("can not add item to listview area");
                  return;
                 }
              }
           }
         else
           {
            Print("text area/listview area not cleared");
            return;
           }
        }
      else return;
     }
  }

Il peut maintenant être utilisé dans le code de l'Expert Advisor.


2.11. Le code de l'Expert Advisor

L'Expert Advisor n'a pas de variables d'entrée puisque l'application est censée être entièrement interactive.

Nous déclarons d'abord une variable globale qui est une instance de la classe CRssReader. Dans la fonction OnInit(), nous initialisons le dialogue de l'application avec un appel à la méthode principale Create(). Si cela réussit, la méthode Run() d'une classe ancêtre est appelée.

Dans la fonction OOnDeinit()), la méthode Destroy() de la classe parent est appelée pour supprimer l'intégralité de l'application et retirer l'Expert Advisor du graphique.

La fonction OnChartEvent() contient un appel à une méthode ancêtre de la classe CRssReader, qui gérera le traitement de tous les événements.

//Expert Advisor code begins here
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CRssReader ExtDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create application dialog
   if(!ExtDialog.Create(0,"RSSReader",0,20,20,518,394))
      return(INIT_FAILED);
//--- run application
   ExtDialog.Run();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }

Le code doit ensuite être compilé et le programme sera prêt à être utilisé.

Lorsque l'Expert Advisor RssReader.mq5 est chargé sur un graphique, une boîte de dialogue d'application vide apparaît comme suit :

Fig. 2. Capture d'écran de la boîte de dialogue d'application vide du RssReader Expert Advisor

Fig. 2. Capture d'écran du dialogue d'application vide du RssReader de l’Expert Advisor

Entrez une adresse Web et le contenu RSS sera chargé dans la boîte de dialogue de l'application, comme illustré par l'image ci-dessous :

Fig. 3. RssReader EA fonctionnant dans le terminal

Fig. 3. RssReader EA fonctionnant dans le terminal

J'ai testé le programme avec une large gamme de flux RSS. Le seul problème que j'ai observé était lié à l'affichage de certains caractères indésirables, principalement le résultat de documents RSS contenant des caractères généralement trouvés dans les documents HTML.

J'ai également remarqué que la modification de la période d'un graphique pendant que l'application est en cours d'exécution entraîne la réinitialisation de l'EA et peut entraîner un mauvais dessin des contrôles de l'application.

Je n'ai pas pu corriger ce comportement, mon conseil est donc d'éviter de modifier la période du graphique lorsque le programme RSS Reader est en cours d'exécution.


Conclusion

Nous avons terminé la création d'une application de lecteur RSS entièrement interactive pour MetaTrader 5, en utilisant des techniques de programmation orientées objet.

Il y a beaucoup plus de fonctionnalités qui pourraient être ajoutées à l'application et je suis sûr qu'il existe de nombreuses autres façons d'organiser l'interface utilisateur. J'espère que ceux qui ont peut-être de meilleures compétences en conception d'interface graphique d'application amélioreront l'application et partageront leurs créations.

P.S. Veuillez noter que le fichier easyxml.mqh disponible en téléchargement ici n'est pas le même que celui disponible dans la base de code, il contient des modifications déjà mentionnées dans l'article. Toutes les inclusions nécessaires se trouvent dans le fichier RssReader.zip.


Traduit de l’anglais par MetaQuotes Ltd.
Article original : https://www.mql5.com/en/articles/1589

Fichiers joints |
easyxml.mqh (25.66 KB)
easyxmlnode.mqh (7.67 KB)
ListViewArea.mqh (19.89 KB)
TextArea.mqh (13.47 KB)
TitleArea.mqh (13.56 KB)
RssReader.mq5 (27.83 KB)
RssReader.zip (20.49 KB)
Idées de trading basées sur la direction des prix et la vitesse de déplacement Idées de trading basées sur la direction des prix et la vitesse de déplacement
L'article propose une revue d'idée basée sur l'analyse de la direction du mouvement des prix et de leur vitesse. Nous avons effectué sa formalisation dans le langage MQL4 présenté en tant qu'expert advisor pour explorer la viabilité de la stratégie envisagée. Nous déterminons également les meilleurs paramètres via la vérification, l'examen et l'optimisation d'un exemple donné dans l'article.
Le MQL5 Cookbook : Ordres ОСО Le MQL5 Cookbook : Ordres ОСО
L’activité de trading de tout trader implique divers mécanismes et interrelations, y compris les relations entre les ordres. Cet article suggère une solution de traitement des ordres OCO. Les classes de bibliothèque standard sont largement impliquées, et de nouveaux types de données sont créés ici.
Conseils pour l'achat d'un produit sur le marché. Guide détaillé Conseils pour l'achat d'un produit sur le marché. Guide détaillé
Ce guide détaillé fournit des astuces pour mieux comprendre et rechercher un produit requis. L'article tente de découvrir différentes méthodes de recherche d'un produit approprié, de tri des produits indésirables, de détermination de l'efficacité et de l'essentialité du produit pour vous.
Sur les méthodes d'analyse technique et de prévision du marché Sur les méthodes d'analyse technique et de prévision du marché
L'article démontre les capacités et le potentiel d'une méthode mathématique bien connue associée à une pensée visuelle et à des perspectives de marché « out of the box ». D'une part, il sert à attirer l'attention d'un large public car il peut amener les esprits créatifs à reconsidérer le paradigme de trading en tant que tel. Et d'autre part, il peut donner lieu à des développements alternatifs et à des implémentations de code de programme concernant un large éventail d'outils d'analyse et de prévision.