]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/dcbase.cpp
don't write the strings to the stream one char at a time, it's *horribly* slow
[wxWidgets.git] / src / common / dcbase.cpp
index da3b41768b859ecb776e99daab77a77c057230cb..83e9980940dd5beb368f971292dda3e1c6c7352a 100644 (file)
@@ -1,32 +1,73 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        dc.cpp
-// Purpose:     wxDC Class
-// Author:      Brian Macy
+// Name:        common/dcbase.cpp
+// Purpose:     generic methods of the wxDC Class
+// Author:      Vadim Zeitlin
 // Modified by:
 // Created:     05/25/99
 // RCS-ID:      $Id$
-// Copyright:   (c) Julian Smart and Markus Holzem
-// Licence:    wxWindows license
+// Copyright:   (c) wxWindows team
+// Licence:     wxWindows license
 /////////////////////////////////////////////////////////////////////////////
 
+// ============================================================================
+// declarations
+// ============================================================================
+
 #ifdef __GNUG__
-#pragma implementation "dcbase.h"
+    #pragma implementation "dcbase.h"
 #endif
 
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-#pragma hdrstop
-#endif
-
-#ifndef WX_PRECOMP
-#include "wx/defs.h"
+    #pragma hdrstop
 #endif
 
 #include "wx/dc.h"
 
-void wxDCBase::DrawLines(const wxList *list, long xoffset, long yoffset)
+#include <math.h>
+
+// bool wxDCBase::sm_cacheing = FALSE;
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// special symbols
+// ----------------------------------------------------------------------------
+
+void wxDCBase::DoDrawCheckMark(wxCoord x1, wxCoord y1,
+                               wxCoord width, wxCoord height)
+{
+    wxCHECK_RET( Ok(), wxT("invalid window dc") );
+
+    wxCoord x2 = x1 + width,
+            y2 = y1 + height;
+
+    // this is to yield width of 3 for width == height == 10
+    SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
+
+    // we're drawing a scaled version of wx/generic/tick.xpm here
+    wxCoord x3 = x1 + (4*width) / 10,   // x of the tick bottom
+            y3 = y1 + height / 2;       // y of the left tick branch
+    DoDrawLine(x1, y3, x3, y2);
+    DoDrawLine(x3, y2, x2, y1);
+
+    CalcBoundingBox(x1, y1);
+    CalcBoundingBox(x2, y2);
+}
+
+// ----------------------------------------------------------------------------
+// line/polygons
+// ----------------------------------------------------------------------------
+
+void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
 {
     int n = list->Number();
     wxPoint *points = new wxPoint[n];
@@ -46,7 +87,7 @@ void wxDCBase::DrawLines(const wxList *list, long xoffset, long yoffset)
 
 
 void wxDCBase::DrawPolygon(const wxList *list,
-                           long xoffset, long yoffset,
+                           wxCoord xoffset, wxCoord yoffset,
                            int fillStyle)
 {
     int n = list->Number();
@@ -65,11 +106,14 @@ void wxDCBase::DrawPolygon(const wxList *list,
     delete [] points;
 }
 
+// ----------------------------------------------------------------------------
+// splines
+// ----------------------------------------------------------------------------
 
-#ifdef wxUSE_SPLINES
+#if wxUSE_SPLINES
 
 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
-void wxDCBase::DrawSpline(long x1, long y1, long x2, long y2, long x3, long y3)
+void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
 {
     wxList point_list;
 
@@ -105,4 +149,398 @@ void wxDCBase::DrawSpline(int n, wxPoint points[])
     DrawSpline(&list);
 }
 
-#endif // wxUSE_SPLINES
\ No newline at end of file
+// ----------------------------------- spline code ----------------------------------------
+
+void wx_quadratic_spline(double a1, double b1, double a2, double b2,
+                         double a3, double b3, double a4, double b4);
+void wx_clear_stack();
+int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
+        double *y3, double *x4, double *y4);
+void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
+          double x4, double y4);
+static bool wx_spline_add_point(double x, double y);
+static void wx_spline_draw_point_array(wxDCBase *dc);
+
+wxList wx_spline_point_list;
+
+#define                half(z1, z2)        ((z1+z2)/2.0)
+#define                THRESHOLD        5
+
+/* iterative version */
+
+void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
+                 double b4)
+{
+    register double  xmid, ymid;
+    double           x1, y1, x2, y2, x3, y3, x4, y4;
+
+    wx_clear_stack();
+    wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
+
+    while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
+        xmid = (double)half(x2, x3);
+        ymid = (double)half(y2, y3);
+        if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
+            fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
+            wx_spline_add_point( x1, y1 );
+            wx_spline_add_point( xmid, ymid );
+        } else {
+            wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
+                 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
+            wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
+                 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
+        }
+    }
+}
+
+/* utilities used by spline drawing routines */
+
+typedef struct wx_spline_stack_struct {
+    double           x1, y1, x2, y2, x3, y3, x4, y4;
+} Stack;
+
+#define         SPLINE_STACK_DEPTH             20
+static Stack    wx_spline_stack[SPLINE_STACK_DEPTH];
+static Stack   *wx_stack_top;
+static int      wx_stack_count;
+
+void wx_clear_stack()
+{
+    wx_stack_top = wx_spline_stack;
+    wx_stack_count = 0;
+}
+
+void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
+{
+    wx_stack_top->x1 = x1;
+    wx_stack_top->y1 = y1;
+    wx_stack_top->x2 = x2;
+    wx_stack_top->y2 = y2;
+    wx_stack_top->x3 = x3;
+    wx_stack_top->y3 = y3;
+    wx_stack_top->x4 = x4;
+    wx_stack_top->y4 = y4;
+    wx_stack_top++;
+    wx_stack_count++;
+}
+
+int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
+                  double *x3, double *y3, double *x4, double *y4)
+{
+    if (wx_stack_count == 0)
+        return (0);
+    wx_stack_top--;
+    wx_stack_count--;
+    *x1 = wx_stack_top->x1;
+    *y1 = wx_stack_top->y1;
+    *x2 = wx_stack_top->x2;
+    *y2 = wx_stack_top->y2;
+    *x3 = wx_stack_top->x3;
+    *y3 = wx_stack_top->y3;
+    *x4 = wx_stack_top->x4;
+    *y4 = wx_stack_top->y4;
+    return (1);
+}
+
+static bool wx_spline_add_point(double x, double y)
+{
+  wxPoint *point = new wxPoint ;
+  point->x = (int) x;
+  point->y = (int) y;
+  wx_spline_point_list.Append((wxObject*)point);
+  return TRUE;
+}
+
+static void wx_spline_draw_point_array(wxDCBase *dc)
+{
+  dc->DrawLines(&wx_spline_point_list, 0, 0 );
+  wxNode *node = wx_spline_point_list.First();
+  while (node)
+  {
+    wxPoint *point = (wxPoint *)node->Data();
+    delete point;
+    delete node;
+    node = wx_spline_point_list.First();
+  }
+}
+
+void wxDCBase::DoDrawSpline( wxList *points )
+{
+    wxCHECK_RET( Ok(), wxT("invalid window dc") );
+
+    wxPoint *p;
+    double           cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
+    double           x1, y1, x2, y2;
+
+    wxNode *node = points->First();
+    p = (wxPoint *)node->Data();
+
+    x1 = p->x;
+    y1 = p->y;
+
+    node = node->Next();
+    p = (wxPoint *)node->Data();
+
+    x2 = p->x;
+    y2 = p->y;
+    cx1 = (double)((x1 + x2) / 2);
+    cy1 = (double)((y1 + y2) / 2);
+    cx2 = (double)((cx1 + x2) / 2);
+    cy2 = (double)((cy1 + y2) / 2);
+
+    wx_spline_add_point(x1, y1);
+
+    while ((node = node->Next()) != NULL)
+    {
+        p = (wxPoint *)node->Data();
+        x1 = x2;
+        y1 = y2;
+        x2 = p->x;
+        y2 = p->y;
+        cx4 = (double)(x1 + x2) / 2;
+        cy4 = (double)(y1 + y2) / 2;
+        cx3 = (double)(x1 + cx4) / 2;
+        cy3 = (double)(y1 + cy4) / 2;
+
+        wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
+
+        cx1 = cx4;
+        cy1 = cy4;
+        cx2 = (double)(cx1 + x2) / 2;
+        cy2 = (double)(cy1 + y2) / 2;
+    }
+
+    wx_spline_add_point( cx1, cy1 );
+    wx_spline_add_point( x2, y2 );
+
+    wx_spline_draw_point_array( this );
+}
+
+#endif // wxUSE_SPLINES
+
+// ----------------------------------------------------------------------------
+// enhanced text drawing
+// ----------------------------------------------------------------------------
+
+void wxDCBase::GetMultiLineTextExtent(const wxString& text,
+                                      wxCoord *x,
+                                      wxCoord *y,
+                                      wxCoord *h,
+                                      wxFont *font)
+{
+    wxCoord widthTextMax = 0, widthLine,
+            heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
+
+    wxString curLine;
+    for ( const wxChar *pc = text; ; pc++ )
+    {
+        if ( *pc == _T('\n') || *pc == _T('\0') )
+        {
+            if ( curLine.empty() )
+            {
+                // we can't use GetTextExtent - it will return 0 for both width
+                // and height and an empty line should count in height
+                // calculation
+
+                // assume that this line has the same height as the previous
+                // one
+                if ( !heightLineDefault )
+                    heightLineDefault = heightLine;
+
+                if ( !heightLineDefault )
+                {
+                    // but we don't know it yet - choose something reasonable
+                    GetTextExtent(_T("W"), NULL, &heightLineDefault,
+                                  NULL, NULL, font);
+                }
+
+                heightTextTotal += heightLineDefault;
+            }
+            else
+            {
+                GetTextExtent(curLine, &widthLine, &heightLine,
+                              NULL, NULL, font);
+                if ( widthLine > widthTextMax )
+                    widthTextMax = widthLine;
+                heightTextTotal += heightLine;
+            }
+
+            if ( *pc == _T('\n') )
+            {
+               curLine.clear();
+            }
+            else
+            {
+               // the end of string
+               break;
+            }
+        }
+        else
+        {
+            curLine += *pc;
+        }
+    }
+
+    if ( x )
+        *x = widthTextMax;
+    if ( y )
+        *y = heightTextTotal;
+    if ( h )
+        *h = heightLine;
+}
+
+void wxDCBase::DrawLabel(const wxString& text,
+                         const wxBitmap& bitmap,
+                         const wxRect& rect,
+                         int alignment,
+                         int indexAccel,
+                         wxRect *rectBounding)
+{
+    // find the text position
+    wxCoord widthText, heightText, heightLine;
+    GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
+
+    wxCoord width, height;
+    if ( bitmap.Ok() )
+    {
+        width = widthText + bitmap.GetWidth();
+        height = bitmap.GetHeight();
+    }
+    else // no bitmap
+    {
+        width = widthText;
+        height = heightText;
+    }
+
+    wxCoord x, y;
+    if ( alignment & wxALIGN_RIGHT )
+    {
+        x = rect.GetRight() - width;
+    }
+    else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
+    {
+        x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
+    }
+    else // alignment & wxALIGN_LEFT
+    {
+        x = rect.GetLeft();
+    }
+
+    if ( alignment & wxALIGN_BOTTOM )
+    {
+        y = rect.GetBottom() - height;
+    }
+    else if ( alignment & wxALIGN_CENTRE_VERTICAL )
+    {
+        y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
+    }
+    else // alignment & wxALIGN_TOP
+    {
+        y = rect.GetTop();
+    }
+
+    // draw the bitmap first
+    wxCoord x0 = x,
+            y0 = y,
+            width0 = width;
+    if ( bitmap.Ok() )
+    {
+        DrawBitmap(bitmap, x, y, TRUE /* use mask */);
+
+        wxCoord offset = bitmap.GetWidth() + 4;
+        x += offset;
+        width -= offset;
+
+        y += (height - heightText) / 2;
+    }
+
+    // we will draw the underscore under the accel char later
+    wxCoord startUnderscore = 0,
+            endUnderscore = 0,
+            yUnderscore = 0;
+
+    // split the string into lines and draw each of them separately
+    wxString curLine;
+    for ( const wxChar *pc = text; ; pc++ )
+    {
+        if ( *pc == _T('\n') || *pc == _T('\0') )
+        {
+            int xRealStart = x; // init it here to avoid compielr warnings
+
+            if ( !curLine.empty() )
+            {
+                // NB: can't test for !(alignment & wxALIGN_LEFT) because
+                //     wxALIGN_LEFT is 0
+                if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
+                {
+                    wxCoord widthLine;
+                    GetTextExtent(curLine, &widthLine, NULL);
+
+                    if ( alignment & wxALIGN_RIGHT )
+                    {
+                        xRealStart += width - widthLine;
+                    }
+                    else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
+                    {
+                        xRealStart += (width - widthLine) / 2;
+                    }
+                }
+                //else: left aligned, nothing to do
+
+                DrawText(curLine, xRealStart, y);
+            }
+
+            y += heightLine;
+
+            // do we have underscore in this line? we can check yUnderscore
+            // because it is set below to just y + heightLine if we do
+            if ( y == yUnderscore )
+            {
+                // adjust the horz positions to account for the shift
+                startUnderscore += xRealStart;
+                endUnderscore += xRealStart;
+            }
+
+            if ( *pc == _T('\0') )
+                break;
+
+            curLine.clear();
+        }
+        else // not end of line
+        {
+            if ( pc - text.c_str() == indexAccel )
+            {
+                // remeber to draw underscore here
+                GetTextExtent(curLine, &startUnderscore, NULL);
+                curLine += *pc;
+                GetTextExtent(curLine, &endUnderscore, NULL);
+
+                yUnderscore = y + heightLine;
+            }
+            else
+            {
+                curLine += *pc;
+            }
+        }
+    }
+
+    // draw the underscore if found
+    if ( startUnderscore != endUnderscore )
+    {
+        // it should be of the same colour as text
+        SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
+
+        yUnderscore--;
+
+        DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
+    }
+
+    // return bounding rect if requested
+    if ( rectBounding )
+    {
+        *rectBounding = wxRect(x, y - heightText, widthText, heightText);
+    }
+
+    CalcBoundingBox(x0, y0);
+    CalcBoundingBox(x0 + width0, y0 + height);
+}