///////////////////////////////////////////////////////////////////////////// // Name: dragimag.cpp // Purpose: wxDragImage sample // Author: Julian Smart // Modified by: // Created: 28/2/2000 // RCS-ID: $Id$ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "wx/image.h" // Under Windows, change this to 1 // to use wxGenericDragImage #define wxUSE_GENERIC_DRAGIMAGE 1 #if wxUSE_GENERIC_DRAGIMAGE #include "wx/generic/dragimgg.h" #define wxDragImage wxGenericDragImage #else #include "wx/dragimag.h" #endif #include "dragimag.h" #if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) #include "mondrian.xpm" #include "dragicon.xpm" #endif // main program IMPLEMENT_APP(MyApp) // MyCanvas IMPLEMENT_CLASS(MyCanvas, wxScrolledWindow) BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow) EVT_PAINT(MyCanvas::OnPaint) EVT_ERASE_BACKGROUND(MyCanvas::OnEraseBackground) EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent) END_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 = (DragShape*) NULL; m_dragImage = (wxDragImage*) NULL; m_currentlyHighlighted = (DragShape*) NULL; } 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().Ok()) { 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(); delete m_dragImage; m_dragImage = NULL; wxClientDC dc(this); if (m_currentlyHighlighted) { m_currentlyHighlighted->Draw(dc); } m_draggedShape->SetShow(TRUE); m_draggedShape->Draw(dc); m_currentlyHighlighted = (DragShape*) NULL; m_draggedShape = (DragShape*) NULL; } 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); wxClientDC dc(this); EraseShape(m_draggedShape, dc); DrawShapes(dc); switch (m_draggedShape->GetDragMethod()) { case SHAPE_DRAG_BITMAP: { m_dragImage = new wxDragImage(m_draggedShape->GetBitmap(), wxCursor(wxCURSOR_HAND)); break; } case SHAPE_DRAG_TEXT: { m_dragImage = new wxDragImage(wxString("Dragging some test text"), wxCursor(wxCURSOR_HAND)); break; } case SHAPE_DRAG_ICON: { // Can anyone explain why this test is necessary, // to prevent a gcc error? #ifdef __WXMOTIF__ wxIcon icon(dragicon_xpm); #else wxIcon icon(wxICON(dragicon)); #endif m_dragImage = new wxDragImage(icon, 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)) { delete m_dragImage; m_dragImage = (wxDragImage*) NULL; 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 == (DragShape*) NULL) || (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) { wxClientDC clientDC(this); m_currentlyHighlighted->Draw(clientDC); m_currentlyHighlighted = (DragShape*) NULL; } if (mustHighlightNew) { wxClientDC clientDC(this); m_currentlyHighlighted = onShape; m_currentlyHighlighted->Draw(clientDC, wxINVERT); } // Move and show the image again m_dragImage->Move(event.GetPosition()); if (mustUnhighlightOld || mustHighlightNew) m_dragImage->Show(); } } } void MyCanvas::DrawShapes(wxDC& dc) { wxNode* node = m_displayList.First(); while (node) { DragShape* shape = (DragShape*) node->Data(); if (shape->IsShown()) shape->Draw(dc); node = node->Next(); } } 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() { wxNode* node = m_displayList.First(); while (node) { DragShape* shape = (DragShape*) node->Data(); delete shape; node = node->Next(); } m_displayList.Clear(); } DragShape* MyCanvas::FindShape(const wxPoint& pt) const { wxNode* node = m_displayList.First(); while (node) { DragShape* shape = (DragShape*) node->Data(); if (shape->HitTest(pt)) return shape; node = node->Next(); } return (DragShape*) NULL; } // MyFrame IMPLEMENT_DYNAMIC_CLASS( MyFrame, wxFrame ) BEGIN_EVENT_TABLE(MyFrame,wxFrame) EVT_MENU (wxID_ABOUT, MyFrame::OnAbout) EVT_MENU (wxID_EXIT, MyFrame::OnQuit) END_EVENT_TABLE() MyFrame::MyFrame() : wxFrame( (wxFrame *)NULL, -1, "wxDragImage sample", wxPoint(20,20), wxSize(470,360) ) { wxMenu *file_menu = new wxMenu(); file_menu->Append( wxID_ABOUT, "&About..."); file_menu->Append( TEST_USE_SCREEN, "&Use whole screen for dragging", "Use whole screen", TRUE); file_menu->Append( wxID_EXIT, "E&xit"); wxMenuBar *menu_bar = new wxMenuBar(); menu_bar->Append(file_menu, "&File"); SetMenuBar( menu_bar ); CreateStatusBar(2); int widths[] = { -1, 100 }; SetStatusWidths( 2, widths ); m_canvas = new MyCanvas( this, -1, 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 //----------------------------------------------------------------------------- BEGIN_EVENT_TABLE(MyApp, wxApp) EVT_MENU(TEST_USE_SCREEN, MyApp::OnUseScreen) END_EVENT_TABLE() MyApp::MyApp() { // Drag across whole screen m_useScreen = FALSE; } bool MyApp::OnInit() { #if wxUSE_LIBPNG wxImage::AddHandler( new wxPNGHandler ); #endif wxImage image; if (image.LoadFile("backgrnd.png", wxBITMAP_TYPE_PNG)) { m_background = image.ConvertToBitmap(); } MyFrame *frame = new MyFrame(); wxString rootName("shape0"); int i; for (i = 1; i < 4; i++) { wxString filename; filename.Printf(wxT("%s%d.png"), (const wxChar*)rootName, i); if (image.LoadFile(filename, wxBITMAP_TYPE_PNG)) { DragShape* newShape = new DragShape(image.ConvertToBitmap()); 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 Motif or 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, 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& 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; } DragShape::~DragShape() { } bool DragShape::HitTest(const wxPoint& pt) const { wxRect rect(GetRect()); return rect.Inside(pt.x, pt.y); } bool DragShape::Draw(wxDC& dc, int op) { if (m_bitmap.Ok()) { wxMemoryDC memDC; memDC.SelectObject(m_bitmap); dc.Blit(m_pos.x, m_pos.y, m_bitmap.GetWidth(), m_bitmap.GetHeight(), & memDC, 0, 0, op, TRUE); return TRUE; } else return FALSE; }