English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Creazione di un'applicazione interattiva per visualizzare feed RSS in MetaTrader 5

Creazione di un'applicazione interattiva per visualizzare feed RSS in MetaTrader 5

MetaTrader 5Expert Advisors | 12 gennaio 2022, 13:00
131 0
Francis Dube
Francis Dube

Contenuto


Introduzione

L'articolo "Reading RSS News Feeds by Means of MQL4" descriveva uno script piuttosto rudimentale che poteva essere utilizzato per visualizzare i feed RSS nella console del terminale per mezzo di una semplice libreria originariamente costruita per l'analisi di documenti HTML.

Con l'avvento di MetaTrader 5 e del linguaggio di programmazione MQL5 ho pensato che fosse possibile creare un'applicazione interattiva che fosse in grado di visualizzare meglio i contenuti RSS. In questo articolo viene descritto come produrre questa applicazione utilizzando l'ampia libreria MQL5 Standard e alcuni altri strumenti sviluppati dai collaboratori della comunità MQL5.


1. Documenti RSS in generale

Prima di fare i conti con le specifiche dell'applicazione, penso che sia necessario dare una panoramica della struttura generale di un documento

Per comprendere la descrizione da seguire è necessario avere familiarità con il linguaggio di markup estensibile e i concetti correlati. Fare riferimento a XML Tutorial se non si ha familiarità con i documenti XML. Si noti che in questo articolo, nodo fa riferimento a un tag in un documento XML. Come accennato nell'articolo MQL4 di cui sopra, i file RSS sono semplicemente documenti XML con una struttura di tag specifica.

Ogni documento RSS ha un contenitore globale, il tag RSS. Questo è comune a tutti i documenti RSS. Il tag del canale è sempre un discendente diretto del tag RSS. Contiene informazioni sul sito Web descritto nel feed. Da qui in poi i documenti RSS possono variare in termini di tag specifici che contengono, ma ci sono alcuni tag che tutti i documenti dovrebbero contenere per essere verificati RSS-file.

I tag richiesti sono:

  • title - Il titolo del canale. Dovrebbe contenere il nome del sito web;
  • link - URL del sito web che fornisce questo canale;
  • descrizione - Riepilogo di ciò di cui tratta il sito web;
  • item - un tag item almeno, per il contenuto.

I tag mostrati sopra dovrebbero essere tutti nodi figlio del tag del canale. Il nodo dell'elemento è quello che contiene i dati relativi a contenuti specifici.

Ogni nodo elemento, a sua volta, deve contenere anche i seguenti tag:

  • titolo - Titolo del contenuto;
  • link - Il link URL al contenuto;
  • descrizione - Riepilogo del contenuto;
  • data - Data in cui il contenuto è stato pubblicato sul sito web.

Tutti i documenti RSS contengono i tag descritti e seguono la stessa struttura.

Di seguito è riportato un esempio di documento RSS completo.

<?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. Struttura generale dell'applicazione

Qui darò una descrizione delle informazioni che il lettore RSS dovrebbe visualizzare e una panoramica dell'interfaccia utente grafica dell'applicazione.

Il primo aspetto che l'applicazione dovrebbe visualizzare è il titolo del canale, che è contenuto nel tag title. Queste informazioni serviranno come indicazione del sito web i riferimenti del feed.

L'applicazione dovrebbe anche visualizzare un'istantanea di tutto il contenuto descritto dal feed, questo si riferisce a tutti i tag degli elementi nel documento. Per ogni tag dell'elemento, verrà visualizzato il titolo del contenuto. Infine, voglio che l'RSS Reader sia in grado di mostrare la descrizione del contenuto, questi saranno i dati contenuti nel tag description di ciascun nodo dell'elemento.


2.1. Interfaccia Utente

L'interfaccia utente è una funzione delle informazioni che devono essere visualizzate dall'applicazione.

L'idea che ho avuto per un'interfaccia utente è meglio rappresentata dal diagramma qui sotto.


Sketch della finestra di dialogo dell'applicazione

Fig. 1. Schizzo della finestra di dialogo dell'applicazione


Il diagramma mostra le diverse sezioni che compongono l'interfaccia utente.

  • Il primo è la barra del titolo. Qui è dove verrà visualizzato il titolo del canale;
  • Area di ingresso. È qui che gli utenti inseriranno l'indirizzo web di un feed RSS;
  • Area del titolo. Il titolo per contenuti specifici verrà visualizzato qui;
  • Area di testo. La descrizione del contenuto è mostrata qui;
  • Area di visualizzazione elenco. Questo elenco scorrevole visualizzerà i titoli di tutti i contenuti contenuti nel feed;
  • Il pulsante a sinistra reimposta e cancella il testo visualizzato nelle aree titolo, testo ed elenco;
  • Il pulsante Aggiorna feed corrente recupera i nuovi aggiornamenti per un feed attualmente caricato.

Il lettore RSS funzionerà nel modo seguente: quando il programma viene caricato sul grafico, viene visualizzata la finestra di dialogo vuota dell'applicazione, l'utente deve quindi inserire l'indirizzo Web di un feed RSS desiderato nell'area di input, quindi premere invio. Questo caricherà tutti i titoli dei contenuti, ovvero i valori del tag title per ogni tag dell'elemento nell'area di visualizzazione dell'elenco. L'elenco sarà numerato da 1, che rappresenta il contenuto pubblicato più di recente.

Ogni voce di elenco sarà cliccabili, facendo clic su una voce di elenco, verrà evidenziata e la descrizione corrispondente del contenuto del titolo verrà visualizzata nell'area di testo. Allo stesso tempo, il titolo del contenuto verrà mostrato più chiaramente nella sezione dell'area del titolo. Se si verifica un errore durante il caricamento del feed per qualsiasi motivo, verrà visualizzato un messaggio di errore nella sezione dell'area di testo.

Il pulsante di ripristino può quindi essere utilizzato per cancellare qualsiasi testo nell'area di testo, nell'area di visualizzazione elenco, nelle sezioni dell'area del titolo.

Aggiorna il feed corrente controlla semplicemente eventuali aggiornamenti per il feed corrente.


2.2. Implementazione del codice

Il lettore RSS verrà implementato come Expert Advisor e verrà utilizzata la libreria standard MQL5.

Il codice sarà contenuto in una classe CRssReader che sarà un discendente della classe CAppDialog. La classe CAppDialog, fornita nel file dialog.mqh include, consentirà l'implementazione della finestra di dialogo dell'applicazione, che fornisce funzionalità per una barra del titolo e controlli dell'applicazione per la minimizzazione, l'ottimizzazione e la chiusura. Questa sarà la base dell'interfaccia utente, oltre alla quale verranno aggiunte altre sezioni. Per le sezioni da aggiungere, vale a dire: l'area del titolo, l'area di testo, l'area di visualizzazione elenco e i pulsanti. Ognuno sarà un controllo. I pulsanti verranno implementati come controllo pulsante descritto nel file di include Button.mqh.

Il controllo visualizzazione elenco definito nel file di include ListViewArea.mqh verrà utilizzato per costruire la sezione dell'area di visualizzazione elenco del lettore RSS.

Il controllo di modifica sarà ovviamente sufficiente per costruire la sezione dell'area di input, questo controllo è definito nel file Edit.mqh.

Le sezioni dell'area del titolo e dell'area di testo offrono una sfida unica quando si tratta della loro implementazione. Il problema è che entrambi devono avere il supporto per il testo che può essere visualizzato su più righe. Gli oggetti di testo in MQL5 non riconoscono i nuovi caratteri dell'avanzamento riga. Un altro problema è che solo un numero limitato di caratteri stringa può essere visualizzato in una riga di un oggetto di testo. Ciò significa che se si crea un oggetto di testo con una descrizione abbastanza lunga, l'oggetto verrà visualizzato con il testo tagliato, verrà visualizzato solo un certo numero di caratteri. Per tentativi ed errori ho scoperto che il limite di caratteri è 63, inclusi spazi e segni di punteggiatura.

Per ovviare a questi problemi ho deciso di implementare entrambe le sezioni come controlli di visualizzazione elenco modificati. Per la sezione dell'area del titolo, il controllo della visualizzazione elenco modificato non sarà scorrevole e avrà un numero fisso di voci di elenco (2). Ogni voce di elenco non sarà selezionabile o cliccabile e l'aspetto fisico del controllo sarà tale da non sembrare un elenco. Queste 2 voci di elenco rappresenteranno due righe di testo. Se il testo è troppo lungo per adattarsi a una riga, verrà diviso di conseguenza e visualizzato come 2 righe di testo. Il controllo per la sezione dell'area del titolo verrà definito nel file TitleArea.mqh.

Per la sezione dell'area di testo verrà applicato un approccio simile, solo che questa volta il numero di voci di elenco sarà dinamico e il controllo della visualizzazione elenco modificato sarà scorrevole verticalmente. Questo controllo verrà fornito nel file TextArea.mqh.

Le librerie menzionate finora si occupano dell'interfaccia utente. C'è ancora un'altra libreria importante cruciale per questa applicazione che deve essere discussa. Questa è la libreria utilizzata per analizzare i documenti XML.


2.3. Il parser XML semplice

Poiché un documento RSS è un file XML, viene applicata la libreria EasyXML - XML Parser sviluppata da liquinaut che si trova nella Code Base.

La libreria è piuttosto ampia e contiene quasi tutte le funzionalità necessarie per il nostro lettore RSS. Ho apportato alcune modifiche alla libreria originale per aggiungere alcune funzionalità extra che ritenevo necessarie.

Queste erano aggiunte minori. Il primo dei quali è stato l'aggiunta di un metodo aggiuntivo chiamato loadXmlFromUrlWebReq(). Questo metodo fornisce semplicemente un'alternativa all'utilizzo di loadXmlFromUrl(), che si basa sulla libreria WinInet per l'elaborazione delle richieste Web, loadXmlFromUrlWebReq() utilizza la funzione WebRequest() incorporata per abilitare i download da 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));
  }

La seconda aggiunta è stata il metodo GetErrorMsg(), che consente di recuperare il messaggio di errore emesso dal parser ogni volta che si verifica un errore.

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

L'ultima aggiunta è stata fatta per correggere un difetto piuttosto grave che ho trovato durante il test del parser easyxml.

Ho scoperto che la libreria non era in grado di riconoscere le dichiarazioni del foglio di stile XML. Il codice confonde una dichiarazione di foglio di stile con un attributo. Ciò ha causato il blocco del programma in un ciclo infinito, poiché il codice cercava continuamente il valore dell'attributo corrispondente, che non è mai esistito.

Questo è stato facilmente rettificato, con una piccola modifica del metodo 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);
  }

Nonostante questo problema, non togliere nulla a liquinaut, easyxml.mqh è uno strumento eccellente.


2.4. Codice di Expert Advisor

Ora che tutte le librerie necessarie per l'applicazione sono state descritte, è il momento di riunire questi componenti per definire la classe CRssReader.

Si noti che il codice RSS Reader Expert Advisor inizierà con la definizione della 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

Iniziamo includendo i file necessari. Le direttive di definizione vengono utilizzate per impostare i parametri fisici dei controlli in pixel.

Il INDENT_LEFT, INDENT_RIGHT, INDENT_TOP e INDENT_DOWN impostano la distanza tra un controllo e il bordo della finestra di dialogo dell'applicazione.

  • CONTROLS_GAP_Y è la distanza verticale tra due controlli;
  • EDIT_HEIGHT imposta l'altezza del controllo Edit che costituisce l'area di input;
  • BUTTON_WIDTH e BUTTON_HEIGHT definire la larghezza e l'altezza di tutti i controlli dei pulsanti;
  • TEXTAREA_HEIGHT è l'altezza della sezione dell'area di testo;
  • LIST_HEIGHT imposta l'altezza del controllo visualizzazione elenco.

Dopo le definizioni iniziamo la definizione della 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

Come accennato in precedenza, CRssReader eredita dalla classe CAppDialog.

La classe ha diverse proprietà private indicate di seguito:

  • m_shift - questa variabile di tipo intero memorizza l'indice del nodo del primo elemento, nell'array ChannelChildnodes;
  • m_rssurl - è un valore stringa che conserva una copia dell'ultimo URL che è stato inserito;
  • m_textareaoutput[] -è una matrice di stringhe, ogni elemento corrisponde a una riga di testo con un certo numero di caratteri;
  • m_titleareaoutput[] - anche questo array di stringhe ha lo stesso scopo del precedente array di stringhe;
  • m_button1 e m_button2 sono oggetti di tipo CButton;
  • m_listview è un oggetto che rappresenta un controllo elenco;
  • m_edit proprietà di un oggetto CEdit e implementa l'area di input;
  • m_titleview è un oggetto per il campo di visualizzazione dell'area del titolo;
  • m_textview - l'oggetto per la sezione dell'area di testo;
  • m_xmldocument è un oggetto documento xml;
  • RssNode è l'oggetto nodo radice;
  • ChannelNode è l'oggetto del nodo canale;
  • L'array ChannelChildNodes è un insieme di puntatori ai discendenti diretti del tag Channel.

La nostra classe avrà solo due metodi esposti pubblicamente.

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);

Il primo metodo Create() imposta la dimensione e la posizione iniziale della finestra di dialogo dell'applicazione.

Inizializza inoltre tutti i controlli dell'app RSS Reader (ricorda che la nostra classe eredita dalla classe CAppDialog e quindi i metodi pubblici della classe padre e dei suoi antenati possono essere chiamati da istanze di 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);
  }

Il secondo, è il metodo OnEvent(), la funzione abilita l'interattività assegnando eventi specifici a un controllo corrispondente e una funzione handler.

//+------------------------------------------------------------------+
//| 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. Metodi per l'inizializzazione dei controlli

I metodi protetti CreateEdit(), CreateButton1(), CreateButton2(), CreateTitleView(), CreateListView() e CreateTextView() vengono chiamati dalla funzione principale Create() per l'inizializzazione di un controllo corrispondente.

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

È in ciascuna di queste funzioni che vengono impostate le dimensioni, la posizione e le proprietà (ad es. carattere, dimensione del carattere, colore, colore del bordo, tipo di bordo) di un controllo.

//+------------------------------------------------------------------+
//| 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. Metodi per l'elaborazione dei documenti RSS

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

2.6.1. LoadDocument()

Questa funzione ha un paio di ruoli importanti da svolgere. Il principale è l'elaborazione delle richieste web. LoadXmlFromUrlWebReq() viene chiamato per scaricare il file RSS.

Se questo viene completato correttamente, la funzione passa alla sua seconda attività di inizializzazione dei puntatori RssNode, ChannelNode e anche di riempimento dell'array ChannelChildnodes. È qui che vengono impostate le proprietà m_rssurl e m_shift. Una volta fatto tutto questo, la funzione restituisce true.

Se il file RSS non può essere scaricato, l'area del titolo, l'area di visualizzazione elenco e le sezioni dell'area di testo vengono cancellate da qualsiasi testo e viene visualizzato un messaggio di stato sulla barra del titolo. Questo è seguito dall'output di un messaggio di errore nella sezione dell'area di testo. Quindi la funzione restituisce false.

//+------------------------------------------------------------------+
//|   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()

Questa funzione di supporto viene utilizzata nel metodo LoadDocument(). Restituisce un valore intero che è il numero di nodi elemento discendenti del tag del canale.

Se non sono presenti nodi elemento, il documento sarà un documento RSS non valido e la funzione restituirà 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()

Questa funzione ripristina tutti i puntatori CEasyXmlNode.

Innanzitutto gli elementi dell'array ChannelChildnodes vengono eliminati chiamando il metodo Shutdown() della classe CArrayObj. L'array viene quindi liberato con una singola chiamata di ArrayFree().

Successivamente il puntatore al nodo del canale viene eliminato e l'albero dei documenti del parser easyxml viene cancellato. Queste azioni fanno in modo che i puntatori RssNode e ChannelNode diventino puntatori non danneggiati, motivo per cui a entrambi viene assegnato il valore 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. Metodi per estrarre informazioni dalla struttura del documento

Queste funzioni servono per ottenere testo da un documento RSS.

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

2.7.1. getChannelTitle()

Questa funzione recupera il titolo del canale corrente del documento RSS.

Inizia controllando la validità del puntatore del nodo canale. Se il puntatore è valido, esegue il ciclo di tutti i discendenti diretti del nodo canale alla ricerca del tag title.

Il ciclo for utilizza la proprietà m_shift per limitare il numero di discendenti dei nodi di canale da cui eseguire la ricerca. Se la funzione non riesce, restituisce 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 funzione prende come input un puntatore a un tag elemento e attraversa i discendenti di quel tag alla ricerca di un tag title e ne restituisce il valore.

Le funzioni getDescription() e getDate() seguono lo stesso formato e funzionano in modo simile. Una chiamata riuscita della funzione restituisce un valore stringa, altrimenti NULL viene restituito come output.

//+------------------------------------------------------------------+
//| 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. Metodi per la formattazione del testo

Queste funzioni servono per preparare il testo per l'output come oggetti di testo al fine di superare alcune delle limitazioni degli oggetti di testo.

 //--- 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()

Questa è la funzione principale che prepara il testo estratto da un documento RSS per l'output nell'applicazione.

Fondamentalmente prende un valore di input stringa e divide il testo in righe di "n" caratteri. "n" è un valore intero del numero di caratteri in una singola riga di testo. Dopo ogni "n" caratteri nel testo, il codice cerca un posto adatto per inserire un nuovo carattere di avanzamento riga. Quindi l'intero valore stringa viene elaborato e i nuovi caratteri dell'avanzamento riga vengono inseriti nel testo originale.

La funzione StringSplit() viene utilizzata per creare una matrice di stringhe, ognuna delle quali non è più lunga "n" caratteri. La funzione restituisce un valore booleano e anche una matrice di valori stringa pronti per l'output.

//+------------------------------------------------------------------+
//| 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()

Questa funzione è diventata una necessità dopo aver notato che un buon numero di documenti RSS contiene tag HTML all'interno dei nodi XML.

Alcuni documenti RSS vengono pubblicati in questo modo, poiché molte applicazioni di aggregazione RSS funzionano nel browser.

La funzione accetta un valore stringa e cerca i tag all'interno del testo. Se vengono trovati dei tag, la funzione scorre in loop ogni carattere del testo e memorizza la posizione di ogni carattere tag di apertura e chiusura nella matrice a 2 dimensioni a[][]. Questa matrice viene utilizzata per estrarre il testo tra i tag e viene restituita la stringa estratta. Se non viene trovato alcun tag, la stringa di input viene restituita così come è.

//+------------------------------------------------------------------+
//| 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);
  }

Di seguito è riportato un esempio parziale di tale documento.

<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()

Questa funzione sostituisce semplicemente alcune costanti di stringa con il carattere corretto.

Ad esempio, come carattere commerciale in alcuni documenti xml può essere rappresentato come "&amp". Questa funzione utilizza la funzione StringReplace() incorporata per sostituire questo tipo di eventi.

//+------------------------------------------------------------------+
//| 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()

Si tratta di una funzione di aiuto chiamata nella funzione removeTags(). Prende come input una stringa e un valore intero.

Il valore intero di input rappresenta la posizione di un carattere nella stringa, da cui la funzione inizierà a cercare un carattere tag di apertura, ad es. "<". Se viene trovato un tag di apertura, la funzione inizia a cercare un tag di chiusura e restituisce come output la postazione del corrispondente carattere del tag di chiusura ">". Se non ci sono tag trovati, la funzione restituisce -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. Metodi per la gestione degli eventi di controlli indipendenti

Queste funzioni gestiscono gli eventi acquisiti di un controllo specifico.

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

2.9.1. OnChangeListView()

Si tratta di una funzione del gestore eventi che viene chiamata ogni volta che si fa clic su una delle voci di elenco nella sezione dell'area di visualizzazione elenco dell'applicazione.

La funzione è responsabile dell'abilitazione della visualizzazione del riepilogo della descrizione per alcuni contenuti a cui si fa riferimento nel documento RSS.

La funzione cancella l'area di testo e le sezioni dell'area del titolo di qualsiasi testo, recupera nuovi dati dall'albero del documento e li prepara per l'output. Tutto ciò si verifica solo se l'array ChannelChildnodes non è vuoto.

//+------------------------------------------------------------------+
//| 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 funzione del gestore viene chiamata ogni volta che un utente termina l'immissione di testo nell'area di input.

La funzione chiama il metodo LoadDocument(). Se il download ha esito positivo, il testo viene cancellato dall'intera applicazione. Successivamente, la didascalia viene modificata e il nuovo contenuto viene inseguito alla sezione dell'area di visualizzazione elenco.

//+------------------------------------------------------------------+
//| 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()

Questi gestori vengono chiamati ogni volta che un utente fa clic sui pulsanti di ripristino o verifica la disponibilità di aggiornamenti del feed.

Facendo clic sul pulsante di ripristino, la finestra di dialogo dell'app viene aggiornata allo stato in cui si trovava quando Expert Advisor è stato avviato per la prima volta.

Facendo clic sul pulsante "controlla aggiornamento feed" si verifica un richiamo del metodo load LoadDocument() e i dati del feed RSS verranno scaricati, aggiornando la sezione dell'area di visualizzazione elenco.

//+------------------------------------------------------------------+
//| 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;
     }
  }

Con questo si conclude la definizione della classe CRssReader.


2.10. Implementazione della 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;
     }
  }

Ora può essere utilizzato nel codice di Expert Advisor.


2.11. Il codice di Expert Advisor

L'Expert Advisor non ha variabili di input poiché l'applicazione è pensata per essere interamente interattiva.

Per prima cosa dichiariamo una variabile globale che è un'istanza della classe CRssReader. Nella funzione OnInit() inizializziamo la finestra di dialogo dell'applicazione con una chiamata al metodo Create() principale. Se l'1 esito positivo, viene chiamato il metodo Run() di una classe ancestor.

Nella funzione OnDeinit() viene chiamato il metodo Destroy() della classe padre per eliminare l'intera applicazione e rimuovere Expert Advisor dal grafico.

La funzione OnChartEvent() contiene una chiamata a un metodo antenato della classe CRssReader, che gestirà l'elaborazione di tutti gli eventi.

//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);
  }

Il codice deve quindi essere compilato e il programma sarà pronto per l'uso.

Quando RssReader.mq5 Expert Advisor viene caricato su un grafico, viene visualizzata una finestra di dialogo vuota dell'applicazione come segue:

Fig. 2. ScreenShot della finestra di dialogo vuota dell'app di RssReader Expert Advisor

Fig. 2. ScreenShot della finestra di dialogo vuota dell'app di RssReader Expert Advisor

Inserisci un indirizzo web e il contenuto RSS verrà caricato nella finestra di dialogo dell'applicazione, come illustrato dall'immagine seguente:

Fig. 3. RssReader EA che lavora nel terminale

Fig. 3. RssReader EA che lavora nel terminale

Ho testato il programma con una vasta gamma di feed RSS. L'unico problema che ho osservato era legato alla visualizzazione di alcuni caratteri indesiderati, per lo più il risultato di documenti RSS contenenti caratteri che di solito si trovano nei documenti HTML.

Ho anche notato che la modifica del periodo di un grafico mentre l'applicazione è in esecuzione, causa la reinizializzazione dell'EA e può comportare che i controlli dell'applicazione non vengano disegnati correttamente.

Non sono stato in grado di correggere questo comportamento, quindi il mio consiglio è di evitare di cambiare il periodo del grafico quando è in esecuzione il programma RSS Reader.


Conclusione

Abbiamo completato la creazione di un'applicazione RSS Reader interamente interattiva per MetaTrader 5, utilizzando tecniche di programmazione orientate agli oggetti.

Ci sono molte più funzionalità che potrebbero essere aggiunte all'applicazione e sono sicuro che ci sono molti altri modi in cui l'interfaccia utente può essere organizzata. Spero che quelli con capacità di progettazione della GUI dell'applicazione possibilmente migliori miglioreranno l'applicazione e condivideranno le loro creazioni.

P.S. Si prega di notare che il file easyxml.mqh disponibile per il download qui non è lo stesso di quello disponibile nella Code Base, contiene modifiche già menzionate nell'articolo. Tutti i include necessari sono nel file RssReader.zip.


Tradotto dall’inglese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/en/articles/1589

File allegati |
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)
Idee di trading basate sulla direzione dei prezzi e sulla velocità di movimento Idee di trading basate sulla direzione dei prezzi e sulla velocità di movimento
L'articolo fornisce la revisione di un'idea basata sull'analisi della direzione del movimento dei prezzi e della loro velocità. Abbiamo eseguito la sua formalizzazione nel linguaggio MQL4 presentato come Expert Advisor per esplorare la fattibilità della strategia in esame. Determiniamo anche i migliori parametri tramite controllo, esame e ottimizzazione dell’esempio fornito nell'articolo.
Manuale MQL5: Ordini ОСО Manuale MQL5: Ordini ОСО
L'attività di trading di qualsiasi trader comporta vari meccanismi e interrelazioni, comprese le relazioni tra gli ordini. Questo articolo suggerisce una soluzione per l'elaborazione degli ordini OCO. Le classi di libreria standard sono ampiamente coinvolte, e vengono creati nuovi tipi di dati.
Suggerimenti per l'acquisto di un prodotto sul mercato. Guida passo-passo Suggerimenti per l'acquisto di un prodotto sul mercato. Guida passo-passo
Questa guida dettagliata fornisce suggerimenti e trucchi per una migliore comprensione e ricerca del prodotto desiderato. L'articolo tenterà di trovare diversi metodi di ricerca di un prodotto, eliminando i prodotti indesiderati, determinando se questo sarà per l’utente efficiente ed essenziale.
Sui metodi di analisi tecnica e previsione di mercato Sui metodi di analisi tecnica e previsione di mercato
L'articolo dimostra le capacità e il potenziale di un noto metodo matematico abbinato al pensiero visivo e a una prospettiva di mercato "fuori dagli schemi". Da un lato, esso è scritto per attirare l'attenzione di un vasto pubblico, per convincere le menti creative a riconsiderare il paradigma di trading in quanto tale. E dall'altro, può dare origine a sviluppi alternativi e implementazioni di codice di programma per una vasta gamma di strumenti per l'analisi e la previsione.