In my previous posts here and here I showed how to create a simple application which uses Document/View framework. Now I’m going to show more complex example – image viewer with scrolling and selection rectangle. As far as I can see from wxForum, implementation of selection-related functionality is some kind of complex but very useful task.
So, let’s start from simple part. The simplest task here is modification of our wxDocument-related class.
DocViewTestDocument.h
#ifndef _DOC_VIEW_TEST_DOCUMENT_H #define _DOC_VIEW_TEST_DOCUMENT_H #include <wx/wx.h> #include <wx/docview.h> class DocViewTestDocument : public wxDocument { DECLARE_DYNAMIC_CLASS(DocViewTestDocument) wxImage m_Image; public: DocViewTestDocument(); virtual wxInputStream & LoadObject(wxInputStream& stream); wxImage & GetImage(); }; #endif
As you can see, we added m_Image
class member for storing loaded image, accessor method GetImage()
for this class member and virtual LoadObject()
method which will be called automatically by document/view framework when user opens a file.
DocViewTestDocument.cpp
#include "DocViewTestDocument.h" #include <../src/tiff/tiffiop.h> IMPLEMENT_DYNAMIC_CLASS(DocViewTestDocument, wxDocument) DocViewTestDocument::DocViewTestDocument() { wxLogTrace(wxTraceMask(), wxT("DocViewTestDocument::DocViewTestDocument")); } wxImage & DocViewTestDocument::GetImage() { return m_Image; } wxInputStream& DocViewTestDocument::LoadObject(wxInputStream& stream) { // disable tiff warnings TIFFSetErrorHandler(0); TIFFSetWarningHandler(0); m_Image.LoadFile(stream, wxBITMAP_TYPE_ANY); return stream; }
In LoadObject()
method we added libtiff-specific calls to avoid all warning messages when loading TIFF images.
Now, after we made all needed changes to our document class, let’s create a control for displaying our image.
DocViewTestCanvas.h
#ifndef _DOC_VIEW_TEST_CANVAS_H #define _DOC_VIEW_TEST_CANVAS_H #include <wx/wx.h> #define DocViewTestCanvasStyle wxNO_BORDER|wxHSCROLL|wxVSCROLL|wxTAB_TRAVERSAL #define DocViewTestCanvasName wxT("DocViewTestCanvas") class DocViewTestView; class DocViewTestCanvas : public wxWindow { DECLARE_DYNAMIC_CLASS(DocViewTestCanvas) DocViewTestView * m_View; void Init(); public: DocViewTestCanvas(); DocViewTestCanvas(wxWindow * parent, wxWindowID id = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = DocViewTestCanvasStyle, const wxString & name = DocViewTestCanvasName); bool Create(wxWindow * parent, wxWindowID id = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = DocViewTestCanvasStyle, const wxString & name = DocViewTestCanvasName); DECLARE_EVENT_TABLE() }; #endif
DocViewTestCanvas.cpp
#include "DocViewTestCanvas.h" #include "DocViewTestView.h" #include <wx/dcbuffer.h> IMPLEMENT_DYNAMIC_CLASS(DocViewTestCanvas, wxWindow) BEGIN_EVENT_TABLE(DocViewTestCanvas, wxWindow) END_EVENT_TABLE() DocViewTestCanvas::DocViewTestCanvas() { Init(); } DocViewTestCanvas::DocViewTestCanvas(wxWindow * parent, wxWindowID id, const wxPoint & pos, const wxSize & size, long style, const wxString & name) { Init(); Create(parent, id, pos, size, style, name); } bool DocViewTestCanvas::Create(wxWindow * parent, wxWindowID id, const wxPoint & pos, const wxSize & size, long style, const wxString & name) { bool res = wxWindow::Create(parent, id, pos, size, style, name); if(res) { } return res; } void DocViewTestCanvas::Init() { m_View = NULL; }
So, we created a stub control which will be used for displaying images. Now let’s add changes to our view class.
DocViewTestView.h
#ifndef _DOC_VIEW_TEST_VIEW_H #define _DOC_VIEW_TEST_VIEW_H #include <wx/wx.h> #include <wx/docview.h> class DocViewTestCanvas; class DocViewTestView : public wxView { DECLARE_DYNAMIC_CLASS(DocViewTestView) double m_Scale; wxBitmap m_ScaledBitmap; wxSize m_ViewOffset; DocViewTestCanvas * m_Canvas; void ReCreateScaledBitmap(); public: DocViewTestView(); wxBitmap & GetScaledBitmap(); const wxSize & GetViewOffset(); void SetViewOffset(const wxSize & value); virtual void OnDraw(wxDC* dc); virtual void OnUpdate(wxView *sender, wxObject *hint = (wxObject *) NULL); virtual bool OnClose(bool deleteWindow = true); }; #endif
DocViewTestView.cpp
#include "DocViewTestView.h" #include "DocViewTestCanvas.h" #include "DocViewTestDocument.h" #include "DocViewTestMainFrame.h" IMPLEMENT_DYNAMIC_CLASS(DocViewTestView, wxView) DocViewTestView::DocViewTestView() : m_Scale(1.0) { m_Canvas = NULL; DocViewTestMainFrame * mainFrame = wxDynamicCast(wxTheApp->GetTopWindow(), DocViewTestMainFrame); if(mainFrame) { SetFrame(mainFrame); m_Canvas = mainFrame->m_Canvas; m_Canvas->SetView(this); } else { wxFAIL_MSG(_("Can't retrieve a pointer to main window")); } } void DocViewTestView::OnDraw(wxDC* dc) { if(m_ScaledBitmap.IsOk()) { dc->DrawBitmap(m_ScaledBitmap, m_ViewOffset.GetWidth(), m_ViewOffset.GetHeight()); } } void DocViewTestView::OnUpdate(wxView *sender, wxObject *hint) { ReCreateScaledBitmap(); if (m_Canvas) { m_Canvas->AdjustScrollBars(); m_Canvas->Refresh(); } } void DocViewTestView::ReCreateScaledBitmap() { DocViewTestDocument * document = wxDynamicCast(GetDocument(), DocViewTestDocument); if(document) { wxSize scaledSize(document->GetImage().GetWidth() * m_Scale, document->GetImage().GetHeight() * m_Scale); m_ScaledBitmap = wxBitmap(document->GetImage().Scale(scaledSize.GetWidth(), scaledSize.GetHeight())); } else { m_ScaledBitmap = wxNullBitmap; } } bool DocViewTestView::OnClose(bool deleteWindow) { wxLogTrace(wxTraceMask(), wxT("DocViewTestView::OnClose")); if (!GetDocument()->Close()) { return false; } if(m_Canvas) { m_Canvas->ClearBackground(); m_Canvas->SetView(NULL); m_Canvas = NULL; } wxFrame * frame = wxDynamicCast(GetFrame(), wxFrame); if(frame) { frame->SetTitle(DocViewTestMainFrameTitle); } SetFrame(NULL); Activate(false); return true; } wxBitmap & DocViewTestView::GetScaledBitmap() { return m_ScaledBitmap; } const wxSize & DocViewTestView::GetViewOffset() { return m_ViewOffset; } void DocViewTestView::SetViewOffset(const wxSize & value) { m_ViewOffset = value; }
As you can see in the code above, some new method of DocViewTestCanvas
class are used. These are SetView()
and AdjustScrollBars()
methods. We’ll need to add these and some other methods to our canvas control. But now let’s see what changes we made to our view class. We added new members: m_Scale
variable which should be used as zooming level, m_ScaledBitmap
variable which will store zoomed (or scaled) image. As you can see, document class stores image as wxImage
and view class stores zoomed image as wxBitmap
. The reason of this is that wxDC
uses wxBitmap
for DrawBitmap()
method and there will be no need to convert image to wxBitmap
each time when we need to draw it. m_ViewOffset
will contain scrolling offset (actually a scrollbar position of our canvas control). m_Canvas
will contain pointer to canvas control obtained from main window. This will allow not to obtain this pointer each time by casting GetFrame()
to DocViewTestMainFrame
. DocViewTestView::ReCreateScaledBitmap()
recreates the zoomed image according to zoom level.
OK, now let’s add missing methods to our canvas control:
DocViewTestCanvas.h
#ifndef _DOC_VIEW_TEST_CANVAS_H #define _DOC_VIEW_TEST_CANVAS_H #include <wx/wx.h> #define DocViewTestCanvasStyle wxNO_BORDER|wxHSCROLL|wxVSCROLL|wxTAB_TRAVERSAL #define DocViewTestCanvasName wxT("DocViewTestCanvas") class DocViewTestView; class DocViewTestCanvas : public wxWindow { DECLARE_DYNAMIC_CLASS(DocViewTestCanvas) wxPoint m_TR; wxPoint m_BL; DocViewTestView * m_View; wxRect m_Selection; static wxSize ScrollingIncrement; void Init(); public: ... DocViewTestView * GetView(); void SetView(DocViewTestView * value); const wxRect & GetSelection(); wxSize GetCurrentSize(); wxPoint GetScrollPosition(); void Scroll(wxPoint pt); void Scroll(int x, int y); void FixViewOffset(); void AdjustScrollBars(); wxRect GetImageDisplayRect(const wxPoint & scrollPos); wxPoint ClientToImage(const wxPoint & pos); bool AutoScroll(wxPoint currentPos, wxPoint scrollPos); DECLARE_EVENT_TABLE() void OnScrollLineUp( wxScrollWinEvent& event ); void OnScrollLineDown( wxScrollWinEvent& event ); void OnScrollPageUp( wxScrollWinEvent& event ); void OnScrollPageDown( wxScrollWinEvent& event ); void OnScrollThumbtrack( wxScrollWinEvent& event ); void OnScrollThumbRelease( wxScrollWinEvent& event ); void OnPaint( wxPaintEvent& event ); void OnIdle( wxIdleEvent& event ); void OnEraseBackground( wxEraseEvent& event ); void OnLeftDown( wxMouseEvent& event ); void OnLeftUp( wxMouseEvent& event ); void OnMotion( wxMouseEvent& event ); }; #endif
DocViewTestCanvas.cpp
#include "DocViewTestCanvas.h" #include "DocViewTestView.h" #include <wx/dcbuffer.h> IMPLEMENT_DYNAMIC_CLASS(DocViewTestCanvas, wxWindow) wxSize DocViewTestCanvas::ScrollingIncrement = wxSize(10,10); BEGIN_EVENT_TABLE(DocViewTestCanvas, wxWindow) EVT_SCROLLWIN_LINEUP( DocViewTestCanvas::OnScrollLineUp ) EVT_SCROLLWIN_LINEDOWN( DocViewTestCanvas::OnScrollLineDown ) EVT_SCROLLWIN_PAGEUP( DocViewTestCanvas::OnScrollPageUp ) EVT_SCROLLWIN_PAGEDOWN( DocViewTestCanvas::OnScrollPageDown ) EVT_SCROLLWIN_THUMBTRACK( DocViewTestCanvas::OnScrollThumbtrack ) EVT_SCROLLWIN_THUMBRELEASE( DocViewTestCanvas::OnScrollThumbRelease ) EVT_PAINT( DocViewTestCanvas::OnPaint ) EVT_IDLE( DocViewTestCanvas::OnIdle ) EVT_ERASE_BACKGROUND( DocViewTestCanvas::OnEraseBackground ) EVT_LEFT_DOWN( DocViewTestCanvas::OnLeftDown ) EVT_LEFT_UP( DocViewTestCanvas::OnLeftUp ) EVT_MOTION( DocViewTestCanvas::OnMotion ) END_EVENT_TABLE() ... DocViewTestView * DocViewTestCanvas::GetView() { return m_View; } void DocViewTestCanvas::SetView(DocViewTestView * value) { m_View = value; } const wxRect & DocViewTestCanvas::GetSelection() { return m_Selection; } wxPoint DocViewTestCanvas::GetScrollPosition() { return wxPoint(GetScrollPos(wxHORIZONTAL), GetScrollPos(wxVERTICAL)); } void DocViewTestCanvas::Scroll(wxPoint pt) { if(pt.x >= 0) SetScrollPos(wxHORIZONTAL, pt.x); if(pt.y >= 0) SetScrollPos(wxVERTICAL, pt.y); Refresh(); } void DocViewTestCanvas::Scroll(int x, int y) { Scroll(wxPoint(x, y)); } wxSize DocViewTestCanvas::GetCurrentSize() { if(!m_View) { return wxSize(0,0); } return wxSize(m_View->GetScaledBitmap().GetWidth(), m_View->GetScaledBitmap().GetHeight()); } void DocViewTestCanvas::AdjustScrollBars() { // image sampled size wxSize imageSize = GetCurrentSize(); // old scroll position wxPoint pt = GetScrollPosition(); SetScrollbar(wxHORIZONTAL, 0, GetClientSize().x, imageSize.x); SetScrollbar(wxVERTICAL, 0, GetClientSize().y, imageSize.y); Scroll(pt.x, pt.y); } void DocViewTestCanvas::FixViewOffset() { if(m_View) { wxPoint pt = GetScrollPosition(); wxRect displayRect = GetImageDisplayRect(pt); wxSize offset(displayRect.GetPosition().x, displayRect.GetPosition().y); m_View->SetViewOffset(offset); } } wxRect DocViewTestCanvas::GetImageDisplayRect(const wxPoint & scrollPos) { wxSize currentSize = GetCurrentSize(); // calculate actual image position if it is centered wxPoint ptTest( (GetClientSize().x - currentSize.GetWidth()) <= 0 ? -scrollPos.x : ((GetClientSize().GetWidth() - currentSize.GetWidth()) * 0.5f), (GetClientSize().y - currentSize.GetHeight()) <= 0 ? -scrollPos.y : ((GetClientSize().GetHeight() - currentSize.GetHeight()) * 0.5f)); // calculate actual image diaply rectangle if centered return wxRect(ptTest, currentSize); } void DocViewTestCanvas::OnIdle( wxIdleEvent& event ) { do { if(!HasCapture()) break; // get scroll position wxPoint scrollPos = GetScrollPosition(); // get mouse in client coordinates wxPoint currentPos = ScreenToClient(wxGetMousePosition()); // auto scroll // check current drag position and update scroll regularly if(AutoScroll(currentPos, scrollPos)) { event.RequestMore(); } FixViewOffset(); } while(false); } void DocViewTestCanvas::OnLeftDown( wxMouseEvent& event ) { SetFocus(); wxRect imageRect = GetImageDisplayRect(GetScrollPosition()); do { if(!m_View) break; wxPoint cursorPosOnImage = ClientToImage(event.GetPosition()); if(!imageRect.Contains(event.GetPosition())) break; CaptureMouse(); Refresh(); m_TR = m_BL = event.GetPosition(); m_Selection.SetPosition(event.GetPosition()); m_Selection.SetSize(wxSize(0,0)); } while(false); } void DocViewTestCanvas::OnLeftUp( wxMouseEvent& event ) { if(HasCapture()) { ReleaseMouse(); Refresh(); wxMessageBox(wxString::Format( _("Selection rectangle\r\nScreen coordinates:\r\nPosition=(%i,%i);\r\nSize=(%i,%i)\r\nLogical coordinates:\r\nPosition=(%i,%i);\r\nSize=(%i,%i)"), m_Selection.GetLeft(), m_Selection.GetTop(), m_Selection.GetWidth(), m_Selection.GetHeight(), ClientToImage(m_Selection.GetTopLeft()).x, ClientToImage(m_Selection.GetTopLeft()).y, m_Selection.GetWidth(), m_Selection.GetHeight())); m_TR = m_BL = wxPoint(0,0); m_Selection.SetPosition(m_TR); m_Selection.SetSize(wxSize(0,0)); } } void DocViewTestCanvas::OnMotion( wxMouseEvent& event ) { if(HasCapture()) { // current point wxPoint currentPos = event.GetPosition(); // get scroll position wxPoint scrollPos = GetScrollPosition(); AutoScroll(currentPos, scrollPos); FixViewOffset(); Refresh(); } } bool DocViewTestCanvas::AutoScroll(wxPoint currentPos, wxPoint scrollPos) { bool res = false; // If mouse is captured set rect coords if inside the image and autoscroll if posible // get image display rectangle wxRect displayRect = GetImageDisplayRect(scrollPos); wxSize oldSelection(m_TR.x-m_BL.x, m_TR.y-m_BL.y); // check if the current drag position is inside the image - do not allow to draw rectangle out of the image m_TR.x = wxMin(wxMax(currentPos.x, displayRect.GetLeft()), displayRect.GetRight()); m_TR.y = wxMin(wxMax(currentPos.y, displayRect.GetTop()), displayRect.GetBottom()); // Check current drag position and update scroll regularly if(currentPos.x <= 0) { Scroll(wxMax(scrollPos.x - DocViewTestCanvas::ScrollingIncrement.GetWidth(), 0), -1); m_BL.x += (scrollPos.x - GetScrollPosition().x); res = true; } if(currentPos.y <= 0) { Scroll(-1,wxMax(scrollPos.y - DocViewTestCanvas::ScrollingIncrement.GetHeight(), 0)); m_BL.y += (scrollPos.y - GetScrollPosition().y); res = true; } if(currentPos.x >= GetClientSize().GetWidth()) { Scroll(scrollPos.x + DocViewTestCanvas::ScrollingIncrement.GetWidth(), -1); m_BL.x -= (GetScrollPosition().x - scrollPos.x); res = true; } if(currentPos.y >= GetClientSize().y) { Scroll(-1, scrollPos.y + DocViewTestCanvas::ScrollingIncrement.GetHeight()); m_BL.y -= (GetScrollPosition().y - scrollPos.y); res = true; } m_Selection = wxRect(wxMin(m_TR.x, m_BL.x), wxMin(m_TR.y, m_BL.y), abs(m_TR.x-m_BL.x), abs(m_TR.y-m_BL.y)); return res; } void DocViewTestCanvas::OnScrollLineUp( wxScrollWinEvent& event ) { int increment = (event.GetOrientation() == wxHORIZONTAL ? DocViewTestCanvas::ScrollingIncrement.GetWidth() : DocViewTestCanvas::ScrollingIncrement.GetHeight()); SetScrollPos(event.GetOrientation(), GetScrollPos(event.GetOrientation()) - increment); FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnScrollLineDown( wxScrollWinEvent& event ) { int increment = (event.GetOrientation() == wxHORIZONTAL ? DocViewTestCanvas::ScrollingIncrement.GetWidth() : DocViewTestCanvas::ScrollingIncrement.GetHeight()); SetScrollPos(event.GetOrientation(), GetScrollPos(event.GetOrientation()) + increment); FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnScrollPageUp( wxScrollWinEvent& event ) { SetScrollPos(event.GetOrientation(), GetScrollPos(event.GetOrientation()) - GetScrollThumb(event.GetOrientation())); FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnScrollPageDown( wxScrollWinEvent& event ) { SetScrollPos(event.GetOrientation(), GetScrollPos(event.GetOrientation()) + GetScrollThumb(event.GetOrientation())); FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnScrollThumbtrack( wxScrollWinEvent& event ) { SetScrollPos(event.GetOrientation(), event.GetPosition()); FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnScrollThumbRelease( wxScrollWinEvent& event ) { FixViewOffset(); Refresh(); } void DocViewTestCanvas::OnPaint( wxPaintEvent& event ) { wxBufferedPaintDC dc(this); dc.SetBackground(wxBrush(GetBackgroundColour())); dc.Clear(); if(m_View) { m_View->OnDraw(&dc); if(HasCapture() && !m_Selection.IsEmpty()) { dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetLogicalFunction(wxXOR); dc.SetPen(wxPen(*wxWHITE, 3)); dc.DrawRoundedRectangle(m_Selection.GetPosition(), m_Selection.GetSize(), 1.0f); } } } wxPoint DocViewTestCanvas::ClientToImage(const wxPoint & pos) { wxPoint scrollPos = GetScrollPosition(); wxRect imageRect = GetImageDisplayRect(scrollPos); return wxPoint(pos.x - imageRect.GetLeft(), pos.y - imageRect.GetTop()); } void DocViewTestCanvas::OnEraseBackground(wxEraseEvent & event) { }
As you can see, the amount of changes is large enough. So, let’s see what each method does.
GetView()
andSetView()
are accessor and mutator methods form_View
variable which contains pointer toDocViewTestView
object associated with our control.m_Selection
class member contains coordinates of selection rectangle andGetSelection()
is accessor method for this class member.GetScrollPosition()
method returns the scrolling offset aswxPoint
object.Scroll()
method scrolls the control to specified offset.GetCurrentSize()
method returns the size of scrolling area obtained fromDocViewTestView
object associated with control.FixViewOffset()
method updatesm_ViewOffset
class member ofDocViewTestView
object associated with control with value obtained from current scrolling offset.GetImageDisplayRect ()
method returns the coordinates of currently visible area.OnLeftDown()
method is wxEVT_LEFT_DOWN event which occurs when user presses left mouse button on the control.OnLeftUp()
method is wxEVT_LEFT_UP event which occurs when user releases left mouse button.OnMotion()
method is event handler for wxEVT_MOTION event which occurs when user moves mouse pointer over the control.OnScroll*()
methods are avant handlers for scroll events.OnPaint()
method is wxEVT_PAINT handler and callsOnDraw()
method fromDocViewTestView
object associated with control and then draws selection rectangle of needed.ClientToImage()
method converts client coordinates to logical coordinates on zoomed image.
Very important role here plays OnIdle()
method which is an event handler for wxEVT_IDLE event and makes the control to scroll smoothly (in fact, it causes the control to refresh more often and that’s why scrolling looks more smoothly). AutoScroll() method performs scrolling and recalculates the coordinates of selection area.
Selection area (m_Selection
) is calculated from m_TR
and m_BR
variables which contain the coordinates of top-right and bottom-left corners of selection area.
Now, after we added all needed changes to document, view and canvas classes, we need to add the canvas to our main frame.
DocViewTestMainFrame.h
#ifndef _DOC_VIEW_TEST_MAINFRAME_H #define _DOC_VIEW_TEST_MAINFRAME_H #include <wx/wx.h> #include <wx/docview.h> #include <wx/aui/aui.h> #define DocViewTestMainFrameTitle _("DocView Test") class DocViewTestCanvas; class DocViewTestMainFrame : public wxDocParentFrame { DECLARE_DYNAMIC_CLASS(DocViewTestMainFrame) wxAuiManager m_AuiManager; DocViewTestCanvas * m_Canvas; void CreateControls(); wxMenuBar * CreateMenuBar(); public: DocViewTestMainFrame(); DocViewTestMainFrame(wxDocManager * docManager, wxFrame * parent, wxWindowID id = wxID_ANY, const wxString & title = DocViewTestMainFrameTitle); ~DocViewTestMainFrame(); bool Create(wxDocManager * docManager, wxFrame * parent, wxWindowID id = wxID_ANY, const wxString & title = DocViewTestMainFrameTitle); DocViewTestCanvas * GetCanvas(); DECLARE_EVENT_TABLE() void OnExit(wxCommandEvent & event); }; #endif
DocViewTestMainFrame.cpp
#include "DocViewTestCanvas.h" ... void DocViewTestMainFrame::CreateControls() { SetMenuBar(CreateMenuBar()); m_AuiManager.SetManagedWindow(this); m_Canvas = new DocViewTestCanvas(this); m_AuiManager.AddPane(m_Canvas, wxAuiPaneInfo().CenterPane().Name(_("Canvas"))); m_AuiManager.Update(); } wxMenuBar * DocViewTestMainFrame::CreateMenuBar() { wxMenuBar * result = new wxMenuBar; wxMenu * fileMenu = new wxMenu; //fileMenu->Append(wxID_NEW, _("New\tCtrl+N")); fileMenu->Append(wxID_OPEN, _("Open\tCtrl+O")); fileMenu->AppendSeparator(); fileMenu->Append(wxID_SAVE, _("Save\tCtrl+S")); fileMenu->Append(wxID_SAVEAS, _("Save as...")); fileMenu->AppendSeparator(); fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4")); wxMenu * helpMenu = new wxMenu; helpMenu->Append(wxID_ABOUT, _("About...")); result->Append(fileMenu, _("File")); result->Append(helpMenu, _("Help")); return result; }
I commented the creation of wxID_NEW menu item because our application is image viewer and should load only existing files but not create new ones.
Now we can compile the source code and after we execute the application and load some image file, we’ll see something like this:
After we select some area and release the mouse button, message box with coordinates of selection rectangle will appear:
That’s all.
You can download the source code of sample application described above.