English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MetaTrader 5에서 RSS 피드를 표시하는 대화형 애플리케이션 구축

MetaTrader 5에서 RSS 피드를 표시하는 대화형 애플리케이션 구축

MetaTrader 5전문 어드바이저 | 12 10월 2021, 17:32
119 0
Francis Dube
Francis Dube

내용물


소개

"MQL4를 통해 RSS 뉴스 피드 읽기" 글은 원래 HTML 구문 분석을 위해 구축된 간단한 라이브러리를 통해 터미널 콘솔에 RSS 피드를 표시하는 데 사용할 수 있는 다소 기초적인 스크립트에 대해 설명했습니다. 서류.

MetaTrader 5와 MQL5 프로그래밍 언어의 출현으로 RSS 콘텐츠를 더 잘 표시할 수 있는 대화형 응용 프로그램을 만들 수 있다고 생각했습니다. 이 문서에서는 광범위한 MQL5 표준 라이브러리와 MQL5 커뮤니티 기여자가 개발한 기타 도구를 사용하여 이 애플리케이션을 생성하는 방법을 설명합니다.


1. RSS 문서 일반

애플리케이션의 세부 사항을 파악하기 전에 RSS 문서의 일반적인 구조에 대한 개요를 제공해야 한다고 생각합니다.

다음 설명을 이해하려면 확장 가능한 마크업 언어 및 관련 개념에 익숙해야 합니다. XML 문서에 익숙하지 않다면 XML 튜토리얼을 참조하시기 바랍니다. 이 글에서 노드는 XML 문서의 태그를 나타냅니다. 위에서 언급한 MQL4 글에서 언급했듯이 RSS 파일은 단순히 특정 태그 구조를 가진 XML 문서입니다.

각 RSS 문서에는 전역 컨테이너인 RSS 태그가 있습니다. 이것은 모든 RSS 문서에 공통적입니다. 채널 태그는 항상 RSS 태그의 직계 자손입니다. 여기에는 피드가 설명하는 웹사이트에 대한 정보가 포함됩니다. 여기에서 RSS 문서는 포함된 특정 태그에 따라 다를 수 있지만 RSS 파일을 확인하기 위해 모든 문서에 포함되어야 하는 몇 가지 태그가 있습니다.

필수 태그는 다음과 같습니다.

  • title - 채널의 제목입니다. 웹사이트의 이름을 포함해야 합니다.
  • 링크 - 이 채널을 제공하는 웹사이트의 URL;
  • 설명 - 웹사이트에 대한 요약;
  • item - 콘텐츠에 대한 하나 이상의 항목 태그.

위에 표시된 태그는 모두 채널 태그의 자식 노드여야 합니다. 항목 노드는 특정 콘텐츠와 관련된 데이터를 포함하는 노드입니다.

각 항목 노드에는 다음 태그도 포함되어야 합니다.

  • 제목 - 콘텐츠의 제목입니다.
  • link - 콘텐츠에 대한 URL 링크입니다.
  • 설명 - 내용 요약;
  • date - 콘텐츠가 웹사이트에 게시된 날짜입니다.

모든 RSS 문서는 설명된 태그를 포함하며 동일한 구조를 따릅니다.

완전한 RSS 문서의 예가 아래에 나와 있습니다.

<?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. 애플리케이션의 전체 구조

여기에서는 RSS Reader가 표시해야 하는 정보에 대한 설명과 응용 프로그램의 그래픽 사용자 인터페이스에 대한 개요를 제공합니다.

애플리케이션이 표시해야 하는 첫 번째 측면은 제목 태그에 포함된 채널 제목입니다. 이 정보는 피드 참조 웹사이트를 나타내는 역할을 합니다.

응용 프로그램은 또한 피드가 설명하는 모든 콘텐츠의 스냅샷을 표시해야 합니다. 이는 문서의 모든 항목 태그와 관련이 있습니다. 각 항목 태그에 대해 콘텐츠 제목이 표시됩니다. 마지막으로 RSS 리더가 콘텐츠에 대한 설명을 표시할 수 있기를 원합니다. 이것은 각 항목 노드의 설명 태그에 포함된 데이터입니다.


2.1. 사용자 인터페이스

사용자 인터페이스는 애플리케이션에 의해 표시되는 정보의 기능입니다.

사용자 인터페이스에 대한 아이디어는 아래 다이어그램에 가장 잘 설명되어 있습니다.


응용 프로그램 대화의 스케치

그림 1. 응용 프로그램 대화의 스케치


다이어그램은 사용자 인터페이스를 구성하는 다양한 섹션을 보여줍니다.

  • 먼저 제목 표시줄입니다. 여기에 채널 제목이 표시됩니다.
  • 입력 영역. 여기에서 사용자는 RSS 피드의 웹 주소를 입력합니다;
  • 제목 영역. 특정 콘텐츠의 제목이 여기에 표시됩니다.
  • 텍스트 영역. 콘텐츠에 대한 설명이 여기에 표시됩니다.
  • 목록 보기 영역입니다. 이 스크롤 가능한 목록에는 피드에 포함된 모든 콘텐츠의 제목이 표시됩니다.
  • 왼쪽에 있는 버튼은 제목, 텍스트 및 목록 보기 영역에 표시된 텍스트를 재설정하고 지웁니다.
  • 현재 피드 업데이트 버튼은 현재 로드된 피드의 새 업데이트를 검색합니다.

RSS 리더는 다음과 같은 방식으로 작동합니다. 프로그램이 차트에 로드되면 빈 응용 프로그램 대화 상자가 표시되고 사용자는 입력 영역에 원하는 RSS 피드의 웹 주소를 입력한 다음 Enter 키를 눌러야 합니다. 그러면 모든 콘텐츠 제목, 즉 각 항목 태그의 제목 태그 값이 목록 보기 영역에 로드됩니다. 목록은 가장 최근에 게시된 콘텐츠를 나타내는 1부터 번호가 매겨집니다.

각 목록 항목은 클릭할 수 있으며 목록 항목을 클릭하면 강조 표시되고 제목 내용에 대한 해당 설명이 텍스트 영역에 표시됩니다. 동시에 콘텐츠 제목은 제목 영역 섹션에 더 명확하게 표시됩니다. 어떤 이유로든 피드를 로드하는 동안 오류가 발생하면 텍스트 영역 섹션에 오류 메시지가 표시됩니다.

그런 다음 재설정 버튼을 사용하여 텍스트 영역, 목록 보기 영역, 제목 영역 영역 섹션의 모든 텍스트를 지울 수 있습니다.

현재 피드 업데이트는 단순히 현재 피드에 대한 업데이트를 확인합니다.


2.2. 사용자 인터페이스

RSS Reader는 Expert Advisor로 구현되며 MQL5 표준 라이브러리가 사용됩니다.

코드는 CAppDialog 클래스의 자손이 될 CRssReader 클래스에 포함됩니다. Dialog.mqh 포함 파일에 제공된 CAppDialog 클래스는 제목 표시줄에 대한 기능을 제공하는 응용 프로그램 대화 상자와 최소화, 최대화 및 닫기를 위한 응용 프로그램 제어를 구현합니다. 이것은 사용자 인터페이스의 기초가 될 것이며 그 위에 다른 섹션이 추가될 것입니다. 추가할 섹션의 경우, 즉 제목 영역, 텍스트 영역, 목록 보기 영역 및 버튼입니다. 각각은 컨트롤이 될 것입니다. 버튼은 Button.mqh 포함 파일에 설명된 버튼 컨트롤로 구현됩니다.

ListViewArea.mqh 포함 파일에 정의된 목록 보기 컨트롤은 RSS 리더의 목록 보기 영역 섹션을 구성하는 데 사용됩니다.

편집 컨트롤은 분명히 입력 영역 섹션을 구성하는 데 충분할 것입니다. 이 컨트롤은 Edit.mqh 파일에 정의되어 있습니다.

제목 영역 및 텍스트 영역 섹션은 구현과 관련하여 고유한 문제를 제공합니다. 문제는 둘 다 여러 줄에 표시할 수 있는 텍스트를 지원해야 한다는 것입니다. MQL5의 텍스트 개체는 줄 바꿈 문자를 인식하지 못합니다. 또 다른 문제는 제한된 수의 문자열 문자만 텍스트 개체의 한 줄에 표시될 수 있다는 것입니다. 즉, 설명이 충분히 긴 텍스트 개체를 만들면 개체가 텍스트가 잘린 상태로 표시되고 특정 문자 수만 표시됩니다. 시행 착오를 통해 글자 수 제한은 공백과 구두점을 포함하여 63자라는 것을 알았습니다.

이러한 문제를 극복하기 위해 두 섹션을 모두 수정된 목록 보기 컨트롤로 구현하기로 결정했습니다. 제목 영역 섹션의 경우 수정된 목록 보기 컨트롤은 스크롤할 수 없으며 고정된 수의 목록 항목(2)을 갖습니다. 각 목록 항목은 클릭하거나 선택할 수 없으며 컨트롤의 물리적 모양은 목록처럼 보이지 않습니다. 이 2개의 목록 항목은 두 줄의 텍스트를 나타냅니다. 텍스트가 너무 길어서 한 줄에 담을 수 없는 경우, 그에 따라 분할되어 2줄의 텍스트로 표시됩니다. 제목 영역 섹션에 대한 제어는 TitleArea.mqh 파일에 정의됩니다.

텍스트 영역 섹션의 경우 유사한 접근 방식이 적용됩니다. 이번에는 목록 항목 수가 동적이고 수정된 목록 보기 컨트롤이 세로로 스크롤될 수 있습니다. 이 컨트롤은 TextArea.mqh 파일에 제공됩니다.

지금까지 언급된 라이브러리는 사용자 인터페이스를 처리합니다. 이 응용 프로그램에 대해 논의해야 할 중요한 라이브러리가 하나 더 있습니다. 이것은 XML 문서를 구문 분석하는 데 사용되는 라이브러리입니다.


2.3. 쉬운 XML 파서

RSS 문서는 XML 파일이기 때문에 liquinaut에서 개발하고 Code Base에 있는 EasyXML - XML ​​Parser 라이브러리가 적용됩니다.

라이브러리는 매우 광범위하며 RSS 리더에 필요한 거의 모든 기능을 포함합니다. 필요하다고 생각되는 몇 가지 추가 기능을 추가하기 위해 원본 라이브러리를 약간 수정했습니다.

이것들은 사소한 추가 사항이었습니다. 그 중 첫 번째는 loadXmlFromUrlWebReq()라는 추가 메소드가 추가된 것입니다. 이 방법은 웹 요청을 처리하기 위해 WinInet 라이브러리에 의존하는 loadXmlFromUrl()을 사용하는 것에 대한 대안을 제공합니다. loadXmlFromUrlWebReq()는 내장된 WebRequest() 기능을 사용하여 인터넷에서 다운로드할 수 있도록 합니다.

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

두 번째 추가는 GetErrorMsg() 메소드로, 이를 통해 오류가 발생할 때마다 파서가 출력하는 오류 메시지를 검색할 수 있습니다.

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

마지막 추가는 easyxml 파서를 테스트할 때 발견한 다소 심각한 결함을 수정하기 위해 만들어졌습니다.

라이브러리가 XML 스타일 시트 선언을 인식할 수 없다는 것을 발견했습니다. 코드는 속성에 대한 스타일 시트 선언을 실수합니다. 이로 인해 코드가 존재하지 않는 해당 속성 값을 지속적으로 검색함에 따라 프로그램이 무한 루프에 빠지게 되었습니다.

이것은 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);
  }
-->

이 문제에도 불구하고 liquinaut에서 아무것도 빼지 마십시오. easyxml.mqh는 훌륭한 도구입니다.


2.4. Expert Advisor 코드

애플리케이션에 필요한 모든 라이브러리가 설명되었으므로 이제 이러한 구성요소를 함께 가져와 CRssReader 클래스를 정의할 때입니다.

RSS Reader Expert Advisor 코드는 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
-->

필요한 파일을 포함하는 것으로 시작합니다. define 지시문은 컨트롤의 물리적 매개변수를 픽셀 단위로 설정하는 데 사용됩니다.

INDENT_LEFT, INDENT_RIGHT, INDENT_TOP 및 INDENT_DOWN은 컨트롤과 응용 프로그램 대화 상자 가장자리 사이의 거리를 설정합니다.

  • CONTROLS_GAP_Y는 두 컨트롤 사이의 수직 거리입니다.
  • EDIT_HEIGHT는 입력 영역을 구성하는 편집 컨트롤의 높이를 설정합니다.
  • BUTTON_WIDTH 및 BUTTON_HEIGHT는 모든 버튼 컨트롤의 너비와 높이를 정의합니다.
  • TEXTAREA_HEIGHT는 텍스트 영역 섹션의 높이입니다.
  • LIST_HEIGHT는 목록 보기 컨트롤의 높이를 설정합니다.

정의 후에 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
-->

앞서 언급했듯이 CRssReader는 CAppDialog 클래스에서 상속됩니다.

클래스에는 다음과 같은 몇 가지 개인 속성이 있습니다.

  • m_shift - 이 정수 유형 변수는 ChannelChildnodes 배열에서 첫 번째 항목 노드의 인덱스를 저장합니다.
  • m_rssurl - 마지막으로 입력된 URL의 복사본을 유지하는 문자열 값입니다.
  • m_textareaoutput[] -문자열의 배열이며, 각 요소는 특정 수의 문자가 있는 텍스트 행에 해당합니다.
  • m_titleareaoutput[] - 이 배열도 이전 문자열 배열과 동일한 용도로 사용됩니다.
  • m_button1 및 m_button2는 CButton 유형의 개체입니다.
  • m_listview는 목록 컨트롤을 나타내는 개체입니다.
  • m_edit 속성은 CEdit 개체이며 입력 영역을 구현합니다.
  • m_titleview는 제목 영역 표시 필드에 대한 개체입니다.
  • m_textview - 텍스트 영역 섹션의 개체.
  • m_xmldocument는 xml 문서 객체입니다.
  • RssNode는 루트 노드 개체입니다.
  • ChannelNode는 채널 노드의 객체입니다.
  • ChannelChildNodes 배열은 Channel 태그의 직계 자손에 대한 포인터 세트입니다.

우리 클래스에는 공개적으로 노출된 메소드가 두 개뿐입니다.

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

첫 번째 메소드 Create()는 응용 프로그램 대화 상자의 크기와 초기 포지션을 설정합니다.

또한 RSS Reader 앱의 모든 컨트롤을 초기화합니다(우리 클래스는 CAppDialog 클래스에서 상속하므로 상위 클래스와 상위 클래스의 공용 메소드는 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);
  }
-->

두 번째는 OnEvent() 메소드로, 해당 컨트롤과 핸들러 함수에 특정 이벤트를 할당하여 상호 작용을 가능하게 합니다.

//+------------------------------------------------------------------+
//| 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. 컨트롤 초기화 방법

CreateEdit(), CreateButton1(), CreateButton2(), CreateTitleView(), CreateListView() 및 CreateTextView() 보호 메소드는 해당 컨트롤을 초기화하기 위해 기본 Create() 함수에 의해 호출됩니다.

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

컨트롤의 크기, 포지션 및 속성(예: 글꼴, 글꼴 크기, 색상, 테두리 색상, 테두리 유형)이 설정되는 것은 이러한 각 기능입니다.

//+------------------------------------------------------------------+
//| 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. RSS 문서 처리 방법

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

2.6.1. LoadDocument()

이 기능에는 몇 가지 중요한 역할이 있습니다. 주된 것은 웹 요청을 처리하는 것입니다. loadXmlFromUrlWebReq()가 RSS 파일을 다운로드하기 위해 호출됩니다.

이것이 성공적으로 완료되면 함수는 포인터 RssNode, ChannelNode를 초기화하고 배열 ChannelChildnodes를 채우는 두 번째 작업으로 이동합니다. 여기에서 m_rssurl 및 m_shift 속성이 설정됩니다. 이 모든 작업이 완료되면 함수는 true를 반환합니다.

RSS 파일을 다운로드할 수 없는 경우 제목 영역, 목록 보기 영역 및 텍스트 영역 섹션에서 텍스트가 지워지고 제목 표시줄에 상태 메시지가 표시됩니다. 그 다음에는 텍스트 영역 섹션에 오류 메시지가 출력됩니다. 그런 다음 함수는 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()

이 도우미 함수는 LoadDocument() 메소드에서 사용됩니다. 채널 태그의 하위 항목인 항목 노드의 수인 정수 값을 반환합니다.

항목 노드가 없으면 문서는 유효하지 않은 RSS 문서가 되고 함수는 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()

이 함수는 모든 CEasyXmlNode 포인터를 재설정합니다.

먼저 CArrayObj 클래스의 Shutdown() 메소드를 호출하여 ChannelChildnodes 배열의 요소를 삭제합니다. 그런 다음 ArrayFree()를 한 번만 호출하면 배열이 해제됩니다.

다음으로 채널 노드에 대한 포인터가 삭제되고 easyxml 파서의 문서 트리가 지워집니다. 이러한 작업으로 인해 RssNode 및 ChannelNode 포인터가 잘못된 포인터가 되어 둘 다 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. 문서 트리에서 정보를 추출하는 방법

이 함수는 RSS 문서에서 텍스트를 가져오기 위한 것입니다.

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

2.7.1. getChannelTitle()

이 함수는 RSS 문서의 현재 채널 제목을 검색합니다.

채널 노드 포인터의 유효성을 확인하는 것으로 시작합니다. 포인터가 유효하면 제목 태그를 찾는 채널 노드의 모든 직계 자손을 반복합니다.

for 루프는 m_shift 속성을 사용하여 검색할 채널 노드 하위 항목의 수를 제한합니다. 함수가 실패하면 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()

이 함수는 항목 태그에 대한 포인터를 입력으로 사용하고 해당 태그의 하위 항목을 탐색하여 제목 태그를 찾고 값을 반환합니다.

getDescription() 및 getDate() 함수는 동일한 형식을 따르고 유사하게 작동합니다. 함수의 성공적인 호출은 문자열 값을 반환하고, 그렇지 않으면 NULL이 출력으로 반환됩니다.

//+------------------------------------------------------------------+
//| 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. 텍스트 서식 지정 방법

이러한 기능은 텍스트 개체가 가지고 있는 몇 가지 제한 사항을 극복하기 위해 텍스트 개체로 출력할 텍스트를 준비하기 위한 것입니다.

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

RSS 문서에서 추출한 텍스트를 응용 프로그램으로 출력할 수 있도록 준비하는 주요 기능입니다.

기본적으로 문자열 입력 값을 취하고 텍스트를 "n" 문자 줄로 나눕니다. "n"은 한 줄의 텍스트에 있는 문자 수의 정수 값입니다. 텍스트의 모든 "n" 문자 다음에 코드는 새 줄 바꿈 문자를 삽입할 적절한 포지션을 검색합니다. 그런 다음 전체 문자열 값이 처리되고 새 줄 바꿈 문자가 원본 텍스트에 삽입됩니다.

StringSplit() 함수는 각각 "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);
  }
-->


2.8.2. removeTags()

이 기능은 많은 RSS 문서가 XML 노드 내에 HTML 태그를 포함한다는 것을 알게 된 후 필수가 되었습니다.

많은 RSS 집계 응용 프로그램이 브라우저에서 작동하므로 일부 RSS 문서는 이러한 방식으로 게시됩니다.

이 함수는 문자열 값을 취하고 텍스트 내에서 태그를 검색합니다. 태그가 발견되면 함수는 텍스트의 모든 문자를 반복하고 각 여는 태그 문자와 닫는 태그 문자의 포지션을 ​​2차원 배열 a[][]에 저장합니다. 이 배열은 태그 사이의 텍스트를 추출하는 데 사용되며 추출된 문자열이 반환됩니다. 태그가 발견되지 않으면 입력 문자열이 있는 그대로 반환됩니다.

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

그러한 문서의 부분적인 예가 아래에 나와 있습니다.

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

이 함수는 단순히 특정 문자열 상수를 올바른 문자로 대체합니다.

예를 들어 일부 xml 문서의 앰퍼샌드 문자는 "&amp"로 표시될 수 있습니다. 이 함수는 내장된 StringReplace() 함수를 사용하여 이러한 종류의 발생을 대체합니다.

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

removeTags() 함수에서 호출되는 도우미 함수입니다. 문자열과 정수 값을 입력으로 받습니다.

입력 정수 값은 문자열에서 문자의 포지션을 ​​나타내며, 여기에서 함수는 여는 태그 문자(예: "<")를 검색하기 시작합니다. 여는 태그가 발견되면 함수는 닫는 태그를 검색하기 시작하고 해당 닫는 태그 문자 ">"의 포지션을 ​​출력으로 반환합니다. 태그가 발견되지 않으면 함수는 -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. 독립 제어의 이벤트 처리 방법

이러한 함수는 특정 컨트롤의 캡처된 이벤트를 처리합니다.

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

2.9.1. OnChangeListView()

이것은 이벤트 핸들러 함수이며 애플리케이션의 목록 보기 영역 섹션에 있는 목록 항목 중 하나를 클릭할 때마다 호출됩니다.

이 기능은 RSS 문서에서 참조된 일부 콘텐츠에 대한 설명 요약 보기를 활성화하는 역할을 합니다.

이 기능은 모든 텍스트의 텍스트 영역과 제목 영역 섹션을 지우고 문서 트리에서 새 데이터를 검색하여 출력을 위해 준비합니다. 이 모든 것은 ChannelChildnodes 배열이 비어 있지 않은 경우에만 발생합니다.

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

핸들러 함수는 사용자가 입력 영역에 일부 텍스트 입력을 완료할 때마다 호출됩니다.

이 함수는 LoadDocument() 메소드를 호출합니다. 다운로드에 성공하면 전체 애플리케이션에서 텍스트가 지워집니다. 다음으로 캡션이 변경되고 새로운 내용이 목록 보기 영역 섹션에 출력됩니다.

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

이러한 핸들러는 사용자가 재설정 또는 피드 업데이트 확인 버튼을 클릭할 때마다 호출됩니다.

재설정 버튼을 클릭하면 Expert Advisor가 처음 실행되었을 때의 상태로 앱 대화 상자가 새로 고쳐집니다.

"피드 업데이트 확인" 버튼을 클릭하면 로드 LoadDocument() 메소드가 호출되고 RSS 피드 데이터가 다운로드되어 목록 보기 영역 섹션이 새로 고쳐집니다.

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

이것으로 CRssReader 클래스의 정의를 마칩니다.


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

이제 Expert Advisor 코드에서 사용할 수 있습니다.


2.11. Expert Advisor 코드

Expert Advisor는 응용 프로그램이 완전히 대화식이어야 하므로 입력 변수가 없습니다.

먼저 CRssReader 클래스의 인스턴스인 전역 변수를 선언합니다. OnInit() 함수에서 기본 Create() 메소드를 호출하여 애플리케이션 대화 상자를 초기화합니다. 성공하면 상위 클래스의 Run() 메소드가 호출됩니다.

OnDeinit() 함수에서 상위 클래스의 Destroy() 메소드가 호출되어 전체 애플리케이션을 삭제하고 차트에서 Expert Advisor를 제거합니다.

OnChartEvent() 함수에는 모든 이벤트 처리를 처리하는 CRssReader 클래스의 상위 메소드에 대한 호출이 포함되어 있습니다.

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

그런 다음 코드를 컴파일해야 하며 프로그램을 사용할 준비가 됩니다.

RssReader.mq5 Expert Advisor가 차트에 로드되면 다음과 같이 빈 응용 프로그램 대화 상자가 나타납니다.

그림 2. RssReader Expert Advisor의 빈 앱 대화 스크린샷

그림 2. RssReader Expert Advisor의 빈 앱 대화 스크린샷

웹 주소를 입력하면 RSS 콘텐츠가 아래 이미지와 같이 애플리케이션 대화 상자에 로드됩니다.

그림 3. 터미널에서 작동하는 RssReader EA

그림 3. 터미널에서 작동하는 RssReader EA

다양한 RSS 피드로 프로그램을 테스트했습니다. 제가 관찰한 유일한 문제는 원하지 않는 문자의 표시와 관련된 것이었습니다. 대부분 HTML 문서에서 일반적으로 발견되는 문자가 포함된 RSS 문서의 결과였습니다.

또한 응용 프로그램이 실행되는 동안 차트의 기간을 변경하면 EA가 다시 초기화되고 응용 프로그램 컨트롤이 제대로 그려지지 않을 수 있음을 알았습니다.

이 동작을 수정할 수 없었으므로 RSS 리더 프로그램이 실행 중일 때 차트 주기를 변경하지 않는 것이 좋습니다.


결론

객체 지향 프로그래밍 기술을 사용하여 MetaTrader 5를 위한 완전한 대화형 RSS 리더 응용 프로그램의 생성을 완료했습니다.

응용 프로그램에 추가할 수 있는 기능이 훨씬 더 많고 사용자 인터페이스를 배열할 수 있는 방법이 더 많을 것이라고 확신합니다. 더 나은 응용 프로그램 GUI 디자인 기술을 가진 사람들이 응용 프로그램을 개선하고 자신의 창작물을 공유하기를 바랍니다.

추신 여기에서 다운로드할 수 있는 easyxml.mqh 파일은 코드 베이스에서 사용할 수 있는 파일과 동일하지 않으며 글에서 이미 언급한 수정 사항이 포함되어 있습니다. 필요한 모든 포함은 RssReader.zip 파일에 있습니다.


MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/1589

파일 첨부됨 |
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)
가격 방향과 이동 속도에 따른 거래 아이디어 가격 방향과 이동 속도에 따른 거래 아이디어
이 글은 가격의 움직임 방향과 속도에 대한 분석을 기반으로 아이디어에 대한 검토를 제공합니다. 우리는 고려 중인 전략의 실행 가능성을 탐색하기 위해 Expert Advisor으로 제시된 MQL4 언어로 공식화를 수행했습니다. 또한 글에 제공된 예를 확인, 검사 및 최적화하여 최상의 매개변수를 결정합니다.
MQL5 Cookbook: ОСО 주문 MQL5 Cookbook: ОСО 주문
모든 거래자의 거래 활동에는 주문 간의 관계를 비롯한 다양한 메커니즘과 상호 관계가 포함됩니다. 이 글은 OCO 주문 처리의 솔루션을 제안합니다. 표준 라이브러리 클래스가 광범위하게 관련되어 있으며 여기에서 새로운 데이터 유형이 생성됩니다.
시장에서 제품을 구매하기 위한 팁. 단계별 가이드 시장에서 제품을 구매하기 위한 팁. 단계별 가이드
이 단계별 가이드는 필요한 제품을 더 잘 이해하고 검색하기 위한 팁과 요령을 제공합니다. 이 글은 적절한 제품을 찾고, 원하지 않는 제품을 분류하고, 제품 효율성과 필수성을 결정하는 다양한 방법을 수수께끼로 만들려고 합니다.
기술적 분석 및 시장 예측 방법에 관하여 기술적 분석 및 시장 예측 방법에 관하여
이 글은 시각적 사고 및 "즉시 사용 가능한" 시장 전망과 결합된 잘 알려진 수학적 방법의 기능과 잠재력을 보여줍니다. 한편으로는 트레이딩 패러다임 자체를 재고할 수 있는 창의적인 마인드를 가질 수 있어 폭넓은 청중의 이목을 집중시키는 역할을 합니다. 그리고 다른 한편으로는 분석 및 예측을 위한 광범위한 도구와 관련된 대안 개발 및 프로그램 코드 구현을 야기할 수 있습니다.