]> git.saurik.com Git - wxWidgets.git/blobdiff - samples/drawing/drawing.cpp
Minor changes, just trim trailing spaces in webview code.
[wxWidgets.git] / samples / drawing / drawing.cpp
index 8da014ba7226a875ea908532d29dae918fcdc2ec..308ea82388d98235f829804beef5e284735626cf 100644 (file)
@@ -37,6 +37,7 @@
 #include "wx/overlay.h"
 #include "wx/graphics.h"
 #include "wx/filename.h"
+#include "wx/metafile.h"
 
 #define TEST_CAIRO_EVERYWHERE 0
 
@@ -45,8 +46,8 @@
 // ----------------------------------------------------------------------------
 
 // the application icon
-#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) || defined(__WXX11__)
-    #include "mondrian.xpm"
+#ifndef wxHAS_IMAGES_IN_RESOURCES
+    #include "../sample.xpm"
 #endif
 
 // ----------------------------------------------------------------------------
@@ -101,6 +102,8 @@ public:
 #if wxUSE_GRAPHICS_CONTEXT
     void OnGraphicContext(wxCommandEvent& event);
 #endif
+    void OnCopy(wxCommandEvent& event);
+    void OnSave(wxCommandEvent& event);
     void OnShow(wxCommandEvent &event);
     void OnOption(wxCommandEvent &event);
 
@@ -146,6 +149,7 @@ public:
 #if wxUSE_GRAPHICS_CONTEXT
     void UseGraphicContext(bool use) { m_useContext = use; Refresh(); }
 #endif
+    void Draw(wxDC& dc);
 
 protected:
     enum DrawMode
@@ -224,6 +228,8 @@ enum
 #if wxUSE_GRAPHICS_CONTEXT
     File_GraphicContext,
 #endif
+    File_Copy,
+    File_Save,
 
     MenuOption_First,
 
@@ -345,11 +351,10 @@ bool MyApp::OnInit()
 
     // Create the main application window
     MyFrame *frame = new MyFrame(wxT("Drawing sample"),
-                                 wxPoint(50, 50), wxSize(550, 340));
+                                 wxDefaultPosition, wxSize(550, 840));
 
-    // Show it and tell the application that it's our main window
+    // Show it
     frame->Show(true);
-    SetTopWindow(frame);
 
     if ( !LoadImages() )
     {
@@ -360,27 +365,22 @@ bool MyApp::OnInit()
         // still continue, the sample can be used without images too if they're
         // missing for whatever reason
     }
+#if wxUSE_LIBPNG
+      wxImage::AddHandler( new wxPNGHandler );
+#endif
 
     return true;
 }
 
 void MyApp::DeleteBitmaps()
 {
-    delete gs_bmpNoMask;
-    delete gs_bmpWithColMask;
-    delete gs_bmpMask;
-    delete gs_bmpWithMask;
-    delete gs_bmp4;
-    delete gs_bmp4_mono;
-    delete gs_bmp36;
-
-    gs_bmpNoMask = NULL;
-    gs_bmpWithColMask = NULL;
-    gs_bmpMask = NULL;
-    gs_bmpWithMask = NULL;
-    gs_bmp4 = NULL;
-    gs_bmp4_mono = NULL;
-    gs_bmp36 = NULL;
+    wxDELETE(gs_bmpNoMask);
+    wxDELETE(gs_bmpWithColMask);
+    wxDELETE(gs_bmpMask);
+    wxDELETE(gs_bmpWithMask);
+    wxDELETE(gs_bmp4);
+    wxDELETE(gs_bmp4_mono);
+    wxDELETE(gs_bmp36);
 }
 
 // ----------------------------------------------------------------------------
@@ -428,7 +428,22 @@ void MyCanvas::DrawTestBrushes(wxDC& dc)
     y += HEIGHT;
     dc.SetBrush(wxBrush(*wxRED, wxCROSSDIAG_HATCH));
     dc.DrawRectangle(x, y, WIDTH, HEIGHT);
-    dc.DrawText(wxT("Hatched red"), x + 10, y + 10);
+    dc.DrawText(wxT("Diagonally hatched red"), x + 10, y + 10);
+
+    y += HEIGHT;
+    dc.SetBrush(wxBrush(*wxBLUE, wxCROSS_HATCH));
+    dc.DrawRectangle(x, y, WIDTH, HEIGHT);
+    dc.DrawText(wxT("Cross hatched blue"), x + 10, y + 10);
+
+    y += HEIGHT;
+    dc.SetBrush(wxBrush(*wxCYAN, wxVERTICAL_HATCH));
+    dc.DrawRectangle(x, y, WIDTH, HEIGHT);
+    dc.DrawText(wxT("Vertically hatched cyan"), x + 10, y + 10);
+
+    y += HEIGHT;
+    dc.SetBrush(wxBrush(*wxBLACK, wxHORIZONTAL_HATCH));
+    dc.DrawRectangle(x, y, WIDTH, HEIGHT);
+    dc.DrawText(wxT("Horizontally hatched black"), x + 10, y + 10);
 
     y += HEIGHT;
     dc.SetBrush(wxBrush(*gs_bmpMask));
@@ -539,19 +554,29 @@ void MyCanvas::DrawTestLines( int x, int y, int width, wxDC &dc )
 
 void MyCanvas::DrawDefault(wxDC& dc)
 {
-    // mark the origin
-    dc.DrawCircle(0, 0, 10);
-
-#if !defined(wxMAC_USE_CORE_GRAPHICS) || !wxMAC_USE_CORE_GRAPHICS
-    // GetPixel and FloodFill not supported by Mac OS X CoreGraphics
-    // (FloodFill uses Blit from a non-wxMemoryDC)
-    //flood fill using brush, starting at 1,1 and replacing whatever colour we find there
-    dc.SetBrush(wxBrush(wxColour(128,128,0), wxSOLID));
-
-    wxColour tmpColour ;
-    dc.GetPixel(1,1, &tmpColour);
-    dc.FloodFill(1,1, tmpColour, wxFLOOD_SURFACE);
-#endif
+    // Draw circle centered at the origin, then flood fill it with a different
+    // color. Done with a wxMemoryDC because Blit (used by generic
+    // wxDoFloodFill) from a window that is being painted gives unpredictable
+    // results on wxGTK
+    {
+        wxImage img(21, 21, false);
+        img.Clear(1);
+        wxBitmap bmp(img);
+        {
+            wxMemoryDC mdc(bmp);
+            mdc.SetBrush(dc.GetBrush());
+            mdc.SetPen(dc.GetPen());
+            mdc.DrawCircle(10, 10, 10);
+            wxColour c;
+            if (mdc.GetPixel(11, 11, &c))
+            {
+                mdc.SetBrush(wxColour(128, 128, 0));
+                mdc.FloodFill(11, 11, c, wxFLOOD_SURFACE);
+            }
+        }
+        bmp.SetMask(new wxMask(bmp, wxColour(1, 1, 1)));
+        dc.DrawBitmap(bmp, -10, -10, true);
+    }
 
     dc.DrawCheckMark(5, 80, 15, 15);
     dc.DrawCheckMark(25, 80, 30, 30);
@@ -574,7 +599,7 @@ void MyCanvas::DrawDefault(wxDC& dc)
     // rectangle above)
     //dc.SetBrush( *wxTRANSPARENT_BRUSH );
 
-    if (m_smile_bmp.Ok())
+    if (m_smile_bmp.IsOk())
         dc.DrawBitmap(m_smile_bmp, x + rectSize - 20, rectSize - 10, true);
 
     dc.SetBrush( *wxBLACK_BRUSH );
@@ -772,7 +797,7 @@ void MyCanvas::DrawText(wxDC& dc)
     wxCoord height;
     wxCoord descent;
     dc.GetTextExtent( wxT("This is Swiss 18pt text."), &length, &height, &descent );
-    text.Printf( wxT("Dimensions are length %ld, height %ld, descent %ld"), length, height, descent );
+    text.Printf( wxT("Dimensions are length %d, height %d, descent %d"), length, height, descent );
     dc.DrawText( text, 110, 80 );
 
     text.Printf( wxT("CharHeight() returns: %d"), dc.GetCharHeight() );
@@ -797,6 +822,9 @@ void MyCanvas::DrawText(wxDC& dc)
     y += height;
     dc.DrawRectangle( 110, y, 100, height );
     dc.DrawText( wxT("Another visible text"), 110, y );
+
+    y += height;
+    dc.DrawText("And\nmore\ntext on\nmultiple\nlines", 110, y);
 }
 
 static const struct
@@ -894,7 +922,7 @@ void MyCanvas::DrawWithLogicalOps(wxDC& dc)
 
 #if wxUSE_GRAPHICS_CONTEXT
 #ifdef __WXGTK20__
-void MyCanvas::DrawAlpha(wxDC& no_dc)
+void MyCanvas::DrawAlpha(wxDC& WXUNUSED(dummyDC))
 #else
 void MyCanvas::DrawAlpha(wxDC& dc)
 #endif
@@ -955,7 +983,7 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
 {
     wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
     gc->SetFont(font,*wxBLACK);
-    
+
     // make a path that contains a circle and some lines, centered at 0,0
     wxGraphicsPath path = gc->CreatePath() ;
     path.AddCircle( 0, 0, BASE2 );
@@ -965,14 +993,14 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
     path.AddLineToPoint(BASE2, 0);
     path.CloseSubpath();
     path.AddRectangle(-BASE4, -BASE4/2, BASE2, BASE4);
-    
+
     // Now use that path to demonstrate various capbilites of the grpahics context
-    gc->PushState(); // save current translation/scale/other state 
+    gc->PushState(); // save current translation/scale/other state
     gc->Translate(60, 75); // reposition the context origin
 
     gc->SetPen(wxPen("navy", 1));
     gc->SetBrush(wxBrush("pink"));
-    
+
     for( int i = 0 ; i < 3 ; ++i )
     {
         wxString label;
@@ -1005,11 +1033,11 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
         }
         gc->Translate(2*BASE, 0);
     }
-            
+
     gc->PopState(); // restore saved state
     gc->PushState(); // save it again
     gc->Translate(60, 200); // offset to the lower part of the window
-        
+
     gc->DrawText("Scale", 0, -BASE2);
     gc->Translate(0, 20);
 
@@ -1017,14 +1045,14 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
     for( int i = 0 ; i < 8 ; ++i )
     {
         gc->Scale(1.08, 1.08); // increase scale by 8%
-        gc->Translate(5,5);    
+        gc->Translate(5,5);
         gc->DrawPath(path);
     }
 
     gc->PopState(); // restore saved state
     gc->PushState(); // save it again
-    gc->Translate(400, 200); 
-    
+    gc->Translate(400, 200);
+
     gc->DrawText("Rotate", 0, -BASE2);
 
     // Move the origin over to the next location
@@ -1034,16 +1062,16 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
     // and changing colors as we go
     for ( int angle = 0 ; angle < 360 ; angle += 30 )
     {
-        gc->PushState(); // save this new current state so we can 
+        gc->PushState(); // save this new current state so we can
         //  pop back to it at the end of the loop
         wxImage::RGBValue val = wxImage::HSVtoRGB(wxImage::HSVValue(float(angle)/360, 1, 1));
         gc->SetBrush(wxBrush(wxColour(val.red, val.green, val.blue, 64)));
         gc->SetPen(wxPen(wxColour(val.red, val.green, val.blue, 128)));
-        
+
         // use translate to artfully reposition each drawn path
         gc->Translate(1.5 * BASE2 * cos(DegToRad(angle)),
                      1.5 * BASE2 * sin(DegToRad(angle)));
-                     
+
         // use Rotate to rotate the path
         gc->Rotate(DegToRad(angle));
 
@@ -1052,8 +1080,15 @@ void MyCanvas::DrawGraphics(wxGraphicsContext* gc)
         gc->PopState();
     }
     gc->PopState();
+
+    gc->PushState();
+    gc->Translate(60, 400);
+    gc->DrawText("Scaled smiley inside a square", 0, 0);
+    gc->DrawRectangle(BASE2, BASE2, 100, 100);
+    gc->DrawBitmap(m_smile_bmp, BASE2, BASE2, 100, 100);
+    gc->PopState();
 }
-#endif
+#endif // wxUSE_GRAPHICS_CONTEXT
 
 void MyCanvas::DrawCircles(wxDC& dc)
 {
@@ -1231,6 +1266,7 @@ void MyCanvas::DrawGradients(wxDC& dc)
     r.Offset(0, TEXT_HEIGHT);
     dc.GradientFillLinear(r, *wxWHITE, *wxBLUE, wxUP);
 
+    wxRect  gfr = wxRect(r);
 
     // RHS: concentric
     r = wxRect(200, 10, 50, 50);
@@ -1283,6 +1319,127 @@ void MyCanvas::DrawGradients(wxDC& dc)
     dc.DrawRectangle(r4);
     r4.Deflate(1);
     dc.GradientFillLinear(r4, wxColour(0,0,0), wxColour(0,255,0), wxWEST);
+
+#if wxUSE_GRAPHICS_CONTEXT
+    if (m_useContext)
+    {
+        wxGCDC                      &gdc = (wxGCDC&)dc;
+        wxGraphicsContext           *gc = gdc.GetGraphicsContext();
+        wxGraphicsPath              pth;
+        wxGraphicsGradientStops     stops;
+
+        gfr.Offset(0, gfr.height + 10);
+        dc.DrawText(wxT("Linear Gradient with Stops"), gfr.x, gfr.y);
+        gfr.Offset(0, TEXT_HEIGHT);
+
+        stops = wxGraphicsGradientStops(wxColour(255,0,0), wxColour(0,0,255));
+        stops.Add(wxColour(255,255,0), 0.33f);
+        stops.Add(wxColour(0,255,0), 0.67f);
+
+        gc->SetBrush(gc->CreateLinearGradientBrush(gfr.x, gfr.y,
+                                                   gfr.x + gfr.width, gfr.y + gfr.height,
+                                                   stops));
+        pth = gc->CreatePath();
+        pth.MoveToPoint(gfr.x,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y+gfr.height);
+        pth.AddLineToPoint(gfr.x,gfr.y+gfr.height);
+        pth.CloseSubpath();
+        gc->FillPath(pth);
+
+        gfr.Offset(0, gfr.height + 10);
+        dc.DrawText(wxT("Radial Gradient with Stops"), gfr.x, gfr.y);
+        gfr.Offset(0, TEXT_HEIGHT);
+
+        gc->SetBrush(gc->CreateRadialGradientBrush(gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.width / 2,
+                                                   stops));
+        pth = gc->CreatePath();
+        pth.MoveToPoint(gfr.x,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y+gfr.height);
+        pth.AddLineToPoint(gfr.x,gfr.y+gfr.height);
+        pth.CloseSubpath();
+        gc->FillPath(pth);
+
+        gfr.Offset(0, gfr.height + 10);
+        dc.DrawText(wxT("Linear Gradient with Stops and Gaps"), gfr.x, gfr.y);
+        gfr.Offset(0, TEXT_HEIGHT);
+
+        stops = wxGraphicsGradientStops(wxColour(255,0,0), wxColour(0,0,255));
+        stops.Add(wxColour(255,255,0), 0.33f);
+        stops.Add(wxTransparentColour, 0.33f);
+        stops.Add(wxTransparentColour, 0.67f);
+        stops.Add(wxColour(0,255,0), 0.67f);
+
+        gc->SetBrush(gc->CreateLinearGradientBrush(gfr.x, gfr.y + gfr.height,
+                                                   gfr.x + gfr.width, gfr.y,
+                                                   stops));
+        pth = gc->CreatePath();
+        pth.MoveToPoint(gfr.x,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y+gfr.height);
+        pth.AddLineToPoint(gfr.x,gfr.y+gfr.height);
+        pth.CloseSubpath();
+        gc->FillPath(pth);
+
+        gfr.Offset(0, gfr.height + 10);
+        dc.DrawText(wxT("Radial Gradient with Stops and Gaps"), gfr.x, gfr.y);
+        gfr.Offset(0, TEXT_HEIGHT);
+
+        gc->SetBrush(gc->CreateRadialGradientBrush(gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.width / 2,
+                                                   stops));
+        pth = gc->CreatePath();
+        pth.MoveToPoint(gfr.x,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y+gfr.height);
+        pth.AddLineToPoint(gfr.x,gfr.y+gfr.height);
+        pth.CloseSubpath();
+        gc->FillPath(pth);
+
+        gfr.Offset(0, gfr.height + 10);
+        dc.DrawText(wxT("Gradients with Stops and Transparency"), gfr.x, gfr.y);
+        gfr.Offset(0, TEXT_HEIGHT);
+
+        stops = wxGraphicsGradientStops(wxColour(255,0,0), wxTransparentColour);
+        stops.Add(wxColour(255,0,0), 0.33f);
+        stops.Add(wxTransparentColour, 0.33f);
+        stops.Add(wxTransparentColour, 0.67f);
+        stops.Add(wxColour(0,0,255), 0.67f);
+        stops.Add(wxColour(0,0,255), 1.0f);
+
+        pth = gc->CreatePath();
+        pth.MoveToPoint(gfr.x,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y);
+        pth.AddLineToPoint(gfr.x + gfr.width,gfr.y+gfr.height);
+        pth.AddLineToPoint(gfr.x,gfr.y+gfr.height);
+        pth.CloseSubpath();
+
+        gc->SetBrush(gc->CreateRadialGradientBrush(gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.x + gfr.width / 2,
+                                                   gfr.y + gfr.height / 2,
+                                                   gfr.width / 2,
+                                                   stops));
+        gc->FillPath(pth);
+
+        stops = wxGraphicsGradientStops(wxColour(255,0,0, 128), wxColour(0,0,255, 128));
+        stops.Add(wxColour(255,255,0,128), 0.33f);
+        stops.Add(wxColour(0,255,0,128), 0.67f);
+
+        gc->SetBrush(gc->CreateLinearGradientBrush(gfr.x, gfr.y,
+                                                   gfr.x + gfr.width, gfr.y,
+                                                   stops));
+        gc->FillPath(pth);
+    }
+#endif // wxUSE_GRAPHICS_CONTEXT
 }
 
 void MyCanvas::DrawRegions(wxDC& dc)
@@ -1330,7 +1487,7 @@ void MyCanvas::DrawRegionsHelper(wxDC& dc, wxCoord x, bool firstTime)
     dc.SetBrush( *wxGREY_BRUSH );
     dc.DrawRectangle( x, y, 310, 310 );
 
-    if (m_smile_bmp.Ok())
+    if (m_smile_bmp.IsOk())
     {
         dc.DrawBitmap( m_smile_bmp, x + 150, y + 150, true );
         dc.DrawBitmap( m_smile_bmp, x + 130, y + 10,  true );
@@ -1340,21 +1497,47 @@ void MyCanvas::DrawRegionsHelper(wxDC& dc, wxCoord x, bool firstTime)
     }
 }
 
-#if TEST_CAIRO_EVERYWHERE
-extern wxGraphicsRenderer* gCairoRenderer;
-#endif
-
 void MyCanvas::OnPaint(wxPaintEvent &WXUNUSED(event))
 {
     wxPaintDC pdc(this);
+    Draw(pdc);
+}
 
+void MyCanvas::Draw(wxDC& pdc)
+{
 #if wxUSE_GRAPHICS_CONTEXT
-#if TEST_CAIRO_EVERYWHERE
     wxGCDC gdc;
-    gdc.SetGraphicsContext( gCairoRenderer->CreateContext( pdc ) );
+    wxGraphicsRenderer* const renderer = wxGraphicsRenderer::
+#if TEST_CAIRO_EVERYWHERE
+        GetCairoRenderer()
 #else
-     wxGCDC gdc( pdc ) ;
+        GetDefaultRenderer()
+#endif
+        ;
+
+    wxGraphicsContext* context;
+    if ( wxPaintDC *paintdc = wxDynamicCast(&pdc, wxPaintDC) )
+    {
+        context = renderer->CreateContext(*paintdc);
+    }
+    else if ( wxMemoryDC *memdc = wxDynamicCast(&pdc, wxMemoryDC) )
+    {
+        context = renderer->CreateContext(*memdc);
+    }
+#if wxUSE_METAFILE && defined(wxMETAFILE_IS_ENH)
+    else if ( wxMetafileDC *metadc = wxDynamicCast(&pdc, wxMetafileDC) )
+    {
+        context = renderer->CreateContext(*metadc);
+    }
 #endif
+    else
+    {
+        wxFAIL_MSG( "Unknown wxDC kind" );
+        return;
+    }
+
+    gdc.SetGraphicsContext(context);
+
     wxDC &dc = m_useContext ? (wxDC&) gdc : (wxDC&) pdc ;
 #else
     wxDC &dc = pdc ;
@@ -1365,15 +1548,15 @@ void MyCanvas::OnPaint(wxPaintEvent &WXUNUSED(event))
     m_owner->PrepareDC(dc);
 
     dc.SetBackgroundMode( m_owner->m_backgroundMode );
-    if ( m_owner->m_backgroundBrush.Ok() )
+    if ( m_owner->m_backgroundBrush.IsOk() )
         dc.SetBackground( m_owner->m_backgroundBrush );
-    if ( m_owner->m_colourForeground.Ok() )
+    if ( m_owner->m_colourForeground.IsOk() )
         dc.SetTextForeground( m_owner->m_colourForeground );
-    if ( m_owner->m_colourBackground.Ok() )
+    if ( m_owner->m_colourBackground.IsOk() )
         dc.SetTextBackground( m_owner->m_colourBackground );
 
     if ( m_owner->m_textureBackground) {
-        if ( ! m_owner->m_backgroundBrush.Ok() ) {
+        if ( ! m_owner->m_backgroundBrush.IsOk() ) {
             wxColour clr(0,128,0);
             wxBrush b(clr, wxSOLID);
             dc.SetBackground(b);
@@ -1474,7 +1657,7 @@ void MyCanvas::OnMouseMove(wxMouseEvent &event)
         str.Printf( wxT("Current mouse position: %d,%d"), (int)x, (int)y );
         m_owner->SetStatusText( str );
     }
-    
+
     if ( m_rubberBand )
     {
         int x,y, xx, yy ;
@@ -1504,8 +1687,8 @@ void MyCanvas::OnMouseMove(wxMouseEvent &event)
 
 void MyCanvas::OnMouseDown(wxMouseEvent &event)
 {
-       int x,y,xx,yy ;
-       event.GetPosition(&x,&y);
+    int x,y,xx,yy ;
+    event.GetPosition(&x,&y);
     CalcUnscrolledPosition( x, y, &xx, &yy );
     m_anchorpoint = wxPoint( xx , yy ) ;
     m_currentpoint = m_anchorpoint ;
@@ -1527,15 +1710,15 @@ void MyCanvas::OnMouseUp(wxMouseEvent &event)
         m_overlay.Reset();
         m_rubberBand = false;
 
-        int x,y,xx,yy ;
-        event.GetPosition(&x,&y);
-        CalcUnscrolledPosition( x, y, &xx, &yy );
-        
-        wxString str;
-        str.Printf( wxT("Rectangle selection from %d,%d to %d,%d"), 
-            m_anchorpoint.x, m_anchorpoint.y , (int)xx, (int)yy );
-        wxMessageBox( str , wxT("Rubber-Banding") );
+        wxPoint endpoint = CalcUnscrolledPosition(event.GetPosition());
 
+        // Don't pop up the message box if nothing was actually selected.
+        if ( endpoint != m_anchorpoint )
+        {
+            wxLogMessage("Selected rectangle from (%d, %d) to (%d, %d)",
+                         m_anchorpoint.x, m_anchorpoint.y,
+                         endpoint.x, endpoint.y);
+        }
     }
 }
 
@@ -1553,6 +1736,8 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
 #if wxUSE_GRAPHICS_CONTEXT
     EVT_MENU      (File_GraphicContext, MyFrame::OnGraphicContext)
 #endif
+    EVT_MENU      (File_Copy,     MyFrame::OnCopy)
+    EVT_MENU      (File_Save,     MyFrame::OnSave)
 
     EVT_MENU_RANGE(MenuShow_First,   MenuShow_Last,   MyFrame::OnShow)
 
@@ -1565,7 +1750,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
                  wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE)
 {
     // set the frame icon
-    SetIcon(wxICON(mondrian));
+    SetIcon(wxICON(sample));
 
     wxMenu *menuFile = new wxMenu;
     menuFile->Append(File_ShowDefault, wxT("&Default screen\tF1"));
@@ -1581,7 +1766,7 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
 #if wxUSE_GRAPHICS_CONTEXT
     menuFile->Append(File_ShowAlpha, wxT("&Alpha screen\tF10"));
 #endif
-    menuFile->Append(File_ShowSplines, wxT("&Splines screen\tF11"));
+    menuFile->Append(File_ShowSplines, wxT("Spl&ines screen\tF11"));
     menuFile->Append(File_ShowGradients, wxT("&Gradients screen\tF12"));
 #if wxUSE_GRAPHICS_CONTEXT
      menuFile->Append(File_ShowGraphics, wxT("&Graphics screen"));
@@ -1592,7 +1777,12 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
     menuFile->AppendCheckItem(File_GraphicContext, wxT("&Use GraphicContext\tCtrl-Y"), wxT("Use GraphicContext"));
 #endif
     menuFile->AppendSeparator();
-    menuFile->Append(File_About, wxT("&About...\tCtrl-A"), wxT("Show about dialog"));
+#if wxUSE_METAFILE && defined(wxMETAFILE_IS_ENH)
+    menuFile->Append(File_Copy, wxT("Copy to clipboard"));
+#endif
+    menuFile->Append(File_Save, wxT("&Save...\tCtrl-S"), wxT("Save drawing to file"));
+    menuFile->AppendSeparator();
+    menuFile->Append(File_About, wxT("&About\tCtrl-A"), wxT("Show about dialog"));
     menuFile->AppendSeparator();
     menuFile->Append(File_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));
 
@@ -1658,8 +1848,8 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
     m_xAxisReversed =
     m_yAxisReversed = false;
     m_backgroundMode = wxSOLID;
-    m_colourForeground = *wxRED;
-    m_colourBackground = *wxBLUE;
+    m_colourForeground = *wxBLACK;
+    m_colourBackground = *wxLIGHT_GREY;
     m_textureBackground = false;
 
     m_canvas = new MyCanvas( this );
@@ -1698,6 +1888,38 @@ void MyFrame::OnGraphicContext(wxCommandEvent& event)
 }
 #endif
 
+void MyFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
+{
+#if wxUSE_METAFILE && defined(wxMETAFILE_IS_ENH)
+    wxMetafileDC dc;
+    if (!dc.IsOk())
+        return;
+    m_canvas->Draw(dc);
+    wxMetafile *mf = dc.Close();
+    if (!mf)
+        return;
+    mf->SetClipboard();
+    delete mf;
+#endif
+}
+
+void MyFrame::OnSave(wxCommandEvent& WXUNUSED(event))
+{
+    wxFileDialog dlg(this, wxT("Save as bitmap"), wxT(""), wxT(""),
+#if wxUSE_LIBPNG
+                     wxT("PNG image (*.png)|*.png;*.PNG|")
+#endif
+                     wxT("Bitmap image (*.bmp)|*.bmp;*.BMP"),
+                     wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+    if (dlg.ShowModal() == wxID_OK)
+    {
+        wxBitmap bmp(500, 800);
+        wxMemoryDC mdc(bmp);
+        m_canvas->Draw(mdc);
+        bmp.ConvertToImage().SaveFile(dlg.GetPath());
+    }
+}
+
 void MyFrame::OnShow(wxCommandEvent& event)
 {
     m_canvas->ToShow(event.GetId());
@@ -1778,7 +2000,7 @@ void MyFrame::OnOption(wxCommandEvent& event)
         case Colour_Background:
             {
                 wxColour col = SelectColour();
-                if ( col.Ok() )
+                if ( col.IsOk() )
                 {
                     m_backgroundBrush.SetColour(col);
                 }