wxwidgets/samples/dragimag/dragimag.cpp
Vadim Zeitlin 4f4c5fcfdf Use nullptr instead of NULL in the code and documentation
This is a combination of running clang-tidy with modernize-use-nullptr
check for some ports (GTK, X11, OSX) and manual changes to the ports for
which it couldn't be used easily (MSW, DFB) and also manually updating
the docs.

Also replace NULL with null or nullptr in the comments as this is more
consistent with the use of nullptr in the code and makes it simpler to
grep for the remaining occurrences of NULL itself.

And also use null in the assert messages.

Only a few occurrences of "NULL" are still left in non-C files, mostly
corresponding to unclear comments or string output which it might not be
safe to change.
2022-10-18 01:25:25 +02:00

504 lines
13 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: dragimag.cpp
// Purpose: wxDragImage sample
// Author: Julian Smart
// Modified by:
// Created: 28/2/2000
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/image.h"
// Under Windows, change this to 1
// to use wxGenericDragImage
#define wxUSE_GENERIC_DRAGIMAGE 0
#if wxUSE_GENERIC_DRAGIMAGE
#include "wx/generic/dragimgg.h"
#define wxDragImage wxGenericDragImage
#else
#include "wx/dragimag.h"
#endif
#include "dragimag.h"
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#include "dragicon.xpm"
#endif
// main program
wxIMPLEMENT_APP(MyApp);
// MyCanvas
wxIMPLEMENT_CLASS(MyCanvas, wxScrolledWindow);
wxBEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
EVT_PAINT(MyCanvas::OnPaint)
EVT_ERASE_BACKGROUND(MyCanvas::OnEraseBackground)
EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
wxEND_EVENT_TABLE()
MyCanvas::MyCanvas( wxWindow *parent, wxWindowID id,
const wxPoint &pos, const wxSize &size )
: wxScrolledWindow( parent, id, pos, size, wxSUNKEN_BORDER )
{
SetBackgroundColour(* wxWHITE);
SetCursor(wxCursor(wxCURSOR_ARROW));
m_dragMode = TEST_DRAG_NONE;
m_draggedShape = nullptr;
m_dragImage = nullptr;
m_currentlyHighlighted = nullptr;
}
MyCanvas::~MyCanvas()
{
ClearShapes();
if (m_dragImage)
delete m_dragImage;
}
void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
wxPaintDC dc( this );
PrepareDC( dc );
DrawShapes(dc);
}
void MyCanvas::OnEraseBackground(wxEraseEvent& event)
{
if (wxGetApp().GetBackgroundBitmap().IsOk())
{
wxSize sz = GetClientSize();
wxRect rect(0, 0, sz.x, sz.y);
if (event.GetDC())
{
wxGetApp().TileBitmap(rect, *(event.GetDC()), wxGetApp().GetBackgroundBitmap());
}
else
{
wxClientDC dc(this);
wxGetApp().TileBitmap(rect, dc, wxGetApp().GetBackgroundBitmap());
}
}
else
event.Skip(); // The official way of doing it
}
void MyCanvas::OnMouseEvent(wxMouseEvent& event)
{
if (event.LeftDown())
{
DragShape* shape = FindShape(event.GetPosition());
if (shape)
{
// We tentatively start dragging, but wait for
// mouse movement before dragging properly.
m_dragMode = TEST_DRAG_START;
m_dragStartPos = event.GetPosition();
m_draggedShape = shape;
}
}
else if (event.LeftUp() && m_dragMode != TEST_DRAG_NONE)
{
// Finish dragging
m_dragMode = TEST_DRAG_NONE;
if (!m_draggedShape || !m_dragImage)
return;
m_draggedShape->SetPosition(m_draggedShape->GetPosition()
+ event.GetPosition() - m_dragStartPos);
m_dragImage->Hide();
m_dragImage->EndDrag();
wxDELETE(m_dragImage);
m_draggedShape->SetShow(true);
m_currentlyHighlighted = nullptr;
m_draggedShape = nullptr;
Refresh(true);
}
else if (event.Dragging() && m_dragMode != TEST_DRAG_NONE)
{
if (m_dragMode == TEST_DRAG_START)
{
// We will start dragging if we've moved beyond a couple of pixels
int tolerance = 2;
int dx = abs(event.GetPosition().x - m_dragStartPos.x);
int dy = abs(event.GetPosition().y - m_dragStartPos.y);
if (dx <= tolerance && dy <= tolerance)
return;
// Start the drag.
m_dragMode = TEST_DRAG_DRAGGING;
if (m_dragImage)
delete m_dragImage;
// Erase the dragged shape from the canvas
m_draggedShape->SetShow(false);
// redraw immediately
Refresh(true);
Update();
switch (m_draggedShape->GetDragMethod())
{
case SHAPE_DRAG_BITMAP:
{
m_dragImage = new MyDragImage(this, m_draggedShape->GetBitmap(), wxCursor(wxCURSOR_HAND));
break;
}
case SHAPE_DRAG_TEXT:
{
m_dragImage = new MyDragImage(this, wxString("Dragging some test text"), wxCursor(wxCURSOR_HAND));
break;
}
case SHAPE_DRAG_ICON:
{
m_dragImage = new MyDragImage(this, wxICON(dragicon), wxCursor(wxCURSOR_HAND));
break;
}
}
bool fullScreen = wxGetApp().GetUseScreen();
// The offset between the top-left of the shape image and the current shape position
wxPoint beginDragHotSpot = m_dragStartPos - m_draggedShape->GetPosition();
// Now we do this inside the implementation: always assume
// coordinates relative to the capture window (client coordinates)
//if (fullScreen)
// beginDragHotSpot -= ClientToScreen(wxPoint(0, 0));
if (!m_dragImage->BeginDrag(beginDragHotSpot, this, fullScreen))
{
wxDELETE(m_dragImage);
m_dragMode = TEST_DRAG_NONE;
} else
{
m_dragImage->Move(event.GetPosition());
m_dragImage->Show();
}
}
else if (m_dragMode == TEST_DRAG_DRAGGING)
{
// We're currently dragging. See if we're over another shape.
DragShape* onShape = FindShape(event.GetPosition());
bool mustUnhighlightOld = false;
bool mustHighlightNew = false;
if (m_currentlyHighlighted)
{
if ((onShape == nullptr) || (m_currentlyHighlighted != onShape))
mustUnhighlightOld = true;
}
if (onShape && (onShape != m_currentlyHighlighted) && onShape->IsShown())
mustHighlightNew = true;
if (mustUnhighlightOld || mustHighlightNew)
m_dragImage->Hide();
// Now with the drag image switched off, we can change the window contents.
if (mustUnhighlightOld)
m_currentlyHighlighted = nullptr;
if (mustHighlightNew)
m_currentlyHighlighted = onShape;
if (mustUnhighlightOld || mustHighlightNew)
{
Refresh(mustUnhighlightOld);
Update();
}
// Move and show the image again
m_dragImage->Move(event.GetPosition());
if (mustUnhighlightOld || mustHighlightNew)
m_dragImage->Show();
}
}
}
void MyCanvas::DrawShapes(wxDC& dc)
{
wxList::compatibility_iterator node = m_displayList.GetFirst();
while (node)
{
DragShape* shape = (DragShape*) node->GetData();
if (shape->IsShown() && m_draggedShape != shape)
{
shape->Draw(dc, (m_currentlyHighlighted == shape));
}
node = node->GetNext();
}
}
void MyCanvas::EraseShape(DragShape* shape, wxDC& dc)
{
wxSize sz = GetClientSize();
wxRect rect(0, 0, sz.x, sz.y);
wxRect rect2(shape->GetRect());
dc.SetClippingRegion(rect2.x, rect2.y, rect2.width, rect2.height);
wxGetApp().TileBitmap(rect, dc, wxGetApp().GetBackgroundBitmap());
dc.DestroyClippingRegion();
}
void MyCanvas::ClearShapes()
{
wxList::compatibility_iterator node = m_displayList.GetFirst();
while (node)
{
DragShape* shape = (DragShape*) node->GetData();
delete shape;
node = node->GetNext();
}
m_displayList.Clear();
}
DragShape* MyCanvas::FindShape(const wxPoint& pt) const
{
wxList::compatibility_iterator node = m_displayList.GetFirst();
while (node)
{
DragShape* shape = (DragShape*) node->GetData();
if (shape->HitTest(pt))
return shape;
node = node->GetNext();
}
return nullptr;
}
// MyFrame
wxIMPLEMENT_DYNAMIC_CLASS(MyFrame, wxFrame);
wxBEGIN_EVENT_TABLE(MyFrame,wxFrame)
EVT_MENU (wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU (wxID_EXIT, MyFrame::OnQuit)
wxEND_EVENT_TABLE()
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "wxDragImage sample",
wxPoint(20,20), wxSize(470,360) )
{
wxMenu *file_menu = new wxMenu();
file_menu->Append( wxID_ABOUT, "&About");
file_menu->AppendCheckItem( TEST_USE_SCREEN, "&Use whole screen for dragging", "Use whole screen");
file_menu->Append( wxID_EXIT, "E&xit");
wxMenuBar *menu_bar = new wxMenuBar();
menu_bar->Append(file_menu, "&File");
SetIcon(wxICON(sample));
SetMenuBar( menu_bar );
#if wxUSE_STATUSBAR
CreateStatusBar(2);
int widths[] = { -1, 100 };
SetStatusWidths( 2, widths );
#endif // wxUSE_STATUSBAR
m_canvas = new MyCanvas( this, wxID_ANY, wxPoint(0,0), wxSize(10,10) );
}
void MyFrame::OnQuit( wxCommandEvent &WXUNUSED(event) )
{
Close( true );
}
void MyFrame::OnAbout( wxCommandEvent &WXUNUSED(event) )
{
(void)wxMessageBox( "wxDragImage demo\n"
"Julian Smart (c) 2000",
"About wxDragImage Demo",
wxICON_INFORMATION | wxOK );
}
//-----------------------------------------------------------------------------
// MyApp
//-----------------------------------------------------------------------------
wxBEGIN_EVENT_TABLE(MyApp, wxApp)
EVT_MENU(TEST_USE_SCREEN, MyApp::OnUseScreen)
wxEND_EVENT_TABLE()
MyApp::MyApp()
{
// Drag across whole screen
m_useScreen = false;
}
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
#if wxUSE_LIBPNG
wxImage::AddHandler( new wxPNGHandler );
#endif
wxImage image;
if (image.LoadFile("backgrnd.png", wxBITMAP_TYPE_PNG))
{
m_background = wxBitmap(image);
}
MyFrame *frame = new MyFrame();
wxString rootName("shape0");
for (int i = 1; i < 4; i++)
{
/* For some reason under wxX11, the 2nd LoadFile in this loop fails, with
a BadMatch inside CreateFromImage (inside ConvertToBitmap). This happens even if you copy
the first file over the second file. */
if (image.LoadFile(wxString::Format("%s%d.png", rootName, i), wxBITMAP_TYPE_PNG))
{
DragShape* newShape = new DragShape(wxBitmap(image));
newShape->SetPosition(wxPoint(i*50, i*50));
if (i == 2)
newShape->SetDragMethod(SHAPE_DRAG_TEXT);
else if (i == 3)
newShape->SetDragMethod(SHAPE_DRAG_ICON);
else
newShape->SetDragMethod(SHAPE_DRAG_BITMAP);
frame->GetCanvas()->GetDisplayList().Append(newShape);
}
}
#if 0
// Under GTK, this demonstrates that
// wxScreenDC only gets the root window content.
// We need to be able to copy the overall content
// for full-screen dragging to work.
int w, h;
wxDisplaySize(& w, & h);
wxBitmap bitmap(w, h);
wxScreenDC dc;
wxMemoryDC memDC;
memDC.SelectObject(bitmap);
memDC.Blit(0, 0, w, h, & dc, 0, 0);
memDC.SelectObject(wxNullBitmap);
m_background = bitmap;
#endif
frame->Show( true );
return true;
}
int MyApp::OnExit()
{
return 0;
}
bool MyApp::TileBitmap(const wxRect& rect, wxDC& dc, const wxBitmap& bitmap)
{
int w = bitmap.GetWidth();
int h = bitmap.GetHeight();
int i, j;
for (i = rect.x; i < rect.x + rect.width; i += w)
{
for (j = rect.y; j < rect.y + rect.height; j+= h)
dc.DrawBitmap(bitmap, i, j);
}
return true;
}
void MyApp::OnUseScreen(wxCommandEvent& WXUNUSED(event))
{
m_useScreen = !m_useScreen;
}
// DragShape
DragShape::DragShape(const wxBitmap& bitmap)
{
m_bitmap = bitmap;
m_pos.x = 0;
m_pos.y = 0;
m_dragMethod = SHAPE_DRAG_BITMAP;
m_show = true;
}
bool DragShape::HitTest(const wxPoint& pt) const
{
wxRect rect(GetRect());
return rect.Contains(pt.x, pt.y);
}
bool DragShape::Draw(wxDC& dc, bool highlight)
{
if (m_bitmap.IsOk())
{
wxMemoryDC memDC;
memDC.SelectObject(m_bitmap);
dc.Blit(m_pos.x, m_pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight(),
& memDC, 0, 0, wxCOPY, true);
if (highlight)
{
dc.SetPen(*wxWHITE_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(m_pos.x, m_pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight());
}
return true;
}
else
return false;
}
#if wxUSE_GENERIC_DRAGIMAGE
// MyDragImage
// On some platforms, notably Mac OS X with Core Graphics, we can't blit from
// a window, so we need to draw the background explicitly.
bool MyDragImage::UpdateBackingFromWindow(wxDC& WXUNUSED(windowDC), wxMemoryDC& destDC, const wxRect& WXUNUSED(sourceRect),
const wxRect& destRect) const
{
destDC.SetClippingRegion(destRect);
if (wxGetApp().GetBackgroundBitmap().IsOk())
wxGetApp().TileBitmap(destRect, destDC, wxGetApp().GetBackgroundBitmap());
m_canvas->DrawShapes(destDC);
return true;
}
#endif