///////////////////////////////////////////////////////////////////////////
-// Name: generic/gridctrl.cpp
+// Name: src/generic/gridctrl.cpp
// Purpose: wxGrid controls
// Author: Paul Gammans, Roger Gammans
// Modified by:
#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
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);
#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"
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
{
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);
m_choices.Empty();
- wxStringTokenizer tk(params, _T(','));
+ wxStringTokenizer tk(params, wxT(','));
while ( tk.HasMoreTokens() )
{
m_choices.Add(tk.GetNextToken());
}
}
-#if wxUSE_COMBOBOX
-
-// ----------------------------------------------------------------------------
-// wxGridCellEnumEditor
-// ----------------------------------------------------------------------------
-
-// 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
+// wxGridCellAutoWrapStringRenderer
// ----------------------------------------------------------------------------
-void
-wxGridCellAutoWrapStringEditor::Create(wxWindow* parent,
- wxWindowID id,
- wxEvtHandler* evtHandler)
-{
- wxGridCellTextEditor::DoCreate(parent, id, evtHandler,
- wxTE_MULTILINE | wxTE_RICH);
-}
void
wxGridCellAutoWrapStringRenderer::Draw(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
{
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