X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e0a050e347b1ece07ad5f165ba1b1d16edbeae77..4e15d1caa03346c126015019c1fdf093033ef40b:/src/generic/gridctrl.cpp diff --git a/src/generic/gridctrl.cpp b/src/generic/gridctrl.cpp index e8da62f564..65b7edd192 100644 --- a/src/generic/gridctrl.cpp +++ b/src/generic/gridctrl.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// Name: generic/gridctrl.cpp +// Name: src/generic/gridctrl.cpp // Purpose: wxGrid controls // Author: Paul Gammans, Roger Gammans // Modified by: @@ -18,14 +18,59 @@ #if wxUSE_GRID #include "wx/generic/gridctrl.h" +#include "wx/generic/grideditors.h" #ifndef WX_PRECOMP #include "wx/textctrl.h" #include "wx/dc.h" #include "wx/combobox.h" + #include "wx/settings.h" + #include "wx/log.h" + #include "wx/checkbox.h" #endif // WX_PRECOMP #include "wx/tokenzr.h" +#include "wx/renderer.h" + + +// ---------------------------------------------------------------------------- +// wxGridCellRenderer +// ---------------------------------------------------------------------------- + +void wxGridCellRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int WXUNUSED(row), int WXUNUSED(col), + bool isSelected) +{ + dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID ); + + wxColour clr; + if ( grid.IsThisEnabled() ) + { + if ( isSelected ) + { + if ( grid.HasFocus() ) + clr = grid.GetSelectionBackground(); + else + clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); + } + else + { + clr = attr.GetBackgroundColour(); + } + } + else // grey out fields if the grid is disabled + { + clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); + } + + dc.SetBrush(clr); + dc.SetPen( *wxTRANSPARENT_PEN ); + dc.DrawRectangle(rect); +} + // ---------------------------------------------------------------------------- // wxGridCellDateTimeRenderer @@ -65,7 +110,8 @@ wxString wxGridCellDateTimeRenderer::GetString(const wxGrid& grid, int row, int { void * tempval = table->GetValueAsCustom(row, col,wxGRID_VALUE_DATETIME); - if (tempval){ + if (tempval) + { val = *((wxDateTime *)tempval); hasDatetime = true; delete (wxDateTime *)tempval; @@ -76,13 +122,14 @@ wxString wxGridCellDateTimeRenderer::GetString(const wxGrid& grid, int row, int if (!hasDatetime ) { text = table->GetValue(row, col); - hasDatetime = (val.ParseFormat(text.c_str(), m_iformat, m_dateDef) != (wxChar *)NULL) ; + const char * const end = val.ParseFormat(text, m_iformat, m_dateDef); + hasDatetime = end && !*end; } if ( hasDatetime ) text = val.Format(m_oformat, m_tz ); - //If we faild to parse string just show what we where given? + // If we failed to parse string just show what we where given? return text; } @@ -98,9 +145,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); @@ -125,7 +172,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" @@ -151,7 +198,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 { @@ -175,9 +222,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); @@ -203,98 +250,18 @@ 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()); } } -#if wxUSE_COMBOBOX // ---------------------------------------------------------------------------- -// wxGridCellEnumEditor +// wxGridCellAutoWrapStringRenderer // ---------------------------------------------------------------------------- -// A cell editor which displays an enum number as a textual equivalent. eg -// data in cell is 0,1,2 ... n the cell could be displayed as -// "John","Fred"..."Bob" in the combo choice box - -wxGridCellEnumEditor::wxGridCellEnumEditor(const wxString& choices) - :wxGridCellChoiceEditor() -{ - m_startint = -1; - - if (!choices.empty()) - SetParameters(choices); -} - -wxGridCellEditor *wxGridCellEnumEditor::Clone() const -{ - wxGridCellEnumEditor *editor = new wxGridCellEnumEditor(); - editor->m_startint = m_startint; - return editor; -} - -void wxGridCellEnumEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEnumEditor must be Created first!")); - - wxGridTableBase *table = grid->GetTable(); - - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - { - m_startint = table->GetValueAsLong(row, col); - } - else - { - wxString startValue = table->GetValue(row, col); - if (startValue.IsNumber() && !startValue.empty()) - { - startValue.ToLong(&m_startint); - } - else - { - m_startint=-1; - } - } - - Combo()->SetSelection(m_startint); - Combo()->SetInsertionPointEnd(); - Combo()->SetFocus(); - -} - -bool wxGridCellEnumEditor::EndEdit(int row, int col, wxGrid* grid) -{ - int pos = Combo()->GetSelection(); - bool changed = (pos != m_startint); - if (changed) - { - if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER)) - grid->GetTable()->SetValueAsLong(row, col, pos); - else - grid->GetTable()->SetValue(row, col,wxString::Format(wxT("%i"),pos)); - } - - return changed; -} - -#endif // wxUSE_COMBOBOX - -// ---------------------------------------------------------------------------- -// wxGridCellAutoWrapStringEditor -// ---------------------------------------------------------------------------- - -void -wxGridCellAutoWrapStringEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - wxGridCellTextEditor::DoCreate(parent, id, evtHandler, - wxTE_MULTILINE | wxTE_RICH); -} void wxGridCellAutoWrapStringRenderer::Draw(wxGrid& grid, @@ -338,24 +305,34 @@ wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid, wxCoord max_x = rect.GetWidth(); dc.SetFont(attr.GetFont()); - wxStringTokenizer tk(data , _T(" \n\t\r")); + wxStringTokenizer tk(data , wxT(" \n\t\r")); wxString thisline = wxEmptyString; while ( tk.HasMoreTokens() ) { wxString tok = tk.GetNextToken(); - //FIXME: this causes us to print an extra unnecesary + //FIXME: this causes us to print an extra unnecessary // 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(" "); + tok += wxT(" "); dc.GetTextExtent(tok, &x, &y); if ( curr_x + x > max_x) { - lines.Add( wxString(thisline) ); - thisline = tok; - curr_x=x; + if ( curr_x == 0 ) + { + // 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); + } + else + { + lines.Add(thisline); + thisline = tok; + curr_x = x; + } } else { @@ -376,7 +353,10 @@ wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid, wxDC& dc, int row, int col) { - wxCoord x,y, height , width = grid.GetColSize(col) -10; + 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); @@ -397,5 +377,502 @@ wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid, return wxSize(width,height); } + +// ---------------------------------------------------------------------------- +// wxGridCellStringRenderer +// ---------------------------------------------------------------------------- + +void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, + const wxGridCellAttr& attr, + wxDC& dc, + bool isSelected) +{ + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); + + // TODO some special colours for attr.IsReadOnly() case? + + // different coloured text when the grid is disabled + if ( grid.IsThisEnabled() ) + { + if ( isSelected ) + { + wxColour clr; + if ( grid.HasFocus() ) + clr = grid.GetSelectionBackground(); + else + clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); + dc.SetTextBackground( clr ); + dc.SetTextForeground( grid.GetSelectionForeground() ); + } + else + { + dc.SetTextBackground( attr.GetBackgroundColour() ); + dc.SetTextForeground( attr.GetTextColour() ); + } + } + else + { + dc.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + } + + dc.SetFont( attr.GetFont() ); +} + +wxSize wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr& attr, + wxDC& dc, + const wxString& text) +{ + wxCoord x = 0, y = 0, max_x = 0; + dc.SetFont(attr.GetFont()); + wxStringTokenizer tk(text, wxT('\n')); + while ( tk.HasMoreTokens() ) + { + dc.GetTextExtent(tk.GetNextToken(), &x, &y); + max_x = wxMax(max_x, x); + } + + y *= 1 + text.Freq(wxT('\n')); // multiply by the number of lines. + + return wxSize(max_x, y); +} + +wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, grid.GetCellValue(row, col)); +} + +void wxGridCellStringRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxRect rect = rectCell; + rect.Inflate(-1); + + // erase only this cells background, overflow cells should have been erased + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + int hAlign, vAlign; + attr.GetAlignment(&hAlign, &vAlign); + + int overflowCols = 0; + + if (attr.GetOverflow()) + { + int cols = grid.GetNumberCols(); + int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth(); + int cell_rows, cell_cols; + attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <= 0 + if ((best_width > rectCell.width) && (col < cols) && grid.GetTable()) + { + int i, c_cols, c_rows; + for (i = col+cell_cols; i < cols; i++) + { + bool is_empty = true; + for (int j=row; j < row + cell_rows; j++) + { + // check w/ anchor cell for multicell block + grid.GetCellSize(j, i, &c_rows, &c_cols); + if (c_rows > 0) + c_rows = 0; + if (!grid.GetTable()->IsEmptyCell(j + c_rows, i)) + { + is_empty = false; + break; + } + } + + if (is_empty) + { + rect.width += grid.GetColSize(i); + } + else + { + i--; + break; + } + + if (rect.width >= best_width) + break; + } + + overflowCols = i - col - cell_cols + 1; + if (overflowCols >= cols) + overflowCols = cols - 1; + } + + if (overflowCols > 0) // redraw overflow cells w/ proper hilight + { + hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned + wxRect clip = rect; + clip.x += rectCell.width; + // draw each overflow cell individually + int col_end = col + cell_cols + overflowCols; + if (col_end >= grid.GetNumberCols()) + col_end = grid.GetNumberCols() - 1; + for (int i = col + cell_cols; i <= col_end; i++) + { + clip.width = grid.GetColSize(i) - 1; + dc.DestroyClippingRegion(); + dc.SetClippingRegion(clip); + + SetTextColoursAndFont(grid, attr, dc, + grid.IsInSelection(row,i)); + + grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), + rect, hAlign, vAlign); + clip.x += grid.GetColSize(i) - 1; + } + + rect = rectCell; + rect.Inflate(-1); + rect.width++; + dc.DestroyClippingRegion(); + } + } + + // now we only have to draw the text + SetTextColoursAndFont(grid, attr, dc, isSelected); + + grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), + rect, hAlign, vAlign); +} + +// ---------------------------------------------------------------------------- +// wxGridCellNumberRenderer +// ---------------------------------------------------------------------------- + +wxString wxGridCellNumberRenderer::GetString(const wxGrid& grid, int row, int col) +{ + wxGridTableBase *table = grid.GetTable(); + wxString text; + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) + { + text.Printf(wxT("%ld"), table->GetValueAsLong(row, col)); + } + else + { + text = table->GetValue(row, col); + } + + return text; +} + +void wxGridCellNumberRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + SetTextColoursAndFont(grid, attr, dc, isSelected); + + // draw the text right aligned by default + int hAlign = wxALIGN_RIGHT, + vAlign = wxALIGN_INVALID; + attr.GetNonDefaultAlignment(&hAlign, &vAlign); + + wxRect rect = rectCell; + rect.Inflate(-1); + + grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); +} + +wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, GetString(grid, row, col)); +} + +// ---------------------------------------------------------------------------- +// wxGridCellFloatRenderer +// ---------------------------------------------------------------------------- + +wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, + int precision, + int format) +{ + SetWidth(width); + SetPrecision(precision); + SetFormat(format); +} + +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; +} + +wxString wxGridCellFloatRenderer::GetString(const wxGrid& grid, int row, int col) +{ + wxGridTableBase *table = grid.GetTable(); + + bool hasDouble; + double val; + wxString text; + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) + { + val = table->GetValueAsDouble(row, col); + hasDouble = true; + } + else + { + text = table->GetValue(row, col); + hasDouble = text.ToDouble(&val); + } + + if ( hasDouble ) + { + if ( !m_format ) + { + if ( m_width == -1 ) + { + if ( m_precision == -1 ) + { + // default width/precision + m_format = wxT("%"); + } + else + { + m_format.Printf(wxT("%%.%d"), m_precision); + } + } + else if ( m_precision == -1 ) + { + // default precision + m_format.Printf(wxT("%%%d."), m_width); + } + else + { + 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); + + } + //else: text already contains the string + + return text; +} + +void wxGridCellFloatRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + SetTextColoursAndFont(grid, attr, dc, isSelected); + + // draw the text right aligned by default + int hAlign = wxALIGN_RIGHT, + vAlign = wxALIGN_INVALID; + attr.GetNonDefaultAlignment(&hAlign, &vAlign); + + wxRect rect = rectCell; + rect.Inflate(-1); + + grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); +} + +wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, GetString(grid, row, col)); +} + +void wxGridCellFloatRenderer::SetParameters(const wxString& params) +{ + if ( !params ) + { + // reset to defaults + SetWidth(-1); + SetPrecision(-1); + SetFormat(wxGRID_FLOAT_FORMAT_DEFAULT); + } + else + { + wxString rest; + wxString tmp = params.BeforeFirst(wxT(','), &rest); + if ( !tmp.empty() ) + { + long width; + if ( tmp.ToLong(&width) ) + { + SetWidth((int)width); + } + else + { + wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str()); + } + } + + tmp = rest.BeforeFirst(wxT(',')); + if ( !tmp.empty() ) + { + long precision; + if ( tmp.ToLong(&precision) ) + { + SetPrecision((int)precision); + } + else + { + 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); + } + } + } +} + +// ---------------------------------------------------------------------------- +// wxGridCellBoolRenderer +// ---------------------------------------------------------------------------- + +wxSize wxGridCellBoolRenderer::ms_sizeCheckMark; + +wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& WXUNUSED(attr), + wxDC& WXUNUSED(dc), + int WXUNUSED(row), + int WXUNUSED(col)) +{ + // compute it only once (no locks for MT safeness in GUI thread...) + if ( !ms_sizeCheckMark.x ) + { + ms_sizeCheckMark = wxRendererNative::Get().GetCheckBoxSize(&grid); + } + + return ms_sizeCheckMark; +} + +void wxGridCellBoolRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + + // draw a check mark in the centre (ignoring alignment - TODO) + wxSize size = GetBestSize(grid, attr, dc, row, col); + + // don't draw outside the cell + wxCoord minSize = wxMin(rect.width, rect.height); + if ( size.x >= minSize || size.y >= minSize ) + { + // and even leave (at least) 1 pixel margin + size.x = size.y = minSize; + } + + // draw a border around checkmark + int vAlign, hAlign; + attr.GetAlignment(&hAlign, &vAlign); + + wxRect rectBorder; + if (hAlign == wxALIGN_CENTRE) + { + rectBorder.x = rect.x + rect.width / 2 - size.x / 2; + rectBorder.y = rect.y + rect.height / 2 - size.y / 2; + rectBorder.width = size.x; + rectBorder.height = size.y; + } + else if (hAlign == wxALIGN_LEFT) + { + rectBorder.x = rect.x + 2; + rectBorder.y = rect.y + rect.height / 2 - size.y / 2; + rectBorder.width = size.x; + rectBorder.height = size.y; + } + else if (hAlign == wxALIGN_RIGHT) + { + rectBorder.x = rect.x + rect.width - size.x - 2; + rectBorder.y = rect.y + rect.height / 2 - size.y / 2; + rectBorder.width = size.x; + rectBorder.height = size.y; + } + + bool value; + if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) + { + value = grid.GetTable()->GetValueAsBool(row, col); + } + else + { + wxString cellval( grid.GetTable()->GetValue(row, col) ); + value = wxGridCellBoolEditor::IsTrueValue(cellval); + } + + int flags = 0; + if (value) + flags |= wxCONTROL_CHECKED; + + wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); +} + #endif // wxUSE_GRID