]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/gridctrl.cpp
Improve drawing of the tree item buttons in the generic renderer.
[wxWidgets.git] / src / generic / gridctrl.cpp
index 955c652ce868a95e45ec841400b96053fd51e775..25c3359d3b493c722326e03b0e9356c337e4d9d3 100644 (file)
@@ -1,10 +1,9 @@
 ///////////////////////////////////////////////////////////////////////////
-// Name:        generic/gridctrl.cpp
+// Name:        src/generic/gridctrl.cpp
 // Purpose:     wxGrid controls
 // Author:      Paul Gammans, Roger Gammans
 // Modified by:
 // Created:     11/04/2001
-// RCS-ID:      $Id$
 // Copyright:   (c) The Computer Surgery (paul@compsurg.co.uk)
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -24,7 +23,7 @@
     #include "wx/textctrl.h"
     #include "wx/dc.h"
     #include "wx/combobox.h"
-       #include "wx/settings.h"
+    #include "wx/settings.h"
     #include "wx/log.h"
     #include "wx/checkbox.h"
 #endif // WX_PRECOMP
@@ -47,7 +46,7 @@ void wxGridCellRenderer::Draw(wxGrid& grid,
     dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID );
 
     wxColour clr;
-    if ( grid.IsEnabled() )
+    if ( grid.IsThisEnabled() )
     {
         if ( isSelected )
         {
@@ -145,9 +144,9 @@ void wxGridCellDateTimeRenderer::Draw(wxGrid& grid,
     SetTextColoursAndFont(grid, attr, dc, isSelected);
 
     // draw the text right aligned by default
-    int hAlign, vAlign;
-    attr.GetAlignment(&hAlign, &vAlign);
-    hAlign = wxRIGHT;
+    int hAlign = wxALIGN_RIGHT,
+        vAlign = wxALIGN_INVALID;
+    attr.GetNonDefaultAlignment(&hAlign, &vAlign);
 
     wxRect rect = rectCell;
     rect.Inflate(-1);
@@ -172,7 +171,7 @@ void wxGridCellDateTimeRenderer::SetParameters(const wxString& params)
 #endif // wxUSE_DATETIME
 
 // ----------------------------------------------------------------------------
-// wxGridCellChoiceNumberRenderer
+// wxGridCellEnumRenderer
 // ----------------------------------------------------------------------------
 // Renders a number as a textual equivalent.
 // eg data in cell is 0,1,2 ... n the cell could be rendered as "John","Fred"..."Bob"
@@ -198,7 +197,7 @@ wxString wxGridCellEnumRenderer::GetString(const wxGrid& grid, int row, int col)
     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
     {
         int choiceno = table->GetValueAsLong(row, col);
-        text.Printf(_T("%s"), m_choices[ choiceno ].c_str() );
+        text.Printf(wxT("%s"), m_choices[ choiceno ].c_str() );
     }
     else
     {
@@ -222,9 +221,9 @@ void wxGridCellEnumRenderer::Draw(wxGrid& grid,
     SetTextColoursAndFont(grid, attr, dc, isSelected);
 
     // draw the text right aligned by default
-    int hAlign, vAlign;
-    attr.GetAlignment(&hAlign, &vAlign);
-    hAlign = wxRIGHT;
+    int hAlign = wxALIGN_RIGHT,
+        vAlign = wxALIGN_INVALID;
+    attr.GetNonDefaultAlignment(&hAlign, &vAlign);
 
     wxRect rect = rectCell;
     rect.Inflate(-1);
@@ -250,7 +249,7 @@ void wxGridCellEnumRenderer::SetParameters(const wxString& params)
 
     m_choices.Empty();
 
-    wxStringTokenizer tk(params, _T(','));
+    wxStringTokenizer tk(params, wxT(','));
     while ( tk.HasMoreTokens() )
     {
         m_choices.Add(tk.GetNextToken());
@@ -295,89 +294,164 @@ wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid,
                                                const wxRect& rect,
                                                int row, int col)
 {
-    wxString  data = grid.GetCellValue(row, col);
-
-    wxArrayString lines;
     dc.SetFont(attr.GetFont());
+    const wxCoord maxWidth = rect.GetWidth();
 
-    //Taken from wxGrid again!
-    wxCoord x = 0, y = 0, curr_x = 0;
-    wxCoord max_x = rect.GetWidth();
+    // Transform logical lines into physical ones, wrapping the longer ones.
+    const wxArrayString
+        logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
 
-    dc.SetFont(attr.GetFont());
-    wxStringTokenizer tk(data , _T(" \n\t\r"));
-    wxString thisline = wxEmptyString;
+    // Trying to do anything if the column is hidden anyhow doesn't make sense
+    // and we run into problems in BreakLine() in this case.
+    if ( maxWidth <= 0 )
+        return logicalLines;
 
-    while ( tk.HasMoreTokens() )
+    wxArrayString physicalLines;
+    for ( wxArrayString::const_iterator it = logicalLines.begin();
+          it != logicalLines.end();
+          ++it )
     {
-        wxString tok = tk.GetNextToken();
-        //FIXME: this causes us to print an extra unnecesary
-        //       space at the end of the line. But it
-        //       is invisible , simplifies the size calculation
-        //       and ensures tokens are separated in the display
-        tok += _T(" ");
+        const wxString& line = *it;
+
+        if ( dc.GetTextExtent(line).x > maxWidth )
+        {
+            // Line does not fit, break it up.
+            BreakLine(dc, line, maxWidth, physicalLines);
+        }
+        else // The entire line fits as is
+        {
+            physicalLines.push_back(line);
+        }
+    }
+
+    return physicalLines;
+}
 
-        dc.GetTextExtent(tok, &x, &y);
-        if ( curr_x + x > max_x)
+void
+wxGridCellAutoWrapStringRenderer::BreakLine(wxDC& dc,
+                                            const wxString& logicalLine,
+                                            wxCoord maxWidth,
+                                            wxArrayString& lines)
+{
+    wxCoord lineWidth = 0;
+    wxString line;
+
+    // For each word
+    wxStringTokenizer wordTokenizer(logicalLine, wxS(" \t"), wxTOKEN_RET_DELIMS);
+    while ( wordTokenizer.HasMoreTokens() )
+    {
+        const wxString word = wordTokenizer.GetNextToken();
+        const wxCoord wordWidth = dc.GetTextExtent(word).x;
+        if ( lineWidth + wordWidth < maxWidth )
+        {
+            // Word fits, just add it to this line.
+            line += word;
+            lineWidth += wordWidth;
+        }
+        else
         {
-            if ( curr_x == 0 )
+            // Word does not fit, check whether the word is itself wider that
+            // available width
+            if ( wordWidth < maxWidth )
             {
-                // this means that a single token is wider than the maximal
-                // width -- still use it as is as we need to show at least the
-                // part of it which fits
-                lines.Add(tok);
+                // Word can fit in a new line, put it at the beginning
+                // of the new line.
+                lines.push_back(line);
+                line = word;
+                lineWidth = wordWidth;
             }
-            else
+            else // Word cannot fit in available width at all.
             {
-                lines.Add(thisline);
-                thisline = tok;
-                curr_x = x;
+                if ( !line.empty() )
+                {
+                    lines.push_back(line);
+                    line.clear();
+                    lineWidth = 0;
+                }
+
+                // Break it up in several lines.
+                lineWidth = BreakWord(dc, word, maxWidth, lines, line);
             }
         }
-        else
-        {
-            thisline+= tok;
-            curr_x += x;
-        }
     }
-    //Add last line
-    lines.Add( wxString(thisline) );
 
-    return lines;
+    if ( !line.empty() )
+        lines.push_back(line);
 }
 
 
+wxCoord
+wxGridCellAutoWrapStringRenderer::BreakWord(wxDC& dc,
+                                            const wxString& word,
+                                            wxCoord maxWidth,
+                                            wxArrayString& lines,
+                                            wxString& line)
+{
+    wxArrayInt widths;
+    dc.GetPartialTextExtents(word, widths);
+
+    // TODO: Use binary search to find the first element > maxWidth.
+    const unsigned count = widths.size();
+    unsigned n;
+    for ( n = 0; n < count; n++ )
+    {
+        if ( widths[n] > maxWidth )
+            break;
+    }
+
+    if ( n == 0 )
+    {
+        // This is a degenerate case: the first character of the word is
+        // already wider than the available space, so we just can't show it
+        // completely and have to put the first character in this line.
+        n = 1;
+    }
+
+    lines.push_back(word.substr(0, n));
+
+    // Check if the remainder of the string fits in one line.
+    //
+    // Unfortunately we can't use the existing partial text extents as the
+    // extent of the remainder may be different when it's rendered in a
+    // separate line instead of as part of the same one, so we have to
+    // recompute it.
+    const wxString rest = word.substr(n);
+    const wxCoord restWidth = dc.GetTextExtent(rest).x;
+    if ( restWidth <= maxWidth )
+    {
+        line = rest;
+        return restWidth;
+    }
+
+    // Break the rest of the word into lines.
+    //
+    // TODO: Perhaps avoid recursion? The code is simpler like this but using a
+    // loop in this function would probably be more efficient.
+    return BreakWord(dc, rest, maxWidth, lines, line);
+}
+
 wxSize
 wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid,
                                               wxGridCellAttr& attr,
                                               wxDC& dc,
                                               int row, int col)
 {
-    wxCoord x,y, height , width = grid.GetColSize(col) -20;
-    // for width, subtract 20 because ColSize includes a magin of 10 pixels
-    // that we do not want here and because we always start with an increment
-    // by 10 in the loop below.
-    int count = 250; //Limit iterations..
-
-    wxRect rect(0,0,width,10);
+    const int lineHeight = dc.GetCharHeight();
 
-    // M is a nice large character 'y' gives descender!.
-    dc.GetTextExtent(wxT("My"), &x, &y);
-
-    do
-    {
-        width+=10;
-        rect.SetWidth(width);
-        height = y * (wx_truncate_cast(wxCoord, GetTextLines(grid,dc,attr,rect,row,col).GetCount()));
-        count--;
     // Search for a shape no taller than the golden ratio.
-    } while (count && (width  < (height*1.68)) );
-
+    wxSize size;
+    for ( size.x = 10; ; size.x += 10 )
+    {
+        const size_t
+            numLines = GetTextLines(grid, dc, attr, size, row, col).size();
+        size.y = numLines * lineHeight;
+        if ( size.x >= size.y*1.68 )
+            break;
+    }
 
-    return wxSize(width,height);
+    return size;
 }
 
-
 // ----------------------------------------------------------------------------
 // wxGridCellStringRenderer
 // ----------------------------------------------------------------------------
@@ -392,7 +466,7 @@ void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid,
     // TODO some special colours for attr.IsReadOnly() case?
 
     // different coloured text when the grid is disabled
-    if ( grid.IsEnabled() )
+    if ( grid.IsThisEnabled() )
     {
         if ( isSelected )
         {
@@ -425,7 +499,7 @@ wxSize wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr& attr,
 {
     wxCoord x = 0, y = 0, max_x = 0;
     dc.SetFont(attr.GetFont());
-    wxStringTokenizer tk(text, _T('\n'));
+    wxStringTokenizer tk(text, wxT('\n'));
     while ( tk.HasMoreTokens() )
     {
         dc.GetTextExtent(tk.GetNextToken(), &x, &y);
@@ -554,7 +628,7 @@ wxString wxGridCellNumberRenderer::GetString(const wxGrid& grid, int row, int co
     wxString text;
     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
     {
-        text.Printf(_T("%ld"), table->GetValueAsLong(row, col));
+        text.Printf(wxT("%ld"), table->GetValueAsLong(row, col));
     }
     else
     {
@@ -576,9 +650,9 @@ void wxGridCellNumberRenderer::Draw(wxGrid& grid,
     SetTextColoursAndFont(grid, attr, dc, isSelected);
 
     // draw the text right aligned by default
-    int hAlign, vAlign;
-    attr.GetAlignment(&hAlign, &vAlign);
-    hAlign = wxALIGN_RIGHT;
+    int hAlign = wxALIGN_RIGHT,
+        vAlign = wxALIGN_INVALID;
+    attr.GetNonDefaultAlignment(&hAlign, &vAlign);
 
     wxRect rect = rectCell;
     rect.Inflate(-1);
@@ -598,10 +672,13 @@ wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid,
 // wxGridCellFloatRenderer
 // ----------------------------------------------------------------------------
 
-wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, int precision)
+wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width,
+                                                 int precision,
+                                                 int format)
 {
     SetWidth(width);
     SetPrecision(precision);
+    SetFormat(format);
 }
 
 wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const
@@ -609,6 +686,7 @@ wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const
     wxGridCellFloatRenderer *renderer = new wxGridCellFloatRenderer;
     renderer->m_width = m_width;
     renderer->m_precision = m_precision;
+    renderer->m_style = m_style;
     renderer->m_format = m_format;
 
     return renderer;
@@ -641,22 +719,30 @@ wxString wxGridCellFloatRenderer::GetString(const wxGrid& grid, int row, int col
                 if ( m_precision == -1 )
                 {
                     // default width/precision
-                    m_format = _T("%f");
+                    m_format = wxT("%");
                 }
                 else
                 {
-                    m_format.Printf(_T("%%.%df"), m_precision);
+                    m_format.Printf(wxT("%%.%d"), m_precision);
                 }
             }
             else if ( m_precision == -1 )
             {
                 // default precision
-                m_format.Printf(_T("%%%d.f"), m_width);
+                m_format.Printf(wxT("%%%d."), m_width);
             }
             else
             {
-                m_format.Printf(_T("%%%d.%df"), m_width, m_precision);
+                m_format.Printf(wxT("%%%d.%d"), m_width, m_precision);
             }
+
+            bool isUpper = ( ( m_style & wxGRID_FLOAT_FORMAT_UPPER ) == wxGRID_FLOAT_FORMAT_UPPER);
+            if ( ( m_style & wxGRID_FLOAT_FORMAT_SCIENTIFIC ) == wxGRID_FLOAT_FORMAT_SCIENTIFIC)
+                m_format += isUpper ? wxT('E') : wxT('e');
+            else if ( ( m_style & wxGRID_FLOAT_FORMAT_COMPACT ) == wxGRID_FLOAT_FORMAT_COMPACT)
+                m_format += isUpper ? wxT('G') : wxT('g');
+            else
+                m_format += wxT('f');
         }
 
         text.Printf(m_format, val);
@@ -679,9 +765,9 @@ void wxGridCellFloatRenderer::Draw(wxGrid& grid,
     SetTextColoursAndFont(grid, attr, dc, isSelected);
 
     // draw the text right aligned by default
-    int hAlign, vAlign;
-    attr.GetAlignment(&hAlign, &vAlign);
-    hAlign = wxALIGN_RIGHT;
+    int hAlign = wxALIGN_RIGHT,
+        vAlign = wxALIGN_INVALID;
+    attr.GetNonDefaultAlignment(&hAlign, &vAlign);
 
     wxRect rect = rectCell;
     rect.Inflate(-1);
@@ -704,10 +790,12 @@ void wxGridCellFloatRenderer::SetParameters(const wxString& params)
         // reset to defaults
         SetWidth(-1);
         SetPrecision(-1);
+        SetFormat(wxGRID_FLOAT_FORMAT_DEFAULT);
     }
     else
     {
-        wxString tmp = params.BeforeFirst(_T(','));
+        wxString rest;
+        wxString tmp = params.BeforeFirst(wxT(','), &rest);
         if ( !tmp.empty() )
         {
             long width;
@@ -717,11 +805,11 @@ void wxGridCellFloatRenderer::SetParameters(const wxString& params)
             }
             else
             {
-                wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
+                wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
             }
         }
 
-        tmp = params.AfterFirst(_T(','));
+        tmp = rest.BeforeFirst(wxT(','));
         if ( !tmp.empty() )
         {
             long precision;
@@ -731,7 +819,44 @@ void wxGridCellFloatRenderer::SetParameters(const wxString& params)
             }
             else
             {
-                wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str());
+                wxLogDebug(wxT("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str());
+            }
+        }
+
+        tmp = rest.AfterFirst(wxT(','));
+        if ( !tmp.empty() )
+        {
+            if ( tmp[0] == wxT('f') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_FIXED);
+            }
+            else if ( tmp[0] == wxT('e') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC);
+            }
+            else if ( tmp[0] == wxT('g') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_COMPACT);
+            }
+            else if ( tmp[0] == wxT('E') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC |
+                          wxGRID_FLOAT_FORMAT_UPPER);
+            }
+            else if ( tmp[0] == wxT('F') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_FIXED |
+                          wxGRID_FLOAT_FORMAT_UPPER);
+            }
+            else if ( tmp[0] == wxT('G') )
+            {
+                SetFormat(wxGRID_FLOAT_FORMAT_COMPACT |
+                          wxGRID_FLOAT_FORMAT_UPPER);
+            }
+            else
+            {
+                wxLogDebug("Invalid wxGridCellFloatRenderer format "
+                           "parameter string '%s ignored", params);
             }
         }
     }
@@ -743,11 +868,6 @@ void wxGridCellFloatRenderer::SetParameters(const wxString& params)
 
 wxSize wxGridCellBoolRenderer::ms_sizeCheckMark;
 
-// FIXME these checkbox size calculations are really ugly...
-
-// between checkmark and box
-static const wxCoord wxGRID_CHECKMARK_MARGIN = 2;
-
 wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid,
                                            wxGridCellAttr& WXUNUSED(attr),
                                            wxDC& WXUNUSED(dc),
@@ -757,18 +877,7 @@ wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid,
     // compute it only once (no locks for MT safeness in GUI thread...)
     if ( !ms_sizeCheckMark.x )
     {
-        // get checkbox size
-        wxCheckBox *checkbox = new wxCheckBox(&grid, wxID_ANY, wxEmptyString);
-        wxSize size = checkbox->GetBestSize();
-        wxCoord checkSize = size.y + 2 * wxGRID_CHECKMARK_MARGIN;
-
-#if defined(__WXMOTIF__)
-        checkSize -= size.y / 2;
-#endif
-
-        delete checkbox;
-
-        ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize;
+        ms_sizeCheckMark = wxRendererNative::Get().GetCheckBoxSize(&grid);
     }
 
     return ms_sizeCheckMark;