English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Construindo uma Aplicação Interativa para exibir feeds RSS no MetaTrader 5

Construindo uma Aplicação Interativa para exibir feeds RSS no MetaTrader 5

MetaTrader 5Experts | 9 junho 2015, 12:29
1 595 0
Francis Dube
Francis Dube

Conteúdo


Introdução

O artigo "Reading RSS News Feeds by Means of MQL4" descreveu um roteiro bastante rudimentar que poderia ser usado para exibir os feeds RSS no console do terminal através de uma biblioteca simples, que foi originalmente construída para analisar documentos em HTML.

Com o advento do MetaTrader 5 e a linguagem de programação MQL5 eu pensei que seria possível criar uma aplicação interativa que seria capaz de exibir melhor o conteúdo RSS. Este artigo descreve como produzir este aplicativo usando a extensiva Biblioteca Padrão MQL5 e algumas outras ferramentas desenvolvidas pelos colaboradores da comunidade MQL5.


1. Documentos RSS em geral

Antes de nos familiarizarmos com as especificidades da aplicação, eu acho que é necessário fornecer um panorama da estrutura geral de um Documento RSS.

A fim de compreender a descrição a seguir você precisará estar familiarizado com a linguagem de marcação extensível e seus conceitos relacionados. Por favor, consulte o Tutorial XML caso você não esteja familiarizado com documentos XML. Note que neste artigo, um nó se refere a uma tag (etiqueta) em um documento XML. Conforme mencionado no artigo em MQL4 referenciado acima, os arquivos RSS são simplesmente documentos XML com uma estrutura de tag (etiqueta) específica.

Cada documento RSS possui um container global, a tag RSS. Isto é comum a todos os documentos RSS. O tag canal é sempre um descendente direto da tag RSS. Esta contém as informações sobre o site que o feed descreve. A partir daqui os documentos RSS podem variar em termos de tags específicas que eles contêm, mas existem algumas tags que todos os documentos devem conter a fim de ser verificado como arquivos RSS.

As tags necessárias são:

  • Título - O título do canal. Deve conter o nome do site;
  • link - URL do site que oferece esse canal;
  • Descrição - Resumo do que se trata o site;
  • item - ter pelo menos uma tag item para o conteúdo.

As tags mostradas acima devem ser todos nós filho da tag canal. O nó item é aquele que contém os dados relativos ao conteúdo específico.

Cada nó item, por sua vez, também deve conter as seguintes tags:

  • Título - Título do conteúdo;
  • link - O link URL para o conteúdo;
  • Descrição - Resumo do conteúdo;
  • data - Data em que o conteúdo foi publicado no site.

Todos os documentos RSS contem as tags descritas e seguem a mesma estrutura.

Um exemplo de um documento RSS completo é dado abaixo.


<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. Estrutura geral da aplicação

Aqui eu irei fornecer uma descrição das informações que o Leitor RSS deve exibir e um resumo da interface gráfica do usuário do aplicativo.

O primeiro aspecto que a aplicação deve exibir é o título do canal, que está contido na tag título. Esta informação servirá como uma indicação do website que o feed faz referência.

O aplicativo também deve exibir uma panorâmica de todo o conteúdo que o feed descreve, este refere-se a todas as tags item no documento. Para cada tag item, o título do conteúdo será exibido. Por último, quero que o leitor RSS seja capaz de mostrar a descrição do conteúdo, este será os dados contidos na tag descrição de cada nó item.


2.1. A interface de usuário

A interface de usuário é uma função da informação a ser apresentada pela aplicação.

A idéia que eu tinha para uma interface de usuário é melhor representada pelo diagrama abaixo.


Esboço do diálogo do aplicativo

Fig. 1. Esboço do diálogo do aplicativo


O diagrama mostra as diferentes seções que compõem a interface de usuário.

  • A primeira é a barra de título. Este é o lugar onde o título do canal será exibido;
  • Área de entrada. É aqui que os usuários irão inserir o endereço da Web de um feed RSS;
  • Área do título. O título do conteúdo específico será exibido aqui;
  • Área de texto. É onde a descrição do conteúdo é mostrada;
  • Área de exibição da lista. Esta lista de rolagem irá exibir os títulos de todo o conteúdo que o feed contém;
  • O botão na esquerda reseta e limpa o texto exibido na área do título, de texto e de exibição da lista;
  • O botão de atualização do feed atual obtém as novas atualizações para o feed carregado no momento.

O Leitor RSS funcionará da seguinte forma - quando o programa é carregado ao gráfico, é exibido o diálogo do aplicativo vazio, o usuário então tem que digitar o endereço web do feed RSS desejado na área de entrada, e em seguida, pressionar enter. Isso irá carregar todo o conteúdo de títulos, ou seja, os valores da tag título para cada tag item para a área de exibição da lista. A lista será numerada de 1, representando o conteúdo publicado mais recentemente.

Cada item da lista pode ser selecionado, quando você clicar em um item da lista, ele será destacado e exibirá a descrição correspondente do conteúdo do título na área de texto. Ao mesmo tempo, o conteúdo do título será mostrado mais claramente na seção da área do título. Se ocorrer um erro durante o carregamento do feed por qualquer motivo, uma mensagem de erro será exibido na seção de área de texto.

O botão reset poderá, então, ser usado para limpar qualquer texto na área de texto, de visualização da lista e de título.

A atualização do feed simplesmente verifica se há atualizações para o feed atual.


2.2. Implementação do código

O Leitor RSS será implementado como um Expert Advisor e será usado a Biblioteca Padrão MQL5.

O código estará contido na classe CRssReader que será descendente da classe CAppDialog. A classe CAppDialog, fornecida no arquivo include Dialog.mqh, permitirá a implementação do diálogo do aplicativo, que oferece funcionalidade para a barra de título, e os controles da aplicação para minimizar, maximizar e fechar. Esta será a base da interface do usuário, que, por sua vez, serão adicionadas outras seções. Para as seções a serem adicionadas, temos: A área de título, área de texto, área de visualização da lista e os botões. Cada um será um controle. Os botões serão implementados como um controle do botão descrito no arquivo include Button.mqh.

O controle de visualização da lista, definido no arquivo include ListViewArea.mqh será usado para construir a seção da área de visualização da lista do Leitor RSS.

O controle de edição será, obviamente, suficiente para a construção da seção da área de entrada, esse controle é definido no arquivo Edit.mqh.

As seções área do título e área do texto fornecem um desafio único quando se trata de sua implementação. O problema é que ambos necessitam ter suporte para o texto, que pode ser exibido em várias linhas. Os objetos de texto em MQL5 não reconhecem uma linha nova dos caracteres do feed. Outra questão é que apenas um número limitado de caracteres de string poderão ser exibidos em uma linha de um objeto de texto. Isto significa que se você criar um objeto de texto com uma descrição suficientemente longa, o objeto será exibido com o texto cortado, apenas um determinado número de caracteres que serão exibidos. Por tentativa e erro, descobri que o limite é de 63 caracteres, incluindo espaços e pontuação.

Para superar esses problemas, decidi implementar ambas as seções como controles de exibição da lista modificados. Para a seção área do título, o controle de exibição da lista modificado não será rolável e terá um número fixo de itens da lista (2). Cada item da lista não será capaz de ser clicado ou selecionável e a aparência física do controle será tal que não se parecerá com uma lista. Esses 2 itens da lista irá representar duas linhas de texto. Se o texto for muito grande para caber em uma linha, ele será dividido em conformidade e exibido como duas linhas de texto. O controle da seção área de título será definido no arquivo TitleArea.mqh.

Para a seção área de texto, uma abordagem semelhante será aplicada, só que desta vez o número de itens da lista será dinâmico e o controle de visualização da lista modificado será rolável verticalmente. Este controle será fornecido pelo arquivo TextArea.mqh.

As bibliotecas mencionadas até agora cuidam da interface de usuário. Há ainda uma biblioteca mais importante, que é crucial para este aplicativo e que precisa ser discutido. Esta é a biblioteca usada para analisar os documentos XML.


2.3. O analisador easy XML

Visto que um documento RSS é um arquivo XML, a biblioteca EasyXML - Analisador XML, desenvolvida por liquinaut e encontrado na base de código, é utilizado.

A biblioteca é bastante extensa e contém quase que toda a funcionalidade necessária para o nosso Leitor RSS. Eu fiz algumas modificações na biblioteca original para adicionar alguns recursos extras que eu senti serem necessários.

Estas foram pequenas adições. O primeiro deles foi a adição de um método adicional chamado loadXmlFromUrlWebReq(). Este método simplesmente fornece uma alternativa ao uso loadXmlFromUrl(), que conta com a biblioteca de WinInet para o processamento de solicitações da web, loadXmlFromUrlWebReq() usa a função embutida WebRequest() para permitir os downloads da Internet.

//+---------------------------------------------------------------------------+
//| carrega a XML através da URL fornecida usando a função webrequest da MQL5 |
//+---------------------------------------------------------------------------+
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);
//--- verificar erros
   if(res==-1)
     {
      Err=EASYXML_ERR_WEBREQUEST_URL;
      return(Error());
     }
//---download do arquivo bem sucedido
   sStream=CharArrayToString(result,0,-1,CP_UTF8);
//---set up cache file
   if(blSaveToCache)
     {
      bool bResult=writeStreamToCacheFile(sStream);
      if(!bResult) Error(-1,false);
     }
//---
   return(loadXmlFromString(sStream));
  }

A segunda adição foi o método GetErrorMsg(), isto permite uma saída para recuperar a mensagem de erro pelo analisador sempre que ocorrer um erro.

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

A última adição foi feita para corrigir uma falha bastante grave que eu encontrei ao testar o analisador easyxml.

Eu descobri que a biblioteca não era capaz de reconhecer as declarações da folha de estilo XML. O código faz equívoco com uma declaração de folha de estilo por um atributo. Isso fez com que o programa ficasse preso em um loop infinito, já que o código procurava continuamente o valor do atributo, que nunca existiu.

Isto foi facilmente corrigido, com uma pequena modificação no método skipProlog().

//+------------------------------------------------------------------+
//| pula o prolog XML                                                |
//+------------------------------------------------------------------+
bool CEasyXml::skipProlog(string &pText,int &pPos)
  {
//--- pula a declaração XML
   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);
        }
     }
//--- pula as declarações da folha de estilo
   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);
        }
     }
//--- pula os comentários
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

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

//--- pula os comentários
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

   return(true);
  }

Apesar deste problema, as outras bibliotecas de liquinaut não necessitaram mudanças, easyxml.mqh é uma excelente ferramenta.


2.4. Código do Expert Advisor

Agora que todas as bibliotecas necessárias para a aplicação foram descritas, está na hora de trazer esses componentes em conjunto para definir a classe CRssReader.

Por favor, note que o código do Expert Advisor Leitor RSS irá começar com a definição da 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                                                          |
//+------------------------------------------------------------------+
//--- récuos e lacunas
#define INDENT_LEFT                         (11)      // récuo esquerdo (com permissão para a largura da borda)
#define INDENT_TOP                          (11)      // recuo superior (com permissão para a largura da borda)
#define INDENT_RIGHT                        (11)      // récuo direito (com permissão para a largura da borda)
#define INDENT_BOTTOM                       (11)      // recuo inferior (com permissão para a largura da borda)
#define CONTROLS_GAP_X                      (5)       // lacuna pela coordenada X
#define CONTROLS_GAP_Y                      (5)       // lacuna pela coordenada Y

#define EDIT_HEIGHT                         (20)      // tamanho pela coordenada Y
#define BUTTON_WIDTH                        (150)     // tamanho pela coordenada X
#define BUTTON_HEIGHT                       (20)      // tamanho pela coordenada Y
#define TEXTAREA_HEIGHT                     (131)     // tamanho pela coordenada Y
#define LIST_HEIGHT                         (93)      // tamanho pela coordenada Y

Começamos por incluir os arquivos necessários. As diretivas define são usadas para definir os parâmetros físicos dos controles em pixels.

O INDENT_LEFT, INDENT_RIGHT, INDENT_TOP e INDENT_DOWN definem a distância entre um controle e borda do diálogo do aplicativo.

  • CONTROLS_GAP_Y é a distância vertical entre dois controles;
  • EDIT_HEIGHT define a altura do controle Edit que constitui a área de entrada;
  • BUTTON_WIDTH e BUTTON_HEIGHT definem a largura e a altura de todos os controles de botão;
  • TEXTAREA_HEIGHT é a altura da seção área de texto;
  • LIST_HEIGHT define a altura do controle de visualização da lista.

Após a definição, nós começamos a definir a classe CRssReader.

//+------------------------------------------------------------------+
//| Classe CRssReader                                                |
//| Uso: classe principal para o aplicativo RSS                      |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                    // índice da primeira tag item
   string            m_rssurl;                   // cópia do endereço web do último feed
   string            m_textareaoutput[];         // array de strings preparado para a sáida pro painel área do texto
   string            m_titleareaoutput[];        // array de strings preparado para a sáida pro painel área do título
   CButton           m_button1;                  // o objeto botão
   CButton           m_button2;                  // o objeto botão      
   CEdit             m_edit;                     // painel de entrada
   CTitleArea        m_titleview;                // exibição do objeto de campo
   CListViewArea     m_listview;                 // o objeto lista
   CTextArea         m_textview;                 // objeto área de texto
   CEasyXml          m_xmldocument;              // objeto documento XML
   CEasyXmlNode     *RssNode;                    // objeto nó raiz
   CEasyXmlNode     *ChannelNode;                // objeto nó canal
   CEasyXmlNode     *ChannelChildNodes[];        // array de objetos do nó filho do canal

Como mencionado anteriormente, CRssReader herda da classe CAppDialog.

A classe tem várias propriedades particulares, que são dadas abaixo:

  • m_shift - esta variável do tipo inteiro armazena o índice do primeiro nó item, no array ChannelChildnodes;
  • m_rssurl - é um valor de string que mantém uma cópia da última URL que foi de entrada;
  • m_textareaoutput[] -é um array de strings, cada elemento corresponde a uma linha de texto com um determinado número de caracteres;
  • m_titleareaoutput[] - este também é um array de strings que tem o mesmo propósito do array de string anterior;
  • m_button1 e m_button2 são objetos do tipo CButton;
  • m_listview é um objeto que representa um controle de lista;
  • m_edit é uma propriedade do objeto CEdit e implementa a área de entrada;
  • m_titleview é um objeto para exibir o campo área do título;
  • m_textview - o objeto para a seção área de texto;
  • m_xmldocument é um objeto de documento XML;
  • RssNode é o objeto nó raiz;
  • ChannelNode é o objeto do nó canal;
  • O array ChannelChildNodes é um conjunto de ponteiros para os descendentes diretos da tag canal.

Nossa classe terá apenas dois métodos expostos publicamente.

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- criar
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- manipulador de evento do gráfico
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

O primeiro método Create() define o tamanho e a posição inicial do diálogo do aplicativo.

Ela também inicializa todos os controles do aplicativo Leitor RSS (lembre-se que a nossa classe herda da classe CAppDialog e, portanto, os métodos públicos da classe pai e seus antecessores ​​podem ser chamados por instâncias do CRssReader).

//+------------------------------------------------------------------+
//| Criar                                                            |
//+------------------------------------------------------------------+
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);
//--- cria controles dependentes
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- bem sucedido
   return(true);
  }

O segundo, é o método OnEvent(), a função permite a interatividade através da atribuição de eventos específicos para o controle correspondente e uma função de manipulação.

//+------------------------------------------------------------------+
//| Manipulador de Evento                                            |
//+------------------------------------------------------------------+
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étodos para a inicialização dos controles

Os métodos do tipo protected CreateEdit(), CreateButton1(), CreateButton2(), CreateTitleView(), CreateListView() e CreateTextView() são chamados pela função principal Create() para inicializar um controle correspondente.

protected:
   // --- Criando os controles
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);

É em cada uma destas funções que o tamanho, posição e propriedades (ou seja, fonte, tamanho da fonte, cor, cor da borda, tipo de borda) de um controle são definidas.

//+------------------------------------------------------------------+
//| Criar o campo de exibição                                        |
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do botão 1                                               |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do botão 2                                               |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- coordenadas
   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;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criaçao do campo de exibição                                     |
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- coordenadas
   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);
     }
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do elemento "ListView"                                   |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- coordenadas
   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;
//--- criar
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- preenchimento com as strings
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criaçao do campo de exibição                                     |
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- coordenadas
   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();
//--- criar 
   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);
     }
//----bem sucedido      
   return(true);
  }


2.6. Métodos para o processamento de documentos RSS

// --- processamento do documento RSS
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);

2.6.1. LoadDocument()

Esta função tem vários papéis importantes a desempenhar. A principal delas é o processamento de solicitações da Web. A loadXmlFromUrlWebReq() é chamada para baixar o arquivo RSS.

Se ela for concluída com êxito, a função se move para a sua segunda tarefa de inicialização dos ponteiros RssNode, ChannelNode e também preenchendo os arrays ChannelChildnodes. É aqui que as propriedades m_rssurl e m_shift são definidas. Uma vez que tudo isso foi feito, a função retornará true.

Se o arquivo RSS não pôde ser baixado, as seções área de título, área de exibição da lista e área de texto serão limpadas de qualquer texto e uma mensagem de status será exibida na barra de título. Isto é seguido pela saída de uma mensagem de erro na seção área de texto. Em seguida, a função retornará false.

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

Esta função auxiliar é utilizada no método LoadDocument(). Ela retorna um valor inteiro que é o número de nós itens que são descendentes da tag canal.

Se não houver nós itens, o documento será um RSS inválido e a função retornará 0.

//+------------------------------------------------------------------+
//| função que conta o número de tags item no documento              |
//+------------------------------------------------------------------+
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()

Esta função reseta todos os ponteiros CEasyXmlNode.

Primeiro, os elementos do array ChannelChildnodes são eliminados pela chamanda do método Shutdown() da classe CArrayObj. O array é então liberado com uma única chamada de ArrayFree().

Em seguida, o ponteiro para o nó canal é excluído e a árvore de documento analisador easyxml é liberada. Essas ações resultam nos ponteiros RssNode e ChannelNode se tornando ponteiros ruins, que é por isso que ambos são atribuídos com o valor NULL.

//+------------------------------------------------------------------+
//| libera a árvore documento e reseta o valor dos ponteiros         |
//+------------------------------------------------------------------+ 
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étodos para extração de informações da árvore de documentos

Estas funções são para obter o texto de um documento RSS.

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

2.7.1. getChannelTitle()

Essa função recupera o título do canal atual do documento RSS.

Ela começa verificando a validade do ponteiro nó canal. Se o ponteiro for válido, ele percorre todos os descendentes diretos do nó canal procurando a tag título.

O loop for usa a propriedade m_shift para limitar o número de descendentes do nó canal para busca. Se a função é malsucedida ele retorna NULL.

//+------------------------------------------------------------------+
//| obter o título do canal                                          |
//+------------------------------------------------------------------+
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;
        }
     }
//---valor de retorno
   return(ret);
  }


2.7.2. getTitle()

A função usa como entrada um ponteiro para uma tag item, atravessando os descendentes dessa tag afim de procurar uma tag título e retornar o seu valor.

As funções getDescription() e getDate() seguem o mesmo formato, funcionando de forma semelhante. Uma chamada bem-sucedida da função retorna um valor de string, caso contrário, é retornado NULL como saída.

//+------------------------------------------------------------------+
//| exibição do título                                               |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| exibição da descrição                                            |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| exibição da data                                                 |
//+------------------------------------------------------------------+ 
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étodos para a formatação de texto

Estas funções são para preparar o texto para a saída como objetos de texto, a fim de ultrapassar algumas das limitações que os objetos de texto possuem.

 //--- formatação do texto 
   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()

Esta é a função principal, que prepara o texto extraído de um documento RSS para a saída do aplicativo.

Basicamente é preciso de um valor de entrada do tipo string, e dividir o texto em linhas de "n" caracteres. Com "n" sendo um valor inteiro que representa o número de caracteres de uma única linha de texto. Após todos os "n" caracteres no texto, o código procura por um local adequado para inserir uma nova linha de caractere do feed. Em seguida, todo o valor da string é processado e a nova linha de caracteres do feed são inseridos no texto original.

A função StringSplit() é usada para criar um array de strings, contendo não mais do que "n" caracteres de comprimento. A função retorna um valor booleano e também um array de valores de string que estão prontos para a saída.

//+------------------------------------------------------------------+
//| formatar a string para a saída do painel area de texto           |
//+------------------------------------------------------------------+
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);}
// bem sucedido 
   return(true);
  }


2.8.2. removeTags()

Esta função tornou-se uma necessidade após eu notar que um bom número de documentos RSS contêm tags HTML dentro dos nós XML.

Alguns documentos RSS são publicados dessa maneira, já que muitos aplicativos que agregam RSS funcionam no browser.

A função toma o valor de string e procura por tags dentro do texto. Se quaisquer tags são encontradas, a função percorre todos os caracteres do texto e armazena a posição de cada caractere de tag de abertura e fechamento no array bidimensional a[][]. Este array é usado para extrair o texto entre as tags e retornar a string extraída. Se nenhuma tag foi encontrada, a string de entrada é retornada em sua forma original.

//+------------------------------------------------------------------+
//| remoção das 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);
  }

Um exemplo parcial de tal documento é mostrado abaixo.

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

Esta função simplesmente substitui certas constantes da string por caracteres corretos.

Por exemplo, o caractere E comercial em alguns documentos XML podem ser representados como "&amp". Esta função usa a função embutida StringReplace() para substituir esses tipos de ocorrências.

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

Esta é uma função auxiliar chamada na função removeTags(). Ela toma como entrada uma string e um valor inteiro.

O valor inteiro de entrada representa a posição de um caractere na string, a partir do qual a função vai começar a procurar pelo caractere tag de abertura, ou seja, "<". Se uma tag de abertura foi encontrada, em seguida, a função começa a procurar por uma tag de fechamento e retorna como saída a posição do caractere correspondente a tag de fechamento ">". Se nenhuma tag foi encontrada a função retorna -1.

//+------------------------------------------------------------------+
//| posição das tags                                                 |
//+------------------------------------------------------------------+
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étodos para a manipulação de eventos de controles independentes

Estas funções lidam com os eventos capturados de um controle específico.

//--- manipuladores dos eventos de controle dependentes
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };

2.9.1. OnChangeListView()

Esta é uma função manipuladora de eventos, sendo chamada sempre que um dos itens da lista da seção área de visualização da lista for clicado.

A função é responsável por permitir a visualização de uma descrição resumida para algum conteúdo referenciado no documento RSS.

A função limpa todo o texto das seções área de texto e área de título, recupera novos dados a partir da árvore de documentos e o prepara para a saída. Tudo isso só ocorre se o array ChannelChildnodes não estiver vazio.

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

A função de manipulação é chamada sempre que um usuário terminar de inserir algum texto na área de entrada.

A função chama o método LoadDocument(). Se o download for bem sucedido, o texto é eliminado de todo o aplicativo. Em seguida, a legenda é alterada e o novo conteúdo é enviado para a seção área de visualização da lista.

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

Esses manipuladores são chamados sempre que um usuário clicar no botão de reset ou verificar se há atualizações para o feed.

Ao clicar no botão de reset, ele atualiza o diálogo do aplicativo para o estado em que estava quando o Expert Advisor foi lançado pela primeira vez.

Clicando no botão "Verificar Atualizações para o feed", causa uma chamada do método de carregamento LoadDocument() e o download dos dados do feed RSS, atualizando a seção área de visualização da lista.

//+------------------------------------------------------------------+
//| Manipulador de evento que atualiza o diálogo do aplicativo       |
//+------------------------------------------------------------------+   
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;
        }
     }
  }
//+------------------------------------------------------------------+
//| Manipulador de evento para atualizar o feed atual                |
//+------------------------------------------------------------------+ 
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;
     }
  }

Isso conclui a definição da classe CRssReader.


2.10. A implementação da classe CRssReader

//+------------------------------------------------------------------+
//| Classe CRssReader                                                |
//| Uso: classe principal para o aplicativo RSS                      |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                   // índice da primeira tag item
   string            m_rssurl;                  // cópia do endereço web do último feed 
   string            m_textareaoutput[];        // array de strings preparado para a sáida pro painel área do texto
   string            m_titleareaoutput[];       // array de strings preparado para a sáida pro painel área do título
   CButton           m_button1;                 // o objeto botão
   CButton           m_button2;                 // o objeto botão      
   CEdit             m_edit;                    // painel de entrada
   CTitleArea        m_titleview;               // exibição do objeto de campo
   CListViewArea     m_listview;                // o objeto lista
   CTextArea         m_textview;                // objeto área de texto
   CEasyXml          m_xmldocument;             // objeto documento XML
   CEasyXmlNode     *RssNode;                   // objeto nó raiz
   CEasyXmlNode     *ChannelNode;               // objeto nó canal
   CEasyXmlNode     *ChannelChildNodes[];       //array de objetos do nó filho do canal

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- criar
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- manipulador de evento do gráfico
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   // --- criando os controles
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);
   // --- processamento do documentos RSS
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);
   //--- os getters
   string            getChannelTitle(void);
   string            getTitle(CEasyXmlNode *Node);
   string            getDescription(CEasyXmlNode *Node);
   string            getDate(CEasyXmlNode *Node);
   //--- formatação do texto 
   bool              FormatString(string v,string &array[],int n);
   string            removeTags(string _string);
   string            removeSpecialCharacters(string s_tring);
   int               tagPosition(string _string,int w);
   //--- manipuladores dos eventos de controle dependentes
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };
//+------------------------------------------------------------------+
//| Manipulador de evento                                            |
//+------------------------------------------------------------------+
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)
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CRssReader::CRssReader(void)
  {

  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CRssReader::~CRssReader(void)
  {
  }
//+------------------------------------------------------------------+
//| Criar                                                            |
//+------------------------------------------------------------------+
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);
//--- criação dos controles dependentes
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do campo de exibição                                     |
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do Botão 1                                               |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- coordenadas
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do Botão 2                                               |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- coordenadas
   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;
//--- criar
   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);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do campo de exibição                                     |
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- coordenadas
   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();
//--- criar 
   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);
     }
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do elemento "ListView"                                   |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- coordenadas
   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;
//--- criar
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- preenchimento com strings
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- bem sucedido
   return(true);
  }
//+------------------------------------------------------------------+
//| Criação do campo de exibição                                     |
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- coordenadas
   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();
//--- criar 
   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);
     }
//----bem sucedido      
   return(true);
  }
//+------------------------------------------------------------------+
//|   Carregando um documento                                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| função que conta o número de tags item no documento              |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| libera a árvore documento e reseta o valor dos ponteiros         |
//+------------------------------------------------------------------+ 
void CRssReader::FreeDocumentTree(void)
  {
   ChannelNode.Children().Shutdown();
   ArrayFree(ChannelChildNodes);
   RssNode.Children().Shutdown();
   m_xmldocument.Clear();
   m_shift=0;
   RssNode=NULL;
   ChannelNode=NULL;
  }
//+------------------------------------------------------------------+
//| obter o título do canal                                          |
//+------------------------------------------------------------------+
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;
        }
     }
//---valor de retorno
   return(ret);
  }
//+------------------------------------------------------------------+
//| exibição do título                                               |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| exibição da descrição                                            |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| exibição da data                                                 |
//+------------------------------------------------------------------+ 
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);
  }
//+------------------------------------------------------------------+
//| formatar a string para a saída do painel area de texto           |
//+------------------------------------------------------------------+
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);}
// bem sucedido 
   return(true);
  }
//+------------------------------------------------------------------+
//| remover caracteres especiais                                     |
//+------------------------------------------------------------------+ 
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);
  }
//+------------------------------------------------------------------+
//| remoção das 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);
  }
//+------------------------------------------------------------------+
//| posição das tags                                                 |
//+------------------------------------------------------------------+
int CRssReader::tagPosition(string _string,int w)
  {
   int iClose=-1;
   if(StringCompare("<",StringSubstr(_string,w,StringLen("<")))==0)
     {
      iClose=StringFind(_string,">",w+StringLen("<"));
     }

   return(iClose);
  }
//+------------------------------------------------------------------+
//| Manipulador de evento                                            |
//+------------------------------------------------------------------+
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;
        }
     }
  }
//+------------------------------------------------------------------+
//| Manipulador de evento                                            |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Manipulador de evento que atualiza o diálogo do aplicativo       |
//+------------------------------------------------------------------+   
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;
        }
     }
  }
//+------------------------------------------------------------------+
//| Manipulador de evento para atualizar o feed atual                |
//+------------------------------------------------------------------+ 
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;
     }
  }

Agora ela pode ser utilizada no código do Expert Advisor.


2.11. O código do Expert Advisor

O Expert Advisor não tem variáveis ​​de entrada já que o aplicativo foi criado para ser totalmente interativo.

Primeiro nós declaramos uma variável global que é uma instância da classe CRssReader. Na função OnInit() nós inicializamos o diálogo do aplicativo com uma chamada para o método principal Create(). Se este for bem sucedido, o método run() de uma classe ancestral é chamada.

Na função OnDeinit() o método destroy() da classe pai é chamada para apagar todo o aplicativo e remover o Expert Advisor do gráfico.

A função OnChartEvent() contém uma chamada a um método ancestral da classe CRssReader, que irá lidar com o processamento de todos os eventos.

//Código do Expert Advisor começa aqui
//+------------------------------------------------------------------+
//| Variáveis globais                                                |
//+------------------------------------------------------------------+
CRssReader ExtDialog;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- criação do diálogo do aplicativo
   if(!ExtDialog.Create(0,"RSSReader",0,20,20,518,394))
      return(INIT_FAILED);
//--- execução da aplicação
   ExtDialog.Run();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Função ChartEvent                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }

O código, em seguida, precisará ser compilado para que o programa esteja pronto para uso.

Quando o Expert Advisor RssReader.mq5 for carregado em um gráfico, um diálogo vazio do aplicativo aparecerá da seguinte forma:

Fig. 2. Captura de tela do diálogo vazio do app do Expert Advisor RssReader

Fig. 2. Captura de tela do diálogo vazio do app do Expert Advisor RssReader

Digite um endereço da web e os conteúdo RSS será carregado para o diálogo do aplicativo, como representado pela imagem abaixo:

Fig. 3. EA RssReader funcionando no terminal

Fig. 3. EA RssReader funcionando no terminal

Eu testei o programa com uma vasta gama de feeds RSS. O único problema observado foi relacionado com a exibição de alguns caracteres indesejados, principalmente o resultado de documentos RSS que continham caracteres encontrados normalmente em documentos HTML.

Eu notei também que a mudança do período de um gráfico enquanto o aplicativo é executado, faz com que a EA reinicialize, podendo levar ao desenho incorreto dos controles do aplicativo.

Eu não fui capaz de corrigir esse comportamento, o meu conselho é evitar a alteração do período do gráfico quando o programa Leitor RSS está sendo executado.


Conclusão

Nós completamos a criação de um aplicativo Leitor RSS inteiramente interativo para o MetaTrader 5, usando as técnicas de programação orientada a objeto.

Há muito mais recursos que poderiam ser adicionados à aplicação e eu tenho certeza que existem muitas outras maneiras que a interface do usuário pode ser organziada. Eu espero que aqueles que possivelmente possuem uma habilidade melhor com design gráfico da aplicação irão melhorar a aplicação e compartilhar suas criações.

P.S. Por favor, note que o arquivo easyxml.mqh disponível para download aqui não é o mesmo que aquele disponível na Base de Código, ele contém as modificações já mencionadas no artigo. Todos os includes necessários estão no arquivo RssReader.zip.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/1589

Arquivos anexados |
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)
Traçando linhas de tendência baseadas em fractais usando MQL4 e MQL5 Traçando linhas de tendência baseadas em fractais usando MQL4 e MQL5
O artigo descreve a automação da plotagem de linhas de tendência com base no indicador Fractals usando MQL4 e MQL5. A estrutura do artigo fornece uma visão comparativa da solução para as duas linguagens. As linhas de tendência são plotados usando os dois últimos fractais conhecidos.
Freelance MQL5.com: Fonte de Renda dos Desenvolvedores (Infográfico) Freelance MQL5.com: Fonte de Renda dos Desenvolvedores (Infográfico)
Por ocasião do quarto aniversário do Serviço Freelance MQL5, nós preparamos uma infográfico demonstrando os resultados do serviço durante todo o tempo de sua existência. Os números falam por si mesmo: mais de 10.000 pedidos no valor total de 600 mil dólares foram executados até a presente data, enquanto que 3.000 clientes e 300 desenvolvedores já utilizaram o serviço.
Guia Prático MQL5: Ordens ОСО Guia Prático MQL5: Ordens ОСО
Qualquer atividade de negociação do trader envolve diversos mecanismos e inter-relações, incluindo as relações entre ordens. Este artigo sugere uma solução de processamento de ordens OCO. As classes da biblioteca padrão são amplamente envolvidas, bem como os novos tipos de dados que são criados aqui.
Redes Neurais Simples e Econômica - Conecte o NeuroPro com o MetaTrader 5 Redes Neurais Simples e Econômica - Conecte o NeuroPro com o MetaTrader 5
Se os programas de redes neurais específicos para negociação parecem ser caros e complexos ou, pelo contrário, muito simples, tente o NeuroPro. Ele é gratuito e contém o melhor conjunto de funcionalidades para amadores. Este artigo irá dizer-lhe como usá-lo em conjunto com o MetaTrader 5.