<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cross-Platform Programming with wxWidgets &#187; Components</title>
	<atom:link href="http://wxwidgets.info/category/components/feed/" rel="self" type="application/rss+xml" />
	<link>http://wxwidgets.info</link>
	<description>Just Make It Cross-Platform</description>
	<lastBuildDate>Tue, 09 Mar 2010 20:50:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Cross-Platform Way of Obtaining MAC Address of Your Machine</title>
		<link>http://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/</link>
		<comments>http://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/#comments</comments>
		<pubDate>Sun, 30 Aug 2009 12:04:45 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[wxWinCE]]></category>

		<guid isPermaLink="false">http://wxwidgets.info/?p=557</guid>
		<description><![CDATA[In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can&#8217;t connect to server if its MAC address is not allowed, regardless of network or broadband connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform [...]]]></description>
			<content:encoded><![CDATA[<p>In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can&#8217;t connect to server if its MAC address is not allowed, regardless of network or <a href="http://www.o2.co.uk/">broadband</a> connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform way. So, I decided to write a small class which allows obtainig MAC address for Windows, Linux, Mac OS and Windows Mobile. Here it is:<br />
<span id="more-557"></span><br />
<strong>MACAddressUtility.h</strong></p>
<pre class="brush: cpp;">
#ifndef _MACADDRESS_UTILITY_H
#define _MACADDRESS_UTILITY_H

class MACAddressUtility
{
public:
	static long GetMACAddress(unsigned char * result);
private:
#if defined(WIN32) || defined(UNDER_CE)
	static long GetMACAddressMSW(unsigned char * result);
#elif defined(__APPLE__)
	static long GetMACAddressMAC(unsigned char * result);
#elif defined(LINUX) || defined(linux)
	static long GetMACAddressLinux(unsigned char * result);
#endif
};

#endif
</pre>
<p><strong>MACAddressUtility.cpp</strong></p>
<pre class="brush: cpp;">
#include &quot;MACAddressUtility.h&quot;

#include &lt;stdio.h&gt;

#if defined(WIN32) || defined(UNDER_CE)
#	include &lt;windows.h&gt;
#	if defined(UNDER_CE)
#		include &lt;Iphlpapi.h&gt;
#	endif
#elif defined(__APPLE__)
#	include &lt;CoreFoundation/CoreFoundation.h&gt;
#	include &lt;IOKit/IOKitLib.h&gt;
#	include &lt;IOKit/network/IOEthernetInterface.h&gt;
#	include &lt;IOKit/network/IONetworkInterface.h&gt;
#	include &lt;IOKit/network/IOEthernetController.h&gt;
#elif defined(LINUX) || defined(linux)
#	include &lt;string.h&gt;
#	include &lt;net/if.h&gt;
#	include &lt;sys/ioctl.h&gt;
#   include &lt;sys/socket.h&gt;
#   include &lt;arpa/inet.h&gt;
#endif

long MACAddressUtility::GetMACAddress(unsigned char * result)
{
	// Fill result with zeroes
	memset(result, 0, 6);
	// Call appropriate function for each platform
#if defined(WIN32) || defined(UNDER_CE)
	return GetMACAddressMSW(result);
#elif defined(__APPLE__)
	return GetMACAddressMAC(result);
#elif defined(LINUX) || defined(linux)
	return GetMACAddressLinux(result);
#endif
	// If platform is not supported then return error code
	return -1;
}

#if defined(WIN32) || defined(UNDER_CE)

inline long MACAddressUtility::GetMACAddressMSW(unsigned char * result)
{

#if defined(UNDER_CE)
	IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information
	DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
	if(GetAdaptersInfo(AdapterInfo, &amp;dwBufLen) == ERROR_SUCCESS)
	{
		memcpy(result, AdapterInfo-&gt;Address, 6);
	}
	else return -1;
#else
	UUID uuid;
	if(UuidCreateSequential(&amp;uuid) == RPC_S_UUID_NO_ADDRESS) return -1;
	memcpy(result, (char*)(uuid.Data4+2), 6);
#endif
	return 0;
}

#elif defined(__APPLE__)

static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)
{
    kern_return_t		kernResult;
    CFMutableDictionaryRef	matchingDict;
    CFMutableDictionaryRef	propertyMatchDict;

    matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);

    if (NULL != matchingDict)
	{
        propertyMatchDict =
			CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
				&amp;kCFTypeDictionaryKeyCallBacks,
				&amp;kCFTypeDictionaryValueCallBacks);

        if (NULL != propertyMatchDict)
		{
            CFDictionarySetValue(propertyMatchDict,
				CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
            CFDictionarySetValue(matchingDict,
				CFSTR(kIOPropertyMatchKey), propertyMatchDict);
            CFRelease(propertyMatchDict);
        }
    }
    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
		matchingDict, matchingServices);
    return kernResult;
}

static kern_return_t GetMACAddress(io_iterator_t intfIterator,
								   UInt8 *MACAddress, UInt8 bufferSize)
{
    io_object_t		intfService;
    io_object_t		controllerService;
    kern_return_t	kernResult = KERN_FAILURE;

	if (bufferSize &lt; kIOEthernetAddressSize) {
		return kernResult;
	}

    bzero(MACAddress, bufferSize);

    while (intfService = IOIteratorNext(intfIterator))
    {
        CFTypeRef	MACAddressAsCFData;        

        // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call,
        // since they are hardware nubs and do not participate in driver matching. In other words,
        // registerService() is never called on them. So we've found the IONetworkInterface and will
        // get its parent controller by asking for it specifically.

        // IORegistryEntryGetParentEntry retains the returned object,
		// so release it when we're done with it.
        kernResult =
			IORegistryEntryGetParentEntry(intfService,
				kIOServicePlane,
				&amp;controllerService);

        if (KERN_SUCCESS != kernResult) {
            printf(&quot;IORegistryEntryGetParentEntry returned 0x%08x\n&quot;, kernResult);
        }
        else {
            // Retrieve the MAC address property from the I/O Registry in the form of a CFData
            MACAddressAsCFData =
				IORegistryEntryCreateCFProperty(controllerService,
					CFSTR(kIOMACAddress),
					kCFAllocatorDefault,
					0);
            if (MACAddressAsCFData) {
                CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr

                // Get the raw bytes of the MAC address from the CFData
                CFDataGetBytes((CFDataRef)MACAddressAsCFData,
					CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
                CFRelease(MACAddressAsCFData);
            }

            // Done with the parent Ethernet controller object so we release it.
            (void) IOObjectRelease(controllerService);
        }

        // Done with the Ethernet interface object so we release it.
        (void) IOObjectRelease(intfService);
    }

    return kernResult;
}

long MACAddressUtility::GetMACAddressMAC(unsigned char * result)
{
	io_iterator_t	intfIterator;
	kern_return_t	kernResult = KERN_FAILURE;
	do
	{
		kernResult = ::FindEthernetInterfaces(&amp;intfIterator);
		if (KERN_SUCCESS != kernResult) break;
	    kernResult = ::GetMACAddress(intfIterator, (UInt8*)result, 6);
    }
	while(false);
    (void) IOObjectRelease(intfIterator);
}

#elif defined(LINUX) || defined(linux)

long MACAddressUtility::GetMACAddressLinux(unsigned char * result)
{
	struct ifreq ifr;
    struct ifreq *IFR;
    struct ifconf ifc;
    char buf[1024];
    int s, i;
    int ok = 0;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == -1)
	{
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    ioctl(s, SIOCGIFCONF, &amp;ifc);

    IFR = ifc.ifc_req;
    for (i = ifc.ifc_len / sizeof(struct ifreq); --i &gt;= 0; IFR++)
	{
        strcpy(ifr.ifr_name, IFR-&gt;ifr_name);
        if (ioctl(s, SIOCGIFFLAGS, &amp;ifr) == 0)
		{
            if (! (ifr.ifr_flags &amp; IFF_LOOPBACK))
			{
                if (ioctl(s, SIOCGIFHWADDR, &amp;ifr) == 0)
				{
                    ok = 1;
                    break;
                }
            }
        }
    }

    shutdown(s, SHUT_RDWR);
    if (ok)
	{
        bcopy( ifr.ifr_hwaddr.sa_data, result, 6);
    }
    else
	{
        return -1;
    }
    return 0;
}

#endif
</pre>
<p><strong>wxMACAddressUtility.h</strong></p>
<pre class="brush: cpp;">
#ifndef _WX_MACADDRESS_UTILITY_H
#define _WX_MACADDRESS_UTILITY_H

#include &quot;MACAddressUtility.h&quot;
#include &lt;wx/wx.h&gt;

class wxMACAddressUtility
{
public:
	static wxString GetMACAddress()
	{
		unsigned char result[6];
		if(MACAddressUtility::GetMACAddress(result) == 0)
		{
			return wxString::Format(wxT(&quot;%02X:%02X:%02X:%02X:%02X:%02X&quot;),
				(unsigned int)result[0], (unsigned int)result[1], (unsigned int)result[2],
				(unsigned int)result[3], (unsigned int)result[4], (unsigned int)result[5]);
		}
		return wxEmptyString;
	}
};

#endif
</pre>
<p><strong>Sample of usage:</strong></p>
<pre class="brush: cpp;">
m_MACAddress = wxMACAddressUtility::GetMACAddress();
</pre>
<p><a href="http://wxwidgets.info//wp-content/uploads/2009/08/macaddr.jpg"><img src="http://wxwidgets.info//wp-content/uploads/2009/08/macaddr.jpg" alt="Get MAC address programmatically" title="Get MAC address programmatically" width="226" height="67" class="alignnone size-full wp-image-558" /></a></p>
<p>As you can see, core class does not depend on wxWidgets and can be used in native applications written without wxWidgets.</p>
<p><a href="http://wxwidgets.info//wp-content/uploads/2009/08/wxMACAddressUtility.7z" title="How to get MAC address in C++: Windows, Linux, Mac OS, Windows Mobile">Download sample application</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Taking Screenshots with wxWidgets under Mac OS is Really Tricky.</title>
		<link>http://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/</link>
		<comments>http://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/#comments</comments>
		<pubDate>Thu, 21 May 2009 21:30:00 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[Articles]]></category>
		<category><![CDATA[wxWidgets]]></category>

		<guid isPermaLink="false">http://wxwidgets.info/?p=550</guid>
		<description><![CDATA[Taking screenshots is a very common task and it was a must for one of my current projects. What was a surprise when I understood that my favourite toolkit can&#8217;t do that in cross-platform manner. It is official bug that wxScreenDC does not work properly under Mac OS and you can&#8217;t use Blit() message for [...]]]></description>
			<content:encoded><![CDATA[<p>Taking screenshots is a very common task and it was a must for one of my current projects. What was a surprise when I understood that my favourite toolkit can&#8217;t do that in cross-platform manner.</p>
<p>It is <a href="http://trac.wxwidgets.org/ticket/9486">official bug</a> that wxScreenDC does not work properly under Mac OS and you can&#8217;t use Blit() message for copying screen onto wxMemoryDC.</p>
<p>After digging the Internet I found a kind of solution which used OpenGL and created wxWidgets-based class which takes screenshots also under Mac OS. It was really hard task for me because I haven&#8217;t used neither Carbon nor Cocoa before. However everything works now and I&#8217;m happy.</p>
<p>Here it is:<br />
<span id="more-550"></span><br />
<strong>wxScreenshotMaker.h</strong></p>
<pre class="brush: cpp;">
#pragma once

#include &lt;wx/wx.h&gt;
#ifdef __WXMAC__
#include &lt;OpenGL/OpenGL.h&gt;
#endif

class wxScreenshotMaker
{
public:
	wxScreenshotMaker();
	~wxScreenshotMaker();
	wxBitmap GetScreenshot();
private:
#ifdef __WXMAC__

	int screenWidth;
	int screenHeight;

	CGLContextObj glContextObj;
	int rowSize;
	unsigned char * glBitmapData;
	unsigned char * glRowData;

	void InitOpenGL();
	void GrabGLScreen();
	void SwizzleBitmap();
	void FinalizeOpenGL();
#endif
};
</pre>
<p><strong>wxScreenshotMaker.cpp</strong></p>
<pre class="brush: cpp;">
#include &quot;wxScreenshotMaker.h&quot;
#ifndef __WXMAC__
#include &lt;wx/dcscreen.h&gt;
#else
#include &lt;CoreFoundation/CoreFoundation.h&gt;
#include &lt;ApplicationServices/ApplicationServices.h&gt;
#include &lt;OpenGL/gl.h&gt;
#endif

wxScreenshotMaker::wxScreenshotMaker()
#ifdef __WXMAC__
: screenWidth(0), screenHeight(0), glBitmapData(NULL), glRowData(NULL)
#endif
{
#ifdef __WXMAC__
	wxSize size = wxGetDisplaySize();
	screenWidth = size.GetWidth();
	screenHeight = size.GetHeight();
	InitOpenGL();
#endif
}

wxScreenshotMaker::~wxScreenshotMaker()
{
#ifdef __WXMAC__
	FinalizeOpenGL();
#endif
}

wxBitmap wxScreenshotMaker::GetScreenshot()
{
#ifndef __WXMAC__
	wxScreenDC screenDC;
	wxBitmap bmp(screenDC.GetSize().GetWidth(), screenDC.GetSize().GetHeight());
	wxMemoryDC mdc(bmp);
	mdc.Blit(0, 0, bmp.GetWidth(), bmp.GetHeight(), &amp;screenDC, 0, 0);
	mdc.SelectObject(wxNullBitmap);
	return bmp;
#else
	if(glBitmapData)
	{
		GrabGLScreen();
		wxImage img = wxImage(screenWidth, screenHeight, glBitmapData, true);
		return wxBitmap(img.Copy());
	}
#endif
}

#ifdef __WXMAC__

void wxScreenshotMaker::InitOpenGL()
{
	do
	{
		rowSize = screenWidth * 3;
		rowSize = (rowSize + 2) &amp; ~2;

		glRowData = (unsigned char *)realloc(glRowData, rowSize);
		glBitmapData = (unsigned char *)realloc(glBitmapData, rowSize * screenHeight);

		bzero(glRowData, rowSize);
		bzero(glBitmapData, rowSize * screenHeight);

		CGDirectDisplayID display = CGMainDisplayID();
		CGLPixelFormatObj pixelFormatObj ;
		GLint numPixelFormats;
		CGLPixelFormatAttribute attribs[] =
		{
			kCGLPFAFullScreen,
			kCGLPFADisplayMask,
			(CGLPixelFormatAttribute)0,    /* Display mask bit goes here */
			(CGLPixelFormatAttribute)0
		};

		attribs[2] = (CGLPixelFormatAttribute) CGDisplayIDToOpenGLDisplayMask(display);

		/* Build a full-screen GL context */
		CGLChoosePixelFormat( attribs, &amp;pixelFormatObj, &amp;numPixelFormats );
		if ( pixelFormatObj == NULL ) break;
		CGLCreateContext( pixelFormatObj, NULL, &amp;glContextObj ) ;
		CGLDestroyPixelFormat( pixelFormatObj ) ;
		if ( glContextObj == NULL ) break;
		CGLSetCurrentContext( glContextObj ) ;
		CGLSetFullScreen( glContextObj ) ;
	}
	while(false);
}

void wxScreenshotMaker::FinalizeOpenGL()
{
    CGLSetCurrentContext( NULL );
    CGLClearDrawable( glContextObj );
    CGLDestroyContext( glContextObj );
	free(glRowData);
	free(glBitmapData);
}

void wxScreenshotMaker::GrabGLScreen()
{
	glReadBuffer(GL_FRONT);
    /* Read framebuffer into our bitmap */
    glFinish();
    glPixelStorei(GL_PACK_ALIGNMENT, 3);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glPixelStorei(GL_PACK_SKIP_ROWS, 0);
    glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
    /*
      * Fetch the data in RGB format, matching the bitmap context.
      */
    glReadPixels((GLint)0, (GLint)0, (GLint)screenWidth, (GLint)screenHeight,
		GL_RGB, GL_BYTE, glBitmapData);

	SwizzleBitmap();
}

void wxScreenshotMaker::SwizzleBitmap()
{
	int top, bottom;
	top = 0;
    bottom = screenHeight - 1;
	void * base = glBitmapData;
	void * topP = NULL;
	void * bottomP = NULL;

	while(top &lt; bottom)
	{
		topP = (void *)((top * rowSize) + (intptr_t)base);
        bottomP = (void *)((bottom * rowSize) + (intptr_t)base);

		bcopy( topP, glRowData, rowSize );
        bcopy( bottomP, topP, rowSize );
        bcopy( glRowData, bottomP, rowSize );

        ++top;
        --bottom;
	}
}

#endif
</pre>
<p><strong>Sample</strong></p>
<pre class="brush: cpp;">
wxScreenshotMaker screenshot;
m_Canvas-&gt;SetBitmap(screenshot.GetScreenshot());
m_Canvas-&gt;Refresh();
</pre>
<p><a title="Download source code: Take screenshot with wxWidgets under Mac OS" href="http://wxwidgets.info//wp-content/uploads/2009/05/wxscreenshotmaker.zip">Download wxScreenshotMaker and sample project</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>AxTk: An Accessibility Toolkit for wxWidgets</title>
		<link>http://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/</link>
		<comments>http://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/#comments</comments>
		<pubDate>Thu, 07 May 2009 16:07:54 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[wxWidgets]]></category>

		<guid isPermaLink="false">http://wxwidgets.info/?p=546</guid>
		<description><![CDATA[http://code.google.com/p/axtk/ What is AxTk? AxTk (pronounced Ay Ex Tee Kay) is an open source, C++ add-on for wxWidgets that helps developers create highly accessible, talking applications for users with impaired vision. It may also be useful for other impairments that benefit from a simplified user interface. AxTk features a new menu-based system that is easy [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.google.com/p/axtk/" target="_blank">http://code.google.com/p/axtk/</a></p>
<h4>What is AxTk?</h4>
<p><strong>AxTk</strong> (pronounced Ay Ex Tee Kay) <strong>is an open source, C++ add-on for wxWidgets that helps developers create highly accessible, talking applications for users with impaired vision</strong>. It may also be useful for other impairments that benefit from a simplified user interface.</p>
<p>AxTk features a new menu-based system that is easy to learn and use, in addition to providing adaptation for some existing GUI controls and dialogs. The developer can choose whether to use the menu system, or to adapt an existing application UI, or use a combination of methods.</p>
<p>AxTk is cross-platform (tested so far on Windows XP, Linux and Mac OS X 10.5), and includes text-to-speech classes with the ability to drive SAPI 5, Apple Speech Synthesis Manager, eSpeak, and Cepstral. Other speech engines can be driven by writing additional handlers.</p>
<p>Note that AxTk is a work in progress and the API is subject to change.<br />
<span id="more-546"></span></p>
<h4>What else is AxTk?</h4>
<p>There is an additional, higher-level layer for building applications that help the user maintain &#8216;resource libraries&#8217; for all kinds of files and services. The motivation is to provided simplified access to many resources and activities that are currently spread amongst many applications and web sites. By making this part of AxTk, we increase the chances of providing a really useful, accessible application platform that can be customised in interesting ways for individuals or government sectors. However, this is currently a less well-developed aspect of AxTk and one which you can ignore fo now. The resource demo shows playing of audio albums and reading Epub books (having converted the XHTML content to plain text first).</p>
<h4>What about Braille output?</h4>
<p>Although nothing is specifically coded yet for Braille devices, they could be supported relatively easily by adding handlers to the wxTextToSpeech system, to output to the device instead of a speech engine.</p>
<h4>Why not just use a screen reader?</h4>
<p>Screen readers don&#8217;t have an in-depth knowledge of the application; in theory, an AxTk application can make use of knowledge of its own structure to make speech more helpful and less verbose. Also, an application&#8217;s custom controls are not accessible to screen readers. In additon, AxTk provides an alternative, menu-based user interface that is not so prone to the problems of accessing conventional layouts via a screen reader. Finally, an AxTk application provides support for application-wide colour and text size changes that are harder to achieve in an ad-hoc way.</p>
<h4>Is this anything to do with MSAA (Microsoft Active Accessibility)?</h4>
<p>No; a different approach is used. Although wxWidgets has some basic MSAA support, other ports have no similar support and it may be hard to create a comprehensive cross-platform solution to accessibility based on OS-level accessibility support. It would still leave applications and users at the mercy of particular screen readers and it would not solve other problems that AxTk sets out to solve: visual adaptation, fine control over speech (such as content voicing), customisable shortcut support, and more.</p>
<p>Having said that, work on integrating MSAA equivalents for other platforms would be very welcome, either as part of AxTk or built into wxWidgets.</p>
<h4>How hard is it to use?</h4>
<p>Apart from some housekeeping that you can pinch from the sample, the amount of extra code can be small if you&#8217;re just adapting existing UIs. For example, to make a dialog self-voicing and responsive to visual appearance changes (assuming the dialog&#8217;s controls are in the set of controls currently handled by AxTk):</p>
<pre class="brush: cpp;">
#include &quot;ax/ax_ui_adaptation.h&quot;
...
class MyDialog: public wxDialog
{
    ...
    AxSelfVoicing m_adapter;
};

MyDialog::MyDialog(...)
{
    ...
    m_adapter.Adapt(this);
}
</pre>
<p>If you have controls that are not currently handled by AxTk, you can write adapter classes and also handler classes that detect the appropriate controls and create their adapters.</p>
<p>Using the new menu system is also pretty straightforward as you can see from the sample (mainframe.cpp). There are some new concepts such as AxActivator, which handles menu item activation, but there&#8217;s plenty in common with conventional wxWidgets programming.</p>
<h4>Where and how do I get it?</h4>
<p>AxTk is hosted at Google Code and is available via SVN or as a source tarball or zip file.</p>
<p><a href="http://code.google.com/p/axtk/" target="_blank">http://code.google.com/p/axtk/</a></p>
<p>The discussion group is axtk-dev_at_googlegroups.com.</p>
<p>The documentation is available here:</p>
<p><a href="http://www.anthemion.co.uk/axtk/html/index.html" target="_blank">http://www.anthemion.co.uk/axtk/html/index.html</a></p>
<h4>Are there demos?</h4>
<p>Yes; you can download axsample binaries for Windows, Linux and Mac from:</p>
<p><a href="http://www.anthemion.co.uk/axtk/" target="_blank">http://www.anthemion.co.uk/axtk/</a></p>
<p>You can ignore axresourcesample unless you&#8217;re interested in the higher-level resource library layer mentioned previously.</p>
<h4>Can I contribute?</h4>
<p>Please do! See docs/todo.txt for some of the things left to do. You&#8217;ll probably find things to do yourself. You can supply patches in the AxTk&#8217;s Issue Tracker, or you can ask to be added to the list of project members in order to make changes in SVN.</p>
<h4>What&#8217;s the build system?</h4>
<p>None to speak of. This would make a good contribution. Currently you can use DialogBlocks to generate projects and makefiles for the two samples, which simply include all the AxTk code.</p>
<h4>What is the license?</h4>
<p>The license is the New BSD License, which is very brief, easy to understand, and industry-friendly. Please see license.txt in the source tarball for details.</p>
<p>I hope you find AxTk useful &#8211; or at least, fun to play with.</p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>How to Create Nice About Box in wxWidgets</title>
		<link>http://wxwidgets.info/how-to-create-nice-about-box-in-wxwidgets/</link>
		<comments>http://wxwidgets.info/how-to-create-nice-about-box-in-wxwidgets/#comments</comments>
		<pubDate>Sun, 25 Jan 2009 12:59:33 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Articles]]></category>
		<category><![CDATA[Tutorilas]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info/?p=480</guid>
		<description><![CDATA[After taking a look at wxWidgets samples I noticed that all of them have simple message box instaed of normal about box. However in real applications About dialog is important enough part of GUI. So, in this post I&#8217;m going to tell a bit about creating About boxes for your software. wxWidgets has builf-in API [...]]]></description>
			<content:encoded><![CDATA[<p>After taking a look at wxWidgets samples I noticed that all of them have simple message box instaed of normal about box. However in real applications About dialog is important enough part of GUI.</p>
<p>So, in this post I&#8217;m going to tell a bit about creating About boxes for your software.</p>
<p>wxWidgets has builf-in API for creating &#8220;standard&#8221; dialog boxes. <a title="wxAboutBox function documentation" href="http://docs.wxwidgets.org/stable/wx_dialogfunctions.html#wxaboutbox" target="_blank">wxAboutBox()</a> function is used for displaying About box and <a title="wxAboutDialogInfo class documentation" href="http://docs.wxwidgets.org/stable/wx_wxaboutdialoginfo.html" target="_blank">wxAboutDialogInfo</a> object, which contains all necessary information, should be passed to wxAboutBox() function.<br />
<span id="more-480"></span><br />
wxAboutDialogInfo can contain such information as:</p>
<ul>
<li>Application name</li>
<li>Application icon</li>
<li>Application description</li>
<li>Application version</li>
<li>List of developers</li>
<li>List of artists</li>
<li>List of documentation writers</li>
<li>List of translators</li>
<li>Copyright message</li>
<li>Web-site URL</li>
<li>Application license</li>
</ul>
<p>Only wxGTK port supports native way of displaying application license. On other platforms generic implementation will be used and it is better to create a separate dialog for this purpose.</p>
<p>Well, here is a small example of using API described above:</p>
<pre class="brush: cpp;">
#include
...
// This object will contain all information displayed in About box
wxAboutDialogInfo info;
// Call of AddDeveloper() method adds a record to list of developers
info.AddDeveloper(_(&quot;John Doe&quot;));
// Call of AddDocWriter() method adds a record to list of documentation writers
info.AddDocWriter(_(&quot;Donald Duck&quot;));
// Call of AddArtist() method adds a record to list of artists
info.AddArtist(_(&quot;Scrooge Mc.Duck&quot;));
// Call of AddTranslator() method adds a record to list of translators
info.AddTranslator(_(&quot;Mickey Mouse&quot;));
// This method adds application description.
info.SetDescription(_(&quot;Sample wxWidgets application for testing wxAboutBox() function.&quot;));
// This method sets application version string
info.SetVersion(wxT(&quot;1.0.0 beta 1&quot;));
// SetName() method sets application name displayed in About box.
// It is better to pass wxApp::GetAppName() to this method
info.SetName(wxTheApp-&gt;GetAppName());
// Sets application Web-site URL
info.SetWebSite(wxT(&quot;http://wxwidgets.info&quot;));
// Sets the icon which will be displayed in About box.
info.SetIcon(wxICON(wxICON_AAA));
// Sets application license string. Only wxGTK port has native way of
// displaying application license. All other ports will use generic way for this purpose.
info.SetLicence(_(&quot;Public Domain&quot;));

// At last, we can display about box
wxAboutBox(info);
</pre>
<p><a href="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_1.png"><img src="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_1.png" alt="wxAboutBox and wxAboutDialogLinfo usage sample - Collapsed" title="wxAboutBox and wxAboutDialogLinfo usage sample - Collapsed" width="379" height="318" class="alignnone size-full wp-image-481" /></a><br />
As you can see, there are several collapsible panels placed to dialog. The amount of these panels changes according theinformation specified in wxAboutDialogInfo object.<br />
After expanding collapsible panels we&#8217;ll get something like this:<br />
<a href="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_2.png"><img src="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_2.png" alt="wxAboutBox Sample - Expanded" title="wxAboutBox Sample - Expanded" width="379" height="450" class="alignnone size-full wp-image-482" /></a><br />
Looks nice but a bit boring. So I decided to create more fancy About box. In fact, I was inspired by <a href="http://wxforum.shadonet.com/viewtopic.php?t=21498">this post at wxForum</a> but used my own way and also made some changes.</p>
<p><strong>wxMozillaLikeAboutBoxDialog.h</strong></p>
<pre class="brush: cpp;">
#ifndef _WXMOZILLALIKEABOUTBOXDIALOG_H_
#define _WXMOZILLALIKEABOUTBOXDIALOG_H_

#include &quot;wx/gbsizer.h&quot;
#include &quot;wx/statline.h&quot;

#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE wxCAPTION|wxSYSTEM_MENU|wxCLOSE_BOX|wxTAB_TRAVERSAL
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE _(&quot;About &quot;)
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME ID_WXMOZILLALIKEABOUTBOXDIALOG
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE wxSize(400, 300)
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION wxDefaultPosition

class wxMozillaLikeAboutBoxDialog: public wxDialog
{
    DECLARE_DYNAMIC_CLASS( wxMozillaLikeAboutBoxDialog )
    DECLARE_EVENT_TABLE()
public:
    wxMozillaLikeAboutBoxDialog();
    wxMozillaLikeAboutBoxDialog( wxWindow* parent,
		wxWindowID id = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME,
		const wxString&amp; caption = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE,
		const wxPoint&amp; pos = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION,
		const wxSize&amp; size = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE,
		long style = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE );

    bool Create( wxWindow* parent,
		wxWindowID id = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME,
		const wxString&amp; caption = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE,
		const wxPoint&amp; pos = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION,
		const wxSize&amp; size = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE,
		long style = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE );

    ~wxMozillaLikeAboutBoxDialog();

    void Init();
    void CreateControls();

    wxString GetAppName() const { return m_AppName ; }
    void SetAppName(wxString value) { m_AppName = value ; }

    wxString GetVersion() const { return m_Version ; }
    void SetVersion(wxString value) { m_Version = value ; }

    wxString GetCopyright() const { return m_Copyright ; }
    void SetCopyright(wxString value) { m_Copyright = value ; }

    wxString GetCustomBuildInfo() const { return m_CustomBuildInfo ; }
    void SetCustomBuildInfo(wxString value) { m_CustomBuildInfo = value ; }

    wxBitmap GetBitmapResource( const wxString&amp; name );
    wxIcon GetIconResource( const wxString&amp; name );

    enum wxBuildInfoFormat
    {
        wxBUILDINFO_SHORT,
        wxBUILDINFO_LONG
    };

    static wxString GetBuildInfo(wxBuildInfoFormat format);

    void SetHeaderBitmap(const wxBitmap &amp; value);
    void ApplyInfo();

private:
    wxPanel* m_ContentPanel;
    wxStaticBitmap* m_HeaderStaticBitmap;
    wxStaticText* m_AppNameStaticText;
    wxStaticText* m_CopyrightStaticText;
    wxStaticText* m_VersionStaticText;
    wxStaticText* m_BuildInfoStaticText;
    wxString m_AppName;
    wxString m_Version;
    wxString m_Copyright;
    wxString m_CustomBuildInfo;
    /// Control identifiers
    enum {
        ID_WXMOZILLALIKEABOUTBOXDIALOG = 10000,
        ID_ContentPanel = 10001
    };
};

wxBitmap wxGetBitmapFromMemory(const void * data, size_t length);

#endif
</pre>
<p><strong>wxMozillaLikeAboutBoxDialog.cpp</strong></p>
<pre class="brush: cpp;">
#include &quot;wx/wxprec.h&quot;

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include &quot;wx/wx.h&quot;
#endif

#include &quot;wxMozillaLikeAboutBoxDialog.h&quot;
#include &lt;wx/mstream.h&gt;

IMPLEMENT_DYNAMIC_CLASS( wxMozillaLikeAboutBoxDialog, wxDialog )

BEGIN_EVENT_TABLE( wxMozillaLikeAboutBoxDialog, wxDialog )
END_EVENT_TABLE()

wxMozillaLikeAboutBoxDialog::wxMozillaLikeAboutBoxDialog()
{
    Init();
}

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

bool wxMozillaLikeAboutBoxDialog::Create( wxWindow* parent, wxWindowID id, const wxString&amp; caption, const wxPoint&amp; pos, const wxSize&amp; size, long style )
{
    SetExtraStyle(wxWS_EX_BLOCK_EVENTS);
    wxDialog::Create( parent, id, caption, pos, size, style );
    CreateControls();
    if (GetSizer())
    {
        GetSizer()-&gt;SetSizeHints(this);
    }
    Centre();
    return true;
}

wxMozillaLikeAboutBoxDialog::~wxMozillaLikeAboutBoxDialog()
{
}

void wxMozillaLikeAboutBoxDialog::Init()
{
    m_ContentPanel = NULL;
    m_HeaderStaticBitmap = NULL;
    m_AppNameStaticText = NULL;
    m_CopyrightStaticText = NULL;
    m_VersionStaticText = NULL;
    m_BuildInfoStaticText = NULL;
}

void wxMozillaLikeAboutBoxDialog::CreateControls()
{
    wxMozillaLikeAboutBoxDialog* itemDialog1 = this;
    wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
    itemDialog1-&gt;SetSizer(itemBoxSizer2);
    m_ContentPanel = new wxPanel( itemDialog1, ID_ContentPanel, wxDefaultPosition, wxSize(200, 300), wxNO_BORDER|wxTAB_TRAVERSAL );
    m_ContentPanel-&gt;SetBackgroundColour(wxColour(255, 255, 255));
    itemBoxSizer2-&gt;Add(m_ContentPanel, 0, wxGROW, 0);
    wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxVERTICAL);
    m_ContentPanel-&gt;SetSizer(itemBoxSizer4);
    m_HeaderStaticBitmap = new wxStaticBitmap( m_ContentPanel, wxID_STATIC, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer4-&gt;Add(m_HeaderStaticBitmap, 0, wxGROW, 0);
    wxGridBagSizer* itemGridBagSizer6 = new wxGridBagSizer(0, 0);
    itemGridBagSizer6-&gt;AddGrowableRow(2);
    itemGridBagSizer6-&gt;AddGrowableRow(3);
    itemGridBagSizer6-&gt;SetEmptyCellSize(wxSize(10, 20));
    itemBoxSizer4-&gt;Add(itemGridBagSizer6, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 10);
    m_AppNameStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(&quot;&quot;), wxDefaultPosition, wxDefaultSize, 0 );
    m_AppNameStaticText-&gt;SetForegroundColour(wxColour(255, 0, 0));
    m_AppNameStaticText-&gt;SetFont(wxFont(28, wxSWISS, wxNORMAL, wxNORMAL, false, wxT(&quot;Arial Narrow&quot;)));
    itemGridBagSizer6-&gt;Add(m_AppNameStaticText, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALIGN_LEFT|wxALIGN_BOTTOM|wxLEFT|wxRIGHT|wxTOP, 5);
    wxStaticText* itemStaticText8 = new wxStaticText( m_ContentPanel, wxID_STATIC, _(&quot;version&quot;), wxDefaultPosition, wxDefaultSize, 0 );
    itemStaticText8-&gt;SetForegroundColour(wxColour(192, 192, 192));
    itemStaticText8-&gt;SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT(&quot;Arial&quot;)));
    itemGridBagSizer6-&gt;Add(itemStaticText8, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_RIGHT|wxALIGN_TOP|wxLEFT|wxBOTTOM, 5);
    m_CopyrightStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(&quot;&quot;), wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE );
    itemGridBagSizer6-&gt;Add(m_CopyrightStaticText, wxGBPosition(2, 0), wxGBSpan(1, 2), wxGROW|wxGROW|wxALL, 5);
    m_VersionStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(&quot;&quot;), wxDefaultPosition, wxDefaultSize, 0 );
    m_VersionStaticText-&gt;SetForegroundColour(wxColour(192, 192, 192));
    m_VersionStaticText-&gt;SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT(&quot;Arial&quot;)));
    itemGridBagSizer6-&gt;Add(m_VersionStaticText, wxGBPosition(1, 1), wxGBSpan(1, 1), wxALIGN_LEFT|wxALIGN_TOP|wxLEFT|wxRIGHT|wxBOTTOM, 5);
    m_BuildInfoStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(&quot;&quot;), wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE );
    itemGridBagSizer6-&gt;Add(m_BuildInfoStaticText, wxGBPosition(3, 0), wxGBSpan(1, 2), wxGROW|wxGROW|wxALL, 5);
    itemBoxSizer4-&gt;Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
    wxStaticLine* itemStaticLine13 = new wxStaticLine( itemDialog1, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
    itemBoxSizer2-&gt;Add(itemStaticLine13, 0, wxGROW, 0);
    wxStdDialogButtonSizer* itemStdDialogButtonSizer14 = new wxStdDialogButtonSizer;
    itemBoxSizer2-&gt;Add(itemStdDialogButtonSizer14, 0, wxALIGN_RIGHT|wxALL, 5);
    wxButton* itemButton15 = new wxButton( itemDialog1, wxID_OK, _(&quot;&amp;OK&quot;), wxDefaultPosition, wxDefaultSize, 0 );
    itemStdDialogButtonSizer14-&gt;AddButton(itemButton15);
    itemStdDialogButtonSizer14-&gt;Realize();
    m_BuildInfoStaticText-&gt;SetLabel(wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBUILDINFO_LONG));
}
</pre>
<p>And here is a method which returns a string with build information</p>
<pre class="brush: cpp;">
wxString wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBuildInfoFormat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == wxBUILDINFO_LONG)
    {
#if defined(__WXMSW__)
        wxbuild &lt;&lt; _T(&quot;-Windows&quot;);
#elif defined(__WXMAC__)
        wxbuild &lt;&lt; _T(&quot;-Mac&quot;);
#elif defined(__UNIX__)
        wxbuild &lt;&lt; _T(&quot;-Linux&quot;);
#endif

#if wxUSE_UNICODE
        wxbuild &lt;&lt; _T(&quot;-Unicode&quot;);
#else
        wxbuild &lt;&lt; _T(&quot;-ANSI&quot;);
#endif // wxUSE_UNICODE
    }
	wxbuild &lt;&lt; _(&quot; build&quot;);
    return wxbuild;
}
</pre>
<p>This method utilizes <code>wxVERSION_STRING</code> macro which returns a string with wxWidgets version.</p>
<pre class="brush: cpp;">
wxBitmap wxGetBitmapFromMemory(const void * data, size_t length)
{
	wxMemoryInputStream stream(data, length);
	return wxBitmap(stream, wxBITMAP_TYPE_ANY);
}

void wxMozillaLikeAboutBoxDialog::SetHeaderBitmap(const wxBitmap &amp; value)
{
    m_HeaderStaticBitmap-&gt;SetBitmap(value);
}
</pre>
<p><code>wxGetBitmapFromMemory()</code> method creates wxBitmap from memory buffer using <code>wxMemoryInputStream</code>.</p>
<pre class="brush: cpp;">
void wxMozillaLikeAboutBoxDialog::ApplyInfo()
{
	wxASSERT_MSG(m_HeaderStaticBitmap-&gt;GetBitmap().IsOk(), _(&quot;Header bitmap for About box is empty&quot;));
	SetTitle(wxString::Format(wxT(&quot;%s %s&quot;), _(&quot;About&quot;), m_AppName.GetData()));
    m_AppNameStaticText-&gt;SetLabel(m_AppName);
    m_VersionStaticText-&gt;SetLabel(m_Version);
    m_CopyrightStaticText-&gt;SetLabel(m_Copyright);
	wxString buildInfo;
	if(m_CustomBuildInfo.IsEmpty())
	{
		buildInfo = wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBUILDINFO_LONG);
	}
	else
	{
		buildInfo = m_CustomBuildInfo;
	}
	m_BuildInfoStaticText-&gt;SetLabel(buildInfo);
	int labelWidth = m_HeaderStaticBitmap-&gt;GetSize().GetWidth() - 20;
	m_VersionStaticText-&gt;Wrap(labelWidth);
	m_CopyrightStaticText-&gt;Wrap(labelWidth);
	m_BuildInfoStaticText-&gt;Wrap(labelWidth);
	m_ContentPanel-&gt;Layout();
	m_ContentPanel-&gt;GetSizer()-&gt;Fit(m_ContentPanel);
    GetSizer()-&gt;Fit(this);
	Centre();
}
</pre>
<p>ApplyInfo method sets labels of all wxStaticText controls, applies header image and then fixes the layout according to changes.</p>
<p>Here is an example of usage of our Mozilla-like About box:</p>
<p><strong>wxAboutBoxTestMainFrame.cpp</strong></p>
<pre class="brush: cpp;">
void wxAboutBoxTestMainFrame::OnMOZILLALIKEABOUTBOXClick( wxCommandEvent&amp; event )
{
	// Create About box
	wxMozillaLikeAboutBoxDialog * dlg = new wxMozillaLikeAboutBoxDialog(this);
	// Set application name
	dlg-&gt;SetAppName(wxTheApp-&gt;GetAppName());
	// Set application version
	dlg-&gt;SetVersion(wxT(&quot;1.0.0 b1&quot;));
	// Set copyright message
	dlg-&gt;SetCopyright(wxString::Format(wxT(&quot;%c %i %s&quot;),
		(wxChar) 0x00A9, wxDateTime::Now().GetYear(),
		_(&quot;Volodymir (T-Rex) Tryapichko. All rights reserved. Please contact author if you have any copyright-related questions.&quot;)));
	// Set build info message. This is optional step. If you don't specify build info message then
	// default one will be used
	dlg-&gt;SetCustomBuildInfo(wxString::Format(wxT(&quot;%s. %s&quot;),
		wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxMozillaLikeAboutBoxDialog::wxBUILDINFO_LONG).GetData(),
		_(&quot;Compiled by T-Rex personally <img src='http://wxwidgets.info/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> &quot;)));
	// Set header bitmap
	dlg-&gt;SetHeaderBitmap(wxGetBitmapFromMemory(header_png, sizeof(header_png)));
	// Apply changes
	dlg-&gt;ApplyInfo();
	// Show dialog
	dlg-&gt;ShowModal();
	// Destroy dialog
	dlg-&gt;Destroy();
}
</pre>
<p><code>header_png</code> is a char array created from PNG image using Bin2C utility:</p>
<pre class="brush: cpp;">
unsigned char header_png[] ={
	0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,
	...
};
</pre>
<p>And here is a screenshot of what we get after compiling the source code listed above:<br />
<a href="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_3.png"><img src="http://wxwidgets.info//wp-content/uploads/2009/01/aboutbox_in_wxwidgets_3.png" alt="Mozilla-like About Box in wxWidgets" title="Mozilla-like About Box in wxWidgets" width="310" height="401" class="alignnone size-full wp-image-483" /></a><br />
<a href="http://wxwidgets.info//wp-content/uploads/2009/01/wxaboutboxtest.7z" title="Doanload Source Code: Mozilla-like About Box for wxWidgets">Download the source code for this tutorial</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/how-to-create-nice-about-box-in-wxwidgets/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Классы редактирования даты и времени в ячейках wxGrid</title>
		<link>http://wxwidgets.info/klassy-redaktirovaniya-daty-i-vremeni-v-yachejkax-wxgrid/</link>
		<comments>http://wxwidgets.info/klassy-redaktirovaniya-daty-i-vremeni-v-yachejkax-wxgrid/#comments</comments>
		<pubDate>Tue, 16 Dec 2008 15:31:21 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Controls]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxGrid]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=12</guid>
		<description><![CDATA[Александр (sandy) Илюшенко любезно предоставил статью о создании редактора ячеек wxGrid: Данная статья посвящена внедрению в грид ячеек для редактирования дат и времени. Сам котрол для дат существует &#8211; wxDatePickerCtrl. Остается вопрос, как прикрутить его к гриду. Я позаимствовал готовый контрол, за что спасибо автору, и переделал его под свои нужды, а именно, мне нужно [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sandyhip.net.ua">Александр (sandy) Илюшенко</a> любезно предоставил статью о создании редактора ячеек wxGrid:</p>
<p>Данная статья посвящена внедрению в грид ячеек для редактирования дат и времени. Сам котрол для дат существует &#8211; <code>wxDatePickerCtrl</code>. Остается вопрос, как прикрутить его к гриду.<br />
<span id="more-12"></span><br />
Я позаимствовал <a href="http://coding.moris.org/archives/2004/04/22/wxwidgets-time-control/">готовый контрол</a>, за что спасибо автору, и переделал его под свои нужды, а именно, мне нужно было время в 24-часовом формате. Реализацию самого эдитора времени, который я назвал Бcode>wxTimeCtrl</code> я приводить в статье не буду, т.к. много текста. Но обращу Ваше внимание на то, что макрос <code>GRID_TIME_EDITOR</code>, объявленный в <code>wxTextCtrl.h</code>, нужен исключительно для использования контрола в гриде, потому, если нужно будет использовать его в ином качестве, закоментируйте строку <code>#define GRID_TIME_EDITOR</code>.<br />
<b>wxGridCell.h</b></p>
<pre class="brush: cpp;">
#pragma once
#include &lt;wx/grid.h&gt;
#include &lt;wx/generic/gridctrl.h&gt;
#include &lt;wx/datectrl.h&gt;
#include &lt;wx/dateevt.h&gt;
#include &quot;wxTimeCtrl.h&quot;

/*** Renderers ***/
/*** Date Renderer ***/
class wxGridCellDateRenderer: public wxGridCellDateTimeRenderer {
public:
    wxGridCellDateRenderer(wxString sOutFormat = wxT(&quot;%d.%m.%Y&quot;),
                           wxString sInputFormat = wxT(&quot;%Y%m%d&quot;));
};
/*** Time Renderer ***/
class wxGridCellTimeRenderer: public wxGridCellDateTimeRenderer {
public:
    wxGridCellTimeRenderer();
};

/*** Editors ***/
/*** Date Editor ***/
class wxGridCellDateEditor: public wxGridCellEditor {
    const wxString m_Format;
    wxString m_startValue;

    void SetDateValue();
    wxDatePickerCtrl* DateCtrl() const;
protected:
//    void OnDateChanged(wxDateEvent&amp; event);
    DECLARE_NO_COPY_CLASS(wxGridCellDateEditor)
public:
    wxGridCellDateEditor(const wxString&amp; format=wxT(&quot;%d.%m.%Y&quot;));
    ~wxGridCellDateEditor();

    void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
    void PaintBackground(const wxRect&amp; rectCell, wxGridCellAttr* attr);
    void SetSize(const wxRect&amp; rectOrig);
    void BeginEdit(int row, int col, wxGrid* grid);
    bool EndEdit(int row, int col, wxGrid* grid);
    bool IsAcceptedKey(wxKeyEvent&amp; event);
    void Reset();
    wxString GetValue() const;
    wxGridCellEditor* Clone() const;
};

/*** Time Editor ***/
class wxGridCellTimeEditor: public wxGridCellEditor {
    wxString m_startValue;

    void SetTimeValue();
    wxTimeCtrl* TimeCtrl() const;
protected:
    DECLARE_NO_COPY_CLASS(wxGridCellTimeEditor)
public:
    wxGridCellTimeEditor();
    ~wxGridCellTimeEditor();

    void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
    void PaintBackground(const wxRect&amp; rectCell, wxGridCellAttr* attr);
    void SetSize(const wxRect&amp; rectOrig);
    void BeginEdit(int row, int col, wxGrid* grid);
    bool EndEdit(int row, int col, wxGrid* grid);
    bool IsAcceptedKey(wxKeyEvent&amp; event);
    void Reset();
    wxString GetValue() const;
    wxGridCellEditor* Clone() const;
};
</pre>
<p>Теперь несколько слов о предоставленных классах:</p>
<ul>
<li><code>wxGridCellDateRenderer</code> - нужен для отображения дат, в том виде, как это нужно программисту, что определяется параметрами <code>wxString sOutFormat</code> - это формат собственно отображения даты, например, <code>wxT("%d.%m.%Y")</code>, т.е. ДД.ММ.ГГГГ, и <code>wxString sInputFormat</code> - это формат, в котором помещается в грид строка, содержащая дату</li>
<li><code>wxGridCellTimeRenderer</code> - нужен для отображения времени. Пераметров он не имеет, принимает и отображает время в формате ЧЧ:ММ:СС</li>
<li><code>wxGridCellDateEditor</code> - это редактор дат, который параметром принимает формат даты</li>
<li><code>wxGridCellTimeEditor</code> - это редактор времени</li>
</ul>
<p><b>wxGridCell.cpp</b></p>
<pre class="brush: cpp;">
#include &quot;wxGridCell.h&quot;

/*** Renderers ***/
/*** Date Renderer ***/
wxGridCellDateRenderer::wxGridCellDateRenderer(wxString sOutFormat,
                                               wxString sInputFormat)
    : wxGridCellDateTimeRenderer(sOutFormat,sInputFormat)
{
}
/*** Time Renderer ***/
wxGridCellTimeRenderer::wxGridCellTimeRenderer()
    : wxGridCellDateTimeRenderer(wxT(&quot;%H:%M:%S&quot;),wxT(&quot;%H:%M:%S&quot;))
{
}

/*** Editors ***/
/*** Date Editor ***/
wxGridCellDateEditor::wxGridCellDateEditor(const wxString&amp; format)
    : wxGridCellEditor(),
    m_Format(format),
    m_startValue(wxT(&quot;&quot;))
{
}

wxGridCellDateEditor::~wxGridCellDateEditor()
{
    if (m_control) {
        m_control-&gt;Destroy();
        m_control = NULL;
    }
}

void wxGridCellDateEditor::Create(wxWindow* parent,
                                  wxWindowID id,
                                  wxEvtHandler* evtHandler)
{
    m_control = new wxDatePickerCtrl(parent, id,
        wxDefaultDateTime, wxDefaultPosition, wxDefaultSize,
        wxDP_SPIN | wxDP_SHOWCENTURY);
    wxGridCellEditor::Create(parent, id, evtHandler);
}

wxDatePickerCtrl* wxGridCellDateEditor::DateCtrl() const
{
    return (wxDatePickerCtrl*)m_control;
}

void wxGridCellDateEditor::PaintBackground(const wxRect&amp; rectCell,
                                           wxGridCellAttr* attr)
{
    wxGridCellEditor::PaintBackground(rectCell, attr);
}

void wxGridCellDateEditor::SetSize(const wxRect&amp; rectOrig)
{
    wxRect rect(rectOrig);
#if defined(__WXGTK__)
    if (rect.x != 0) {
        rect.x+=1;
        rect.y+=1;
        rect.width-=1;
        rect.height-=1;
    }
#elif defined(__WXMSW__)
//    rect.x-=2;
//    rect.y-=2;
//    rect.width+=2;
    rect.height+=2;
#else
    int extra_x=(rect.x&gt;2)?2:1;
    int extra_y=(rect.y&gt;2)?2:1;
    #if defined(__WXMOTIF__)
        extra_x*=2;
        extra_y*=2;
    #endif
    rect.SetLeft(wxMax(0, rect.x-extra_x));
    rect.SetTop(wxMax(0, rect.y-extra_y));
    rect.SetRight(rect.GetRight()+2*extra_x);
    rect.SetBottom(rect.GetBottom()+2*extra_y);
#endif
    wxGridCellEditor::SetSize(rect);
}

void wxGridCellDateEditor::SetDateValue()
{
    wxDateTime tmp;
    if (!tmp.ParseFormat(m_startValue.c_str(),m_Format.c_str())) {
        tmp.SetToCurrent();
    }
    DateCtrl()-&gt;SetValue(tmp);
}

void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));

    m_startValue = grid-&gt;GetTable()-&gt;GetValue(row, col);
    SetDateValue();
    DateCtrl()-&gt;SetFocus();
}

bool wxGridCellDateEditor::EndEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));
    bool changed = false;
    wxDateTime tmp = DateCtrl()-&gt;GetValue();
    wxString s=tmp.Format(m_Format.c_str());

    if (s != m_startValue)
        changed = true;
    if (changed)
        grid-&gt;GetTable()-&gt;SetValue(row, col, s);

    m_startValue = wxEmptyString;
    tmp.SetToCurrent();
    DateCtrl()-&gt;SetValue(tmp);

    return changed;
}

bool wxGridCellDateEditor::IsAcceptedKey(wxKeyEvent&amp; event)
{
    if (wxGridCellEditor::IsAcceptedKey(event)) {
        int keycode=event.GetKeyCode();
        if (keycode&lt;128 &amp;&amp; (wxIsdigit(keycode) || keycode=='.')) {
            return true;
        }
    }
    return false;
}

void wxGridCellDateEditor::Reset()
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));
    SetDateValue();
}

wxString wxGridCellDateEditor::GetValue() const
{
    wxDateTime tmp = DateCtrl()-&gt;GetValue();
    wxString s=tmp.Format(m_Format.c_str());
    return s;
}

wxGridCellEditor* wxGridCellDateEditor::Clone() const
{
    return new wxGridCellDateEditor(m_Format);
}

/*** Time Editor ***/
wxGridCellTimeEditor::wxGridCellTimeEditor()
    : wxGridCellEditor(),
    m_startValue(wxT(&quot;&quot;))
{
}

wxGridCellTimeEditor::~wxGridCellTimeEditor()
{
    if (m_control) {
        m_control-&gt;Destroy();
        m_control = NULL;
    }
}

void wxGridCellTimeEditor::Create(wxWindow* parent,
                                  wxWindowID id,
                                  wxEvtHandler* evtHandler)
{
    m_control = new wxTimeCtrl(parent, id);
    wxGridCellEditor::Create(parent, id, evtHandler);
}

void wxGridCellTimeEditor::PaintBackground(const wxRect&amp; rectCell,
                                           wxGridCellAttr* attr)
{
    wxGridCellEditor::PaintBackground(rectCell, attr);
}

wxTimeCtrl* wxGridCellTimeEditor::TimeCtrl() const
{
    return (wxTimeCtrl*)m_control;
}

void wxGridCellTimeEditor::SetSize(const wxRect&amp; rectOrig)
{
    wxRect rect(rectOrig);
#if defined(__WXGTK__)
    if (rect.x != 0) {
        rect.x+=1;
        rect.y+=1;
        rect.width-=1;
        rect.height-=1;
    }
#elif defined(__WXMSW__)
//    rect.x-=2;
//    rect.y-=2;
//    rect.width+=2;
    rect.height+=2;
#else
    int extra_x=(rect.x&gt;2)?2:1;
    int extra_y=(rect.y&gt;2)?2:1;
    #if defined(__WXMOTIF__)
        extra_x*=2;
        extra_y*=2;
    #endif
    rect.SetLeft(wxMax(0, rect.x-extra_x));
    rect.SetTop(wxMax(0, rect.y-extra_y));
    rect.SetRight(rect.GetRight()+2*extra_x);
    rect.SetBottom(rect.GetBottom()+2*extra_y);
#endif
    wxGridCellEditor::SetSize(rect);
}

void wxGridCellTimeEditor::SetTimeValue()
{
    wxString s=m_startValue;
    if (s.IsEmpty()) {
        s=wxTimeCtrl::GetCurrentTime();
    }
    TimeCtrl()-&gt;SetValue(s);
}

void wxGridCellTimeEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));

    m_startValue = grid-&gt;GetTable()-&gt;GetValue(row, col);
    SetTimeValue();
    TimeCtrl()-&gt;SetFocus();
}

bool wxGridCellTimeEditor::EndEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));

    bool changed = false;
    wxString s=TimeCtrl()-&gt;GetValue();

    if (s != m_startValue)
        changed = true;
    if (changed)
        grid-&gt;GetTable()-&gt;SetValue(row, col, s);

    m_startValue = wxEmptyString;
    TimeCtrl()-&gt;SetValue(m_startValue);

    return changed;
}

bool wxGridCellTimeEditor::IsAcceptedKey(wxKeyEvent&amp; event)
{
    if (wxGridCellEditor::IsAcceptedKey(event)) {
        int keycode=event.GetKeyCode();
        if (keycode&lt;128 &amp;&amp; (wxIsdigit(keycode) || keycode=='.')) {
            return true;
        }
    }
    return false;
}

void wxGridCellTimeEditor::Reset()
{
    wxASSERT_MSG(m_control, wxT(&quot;The wxGridCellEditor must be created first!&quot;));
    SetTimeValue();
}

wxString wxGridCellTimeEditor::GetValue() const
{
    return TimeCtrl()-&gt;GetValue();
}

wxGridCellEditor* wxGridCellTimeEditor::Clone() const
{
    return new wxGridCellTimeEditor();
}
</pre>
<p>Как это все работает? Вот пример, в котором я использую wxGridCtrl из <a href="http://wxwidgets.info/change_behavior_of_wxgrid_ru">предыдущей статьи</a>.<br />
<b>Example.cpp</b></p>
<pre class="brush: cpp;">
#include &quot;wxGridCtrl.h&quot;
#include &quot;wxGridCell.h&quot;
/* ... */
    m_pGrid = new wxGridCtrl(this, IDC_CHILD_GRID);
    m_pGrid-&gt;CreateGrid(0,0);
    m_pGrid-&gt;SetRowLabelSize(20);
    m_pGrid-&gt;SetColLabelSize(20);
    m_pGrid-&gt;DisableDragRowSize();
    m_pGrid-&gt;SetDefaultCellBackgroundColour(wxColour(128,128,128));

    m_pGrid-&gt;AppendCols(3);

    wxGridCellAttr* pDateAttr = new wxGridCellAttr;
    pDateAttr-&gt;SetBackgroundColour(wxColour(255,255,255));
    pDateAttr-&gt;SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE);
    pDateAttr-&gt;SetRenderer(new wxGridCellDateRenderer());
    pDateAttr-&gt;SetEditor(new wxGridCellDateEditor(wxT(&quot;%Y%m%d&quot;)));

    wxGridCellAttr* pTimeAttr = new wxGridCellAttr;
    pTimeAttr-&gt;SetBackgroundColour(wxColour(255,255,255));
    pTimeAttr-&gt;SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE);
    pTimeAttr-&gt;SetRenderer(new wxGridCellTimeRenderer());
    pTimeAttr-&gt;SetEditor(new wxGridCellTimeEditor());

    m_pGrid-&gt;SetColLabelValue(0,wxT(&quot;DateCells&quot;));
    m_pGrid-&gt;SetColLabelValue(1,wxT(&quot;TimeCells&quot;));
    m_pGrid-&gt;SetColAttr(0,pDateAttr);
    m_pGrid-&gt;SetColAttr(1,pTimeAttr);
    m_pGrid-&gt;Refresh();
/* ... */
</pre>
<p>Вот так в результате будет выглядеть наше приложение:<br />
<img src="http://wxwidgets.info//wp-content/uploads/2008/12/custom-gridcelleditor.png" alt="Классы редактирования даты и времени в ячейках wxGrid" title="Классы редактирования даты и времени в ячейках wxGrid" width="447" height="151" class="alignnone size-full wp-image-11" /></p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/klassy-redaktirovaniya-daty-i-vremeni-v-yachejkax-wxgrid/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Переопределение поведения стандартных компонентов. Делаем свой wxGrid</title>
		<link>http://wxwidgets.info/pereopredelenie-povedeniya-standartnyx-komponentov-delaem-svoj-wxgrid/</link>
		<comments>http://wxwidgets.info/pereopredelenie-povedeniya-standartnyx-komponentov-delaem-svoj-wxgrid/#comments</comments>
		<pubDate>Tue, 16 Dec 2008 14:14:52 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Controls]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxGrid]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=3</guid>
		<description><![CDATA[Александр (sandy) Илюшенко любезно предоставил статью о том, как настроить класc wxGrid под свои нужды: Захотелось мне как-то, чтобы в гриде были не номера строк, а маркер. К тому же очень хотелось, чтобы незаполненное пространство грида было не белым, а, примерно, как на рисунке ниже. Навеяно это было в основном аналогичными и другими классами, предоставляемыми [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sandyhip.net.ua">Александр (sandy) Илюшенко</a> любезно предоставил статью о том, как настроить класc <code>wxGrid</code> под свои нужды:</p>
<p>Захотелось мне как-то, чтобы в гриде были не номера строк, а маркер.<br />
К тому же очень хотелось, чтобы незаполненное пространство грида было не белым, а, примерно, как на рисунке ниже.<br />
<img src="http://wxwidgets.info//wp-content/uploads/2008/12/custom-gridctrl.png" alt="Делаем собственный wxGrid" title="Делаем собственный wxGrid" width="336" height="204" class="alignnone size-full wp-image-6" /><br />
Навеяно это было в основном аналогичными и другими классами, предоставляемыми MFC. Тут же и вспомнилось, что подобные классы также прдоставляют очень полезные методы для хранения дополнительных не отображаемых данных, такие как <code>SetData()</code> или <code>GetData()</code>.<br />
<span id="more-3"></span><br />
Вначеле даже была мысль, использовать для этого <code>wxGrid::SetRowLabelValue()</code>, с учетом того, что перерисовку лэйбла я собирался переопределить. Но, покурив исходники грида, я обнаружил, что в функциях <code>bool InsertRows(int pos = 0, int numRows = 1, bool updateLabels = true)</code> и <code>bool DeleteRows(int pos = 0, int numRows = 1, bool updateLabels = true)</code>, третий аргумент не используется. А это значит, что при вставке или удалении строки у меня все посыпется. К тому же неизвестно, как в будущем этот аргумент будет использоваться.</p>
<p>Потому не нашел я ничего лучшего, как переопределить грид и снабдить его необходимыми функциями.<br />
<b>wxGridCtrl.h</b></p>
<pre class="brush: cpp;">
#pragma once
#include &lt;wx/wx.h&gt;
#include &lt;wx/grid.h&gt;
#include &lt;map&gt;

class wxGridCtrl: public wxGrid {
    std::map&lt;int,int&gt; m_mRowData;
    DECLARE_DYNAMIC_CLASS(wxGridCtrl)
protected:
    virtual void DrawRowLabel(wxDC&amp; dc, int row);
    virtual void OnLabelLeftClick(wxGridEvent&amp; event);
    virtual void OnSelectCell(wxGridEvent&amp; event);

    DECLARE_EVENT_TABLE();
public:
    wxGridCtrl();
    wxGridCtrl(wxWindow* parent, const long&amp; id);
/// Override
    bool CreateGrid(int numRows, int numCols,
                    wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells);
    bool AppendRows(int numRows = 1, bool updateLabels = true);
    bool InsertRows(int pos = 0, int numRows = 1, bool updateLabels = true);
    bool DeleteRows(int pos = 0, int numRows = 1, bool updateLabels = true);
/// New
    void SetRowData(const int&amp; row, const int&amp; data);
    int GetRowData(const int&amp; row);
};
</pre>
<p>Теперь, как это все реализовано:</p>
<ul>
<li><code>DrawRowLabel</code> &#8211; это функция, реализующая перерисовку лэйбла грида, а именно, рисующая маркер в лэйбле.</li>
<li><code>OnLabelLeftClick</code> &#8211; усталавливает визуальный курсор в первую колонку, выделенной строки.</li>
<li>Это понадобилось для того, чтобы маркер не оставался на предыдущей строке, когда кликаем по следующей.</li>
<li><code>OnSelectCell</code> &#8211; функция, без которой я не смог обойтись, чтобы все хорошо работало:)</li>
</ul>
<p>Несколько слов о контейнере std::map<int,int> m_mRowData:</p>
<p>Этот контейнер, имеющий ключем номер строки, и содержит значения, присваиваемые функцией <code>SetRowData</code>. И, чтобы данные соответствовали строками после создания грида, добаления, вставки и удаления записей, пришлось переопределить соответсвующие функции: <code>CreateGrid, AppendRows, InsertRows, DeleteRows</code>. Получить данные для соответствующей строки, как и сами догадываетесь, можно функцией <code>GetRowData</code>.<br />
<b>wxGridCtrl.cpp</b></p>
<pre class="brush: cpp;">

#include &quot;wxGridCtrl.h&quot;
#include &quot;cursor.xpm&quot;

IMPLEMENT_DYNAMIC_CLASS(wxGridCtrl, wxGrid);

wxGridCtrl::wxGridCtrl()
{
}

wxGridCtrl::wxGridCtrl(wxWindow* parent, const long&amp; id):
    wxGrid(parent,id,wxDefaultPosition,wxDefaultSize)
{
}

BEGIN_EVENT_TABLE(wxGridCtrl, wxGrid)
    EVT_GRID_LABEL_LEFT_CLICK(wxGridCtrl::OnLabelLeftClick)
    EVT_GRID_SELECT_CELL(wxGridCtrl::OnSelectCell)
END_EVENT_TABLE();

void wxGridCtrl::DrawRowLabel(wxDC&amp; dc, int row)
{
    if (GetRowHeight(row)&lt;=0 || m_rowLabelWidth&lt;=0)
        return;
    wxRect rect;
#ifdef __WXGTK20__
    rect.SetX(1);
    rect.SetY(GetRowTop(row)+1);
    rect.SetWidth(m_rowLabelWidth-2);
    rect.SetHeight(GetRowHeight(row)-2);
    CalcScrolledPosition(0, rect.y, NULL, &amp;rect.y);
    wxWindowDC *win_dc=(wxWindowDC*)&amp;dc;
    wxRendererNative::Get().DrawHeaderButton(win_dc-&gt;m_owner, dc, rect, 0);
#else
    int rowTop=GetRowTop(row),
        rowBottom=GetRowBottom(row)-1;
    dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID));
    dc.DrawLine(m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom);
    dc.DrawLine(0, rowTop, 0, rowBottom);
    dc.DrawLine(0, rowBottom, m_rowLabelWidth, rowBottom);
    dc.SetPen(*wxWHITE_PEN);
    dc.DrawLine(1, rowTop, 1, rowBottom);
    dc.DrawLine(1, rowTop, m_rowLabelWidth - 1, rowTop);
#endif
    if (row==GetGridCursorRow()) {
        dc.DrawBitmap(wxBitmap(cursor_xpm),0,GetRowTop(row),true);
    }
}

void wxGridCtrl::OnLabelLeftClick(wxGridEvent&amp; event)
{
    if (event.GetRow() != -1) {
        SetGridCursor(event.GetRow(),0);
    }
    event.Skip();
}

void wxGridCtrl::OnSelectCell(wxGridEvent&amp; event)
{
    GetGridRowLabelWindow()-&gt;Refresh();
    event.Skip();
}

/// Override
bool wxGridCtrl::CreateGrid(int numRows, int numCols,
                            wxGrid::wxGridSelectionModes selmode)
{
    bool bCreate=wxGrid::CreateGrid(numRows,numCols,selmode);
    if (bCreate) {
        for (int i=0;i&lt;numRows;i++) {
            m_mRowData[i]=0;
        }
    }
    return bCreate;
}

bool wxGridCtrl::AppendRows(int numRows, bool updateLabels)
{
    int nRow=GetNumberRows();
    bool bAdd=wxGrid::AppendRows(numRows,updateLabels);
    if (bAdd) {
        while (nRow&lt;GetNumberRows()) {
            m_mRowData[nRow]=0;
            nRow++;
        }
    }
    return bAdd;
}

bool wxGridCtrl::InsertRows(int pos, int numRows, bool updateLabels)
{
    std::map&lt;int,int&gt; mRow;
    for (int i=0;i&lt;GetNumberRows();i++) {
        mRow[i]=GetRowData(i);
    }
    bool bIns=wxGrid::InsertRows(pos,numRows,updateLabels);
    if (bIns) {
        for (int i=pos+numRows;i&lt;GetNumberRows();i++) {
            m_mRowData[i]=mRow[i-numRows];
        }
        for (int i=pos;i&lt;pos+numRows;i++) {
            m_mRowData[i]=0;
        }
    }
    return bIns;
}

bool wxGridCtrl::DeleteRows(int pos, int numRows, bool updateLabels)
{
    std::map&lt;int,int&gt; mRow;
    for (int i=0;i&lt;GetNumberRows();i++) {
        mRow[i]=GetRowData(i);
    }
    bool bDel=wxGrid::DeleteRows(pos,numRows,updateLabels);
    if (bDel) {
        for (int i=pos;i&lt;GetNumberRows();i++) {
            m_mRowData[i]=mRow[i+numRows];
        }
    }
    return bDel;
}

/// New
void wxGridCtrl::SetRowData(const int&amp; row, const int&amp; data)
{
    if (row&lt;GetNumberRows()) {
        m_mRowData[row]=data;
    }
}

int wxGridCtrl::GetRowData(const int&amp; row)
{
    int data=0;
    if (row&lt;GetNumberRows() &amp;&amp; m_mRowData.find(row)!=m_mRowData.end()) {
        data=m_mRowData[row];
    }
    return data;
}

Что касается цветов, это я не включил в класс. Мало ли как кому нужно будет?

Пример использования примерно следующий:

Exmple.cpp

#include &quot;wxGridCtrl.h&quot;
/* ... */
    m_pGrid = new wxGridCtrl(this, wxID_ANY);
    m_pGrid-&gt;CreateGrid(0,0);
    m_pGrid-&gt;SetRowLabelSize(20);
    m_pGrid-&gt;SetColLabelSize(20);
    m_pGrid-&gt;DisableDragRowSize();
    m_pGrid-&gt;SetDefaultCellBackgroundColour(wxColour(128,128,128));

    m_pGrid-&gt;AppendCols(3);
    m_pGrid-&gt;SetColLabelValue(0,wxT(&quot;A&quot;));
    m_pGrid-&gt;SetColLabelValue(1,wxT(&quot;B&quot;));
    m_pGrid-&gt;SetColLabelValue(2,wxT(&quot;C&quot;));

    wxGridCellAttr* pAttr = new wxGridCellAttr;
    pAttr-&gt;SetBackgroundColour(wxColour(255,255,255));

    wxGridCellAttr* pAttrBool = new wxGridCellAttr;
    pAttrBool-&gt;SetBackgroundColour(wxColour(255,255,255));
    pAttrBool-&gt;SetRenderer(new wxGridCellBoolRenderer());
    pAttrBool-&gt;SetEditor(new wxGridCellBoolEditor());
    pAttrBool-&gt;SetAlignment(wxALIGN_CENTRE,wxALIGN_CENTRE);

    m_pGrid-&gt;SetColAttr(0,pAttr);
    m_pGrid-&gt;SetColAttr(1,pAttr);
    m_pGrid-&gt;SetColAttr(2,pAttrBool);
/* ... */
</pre>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/pereopredelenie-povedeniya-standartnyx-komponentov-delaem-svoj-wxgrid/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Отправка SMS под wxWinCE с помощью CE MAPI</title>
		<link>http://wxwidgets.info/send_sms_with_ce_mapi_under_wxwince/</link>
		<comments>http://wxwidgets.info/send_sms_with_ce_mapi_under_wxwince/#comments</comments>
		<pubDate>Wed, 06 Aug 2008 18:13:11 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxWinCE]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=73</guid>
		<description><![CDATA[Появилась необходимость программно отправлять SMS с телефона под управлением Windows Mobile. Решил не изобретать велосипед и поиспользовать MAPI для этих целей. После недолгих поисков набрел на эту статью на CodeProject. Немного переделал код для использования с wxWidgets. Вот что получилось wxSendSMS.h #ifndef _WX_SMS_SENDER_H #define _WX_SMS_SENDER_H #include &#60;wx/wx.h&#62; #include &#60;atlbase.h&#62; #include &#60;cemapi.h&#62; #include &#60;mapiutil.h&#62; class wxSMSSender [...]]]></description>
			<content:encoded><![CDATA[<p>Появилась необходимость программно отправлять SMS с телефона под управлением Windows Mobile. Решил не изобретать велосипед и поиспользовать MAPI для этих целей. После недолгих поисков набрел на эту <a href="http://www.codeproject.com/KB/mobile/SMS_CEMPI.aspx">статью</a> на CodeProject. Немного переделал код для использования с wxWidgets. Вот что получилось<br />
<span id="more-73"></span><br />
<strong>wxSendSMS.h</strong></p>
<pre class="brush: cpp;">
#ifndef _WX_SMS_SENDER_H
#define _WX_SMS_SENDER_H

#include &lt;wx/wx.h&gt;
#include &lt;atlbase.h&gt;
#include &lt;cemapi.h&gt;
#include &lt;mapiutil.h&gt;

class wxSMSSender
{
public:
	virtual bool Send(const wxString &amp; from,
		const wxString &amp; to, const wxString &amp; text) = 0;
};

class wxSMSSenderMAPI : public wxSMSSender
{
	HRESULT GetSMSFolder(const CComPtr&lt;IMsgStore&gt;&amp; msgStore,
		CComPtr&lt;IMAPIFolder&gt;&amp; folder);
	HRESULT GetSMSMsgStore(const CComPtr&lt;IMAPISession&gt;&amp; session,
		CComPtr&lt;IMsgStore&gt;&amp; msgStore);
	HRESULT SendSMSMessage(const CComPtr&lt;IMAPISession&gt;&amp; session,
		LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage);
public:
	virtual bool Send(const wxString &amp; from,
		const wxString &amp; to, const wxString &amp; text);
};

#endif
</pre>
<p><strong>wxSendSMS.cpp</strong></p>
<pre class="brush: cpp;">
#include &quot;wxSMSSender.h&quot;

HRESULT wxSMSSenderMAPI::GetSMSFolder(
			const CComPtr&lt;IMsgStore&gt;&amp; msgStore,
			CComPtr&lt;IMAPIFolder&gt;&amp; folder)
{
	// Now get the Drafts folder.
	SPropTagArray propDefaultFolder;
	propDefaultFolder.cValues = 1;
	propDefaultFolder.aulPropTag[0] = PR_CE_IPM_DRAFTS_ENTRYID;

	ULONG	values;
	LPSPropValue propVals;
	HRESULT hr = msgStore-&gt;GetProps(&amp;propDefaultFolder,
		MAPI_UNICODE, &amp;values, &amp;propVals);
	if (FAILED(hr))
	{
		return hr;
	}

	SBinary&amp; eidDrafts = propVals-&gt;Value.bin;

	hr = msgStore-&gt;OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb,
		NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&amp;folder);
	return hr;
}

HRESULT wxSMSSenderMAPI::GetSMSMsgStore(
			const CComPtr&lt;IMAPISession&gt;&amp; session,
			CComPtr&lt;IMsgStore&gt;&amp; msgStore)
{
	// first we get the msgstores table from the session
	CComPtr&lt;IMAPITable&gt; table;
	HRESULT hr = session-&gt;GetMsgStoresTable(MAPI_UNICODE, &amp;table);
	if (FAILED(hr))
	{
		return FALSE;
	}

	// next we loop over the message stores opening each msgstore and
	// getting its name to see if the name matches SMS.
	// If it does then we break out of the loop
	while (TRUE)
	{
		SRowSet* pRowSet = NULL;
		hr = table-&gt;QueryRows(1, 0, &amp;pRowSet);

		// If we failed to query the
		// rows then we need to break
		if (FAILED(hr)) break;

		// if we got no rows back then just exit the loop
		//remembering to set an error
		if (pRowSet-&gt;cRows == 1)
		{
			ASSERT(pRowSet-&gt;aRow[0].lpProps-&gt;ulPropTag == PR_ENTRYID);
			SBinary&amp; blob = pRowSet-&gt;aRow[0].lpProps-&gt;Value.bin;
			hr = session-&gt;OpenMsgStore(NULL, blob.cb,
				(LPENTRYID)blob.lpb, NULL, 0, &amp;msgStore);
		}
		else
		{
			hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
		}

		// now remember to free the row set
		FreeProws(pRowSet);
		if (FAILED(hr)) break;

		// now get the display name property from the
		// message store to compare it against the name
		// 'SMS'
		SPropTagArray props;
		props.cValues = 1;
		props.aulPropTag[0] = PR_DISPLAY_NAME;

		ULONG values;
		SPropValue* pProps = NULL;
		hr = msgStore-&gt;GetProps(&amp;props, MAPI_UNICODE, &amp;values, &amp;pProps);
		if (FAILED(hr) || values != 1)
		{
			break;
		}

		// if the name matches SMS then break and as
		// hr == S_OK the current MsgStore smart pointer
		// will correctly be set.
		if (_tcsicmp(pProps[0].Value.lpszW, _T(&quot;SMS&quot;)) == 0)
		{
			break;
		}
	}

	// if we failed for some reason then we clear out
	// the msgstore smartpointer and return the error.
	if (FAILED(hr))
	{
		msgStore.Release();
	}

	return hr;
}

HRESULT wxSMSSenderMAPI::SendSMSMessage(const CComPtr&lt;IMAPISession&gt;&amp; session,
			LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
{

	// now get the SMS message store
	CComPtr&lt;IMsgStore&gt; msgStore;
	HRESULT hr = GetSMSMsgStore(session, msgStore);
	if (FAILED(hr)) return hr;

	CComPtr&lt;IMAPIFolder&gt; folder;
	hr = GetSMSFolder(msgStore, folder);
	if (FAILED(hr)) return hr;

	CComPtr&lt;IMessage&gt; message;
	hr = folder-&gt;CreateMessage(NULL, 0 ,&amp;message);
	if (FAILED(hr)) return hr;

	// set the recipients
	// set up the required fields for a recipient
	SPropValue propRecipient[3];
	// it is vital we clear the property structure
	// as there are fields we do not use but MAPI seems
	// to be sentative to them.
	ZeroMemory(&amp;propRecipient, sizeof(propRecipient));
	// set the recipient type which coul be to, cc, bcc
	// but ehre must at least be a to field
	propRecipient[0].ulPropTag = PR_RECIPIENT_TYPE;
	propRecipient[0].Value.l = MAPI_TO;

	// we set the type of address to sms instead of
	// smtp
	propRecipient[1].ulPropTag = PR_ADDRTYPE;
	propRecipient[1].Value.lpszW = _T(&quot;SMS&quot;);
	// we finally set the email address to the
	// phone number of the person we are sending the message
	// to
	propRecipient[2].ulPropTag = PR_EMAIL_ADDRESS;
	propRecipient[2].Value.lpszW = (LPWSTR)lpszTo;

	// set the addrlist to point to the properties
	ADRLIST adrlist;
	adrlist.cEntries = 1;
	adrlist.aEntries[0].cValues = 3;
	adrlist.aEntries[0].rgPropVals = (LPSPropValue)(&amp;propRecipient);

	// finally modify the recipients of the message
	hr = message-&gt;ModifyRecipients(MODRECIP_ADD, &amp;adrlist);
	if (FAILED(hr)) return hr;

	// now we set the additional properties for the
	// message
	SPropValue props[4];

	//note how we zero out the contents of the
	// structure as MAPI is sensative to the
	// contents of other fields we do not use.
	ZeroMemory(&amp;props, sizeof(props));

	// first set the subject of the message
	// as the sms we are going to send
	props[0].ulPropTag = PR_SUBJECT;
	props[0].Value.lpszW = (LPWSTR)lpszMessage;

	// next set the senders email address to
	// the phone number of the person we are
	// sending the message to
	props[1].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
	props[1].Value.lpszW = (LPWSTR)lpszFrom;

	// finally and most importantly tell mapi
	// this is a sms message in need of delivery
	props[2].ulPropTag = PR_MSG_STATUS;
	props[2].Value.ul = MSGSTATUS_RECTYPE_SMS;

    props[3].ulPropTag = PR_MESSAGE_FLAGS;
    props[3].Value.ul = MSGFLAG_FROMME | MSGFLAG_UNSENT;

	hr = message-&gt;SetProps(sizeof(props) / sizeof(SPropValue),
		(LPSPropValue)&amp;props, NULL);
	if (FAILED(hr)) return hr;

	// having set all the required fields we can now
	// pass the message over to the msgstore transport
	// to be delivered.
	hr = message-&gt;SubmitMessage(0);
	if (FAILED(hr)) return hr;

	return FALSE;
}

bool wxSMSSenderMAPI::Send(const wxString &amp; from,
		const wxString &amp; to, const wxString &amp; text)
{
	do
	{
		HRESULT hr = MAPIInitialize(NULL);
		if (FAILED(hr)) break;
		CComPtr&lt;IMAPISession&gt; mapiSession;
		hr = MAPILogonEx(0 ,NULL, NULL, 0, &amp;mapiSession);
		if (FAILED(hr)) break;
		bool result = SUCCEEDED(SendSMSMessage(mapiSession,
			from.GetData(), to.GetData(), text.GetData()));
		mapiSession-&gt;Logoff(0, 0, 0);
		mapiSession.Release();
		MAPIUninitialize();
		return result;
	}
	while(false);
	return false;
}
</pre>
<p>А пользоваться этим всем очень просто:</p>
<pre class="brush: cpp;">
wxSMSSenderMAPI sender;
if(!sender.Send(m_FromTextCtrl-&gt;GetValue(),
	m_ToTextCtrl-&gt;GetValue(), m_SMSTextCtrl-&gt;GetValue()))
{
	wxMessageBox(_(&quot;Не могу отправить SMS!&quot;));
}
</pre>
<p><a href="http://wxwidgets.info//wp-content/uploads/2009/01/wxsendsms.7z" title="Скачать исходник: Отправка SMS с помощью CE MAPI">Исходный код примера отправки SMS с помощью CE MAPI для Windows Mobile</a><br />
Скриншот тестового приложения:<br />
<div id="attachment_74" class="wp-caption alignnone" style="width: 339px"><a href="http://wxwidgets.info//wp-content/uploads/2009/01/send_sms_with_ce_mapi_1.png"><img src="http://wxwidgets.info//wp-content/uploads/2009/01/send_sms_with_ce_mapi_1.png" alt="Отправка SMS с помощью CE MAPI - Скришот под Windows Mobile" title="Отправка SMS с помощью CE MAPI - Скриншот" width="329" height="527" class="size-full wp-image-74" /></a><p class="wp-caption-text">Отправка SMS с помощью CE MAPI - Скришот под Windows Mobile</p></div></p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/send_sms_with_ce_mapi_under_wxwince/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Draw Gradient Buttons (wxWidgets Way)</title>
		<link>http://wxwidgets.info/howto_draw_gradient_buttons/</link>
		<comments>http://wxwidgets.info/howto_draw_gradient_buttons/#comments</comments>
		<pubDate>Sun, 03 Aug 2008 14:16:23 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Components]]></category>
		<category><![CDATA[Controls]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Articles]]></category>
		<category><![CDATA[wxButton]]></category>
		<category><![CDATA[wxWinCE]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=59</guid>
		<description><![CDATA[Several days ago I found How to draw gradient buttons post at Native Mobile blog. Looks nice but I think that using GradienFill() is not very convenient because you need to fill all these TRIVERTEX structures. wxWidgets provides more convenient way of drawing gradients by using wxDC::GradientFillLinear(). Here is how it can be done in [...]]]></description>
			<content:encoded><![CDATA[<p>Several days ago I found <a href="http://nativemobile.blogspot.com/2008/07/how-to-draw-gradient-buttons.html">How to draw gradient buttons</a> post at <a href="http://nativemobile.blogspot.com">Native Mobile</a> blog. Looks nice but I think that using <code>GradienFill()</code> is not very convenient because you need to fill all these <code>TRIVERTEX</code> structures. wxWidgets provides more convenient way of drawing gradients by using <code>wxDC::GradientFillLinear()</code>. Here is how it can be done in wxWidgets:<br />
<span id="more-59"></span></p>
<pre class="brush: cpp;">
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);
</pre>
<p>After some experiments I created a simple owner-drawn button control which utilizes this way of drawing:<br />
<strong>wxGradientButton.h</strong></p>
<pre class="brush: cpp;">
/////////////////////////////////////////////////////////////////////////////
// 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 &amp; label = wxEmptyString,
const wxPoint&amp; pos = wxDefaultPosition,
const wxSize&amp; size = wxSize(100, 100),
long style = wxSIMPLE_BORDER);

/// Creation
bool Create(wxWindow* parent,
wxWindowID id = ID_WXGRADIENTBUTTON,
const wxString &amp; label = wxEmptyString,
const wxPoint&amp; pos = wxDefaultPosition,
const wxSize&amp; 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&amp; event );

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

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

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

/// wxEVT_LEFT_UP event handler for ID_WXGRADIENTBUTTON
void OnLeftUp( wxMouseEvent&amp; 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&amp; name );

/// Retrieves icon resources
wxIcon GetIconResource( const wxString&amp; 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_
</pre>
<p><strong>wxGradientButton.cpp</strong></p>
<pre class="brush: 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 &quot;wx/wx.h&quot;.
#include &quot;wx/wxprec.h&quot;

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include &quot;wx/wx.h&quot;
#endif

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

#include &quot;wxGradientButton.h&quot;
#include &lt;wx/dcbuffer.h&gt;

////@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 &amp; label, const wxPoint&amp; pos,
const wxSize&amp; size, long style)
{
Init();
Create(parent, id, label, pos, size, style);
}

/*!
* wxGradientButton creator
*/

bool wxGradientButton::Create(wxWindow* parent,
wxWindowID id, const wxString &amp; label, const wxPoint&amp; pos,
const wxSize&amp; 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-&gt;SetForegroundColour(wxColour(255, 255, 255));
this-&gt;SetBackgroundColour(wxColour(0, 0, 0));
this-&gt;SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT(&quot;Tahoma&quot;)));
////@end wxGradientButton content construction
}

/*!
* Should we show tooltips?
*/

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

/*!
* Get bitmap resources
*/

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

/*!
* Get icon resources
*/

wxIcon wxGradientButton::GetIconResource( const wxString&amp; 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, &amp;labelSize.x, &amp;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&amp; 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&amp; event )
{
if(GetCapture() != this)
{
CaptureMouse();
Refresh();
}
}

/*!
* wxEVT_LEFT_UP event handler for ID_WXGRADIENTBUTTON
*/

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

/*!
* wxEVT_ERASE_BACKGROUND event handler for ID_WXGRADIENTBUTTON
*/

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

/*!
* wxEVT_SIZE event handler for ID_WXGRADIENTBUTTON
*/

void wxGradientButton::OnSize( wxSizeEvent&amp; event )
{
Refresh();
}
</pre>
<p>After you compile the code, you&#8217;ll get someting like this:</p>
<div id="attachment_61" class="wp-caption alignnone" style="width: 303px"><a href="http://wxwidgets.info//wp-content/uploads/2009/01/howto_draw_gradient_buttons_1.png"><img class="size-full wp-image-61" title="How to draw gradient buttons in C++/wxWidgets - Screenshot for Windows Mobile" src="http://wxwidgets.info//wp-content/uploads/2009/01/howto_draw_gradient_buttons_1.png" alt="wxGradientButton under Windows Mobile" width="293" height="455" /></a><p class="wp-caption-text">wxGradientButton under Windows Mobile</p></div>
<p>You can download the source code of sample application which uses wxGradientButton control <a title="Download wxGradienButton source code" href="http://wxwidgets.info//wp-content/uploads/2009/01/wxgradientbutton.7z">here</a>. Sample application compiles for Win32 and Windows Mobile 6 platforms.</p>
<p>Read discussion of wxGradientButton at <a href="http://wxforum.shadonet.com/viewtopic.php?p=88479">wxForum</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/howto_draw_gradient_buttons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Реализация Job Queue на wxWidgets (+исходник)</title>
		<link>http://wxwidgets.info/job_queue_by_eran_ru/</link>
		<comments>http://wxwidgets.info/job_queue_by_eran_ru/#comments</comments>
		<pubDate>Sat, 10 May 2008 14:23:36 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxThread]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=104</guid>
		<description><![CDATA[При работе с потоками часто приходится делать кучу однотипных задач: создавать класс, производный от wxThread, реализовывать метод Entry() для этого класса, синхронизацию с главным потоком и т.д. Eran, автор CodeLite IDE поделился кодом класса JobQueue, который реализует пул потоков и позволяет выполнять задачи в фоновом режиме. class MyJob : public Job { public: MyJob(){} ~MyJob(){} [...]]]></description>
			<content:encoded><![CDATA[<p>При работе с потоками часто приходится делать кучу однотипных задач: создавать класс, производный от <code>wxThread</code>, реализовывать метод <code>Entry()</code> для этого класса, синхронизацию с главным потоком и т.д.</p>
<p>Eran, автор <a href="http://codelite.org">CodeLite IDE</a> поделился кодом класса JobQueue, который реализует пул потоков и позволяет выполнять задачи в фоновом режиме.</p>
<pre class="brush: cpp;">
class MyJob : public Job {
public:
        MyJob(){}
        ~MyJob(){}

        // Implement your processing code here
        void Process() {
                wxPrintf(wxT(&quot;Just doing my work...n&quot;));
        };
};

// define some custom job with progress report
class MyJobWithProgress : public Job {
public:
        MyJobWithProgress(wxEvtHandler *parent) : Job(parent){}
        ~MyJobWithProgress(){}

        // Implement your processing code here
        void Process() {
                // report to parent that we are at stage 0
                Post(0, wxT(&quot;Stage Zero&quot;));
                // do the work
                wxPrintf(wxT(&quot;Just doing my work...n&quot;));
                // report to parent that we are at stage 1
                Post(1, wxT(&quot;Stage Zero Completed!&quot;));
        };
};

// somewhere in your code, start the JobQueue
// for the demo we use pool of size 5
JobQueueSingleton::Instance()-&gt;Start(5);

// whenever you want to process MyJob(), just create new instance of MyJob() and add it to the JobQueue
JobQueueSingleton::Instance()-&gt;AddJob( new MyJob() );

// at shutdown stop the job queue and release all its resources
JobQueueSingleton::Instance()-&gt;Stop();
JobQueueSingleton::Release();

// OR, you can use JobQueue directly without the JobQueueSingleton wrapper class
// so you could have multiple instances of JobQueue
</pre>
<p>Главный поток получает уведомления таким вот образом:</p>
<pre class="brush: cpp;">
// in the event table
EVT_COMMAND(wxID_ANY, wxEVT_CMD_JOB_STATUS, MyFrame::OnJobStatus)

void MyFrame::OnJobStatus(wxCommandEvent &amp;e)
{
    wxString msg;
    msg &lt;&lt; wxT(&quot;Job progress: Stage: &quot;) &lt;&lt; e.GetInt() &lt;&lt; wxT(&quot; Message: &quot;) &lt;&lt; e.GetString();
    wxLogMessage(msg)
}
</pre>
<p><a href="http://wxforum.shadonet.com/download.php?id=673" title="Скачать исходник: Job Queue for wxWidgets">Скачать исходный код</a><br />
<a href="http://wxforum.shadonet.com/viewtopic.php?t=19315" title="Обсуждение wxJobQueue на wxForum">Читать обсуждение на wxForum</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/job_queue_by_eran_ru/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Организуем доступ к базам данных SQLite при разработке кросс-платформенных приложений на C++/wxWidgets &#8211; Часть 2 &#8211; wxActiveRecordGenerator (wxARG)</title>
		<link>http://wxwidgets.info/wx_accessing_sqlite_2/</link>
		<comments>http://wxwidgets.info/wx_accessing_sqlite_2/#comments</comments>
		<pubDate>Thu, 07 Jun 2007 15:37:54 +0000</pubDate>
		<dc:creator>T-Rex</dc:creator>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[DatabaseLayer]]></category>
		<category><![CDATA[SQLite]]></category>
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://wxwidgets.info//?p=233</guid>
		<description><![CDATA[Продолжение статьи, рассказывающей о работе с базами данных в wxWidgets. Первую чать статьи можно почитать здесь. В предыдущей части мы рассмотрели настройку проекта, сборку дополнительных библиотек и создание графического интерфейса приложения. В этой часит рассказывается как создать классы бизнес-логики с помощью утилиты wxARG и о том, как эти классы использовать в своем проекте. Устанавливаем утилиту [...]]]></description>
			<content:encoded><![CDATA[<p>Продолжение статьи, рассказывающей о работе с базами данных в wxWidgets. <a href="http://wxwidgets.info/wx_accessing_sqlite" title="Организуем доступ к базам данных SQLite при разработке кросс-платформенных приложений на C++/wxWidgets - Часть I - Шаблон проекта и GUI">Первую чать статьи можно почитать здесь</a>.<br />
В предыдущей части мы рассмотрели настройку проекта, сборку дополнительных библиотек и создание графического интерфейса приложения. В этой часит рассказывается как создать классы бизнес-логики с помощью утилиты wxARG и о том, как эти классы использовать в своем проекте.<br />
<span id="more-233"></span><br />
<a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_16.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_16.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>Устанавливаем утилиту <b>wxARG</b>, копируем из папки, куда была установлена <b>wxARG</b> два файла <b>wxActiveRecord.h</b> и <b>wxActiveRecord.cpp</b> в папку <b>SQLiteTest/wxActiveRecord</b><br />
Здесь хотелось бы отметить, что файлы <b>wxActiveRecord.h</b> и <b>wxActiveRecord.cpp</b> из последней верчии <b>wxARG-1.2.0-rc2</b> отлично собрались вместе с тестовым проектом, хотя сама утилита работала нестабильно, из-за чего мне пришлось установить поверх нее более раннюю версию <b>wxARG-1.1.0</b>, которая работала нормально.<br />
Итак, запускаем <b>wxARG</b> и выбираем пункт меню <b>File -> Connect to database</b>, в диалоговом окне <b>Database Connection</b> указываем тип базы данных <b>SQLite3 Database</b> и путь к файлу <b>addressbook.db</b>, созданному нашим тестовым приложением. Жмем <b>Connect</b></p>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_17.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_17.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>В диалоговом окне Tables выбираем таблицы <b>groups</b> и <b>persons</b>. Жмем OK</p>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_18.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_18.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>После этого в левой части главного окна <b>wxARG</b> появится список с выбранными таблицами.<br />
Каждая таблица нашей базы данных имеет ключевое поле <b>id</b>, поэтому на вкладке <b>Properties</b> для каждой таблицы в поле <b>ID Field</b> мы указываем поле <b>id</b> и включаем маркер <b>Overwrite (delete custom stuff)</b>.</p>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_19.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_19.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>В нашей базе данных между таблицами <b>groups</b> и <b>persons</b> существует связь один-ко-многим, т.е. каждая группа может содержать множество контактов адресной книги. Переходим на вкладку <b>Relations</b>.<br />
Для таблицы <b>groups</b>:<br />
- Жмем кнопку <b>Has Many</b><br />
- В диалоговом окне <b>Relation Properties</b> указываем название таблицы со стороны &#8220;многие&#8221;, в нашем случае это таблица <b>persons</b><br />
- Указываем поле внешнего ключа в таблице <b>persons</b>. В нашем случае это <b>groupid</b><br />
Значения остальных параметров можно оставить без изменений.</p>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_20.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_20.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>Для таблицы <b>persons</b>:</p>
<ul>
<li>Жмем кнопку <b>Belongs To</b></li>
<li>В диалоговом окне <b>Relation Properties</b> указываем название таблицы со стороны &#8220;один&#8221;, в нашем случае это таблица <b>groups</b></li>
<li>Указываем поле внешнего ключа в таблице <b>persons</b>. В нашем случае это <b>groupid</b></li>
</ul>
<p>Значения остальных параметров можно оставить без изменений.<br />
<a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_21.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_21.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a><br />
В поле <b>Output Dir</b> указываем папку проекта <b>SQLiteTest</b> и жмем <b>Generate</b><br />
После всех проделанных операций, в папке с проектом <b>SQLiteTest</b> должны появиться файлы:</p>
<ul>
<li>Group.h</li>
<li>Group.cpp</li>
<li>Person.h</li>
<li>Person.cpp</li>
</ul>
<p>Эти файлы нам необходимо добавить в проект.<br />
<a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_22.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_22.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p>После того, как все необходимые файлы добавлены, можно приступать к внесению изменений в исходный код.<br />
Для начала в файле <b>wxActiveRecord.h</b> не обходимо раскомментировать  строку с макросом <code class="inlinecode">AR_USE_SQLITE</code>, который указывает, что мы используем СУБД <b>SQLite</b>. В файле <b>wxActiveRecord.cpp</b> необходимо исправить путь к заголовочному файлу <b>wxActiveRecord.h</b></p>
<p><b>wxActiveRecord.h</b></p>
<pre class="brush: cpp;">
...
// COMMENT OUT THE ONES YOU DON'T USE

//#define AR_USE_POSTGRESQL
#define AR_USE_SQLITE
//#define AR_USE_MYSQL
//#define AR_USE_FIREBIRD
...
</pre>
<p><b>wxActiveRecord.cpp</b></p>
<pre class="brush: cpp;">
...

#include &quot;wxActiveRecord.h&quot;

//begin WX_ACTIVE_RECORD
...
</pre>
<p>Теперь, если посмотреть на исходный код файлов, сгенерированных утилитой <b>wxARG</b>, то можно заметить, что каждая пара .h/.cpp файлов содержит описание и реализацию трех классов:</p>
<ul>
<li>XXX – класс таблицы</li>
<li>XXXRow – класс типизированной записи таблицы</li>
<li>XXXRowSet – типизированный список записей</li>
</ul>
<p>где XXX – название таблицы.<br />
И после всего сказанного, вооружившись полученными знаниями, продолжим наш &#8220;happy coding&#8221;.</p>
<p><b>SQLiteTestApp.h</b></p>
<pre class="brush: cpp;">
...
#include &lt;SqliteDatabaseLayer.h&gt;
#include &quot;Group.h&quot;
#include &quot;Person.h&quot;

class SQLiteTestApp : public wxApp
{
	Group * m_GroupTable;
	Person * m_PersonTable;
	...
public:
	...
	Group * GetGroupTable();
	Person * GetPersonTable();
};
...
</pre>
<p><b>SQLiteTestApp.cpp</b></p>
<pre class="brush: cpp;">
...
int SQLiteTestApp::OnExit()
{
	wxDELETE(m_PersonTable);
	wxDELETE(m_GroupTable);
	...
}

bool SQLiteTestApp::ConnectToDatabase()
{
	...
	try
	{
		m_GroupTable = new Group(wxGetApp().GetDatabase(), wxT(&quot;groups&quot;));
		m_PersonTable = new Person(wxGetApp().GetDatabase(), wxT(&quot;persons&quot;));
	}
	catch(DatabaseLayerException &amp; e)
	{
		wxActiveRecord::ProcessException(e);
	}
	return true;
}
...
Group * SQLiteTestApp::GetGroupTable()
{
	return m_GroupTable;
}

Person * SQLiteTestApp::GetPersonTable()
{
	return m_PersonTable;
}
</pre>
<p>Итак, мы добавили новые члены класса <code class="inlinecode">SQLiteTestApp</code>, обеспечивающие доступ к таблицам базы данных и теперь можно приступать к созданию GUI.<br />
Дистрибутив wxWidgets содержит несколько десятков свободно распространяемых иконок, которые мы можем использовать в нашем приложении. Создадим папку <b>SQLiteTest/art</b> и скопируем в нее файлы </p>
<ul>
<li>$(WXWIN)/art/addbookm.xpm</li>
<li>$(WXWIN)/art/delbookm.xpm</li>
<li>$(WXWIN)/art/new.xpm</li>
<li>$(WXWIN)/art/delete.xpm</li>
</ul>
<p><b>SQLiteTestMainFrame.h</b></p>
<pre class="brush: cpp;">
#ifndef _SQLITE_TEST_MAINFRAME_H
#define _SQLITE_TEST_MAINFRAME_H

#include &lt;wx/wx.h&gt;
#include &lt;wx/toolbar.h&gt;
#include &lt;wx/listbox.h&gt;
#include &lt;wx/listctrl.h&gt;
#include &lt;wx/html/htmlwin.h&gt;

class SQLiteTestMainFrame : public wxFrame
{
	wxListBox * m_GroupsListBox;
	wxListView * m_PersonsListView;
	wxHtmlWindow * m_PersonInfoPanel;
	void CreateControls();
	wxToolBar * CreateToolBar();
public:
	SQLiteTestMainFrame();
	bool Create(wxWindow * parent, wxWindowID id, const wxString &amp; title);

	DECLARE_EVENT_TABLE()
	void OnExit(wxCommandEvent &amp; event);
};

#endif
</pre>
<p><b>SQLiteTestMainFrame.cpp</b></p>
<pre class="brush: cpp;">
...
#include &lt;wx/splitter.h&gt;
#include &quot;new.xpm&quot;
#include &quot;delete.xpm&quot;
#include &quot;addbookm.xpm&quot;
#include &quot;delbookm.xpm&quot;

enum
{
	ID_GROUPS_LISTBOX = 10001,
	ID_PERSONS_LISTCTRL,
	ID_PERSON_INFO_PANEL,
	ID_ADD_GROUP,
	ID_DELETE_GROUP,
	ID_ADD_PERSON,
	ID_DELETE_PERSON
};
...
void SQLiteTestMainFrame::CreateControls()
{
	wxMenuBar * menuBar = new wxMenuBar;
	SetMenuBar(menuBar);

	wxMenu * fileMenu = new wxMenu;
	fileMenu-&gt;Append(wxID_EXIT, _(&quot;Exit\tAlt+F4&quot;));

	menuBar-&gt;Append(fileMenu, _(&quot;File&quot;));

	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxSplitterWindow * splitter = new wxSplitterWindow(this, wxID_ANY,
		wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH);
	splitter-&gt;SetMinimumPaneSize(100);
	sizer-&gt;Add(splitter, 1, wxEXPAND);

	m_GroupsListBox = new wxListBox(splitter, ID_GROUPS_LISTBOX,
		wxDefaultPosition, wxDefaultSize);

	wxSplitterWindow * personsplitter = new wxSplitterWindow(splitter, wxID_ANY,
		wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH);
	personsplitter-&gt;SetMinimumPaneSize(100);
	m_PersonsListView = new wxListView(personsplitter, ID_PERSONS_LISTCTRL,
		wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
	m_PersonsListView-&gt;InsertColumn(0, _(&quot;First Name&quot;), wxLIST_FORMAT_LEFT, 120);
	m_PersonsListView-&gt;InsertColumn(1, _(&quot;Last Name&quot;), wxLIST_FORMAT_LEFT, 120);
	m_PersonsListView-&gt;InsertColumn(2, _(&quot;E-Mail&quot;), wxLIST_FORMAT_LEFT, 130);
	m_PersonsListView-&gt;InsertColumn(3, _(&quot;Phone&quot;), wxLIST_FORMAT_LEFT, 130);

	m_PersonInfoPanel = new wxHtmlWindow(personsplitter, ID_PERSON_INFO_PANEL);

	personsplitter-&gt;SetSashGravity(1.0);	

	splitter-&gt;SplitVertically(m_GroupsListBox, personsplitter, 160);
	personsplitter-&gt;SplitHorizontally(m_PersonsListView, m_PersonInfoPanel,
		personsplitter-&gt;GetSize().GetHeight()-180);

	SetToolBar(CreateToolBar());

	CreateStatusBar(2);
	Centre();
}

wxToolBar * SQLiteTestMainFrame::CreateToolBar()
{
	wxToolBar * toolBar = new wxToolBar(this, wxID_ANY, wxDefaultPosition,
		wxDefaultSize, wxTB_FLAT|wxTB_TEXT);
	toolBar-&gt;AddTool(ID_ADD_GROUP, _(&quot;Add Group&quot;), wxBitmap(addbookm_xpm));
	toolBar-&gt;AddTool(ID_DELETE_GROUP, _(&quot;Remove Group&quot;), wxBitmap(delbookm_xpm));
	toolBar-&gt;AddSeparator();
	toolBar-&gt;AddTool(ID_ADD_PERSON, _(&quot;Add Person&quot;), wxBitmap(new_xpm));
	toolBar-&gt;AddTool(ID_DELETE_PERSON, _(&quot;Remove Person&quot;), wxBitmap(delete_xpm));
	toolBar-&gt;Realize();
	return toolBar;
}
...
</pre>
<p>Для работы с классом <b>wxHtmlWindow</b> в свойствах проекта вразделе <b>Linker -> Input</b> в настройках <b>Additional Dependencies</b> нам необходимо добавить библиотеку <b>wxmsw28ud_html.lib</b> для конфигурации <b>Debug</b> и <b>wxmsw28u_html.lib</b> для конфигурации <b>Release</b><br />
После всех внесенных изменений, у нас должно получиться нечто подобное:<br />
<a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_23.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_23.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a><br />
Отлично, интерфейс приложения готов, теперь можно приступить к реализации функционала.</p>
<p><b>RecordIDClientData.h</b></p>
<pre class="brush: cpp;">
#ifndef _RECORD_ID_CLIENT_DATA_H
#define _RECORD_ID_CLIENT_DATA_H

#include &lt;wx/clntdata.h&gt;

class RecordIDClientData : public wxClientData
{
	int m_ID;
public:
	RecordIDClientData(int id) : m_ID(id) {}

	int GetID() {return m_ID;}
	void SetID(int id) {m_ID = id;}
};

#endif
</pre>
<p><b>SQLiteTestMainFrame.h</b></p>
<pre class="brush: cpp;">
...
#include &lt;wx/wx.h&gt;
#include &lt;wx/toolbar.h&gt;
#include &lt;wx/listbox.h&gt;
#include &lt;wx/listctrl.h&gt;
#include &lt;wx/html/htmlwin.h&gt;

class SQLiteTestMainFrame : public wxFrame
{
	...
	void FillGroupsList();
	void FillPersonsList(int groupid);
	...
	DECLARE_EVENT_TABLE()
	void OnExit(wxCommandEvent &amp; event);
	void OnGroupListBoxSelected(wxCommandEvent &amp; event);
	void OnPersonListViewSelected(wxListEvent &amp; event);
	void OnPersonInfoPanelLinkClicked(wxHtmlLinkEvent &amp; event);
};
...
</pre>
<p><b>SQLiteTestMainFrame.cpp</b></p>
<pre class="brush: cpp;">
void SQLiteTestMainFrame::FillGroupsList()
{
	m_GroupsListBox-&gt;Freeze();
	m_GroupsListBox-&gt;Clear();
	GroupRowSet * allGroups = wxGetApp().GetGroupTable()-&gt;All();
	for(unsigned long i = 0; i &lt; allGroups-&gt;Count(); ++i)
	{
		m_GroupsListBox-&gt;Append(allGroups-&gt;Item(i)-&gt;name,
			new RecordIDClientData(allGroups-&gt;Item(i)-&gt;id));
	}
	if(m_GroupsListBox-&gt;GetCount())
	{
		m_GroupsListBox-&gt;SetSelection(0);
		RecordIDClientData * data = (RecordIDClientData *)
			m_GroupsListBox-&gt;GetClientObject(m_GroupsListBox-&gt;GetSelection());
		if(data)
		{
			FillPersonsList(data-&gt;GetID());
		}
	}
	wxGetApp().GetGroupTable()-&gt;CollectRowSet(allGroups);
	m_GroupsListBox-&gt;Thaw();
}

void SQLiteTestMainFrame::FillPersonsList(int groupid)
{
	m_PersonsListView-&gt;Freeze();
	m_PersonsListView-&gt;DeleteAllItems();
	GroupRow * thisGroup = wxGetApp().GetGroupTable()-&gt;Id(groupid);
	if(thisGroup)
	{
		PersonRowSet * allPersons = thisGroup-&gt;GetPersons();
		long item(0);
		for(unsigned long i = 0; i &lt; allPersons-&gt;Count(); ++i)
		{
			item = m_PersonsListView-&gt;InsertItem(item, allPersons-&gt;Item(i)-&gt;first_name);
			m_PersonsListView-&gt;SetItem(item, 1, allPersons-&gt;Item(i)-&gt;last_name);
			m_PersonsListView-&gt;SetItem(item, 2, allPersons-&gt;Item(i)-&gt;email);
			m_PersonsListView-&gt;SetItem(item, 3, allPersons-&gt;Item(i)-&gt;phone);
			m_PersonsListView-&gt;SetItemData(item, (long)allPersons-&gt;Item(i)-&gt;id);
		}
		thisGroup-&gt;CollectRowSet(allPersons);
		if(m_PersonsListView-&gt;GetItemCount())
		{
			m_PersonsListView-&gt;Select(0);
		}
		else
		{
			m_PersonInfoPanel-&gt;SetPage(wxT(&quot;&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;&quot;));
		}
	}
	wxGetApp().GetGroupTable()-&gt;CollectRow(thisGroup);
	m_PersonsListView-&gt;Thaw();
}

void SQLiteTestMainFrame::
        OnGroupListBoxSelected(wxCommandEvent &amp; event)
{
 RecordIDClientData * data =
     (RecordIDClientData *)event.GetClientObject();
 if(data)
 {
  FillPersonsList(data-&gt;GetID());
 }
}

void SQLiteTestMainFrame::OnPersonListViewSelected(wxListEvent &amp; event)
{
	long personid = event.GetData();
	PersonRow * person = wxGetApp().GetPersonTable()-&gt;Id((int)personid);
	if(person)
	{
		m_PersonInfoPanel-&gt;SetPage(wxString::Format(
			wxT(&quot;&lt;html&gt;&lt;body&gt;&lt;h3&gt;%s %s&lt;h3&gt;&lt;body&gt;&lt;html&gt;&quot;),
			person-&gt;first_name, person-&gt;last_name));
		m_PersonInfoPanel-&gt;AppendToPage(wxString::Format(
			wxT(&quot;&lt;b&gt;Gender: &lt;/b&gt; %s&quot;),
			(person-&gt;gender?wxT(&quot;Male&quot;):wxT(&quot;Female&quot;))));
		m_PersonInfoPanel-&gt;AppendToPage(wxT(&quot;&lt;hr&gt;&quot;));
		m_PersonInfoPanel-&gt;AppendToPage(wxString::Format(
			wxT(&quot;&lt;b&gt;Address: &lt;/b&gt; %s&lt;br&gt;&quot;),
			person-&gt;address.GetData()));
		m_PersonInfoPanel-&gt;AppendToPage(wxString::Format(
			wxT(&quot;&lt;b&gt;City: &lt;/b&gt; %s&lt;br&gt;&quot;),
			person-&gt;city.GetData()));
		m_PersonInfoPanel-&gt;AppendToPage(wxString::Format(
			wxT(&quot;&lt;b&gt;Country: &lt;/b&gt; %s&quot;),
			person-&gt;country.GetData()));
		m_PersonInfoPanel-&gt;AppendToPage(wxT(&quot;&lt;hr&gt;&quot;));
		m_PersonInfoPanel-&gt;AppendToPage(
			wxString::Format(wxT(&quot;&lt;b&gt;Phone: &lt;/b&gt; %s&lt;br&gt;&quot;),
			person-&gt;phone.GetData()));
		m_PersonInfoPanel-&gt;AppendToPage(wxString::Format(
			wxT(&quot;&lt;b&gt;E-mail: &lt;/b&gt; &lt;a href=\&quot;mailto:%s\&quot;&gt;%s&lt;/a&gt;&lt;br&gt;&quot;),
			person-&gt;email, person-&gt;email.GetData()));
	}
	else
	{
		m_PersonInfoPanel-&gt;SetPage(
			_(&quot;&lt;html&gt;&lt;body&gt;&lt;h3&gt;Can't find info about selected person&lt;h3&gt;&lt;body&gt;&lt;html&gt;&quot;));
	}
}

void SQLiteTestMainFrame::
    OnPersonInfoPanelLinkClicked(wxHtmlLinkEvent &amp; event)
{
#if defined(__WXMSW__)
 ShellExecute(NULL, NULL,
     event.GetLinkInfo().GetHref().GetData(),
     NULL, NULL, SW_SHOW);
#else
 wxExecute(event.GetLinkInfo().GetHref());
#endif
}
</pre>
<p>Список в левой части окна содержит названия групп. Каждый элемент списка содержит код группы. Привязка данных к элементам списка реализована с помощью объектов класса <b>RecordIDClientData</b>, каждый объект которого содержит код группы. Класс <b>RecordIDClientData</b> является производным от <b>wxClientData</b> и его использование обеспечивает автоматическую очистку памяти при удалении элемента списка. Доступ к данным, ассоциированным с элементом списка групп, производится посредством метода <b>GetClientObject</b> класса <b>wxListBox</b><br />
При выборе записи в списке групп, список контактов в правой части окна заполняется данными контактов данной группы.</p>
<p>При выборе элемента списка контактов, информационное поле в нижней части окна заполняется данными контакта, связанного с выбранным элементом списка. Каждый элемент списка контактов содержит в поле данных код контакта. Доступ к полю данных производится посредством метода <b>GetItemData</b> класса <b>wxListView</b></p>
<p>При нажатии на ссылку с адресом электронной почты в информационном поле, создается новое письмо в почтовом клиенте, назначенном по умолчанию.<br />
<a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_24.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_24.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a><br />
Итак, с отображением данных мы закончили. Теперь нужно реализовать добавление новой записи в таблицу и удаление записей из таблицы.<br />
Для начала создадим диалоговые окна вода данных.</p>
<p><b>EditGroupDialog.h</b></p>
<pre class="brush: cpp;">
#ifndef _EDIT_GROUP_DIALOG_H
#define _EDIT_GROUP_DIALOG_H

#include &lt;wx/wx.h&gt;

class EditGroupDialog : public wxDialog
{
	wxString m_GroupName;
	wxString m_GroupDescription;
	void CreateControls();
public:
	EditGroupDialog(wxWindow * parent);
	bool Create(wxWindow * parent, wxWindowID id, const wxString title);

	const wxString &amp; GetGroupName();
	void SetGroupName(const wxString &amp; value);
	const wxString &amp; GetGroupDescription();
	void SetGroupDescription(const wxString &amp; value);
};

#endif
</pre>
<p><b>EditGroupDialog.cpp</b></p>
<pre class="brush: cpp;">
#include &quot;EditGroupDialog.h&quot;
#include &lt;wx/valgen.h&gt;

enum
{
	ID_EGD_NAME_TEXTCTRL = 10001,
	ID_EGD_DESCRIPTION_TEXTCTRL
};

EditGroupDialog::EditGroupDialog(wxWindow * parent)
{
	Create(parent, wxID_ANY, _(&quot;Editing group&quot;));
}

bool EditGroupDialog::Create(wxWindow * parent, wxWindowID id, const wxString title)
{
	bool res = wxDialog::Create(parent, id, title);
	if(res)
	{
		CreateControls();
	}
	return res;
}

void EditGroupDialog::CreateControls()
{
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxStaticText * nameLabel = new wxStaticText(this, wxID_ANY, _(&quot;Name:&quot;));
	wxStaticText * descriptionLabel = new wxStaticText(this, wxID_ANY, _(&quot;Description:&quot;));

	wxTextCtrl * nameEdit = new wxTextCtrl(this, ID_EGD_NAME_TEXTCTRL, wxEmptyString);
	wxTextCtrl * descriptionEdit = new wxTextCtrl(this, ID_EGD_DESCRIPTION_TEXTCTRL,
		wxEmptyString, wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE);
	nameEdit-&gt;SetValidator(wxGenericValidator(&amp;m_GroupName));
	descriptionEdit-&gt;SetValidator(wxGenericValidator(&amp;m_GroupDescription));

	wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0);
	fg_sizer-&gt;Add(nameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(nameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(descriptionLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(descriptionEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;AddGrowableCol(1);

	sizer-&gt;Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5);
	sizer-&gt;Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5);
}

const wxString &amp; EditGroupDialog::GetGroupName()
{
	return m_GroupName;
}

void EditGroupDialog::SetGroupName(const wxString &amp; value)
{
	m_GroupName = value;
}

const wxString &amp; EditGroupDialog::GetGroupDescription()
{
	return m_GroupDescription;
}

void EditGroupDialog::SetGroupDescription(const wxString &amp; value)
{
	m_GroupDescription = value;
}
</pre>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_25.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_25.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a></p>
<p><b>EditPersonDialog.h</b></p>
<pre class="brush: cpp;">
#ifndef _EDIT_PERSON_DIALOG_H
#define _EDIT_PERSON_DIALOG_H

#include &lt;wx/wx.h&gt;

class EditPersonDialog : public wxDialog
{
	wxString m_FirstName;
	wxString m_LastName;
	wxString m_Address;
	wxString m_City;
	wxString m_Country;
	wxString m_Email;
	wxString m_Phone;
	void CreateControls();
public:
	EditPersonDialog(wxWindow * parent);
	bool Create(wxWindow * parent, wxWindowID id, const wxString title);

	const wxString &amp; GetFirstName();
	const wxString &amp; GetLastName();
	const wxString &amp; GetAddress();
	const wxString &amp; GetCity();
	const wxString &amp; GetCountry();
	const wxString &amp; GetEmail();
	const wxString &amp; GetPhone();
};

#endif
</pre>
<p><b>EditPersonDialog.cpp</b></p>
<pre class="brush: cpp;">
#include &quot;EditPersonDialog.h&quot;
#include &lt;wx/valgen.h&gt;

enum
{
	ID_EPD_FIRSTNAME_TEXTCTRL = 10001,
	ID_EPD_LASTNAME_TEXTCTRL,
	ID_EPD_ADDRESS_TEXTCTRL,
	ID_EPD_CITY_TEXTCTRL,
	ID_EPD_COUNTRY_TEXTCTRL,
	ID_EPD_EMAIL_TEXTCTRL,
	ID_EPD_PHONE_TEXTCTRL
};

EditPersonDialog::EditPersonDialog(wxWindow * parent)
{
	Create(parent, wxID_ANY, _(&quot;Editing person&quot;));
}

bool EditPersonDialog::Create(wxWindow * parent,
wxWindowID id, const wxString title)
{
	bool res = wxDialog::Create(parent, id, title);
	if(res)
	{
		CreateControls();
	}
	return res;
}

void EditPersonDialog::CreateControls()
{
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	SetSizer(sizer);

	wxStaticText * firstnameLabel = new wxStaticText(this, wxID_ANY, _(&quot;First Name:&quot;));
	wxStaticText * lastnameLabel = new wxStaticText(this, wxID_ANY, _(&quot;Last Name:&quot;));
	wxStaticText * addressLabel = new wxStaticText(this, wxID_ANY, _(&quot;Address:&quot;));
	wxStaticText * cityLabel = new wxStaticText(this, wxID_ANY, _(&quot;City:&quot;));
	wxStaticText * countryLabel = new wxStaticText(this, wxID_ANY, _(&quot;Country:&quot;));
	wxStaticText * emailLabel = new wxStaticText(this, wxID_ANY, _(&quot;E-mail:&quot;));
	wxStaticText * phoneLabel = new wxStaticText(this, wxID_ANY, _(&quot;Phone:&quot;));

	wxTextCtrl * firstnameEdit = new wxTextCtrl(this, ID_EPD_FIRSTNAME_TEXTCTRL, wxEmptyString);
	wxTextCtrl * lastnameEdit = new wxTextCtrl(this, ID_EPD_LASTNAME_TEXTCTRL, wxEmptyString);
	wxTextCtrl * addressEdit = new wxTextCtrl(this, ID_EPD_ADDRESS_TEXTCTRL, wxEmptyString);
	wxTextCtrl * cityEdit = new wxTextCtrl(this, ID_EPD_CITY_TEXTCTRL, wxEmptyString);
	wxTextCtrl * countryEdit = new wxTextCtrl(this, ID_EPD_COUNTRY_TEXTCTRL, wxEmptyString);
	wxTextCtrl * emailEdit = new wxTextCtrl(this, ID_EPD_EMAIL_TEXTCTRL, wxEmptyString);
	wxTextCtrl * phoneEdit = new wxTextCtrl(this, ID_EPD_PHONE_TEXTCTRL, wxEmptyString);

	firstnameEdit-&gt;SetMinSize(wxSize(150,-1));

	firstnameEdit-&gt;SetValidator(wxGenericValidator(&amp;m_FirstName));
	lastnameEdit-&gt;SetValidator(wxGenericValidator(&amp;m_LastName));
	addressEdit-&gt;SetValidator(wxGenericValidator(&amp;m_Address));
	cityEdit-&gt;SetValidator(wxGenericValidator(&amp;m_City));
	countryEdit-&gt;SetValidator(wxGenericValidator(&amp;m_Country));
	emailEdit-&gt;SetValidator(wxGenericValidator(&amp;m_Email));
	phoneEdit-&gt;SetValidator(wxGenericValidator(&amp;m_Phone));

	wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0);
	fg_sizer-&gt;Add(firstnameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(firstnameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(lastnameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(lastnameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(addressLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(addressEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(cityLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(cityEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(countryLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(countryEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(emailLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(emailEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(phoneLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;Add(phoneEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	fg_sizer-&gt;AddGrowableCol(1);

	sizer-&gt;Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5);
	sizer-&gt;Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5);

	sizer-&gt;Fit(this);
}

const wxString &amp; EditPersonDialog::GetFirstName()
{
	return m_FirstName;
}

const wxString &amp; EditPersonDialog::GetLastName()
{
	return m_LastName;
}

const wxString &amp; EditPersonDialog::GetAddress()
{
	return m_Address;
}

const wxString &amp; EditPersonDialog::GetCity()
{
	return m_City;
}

const wxString &amp; EditPersonDialog::GetCountry()
{
	return m_Country;
}

const wxString &amp; EditPersonDialog::GetEmail()
{
	return m_Email;
}

const wxString &amp; EditPersonDialog::GetPhone()
{
	return m_Phone;
}
</pre>
<p><a href="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_25.png"><img src="http://wxwidgets.info//wp-content/uploads/2007/06/accessing_sqlite_wx_26.png" alt="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" title="Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1" class="alignnone size-full wp-image-210" /></a><br />
Теперь можно приступать к написанию обработчиков событий от кнопок на панели инструментов.</p>
<p><b>SQLiteTestMainFrame.h</b></p>
<pre class="brush: cpp;">
...
class SQLiteTestMainFrame : public wxFrame
{
	...
	void OnAddGroup(wxCommandEvent &amp; event);
	void OnRemoveGroup(wxCommandEvent &amp; event);
	void OnAddPerson(wxCommandEvent &amp; event);
	void OnRemovePerson(wxCommandEvent &amp; event);
	void OnRemoveGroupUpdateUI(wxUpdateUIEvent &amp; event);
	void OnAddPersonUpdateUI(wxUpdateUIEvent &amp; event);
	void OnRemovePersonUpdateUI(wxUpdateUIEvent &amp; event);
};
...
</pre>
<p><b>SQLiteTestMainFrame.cpp</b></p>
<pre class="brush: cpp;">
...
void SQLiteTestMainFrame::OnAddGroup(wxCommandEvent &amp; event)
{
	EditGroupDialog * dlg = new EditGroupDialog(this);
	if(dlg-&gt;ShowModal() == wxID_OK)
	{
		GroupRow * newGroup = wxGetApp().GetGroupTable()-&gt;New();
		newGroup-&gt;name = dlg-&gt;GetGroupName();
		newGroup-&gt;description = dlg-&gt;GetGroupDescription();
		newGroup-&gt;Save();
		wxGetApp().GetGroupTable()-&gt;CollectRow(newGroup);
		FillGroupsList();
	}
	dlg-&gt;Destroy();
}

void SQLiteTestMainFrame::OnRemoveGroup(wxCommandEvent &amp; event)
{
	int selection = m_GroupsListBox-&gt;GetSelection();
	RecordIDClientData * data = (RecordIDClientData *)
		m_GroupsListBox-&gt;GetClientObject(selection);
	if(data)
	{
		GroupRow * thisGroup = wxGetApp().GetGroupTable()-&gt;Id(data-&gt;GetID());
		if(thisGroup &amp;&amp; (wxMessageBox(_(&quot;Do you really want to delete this group?&quot;),
			_(&quot;Delete group&quot;), wxYES_NO) == wxYES))
		{
			PersonRowSet * thisPersons = thisGroup-&gt;GetPersons();
			for(unsigned long i = 0; i &lt; thisPersons-&gt;Count(); ++i)
			{
				thisPersons-&gt;Item(i)-&gt;Delete();
			}
			thisGroup-&gt;CollectRowSet(thisPersons);
			thisGroup-&gt;Delete();
			wxGetApp().GetGroupTable()-&gt;CollectRow(thisGroup);
			m_GroupsListBox-&gt;Delete(selection);
			if(m_GroupsListBox-&gt;GetCount())
			{
				m_GroupsListBox-&gt;SetSelection(selection &lt;
					(int)m_GroupsListBox-&gt;GetCount() ? selection : 0);
				data = (RecordIDClientData *)m_GroupsListBox-&gt;GetClientObject(
					m_GroupsListBox-&gt;GetSelection());
				if(data)
				{
					FillPersonsList(data-&gt;GetID());
				}
			}
		}
	}
}

void SQLiteTestMainFrame::OnAddPerson(wxCommandEvent &amp; event)
{
	RecordIDClientData * data = (RecordIDClientData *)
		m_GroupsListBox-&gt;GetClientObject(m_GroupsListBox-&gt;GetSelection());
	if(data)
	{
		GroupRow * thisGroup = wxGetApp().GetGroupTable()-&gt;Id(data-&gt;GetID());
		if(thisGroup)
		{
			EditPersonDialog * dlg = new EditPersonDialog(this);
			if(dlg-&gt;ShowModal() == wxID_OK)
			{
				PersonRow * newPerson = wxGetApp().GetPersonTable()-&gt;New();
				newPerson-&gt;groupid= thisGroup-&gt;id;
				newPerson-&gt;first_name = dlg-&gt;GetFirstName();
				newPerson-&gt;last_name = dlg-&gt;GetLastName();
				newPerson-&gt;address = dlg-&gt;GetAddress();
				newPerson-&gt;city = dlg-&gt;GetCity();
				newPerson-&gt;country = dlg-&gt;GetCountry();
				newPerson-&gt;email = dlg-&gt;GetEmail();
				newPerson-&gt;phone = dlg-&gt;GetPhone();
				newPerson-&gt;Save();
				wxGetApp().GetPersonTable()-&gt;CollectRow(newPerson);
				FillPersonsList(thisGroup-&gt;id);
			}
			dlg-&gt;Destroy();
		}
		wxGetApp().GetGroupTable()-&gt;CollectRow(thisGroup);
	}
}

void SQLiteTestMainFrame::OnRemovePerson(wxCommandEvent &amp; event)
{
	long selection = m_PersonsListView-&gt;GetFirstSelected();
	PersonRow * thisPerson = wxGetApp().GetPersonTable()-&gt;Id(
		(int)m_PersonsListView-&gt;GetItemData(selection));
	if(thisPerson &amp;&amp; (wxMessageBox(_(&quot;Do you really want to delete this record?&quot;),
		_(&quot;Delete person&quot;), wxYES_NO) == wxYES))
	{
		int groupid = thisPerson-&gt;groupid;
		thisPerson-&gt;Delete();
		wxGetApp().GetPersonTable()-&gt;CollectRow(thisPerson);
		m_PersonsListView-&gt;DeleteItem(selection);
		if(m_PersonsListView-&gt;GetItemCount())
		{
			m_PersonsListView-&gt;Select(wxMin(selection, m_PersonsListView-&gt;GetItemCount()-1));
		}
	}
}

void SQLiteTestMainFrame::
        OnRemoveGroupUpdateUI(wxUpdateUIEvent &amp; event)
{
 event.Enable(m_GroupsListBox-&gt;GetSelection() &gt;= 0);
}

void SQLiteTestMainFrame::
        OnAddPersonUpdateUI(wxUpdateUIEvent &amp; event)
{
 event.Enable(m_GroupsListBox-&gt;GetSelection() &gt;= 0);
}

void SQLiteTestMainFrame::
        OnRemovePersonUpdateUI(wxUpdateUIEvent &amp; event)
{
 event.Enable(m_PersonsListView-&gt;GetFirstSelected() &gt;= 0);
}
...
</pre>
<p>Как видно, кнопка удаления группы и кнопка добавления контакта активна только в случае, если есть активная запись в списке групп. Кнопка удаления контакта становится активной только в случае, если есть активная запись в списке контактов.<br />
При удалении группы удаляются и все связанные с ней контакты.</p>
<p>Ну вот. Мы закончили. Теперь собираем Release-версию программы и тестируем. Можно переходить к созданию проекта под Linux<br />
<a href="http://wxwidgets.info/wx_accessing_sqlite_3" title="Читать дальше">Продолжение статьи можно почитать здесь</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://wxwidgets.info/wx_accessing_sqlite_2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
