В этот раз я хочу рассказать о том как работать с еще одним online-сервисом, а именно с сервисом online-переводов Google Translate.

Для работы с этим сервисом у Google есть свой программный интерфейс, а именно AJAX Language API for Translation And Detection. Именно его мы и будем использовать.

Для того чтобы осуществить online-перевод текста необходимо сделать http-запрос к сервису переводов, доступному по адресу:

http://ajax.googleapis.com/ajax/services/language/translate

Параметры, которые необходимо передать сервису:

  • v=1.0 – версия сервиса
  • q=<sometext> – URL-encoded текст для перевода.
  • langpair=<source_language>%7C<result_language> – пара названий языков: исходного и результирующего


Например для перевода текста “Hello World” с английского на русский строка запроса будет выглядеть так:

http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=Hello%20World&langpair=en%7Cru

Ну вот, небольшой теоретический экскурс закончен, можно приступать к кодингу:
wxGoogleTranslate.h

#ifndef _WX_GOOGLE_TRANSLATE_H
#define _WX_GOOGLE_TRANSLATE_H

#include <wx/wx.h>

class wxGoogleTranslate
{
	/// \brief Language Info
	struct wxGoogleTranslateLanguageInfo
	{
		/// \brief Full language name
		wxString languageName;
		/// \brief Language code
		wxString languageCode;
	};

	/// \brief Stores information about all supported languages
	static wxGoogleTranslateLanguageInfo m_LanguageInfoArray[];
	/**
	\brief Parses JSON response from Google Translate service
	\param response contains JSON response from Google Translate service
	\param translatedText contains translated text if parsing of JSON response was correct
	\param translateionDetails contains additional information from Google Translate service
		   usually it is error message
    \param translationStatus contains parsing status (error code)
	\return true if translation and parsing was successfull, otherwise false
	*/
	static bool ParseJSONResponse(const wxString & response,
		wxString & translatedText,
		wxString & translateionDetails,
		int & translationStatus);
public:
	/// \brief Returns the list of all suported languages
	/// \param result array for storing language names
	static void GetLanguages(wxArrayString & result);
	/**
	\brief Returns language code by language name
	\param languageName language name
	\return language code on success or empty string on error
	*/
	static wxString GetLanguageCode(const wxString & languageName);
	/**
	\brief Translates given text with Google Translate service
	\param source string to translate
	\param result translation result
	\param sourceLanguageCode source language code
	\param resultLanguageCode destination language code
	\param translationDetails error message
	\param errorCode error code
	\return true on success, otherwise false
	*/
	static bool Translate(const wxString & source, 
		wxString & result, 
		const wxString & sourceLanguageCode, 
		const wxString & resultLanguageCode,
		wxString & translationDetails,
		int & errorCode);
};

#endif

Итак, как я уже писал выше, для того чтобы сформировать корректный URL запроса нам необходимо чтобы текст для перевода был URL-encoded. Для этоко пишем функцию URLEncode()

wxString HexFromInt(const int &value)
{
	wxString szHexHolder;

	if( value < 16)
		szHexHolder.Printf(wxT("0%x"), value );
	else
		szHexHolder.Printf(wxT("%x"), value );

	return szHexHolder.MakeUpper();
}

wxString URLEncode(const wxString &value)
{
	wxString szToReturn = wxT("");
	unsigned int nPos = 0;

	while( value.length() > nPos ) 
	{
		wxChar cChar = value.GetChar(nPos);

		if( (cChar >= wxT('0') && cChar <= wxT('9')) || 
			(cChar >= wxT('a') && cChar <= wxT('z')) || 
			(cChar >= wxT('A') && cChar <= wxT('Z')) || 
			(cChar == wxT('-')) || (cChar == wxT('@')) || 
			(cChar == wxT('*')) || (cChar == wxT('_')) )
		{
			szToReturn.Append( cChar );
		}
		else
		{
			switch( cChar )
			{
			case wxT(' '):  szToReturn.Append(wxT('+')); break;
			case wxT('\n'): szToReturn.Append(wxT("%0D%0A")); break;
			default:
				{
					szToReturn.Append(wxT("%"));
					szToReturn += HexFromInt( cChar );
				}
			}
		}
		nPos++;
	}
	return szToReturn;
}
&#91;/sourcecode&#93;
На самом деле большая часть приведенного выше кода позаимствована из библиотеки wxHTTPEngine, но, к сожалению, исходная реализация не работала с русскими символами и пришлось ее немного доработать напильником.
Так, с URL-encoding'ом разобрались, но это еще не все. Перед тем как формировать URL-encoded строку нам необходимо текст преобразовать в UTF-8. Для этого можно использовать метод <strong>wxString::ToUTF8()</strong>.
Список поддерживаемых сервисом языков можно найти на странице описания Google Language API. У нас в программе для этого будет массив структур, каждая из которых будет содержать полное название языка и его код (который можно будет передать в параметр <strong>languagepair</strong>):

wxGoogleTranslate::wxGoogleTranslateLanguageInfo 
	wxGoogleTranslate::m_LanguageInfoArray[] =
{
	{wxT("AFRIKAANS"),				wxT("af")},
	{wxT("ALBANIAN"),				wxT("sq")},
	{wxT("AMHARIC"),				wxT("am")},
	{wxT("ARABIC"),					wxT("ar")},
	{wxT("ARMENIAN"),				wxT("hy")},
	{wxT("AZERBAIJANI"),			wxT("az")},
	{wxT("BASQUE"),					wxT("eu")},
	{wxT("BELARUSIAN"),				wxT("be")},
	{wxT("BENGALI"),				wxT("bn")},
	{wxT("BIHARI"),					wxT("bh")},
	{wxT("BULGARIAN"),				wxT("bg")},
	{wxT("BURMESE"),				wxT("my")},
	{wxT("CATALAN"),				wxT("ca")},
	{wxT("CHEROKEE"),				wxT("chr")},
	{wxT("CHINESE"),				wxT("zh")},
	{wxT("CHINESE_SIMPLIFIED"),		wxT("zh-CN")},
	{wxT("CHINESE_TRADITIONAL"),	wxT("zh-TW")},
	{wxT("CROATIAN"),				wxT("hr")},
	{wxT("CZECH"),					wxT("cs")},
	{wxT("DANISH"),					wxT("da")},
	{wxT("DHIVEHI"),				wxT("dv")},
	{wxT("DUTCH"),					wxT("nl")},
	{wxT("ENGLISH"),				wxT("en")},
	{wxT("ESPERANTO"),				wxT("eo")},
	{wxT("ESTONIAN"),				wxT("et")},
	{wxT("FILIPINO"),				wxT("tl")},
	{wxT("FINNISH"),				wxT("fi")},
	{wxT("FRENCH"),					wxT("fr")},
	{wxT("GALICIAN"),				wxT("gl")},
	{wxT("GEORGIAN"),				wxT("ka")},
	{wxT("GERMAN"),					wxT("de")},
	{wxT("GREEK"),					wxT("el")},
	{wxT("GUARANI"),				wxT("gn")},
	{wxT("GUJARATI"),				wxT("gu")},
	{wxT("HEBREW"),					wxT("iw")},
	{wxT("HINDI"),					wxT("hi")},
	{wxT("HUNGARIAN"),				wxT("hu")},
	{wxT("ICELANDIC"),				wxT("is")},
	{wxT("INDONESIAN"),				wxT("id")},
	{wxT("INUKTITUT"),				wxT("iu")},
	{wxT("ITALIAN"),				wxT("it")},
	{wxT("JAPANESE"),				wxT("ja")},
	{wxT("KANNADA"),				wxT("kn")},
	{wxT("KAZAKH"),					wxT("kk")},
	{wxT("KHMER"),					wxT("km")},
	{wxT("KOREAN"),					wxT("ko")},
	{wxT("KURDISH"),				wxT("ku")},
	{wxT("KYRGYZ"),					wxT("ky")},
	{wxT("LAOTHIAN"),				wxT("lo")},
	{wxT("LATVIAN"),				wxT("lv")},
	{wxT("LITHUANIAN"),				wxT("lt")},
	{wxT("MACEDONIAN"),				wxT("mk")},
	{wxT("MALAY"),					wxT("ms")},
	{wxT("MALAYALAM"),				wxT("ml")},
	{wxT("MALTESE"),				wxT("mt")},
	{wxT("MARATHI"),				wxT("mr")},
	{wxT("MONGOLIAN"),				wxT("mn")},
	{wxT("NEPALI"),					wxT("ne")},
	{wxT("NORWEGIAN"),				wxT("no")},
	{wxT("ORIYA"),					wxT("or")},
	{wxT("PASHTO"),					wxT("ps")},
	{wxT("PERSIAN"),				wxT("fa")},
	{wxT("POLISH"),					wxT("pl")},
	{wxT("PORTUGUESE"),				wxT("pt-PT")},
	{wxT("PUNJABI"),				wxT("pa")},
	{wxT("ROMANIAN"),				wxT("ro")},
	{wxT("RUSSIAN"),				wxT("ru")},
	{wxT("SANSKRIT"),				wxT("sa")},
	{wxT("SERBIAN"),				wxT("sr")},
	{wxT("SINDHI"),					wxT("sd")},
	{wxT("SINHALESE"),				wxT("si")},
	{wxT("SLOVAK"),					wxT("sk")},
	{wxT("SLOVENIAN"),				wxT("sl")},
	{wxT("SPANISH"),				wxT("es")},
	{wxT("SWAHILI"),				wxT("sw")},
	{wxT("SWEDISH"),				wxT("sv")},
	{wxT("TAJIK"),					wxT("tg")},
	{wxT("TAMIL"),					wxT("ta")},
	{wxT("TAGALOG"),				wxT("tl")},
	{wxT("TELUGU"),					wxT("te")},
	{wxT("THAI"),					wxT("th")},
	{wxT("TIBETAN"),				wxT("bo")},
	{wxT("TURKISH"),				wxT("tr")},
	{wxT("UKRAINIAN"),				wxT("uk")},
	{wxT("URDU"),					wxT("ur")},
	{wxT("UZBEK"),					wxT("uz")},
	{wxT("UIGHUR"),					wxT("ug")},
	{wxT("VIETNAMESE"),				wxT("vi")},
	{wxT("UNKNOWN"),				wxEmptyString}
};

Для получения списка языков будет использоваться метод wxGoogleTranslate::GetLanguages()

void wxGoogleTranslate::GetLanguages(wxArrayString & result)
{
	result.Clear();
	int count = sizeof(m_LanguageInfoArray) / 
		sizeof(wxGoogleTranslateLanguageInfo);
	for(int i = 0; i < count; i++)
	{
		result.Add(m_LanguageInfoArray&#91;i&#93;.languageName);
	}
}
&#91;/sourcecode&#93;
Для получения кода языка предназначен метод <strong>wxGoogleTranslate::GetLanguageCode()</strong>:

wxString wxGoogleTranslate::GetLanguageCode(const wxString & languageName)
{
	int count = sizeof(m_LanguageInfoArray) / 
		sizeof(wxGoogleTranslateLanguageInfo);
	for(int i = 0; i < count; i++)
	{
		if(m_LanguageInfoArray&#91;i&#93;.languageName.Lower().IsSameAs(
			languageName.Lower()))
		{
			return m_LanguageInfoArray&#91;i&#93;.languageCode;
		}
	}
	return wxEmptyString;
}
&#91;/sourcecode&#93;
Теперь можно реализовывать непосредственно метод для перевода текста:
&#91;sourcecode language="cpp"&#93;
bool wxGoogleTranslate::Translate(const wxString & source, 
		wxString & result, 
		const wxString & sourceLanguageCode, 
		const wxString & resultLanguageCode,
		wxString & translationDetails,
		int & errorCode)
{
	do
	{
		wxString urlEncodedSource = 
			URLEncode(wxString::FromAscii(source.ToUTF8()));
		wxString srcURL = wxString::Format(
			wxT("%s?v=1.0&q=%s&langpair=%s%%7C%s"),
			wxT("http://ajax.googleapis.com/ajax/services/language/translate"),
			urlEncodedSource.GetData(),
			sourceLanguageCode.GetData(),
			resultLanguageCode.GetData());
		wxURL url = srcURL;
		if(url.GetError() != wxURL_NOERR) break;
		wxInputStream * in = url.GetInputStream();
		if(!in) break;
		wxString response;
		wxStringOutputStream out(&response);
		in->Read(out);
		wxDELETE(in);
		if(response.IsEmpty()) break;
		if(!wxGoogleTranslate::ParseJSONResponse(response, result, 
			translationDetails, errorCode)) break;
		return true;
	}
	while(false);
	return false;
}

Результат запроса приходит в формате JSON. В стандартной поставке wxWidgets библиотеки для парсинга JSON нет, зато есть сторонняя библиотека wxJSON, которую мы и будем использовать.
Формат ответа от сервиса Google Translate:

{"responseData": {"translatedText":"sometext"}, "responseDetails": "sometext", "responseStatus": 200}

Где

  • translatedText – переведенный текст
  • responseDetails – дополнительная информация от сервиса (сообщение об ошибке)
  • responseStatus – код ошибки

Метод для парсинга ответа сервиса:

bool wxGoogleTranslate::ParseJSONResponse(const wxString & response,
		wxString & translatedText,
		wxString & translateionDetails,
		int & translationStatus)
{
	do
	{
		wxJSONValue  root;
		wxJSONReader reader;
		if(reader.Parse(response, &root) > 0) break;
		translatedText = 
			root[wxT("responseData")][wxT("translatedText")].AsString();
		translateionDetails = 
			root[wxT("responseDetails")].AsString();
		translationStatus = 
			root[wxT("responseStatus")].AsInt();
		return true;
	}
	while(false);
	return false;
}

Ну вот, работу над классом переводчика закончили. Теперь посмотрим как его использовать:

void wxGoogleTranslateClientMainFrame::OnTRANSLATEClick( wxCommandEvent& event )
{
	do
	{
		wxString result;
		wxString details;
		int errorCode(0);
		int sourceLangselection = m_SourceLanguageChoice->GetSelection();
		int resultLangselection = m_ResultLanguageChoice->GetSelection();
		if((sourceLangselection < 0) || (resultLangselection < 0)) break;
		if(wxGoogleTranslate::Translate(m_SourceTextCtrl->GetValue(), 
			result, 
			wxGoogleTranslate::GetLanguageCode(
				m_SourceLanguageChoice->GetString(sourceLangselection)), 
			wxGoogleTranslate::GetLanguageCode(
			m_ResultLanguageChoice->GetString(resultLangselection)), 
				details, errorCode))
		{
			m_ResultTextCtrl->SetValue(result);
		}
		else
		{
			wxMessageBox(wxString::Format(_("Erorr occured: %s"), details.GetData()));
		}
	}
	while(false);
}

Хотелось бы отметить вот что: для загрузки ответа от сервиса используется класс wxURL. Используется синхронно, поэтому работа вызывающего потока приложения блокируется до тех пор пока загрузка не будет завершена.
Использование же класса переводчика заключается в вызове метода Translate(), никаких дополнительных действий по инициализации или деинициализации не требуется.

Исходный код библиотеки wxGoogleTranslate, а также исходный код и исполняемые файлы примера для Windows NT/2000/XP/Vista и для Windows Mobile 2003/5/6 можно загрузить здесь.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

This post has 4 Comments

4
  1. ну все пишешь, пишешь, работаешь аки пчела 🙂
    Вот и я говорю, молодец.

Leave a Reply

Your email address will not be published. Required fields are marked *

О.

Отображаем анимированный GIF под Windows Mobile

Как-то печально обстоят дела с отображением анимации на устройствах под управлением Windows Mobile. Искал решение на .NET Compact Framework, нашел на Stack Overflow. Там предлагают писать собственный контрол, который будет делить изображение на кадры и отображать их с заданной периодичностью. Там в ответах ссылка на статью в MSDN. Собственно, везде это решение рекомендуют, но мне оно как-то не очень понравилось ввиду того, что позволяет отображать только специально подготовленное изображение, что во многих случаях очень неудобно.

А вот для тех, кто пишет ПО для Windows Mobile на C++ с wxWinCE эта проблема решается намного проще, с помощью wxAnimationCtrl. Вобще никаких лишних телодвижений делать не надо:

void wxAnimateMobileMainFrame::OnOPENClick( wxCommandEvent& event )
{
	wxFileDialog * dlg = new wxFileDialog(this, wxFileSelectorPromptStr, wxEmptyString,
		wxEmptyString, _("GIF Files (*.gif)|*.gif"));
	if(dlg->ShowModal() == wxID_OK)
	{
		m_AnimationCtrl->LoadFile(dlg->GetPath());
		m_AnimationCtrl->Play();
	}
	dlg->Destroy();
}

Из полезных возможностей:

  • Загрузка GIF-изображений с любым количеством кадров
  • Поддержка различных интервалов задержки для различных кадров
  • Циклическое воспроизведение (прописывается в самом GIF-файле)

Оказывается, все-таки, для некоторых задач C++ пользовать удобнее (хотя все равно для меня остается загадкой почему в .NET CF этого функционала нет).

Исходный код примера можно загрузить здесь.

Р.

Рисуем градиентные кнопки

Несколько дней назад наткнулся на пост How to draw gradient buttons в блоге Native Mobile. На вид неплохо, но использование функции GradientFill() показалось мне не очень удобным, т.к. для ее использования приходится заполнять кучу полей в структурах TRIVERTEX.

Существует возможность создать подобные кнопки намного проще с использованием библиотеки wxWidgets.


wxBufferedPaintDC dc(this);

wxRect clientRect = GetClientRect();
wxRect gradientRect = clientRect;
gradientRect.SetHeight(gradientRect.GetHeight()/2);
dc.GradientFillLinear(gradientRect,
wxColour(132,125,132), wxColour(74,69,74), wxSOUTH);
gradientRect.Offset(0, gradientRect.GetHeight());
dc.GradientFillLinear(gradientRect,
wxColour(0,0,0), wxColour(57,56,57), wxSOUTH);

dc.SetPen(wxPen(GetBackgroundColour()));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(0, 0, clientRect.GetWidth(), clientRect.GetHeight());
dc.SetFont(GetFont());
dc.SetTextForeground(GetForegroundColour());
dc.DrawLabel(m_Label, clientRect,
wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL);

После нескольких несложных экспериментов я решил написать простенький компонент, представляющий собой кнопку с градиентом. Код компонента приведен ниже:

wxGradientButton.h

/////////////////////////////////////////////////////////////////////////////
// Name:        wxGradientButton.h
// Purpose:     
// Author:      Volodymir (T-Rex) Tryapichko
// Modified by: 
// Created:     01/08/2008 20:25:42
// RCS-ID:      
// Copyright:   Volodymir (T-Rex) Tryapichko, 2008
// Licence:     
/////////////////////////////////////////////////////////////////////////////

#ifndef _WXGRADIENTBUTTON_H_
#define _WXGRADIENTBUTTON_H_


/*!
 * Includes
 */

////@begin includes
////@end includes

/*!
 * Forward declarations
 */

////@begin forward declarations
class wxGradientButton;
////@end forward declarations

/*!
 * Control identifiers
 */

////@begin control identifiers
#define ID_WXGRADIENTBUTTON 10003
#define SYMBOL_WXGRADIENTBUTTON_STYLE wxSIMPLE_BORDER|wxFULL_REPAINT_ON_RESIZE
#define SYMBOL_WXGRADIENTBUTTON_IDNAME ID_WXGRADIENTBUTTON
#define SYMBOL_WXGRADIENTBUTTON_SIZE wxSize(100, 100)
#define SYMBOL_WXGRADIENTBUTTON_POSITION wxDefaultPosition
////@end control identifiers


/*!
 * wxGradientButton class declaration
 */

class wxGradientButton: public wxWindow
{    
    DECLARE_DYNAMIC_CLASS( wxGradientButton )
    DECLARE_EVENT_TABLE()

	wxSize DoGetBestSize() const;
public:
    /// Constructors
    wxGradientButton();
    wxGradientButton(wxWindow* parent, wxWindowID id = ID_WXGRADIENTBUTTON, const wxString & label = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(100, 100), long style = wxSIMPLE_BORDER);

    /// Creation
    bool Create(wxWindow* parent, wxWindowID id = ID_WXGRADIENTBUTTON, const wxString & label = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(100, 100), long style = wxSIMPLE_BORDER);

    /// Destructor
    ~wxGradientButton();

    /// Initialises member variables
    void Init();

    /// Creates the controls and sizers
    void CreateControls();

////@begin wxGradientButton event handler declarations

    /// wxEVT_SIZE event handler for ID_WXGRADIENTBUTTON
    void OnSize( wxSizeEvent& event );

    /// wxEVT_PAINT event handler for ID_WXGRADIENTBUTTON
    void OnPaint( wxPaintEvent& event );

    /// wxEVT_ERASE_BACKGROUND event handler for ID_WXGRADIENTBUTTON
    void OnEraseBackground( wxEraseEvent& event );

    /// wxEVT_LEFT_DOWN event handler for ID_WXGRADIENTBUTTON
    void OnLeftDown( wxMouseEvent& event );

    /// wxEVT_LEFT_UP event handler for ID_WXGRADIENTBUTTON
    void OnLeftUp( wxMouseEvent& event );

////@end wxGradientButton event handler declarations

////@begin wxGradientButton member function declarations

    wxString GetLabel() const { return m_Label ; }
    void SetLabel(wxString value) { m_Label = value ; }

    wxColour GetGradientTopStartColour() const { return m_GradientTopStartColour ; }
    void SetGradientTopStartColour(wxColour value) { m_GradientTopStartColour = value ; }

    wxColour GetGradientTopEndColour() const { return m_GradientTopEndColour ; }
    void SetGradientTopEndColour(wxColour value) { m_GradientTopEndColour = value ; }

    wxColour GetGradientBottomStartColour() const { return m_GradientBottomStartColour ; }
    void SetGradientBottomStartColour(wxColour value) { m_GradientBottomStartColour = value ; }

    wxColour GetGradientBottomEndColour() const { return m_GradientBottomEndColour ; }
    void SetGradientBottomEndColour(wxColour value) { m_GradientBottomEndColour = value ; }

    wxColour GetPressedColourTop() const { return m_PressedColourTop ; }
    void SetPressedColourTop(wxColour value) { m_PressedColourTop = value ; }

    wxColour GetPressedColourBottom() const { return m_PressedColourBottom ; }
    void SetPressedColourBottom(wxColour value) { m_PressedColourBottom = value ; }

    /// Retrieves bitmap resources
    wxBitmap GetBitmapResource( const wxString& name );

    /// Retrieves icon resources
    wxIcon GetIconResource( const wxString& name );
////@end wxGradientButton member function declarations

    /// Should we show tooltips?
    static bool ShowToolTips();

////@begin wxGradientButton member variables
    wxString m_Label;
    wxColour m_GradientTopStartColour;
    wxColour m_GradientTopEndColour;
    wxColour m_GradientBottomStartColour;
    wxColour m_GradientBottomEndColour;
    wxColour m_PressedColourTop;
    wxColour m_PressedColourBottom;
////@end wxGradientButton member variables
};

#endif
    // _WXGRADIENTBUTTON_H_

wxGradientButton.cpp

/////////////////////////////////////////////////////////////////////////////
// Name:        wxGradientButton.cpp
// Purpose:     
// Author:      Volodymir (T-Rex) Tryapichko
// Modified by: 
// Created:     01/08/2008 20:25:42
// RCS-ID:      
// Copyright:   Volodymir (T-Rex) Tryapichko, 2008
// Licence:     
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes
////@end includes

#include "wxGradientButton.h"
#include <wx/dcbuffer.h>

////@begin XPM images
////@end XPM images


/*!
 * wxGradientButton type definition
 */

IMPLEMENT_DYNAMIC_CLASS( wxGradientButton, wxWindow )


/*!
 * wxGradientButton event table definition
 */

BEGIN_EVENT_TABLE( wxGradientButton, wxWindow )

////@begin wxGradientButton event table entries
    EVT_SIZE( wxGradientButton::OnSize )
    EVT_PAINT( wxGradientButton::OnPaint )
    EVT_ERASE_BACKGROUND( wxGradientButton::OnEraseBackground )
    EVT_LEFT_DOWN( wxGradientButton::OnLeftDown )
    EVT_LEFT_UP( wxGradientButton::OnLeftUp )

////@end wxGradientButton event table entries

END_EVENT_TABLE()


/*!
 * wxGradientButton constructors
 */

wxGradientButton::wxGradientButton()
{
    Init();
}

wxGradientButton::wxGradientButton(wxWindow* parent, wxWindowID id, const wxString & label, const wxPoint& pos, const wxSize& size, long style)
{
    Init();
    Create(parent, id, label, pos, size, style);
}


/*!
 * wxGradientButton creator
 */

bool wxGradientButton::Create(wxWindow* parent, wxWindowID id, const wxString & label, const wxPoint& pos, const wxSize& size, long style)
{
////@begin wxGradientButton creation
    wxWindow::Create(parent, id, pos, size, style);
    CreateControls();
////@end wxGradientButton creation
    m_Label = label;
    return true;
}


/*!
 * wxGradientButton destructor
 */

wxGradientButton::~wxGradientButton()
{
////@begin wxGradientButton destruction
////@end wxGradientButton destruction
}


/*!
 * Member initialisation
 */

void wxGradientButton::Init()
{
////@begin wxGradientButton member initialisation
    m_GradientTopStartColour = wxColour(132,125,132);
    m_GradientTopEndColour = wxColour(74,69,74);
    m_GradientBottomStartColour = wxColour(0,0,0);
    m_GradientBottomEndColour = wxColour(57,56,57);
    m_PressedColourTop = wxColour(57,56,57);
    m_PressedColourBottom = wxColour(0,0,0);
////@end wxGradientButton member initialisation
}


/*!
 * Control creation for wxGradientButton
 */

void wxGradientButton::CreateControls()
{    
////@begin wxGradientButton content construction
    this->SetForegroundColour(wxColour(255, 255, 255));
    this->SetBackgroundColour(wxColour(0, 0, 0));
    this->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT("Tahoma")));
////@end wxGradientButton content construction
}


/*!
 * Should we show tooltips?
 */

bool wxGradientButton::ShowToolTips()
{
    return true;
}

/*!
 * Get bitmap resources
 */

wxBitmap wxGradientButton::GetBitmapResource( const wxString& name )
{
    // Bitmap retrieval
////@begin wxGradientButton bitmap retrieval
    wxUnusedVar(name);
    return wxNullBitmap;
////@end wxGradientButton bitmap retrieval
}

/*!
 * Get icon resources
 */

wxIcon wxGradientButton::GetIconResource( const wxString& name )
{
    // Icon retrieval
////@begin wxGradientButton icon retrieval
    wxUnusedVar(name);
    return wxNullIcon;
////@end wxGradientButton icon retrieval
}

wxSize wxGradientButton::DoGetBestSize() const
{
	wxSize labelSize = wxDefaultSize;
	GetTextExtent(m_Label, &labelSize.x, &labelSize.y);
	return wxSize(wxMax(40, labelSize.x + 20), wxMax(20, labelSize.y + 10));
}


/*!
 * wxEVT_PAINT event handler for ID_WXGRADIENTBUTTON
 */

void wxGradientButton::OnPaint( wxPaintEvent& event )
{
    // Before editing this code, remove the block markers.
    wxBufferedPaintDC dc(this);

	wxRect clientRect = GetClientRect();
	wxRect gradientRect = clientRect;
	gradientRect.SetHeight(gradientRect.GetHeight()/2 + ((GetCapture() == this) ? 1 : 0));
	if(GetCapture() != this)
	{
		dc.GradientFillLinear(gradientRect, 
			m_GradientTopStartColour, m_GradientTopEndColour, wxSOUTH);
	}
	else
	{
		dc.SetPen(wxPen(m_PressedColourTop));
		dc.SetBrush(wxBrush(m_PressedColourTop));
		dc.DrawRectangle(gradientRect);
	}

	gradientRect.Offset(0, gradientRect.GetHeight());

	if(GetCapture() != this)
	{
		dc.GradientFillLinear(gradientRect, 
			m_GradientBottomStartColour, m_GradientBottomEndColour, wxSOUTH);
	}
	else
	{
		dc.SetPen(wxPen(m_PressedColourBottom));
		dc.SetBrush(wxBrush(m_PressedColourBottom));
		dc.DrawRectangle(gradientRect);
	}
	dc.SetPen(wxPen(GetBackgroundColour()));
	dc.SetBrush(*wxTRANSPARENT_BRUSH);
	dc.DrawRectangle(0, 0, clientRect.GetWidth(), clientRect.GetHeight());
	dc.SetFont(GetFont());
	dc.SetTextForeground(GetForegroundColour());
	if(GetCapture() == this)
	{
		clientRect.Offset(1, 1);
	}
	dc.DrawLabel(m_Label, clientRect, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL);
}


/*!
 * wxEVT_LEFT_DOWN event handler for ID_WXGRADIENTBUTTON
 */

void wxGradientButton::OnLeftDown( wxMouseEvent& event )
{
	if(GetCapture() != this)
	{
		CaptureMouse();
		Refresh();
	}
}


/*!
 * wxEVT_LEFT_UP event handler for ID_WXGRADIENTBUTTON
 */

void wxGradientButton::OnLeftUp( wxMouseEvent& event )
{
	if(GetCapture() == this)
	{
		ReleaseMouse();
		Refresh();
		if(GetClientRect().Contains(event.GetPosition()))
		{
			wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
			GetEventHandler()->AddPendingEvent(evt);
		}
	}
}


/*!
 * wxEVT_ERASE_BACKGROUND event handler for ID_WXGRADIENTBUTTON
 */

void wxGradientButton::OnEraseBackground( wxEraseEvent& event )
{
}


/*!
 * wxEVT_SIZE event handler for ID_WXGRADIENTBUTTON
 */

void wxGradientButton::OnSize( wxSizeEvent& event )
{
	Refresh();
}

Под Windows Mobile 6 все это выглядит как-то так:

Градиентные кнопки на C++ с использованием библиотеки wxWidgets
Градиентные кнопки на C++ с использованием библиотеки wxWidgets