1 ///////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/gridctrl.cpp
3 // Purpose: wxGrid controls
4 // Author: Paul Gammans, Roger Gammans
8 // Copyright: (c) The Computer Surgery (paul@compsurg.co.uk)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
20 #include "wx/generic/gridctrl.h"
21 #include "wx/generic/grideditors.h"
24 #include "wx/textctrl.h"
26 #include "wx/combobox.h"
27 #include "wx/settings.h"
29 #include "wx/checkbox.h"
32 #include "wx/tokenzr.h"
33 #include "wx/renderer.h"
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
40 void wxGridCellRenderer::Draw(wxGrid
& grid
,
44 int WXUNUSED(row
), int WXUNUSED(col
),
47 dc
.SetBackgroundMode( wxBRUSHSTYLE_SOLID
);
50 if ( grid
.IsThisEnabled() )
54 if ( grid
.HasFocus() )
55 clr
= grid
.GetSelectionBackground();
57 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
);
61 clr
= attr
.GetBackgroundColour();
64 else // grey out fields if the grid is disabled
66 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
);
70 dc
.SetPen( *wxTRANSPARENT_PEN
);
71 dc
.DrawRectangle(rect
);
75 // ----------------------------------------------------------------------------
76 // wxGridCellDateTimeRenderer
77 // ----------------------------------------------------------------------------
81 // Enables a grid cell to display a formatted date and or time
83 wxGridCellDateTimeRenderer::wxGridCellDateTimeRenderer(const wxString
& outformat
, const wxString
& informat
)
86 m_oformat
= outformat
;
87 m_tz
= wxDateTime::Local
;
88 m_dateDef
= wxDefaultDateTime
;
91 wxGridCellRenderer
*wxGridCellDateTimeRenderer::Clone() const
93 wxGridCellDateTimeRenderer
*renderer
= new wxGridCellDateTimeRenderer
;
94 renderer
->m_iformat
= m_iformat
;
95 renderer
->m_oformat
= m_oformat
;
96 renderer
->m_dateDef
= m_dateDef
;
97 renderer
->m_tz
= m_tz
;
102 wxString
wxGridCellDateTimeRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
104 wxGridTableBase
*table
= grid
.GetTable();
106 bool hasDatetime
= false;
109 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_DATETIME
) )
111 void * tempval
= table
->GetValueAsCustom(row
, col
,wxGRID_VALUE_DATETIME
);
115 val
= *((wxDateTime
*)tempval
);
117 delete (wxDateTime
*)tempval
;
124 text
= table
->GetValue(row
, col
);
125 const char * const end
= val
.ParseFormat(text
, m_iformat
, m_dateDef
);
126 hasDatetime
= end
&& !*end
;
130 text
= val
.Format(m_oformat
, m_tz
);
132 // If we failed to parse string just show what we where given?
136 void wxGridCellDateTimeRenderer::Draw(wxGrid
& grid
,
137 wxGridCellAttr
& attr
,
139 const wxRect
& rectCell
,
143 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
145 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
147 // draw the text right aligned by default
148 int hAlign
= wxALIGN_RIGHT
,
149 vAlign
= wxALIGN_INVALID
;
150 attr
.GetNonDefaultAlignment(&hAlign
, &vAlign
);
152 wxRect rect
= rectCell
;
155 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
158 wxSize
wxGridCellDateTimeRenderer::GetBestSize(wxGrid
& grid
,
159 wxGridCellAttr
& attr
,
163 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
166 void wxGridCellDateTimeRenderer::SetParameters(const wxString
& params
)
172 #endif // wxUSE_DATETIME
174 // ----------------------------------------------------------------------------
175 // wxGridCellEnumRenderer
176 // ----------------------------------------------------------------------------
177 // Renders a number as a textual equivalent.
178 // eg data in cell is 0,1,2 ... n the cell could be rendered as "John","Fred"..."Bob"
181 wxGridCellEnumRenderer::wxGridCellEnumRenderer(const wxString
& choices
)
183 if (!choices
.empty())
184 SetParameters(choices
);
187 wxGridCellRenderer
*wxGridCellEnumRenderer::Clone() const
189 wxGridCellEnumRenderer
*renderer
= new wxGridCellEnumRenderer
;
190 renderer
->m_choices
= m_choices
;
194 wxString
wxGridCellEnumRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
196 wxGridTableBase
*table
= grid
.GetTable();
198 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
200 int choiceno
= table
->GetValueAsLong(row
, col
);
201 text
.Printf(wxT("%s"), m_choices
[ choiceno
].c_str() );
205 text
= table
->GetValue(row
, col
);
209 //If we faild to parse string just show what we where given?
213 void wxGridCellEnumRenderer::Draw(wxGrid
& grid
,
214 wxGridCellAttr
& attr
,
216 const wxRect
& rectCell
,
220 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
222 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
224 // draw the text right aligned by default
225 int hAlign
= wxALIGN_RIGHT
,
226 vAlign
= wxALIGN_INVALID
;
227 attr
.GetNonDefaultAlignment(&hAlign
, &vAlign
);
229 wxRect rect
= rectCell
;
232 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
235 wxSize
wxGridCellEnumRenderer::GetBestSize(wxGrid
& grid
,
236 wxGridCellAttr
& attr
,
240 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
243 void wxGridCellEnumRenderer::SetParameters(const wxString
& params
)
253 wxStringTokenizer
tk(params
, wxT(','));
254 while ( tk
.HasMoreTokens() )
256 m_choices
.Add(tk
.GetNextToken());
261 // ----------------------------------------------------------------------------
262 // wxGridCellAutoWrapStringRenderer
263 // ----------------------------------------------------------------------------
267 wxGridCellAutoWrapStringRenderer::Draw(wxGrid
& grid
,
268 wxGridCellAttr
& attr
,
270 const wxRect
& rectCell
,
275 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
277 // now we only have to draw the text
278 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
280 int horizAlign
, vertAlign
;
281 attr
.GetAlignment(&horizAlign
, &vertAlign
);
283 wxRect rect
= rectCell
;
286 grid
.DrawTextRectangle(dc
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
),
287 rect
, horizAlign
, vertAlign
);
292 wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid
& grid
,
294 const wxGridCellAttr
& attr
,
298 wxString data
= grid
.GetCellValue(row
, col
);
301 dc
.SetFont(attr
.GetFont());
303 //Taken from wxGrid again!
304 wxCoord x
= 0, y
= 0, curr_x
= 0;
305 wxCoord max_x
= rect
.GetWidth();
307 dc
.SetFont(attr
.GetFont());
308 wxStringTokenizer
tk(data
, wxT(" \n\t\r"));
309 wxString thisline
= wxEmptyString
;
311 while ( tk
.HasMoreTokens() )
313 wxString tok
= tk
.GetNextToken();
314 //FIXME: this causes us to print an extra unnecessary
315 // space at the end of the line. But it
316 // is invisible , simplifies the size calculation
317 // and ensures tokens are separated in the display
320 dc
.GetTextExtent(tok
, &x
, &y
);
321 if ( curr_x
+ x
> max_x
)
325 // this means that a single token is wider than the maximal
326 // width -- still use it as is as we need to show at least the
327 // part of it which fits
344 lines
.Add( wxString(thisline
) );
351 wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid
& grid
,
352 wxGridCellAttr
& attr
,
356 wxCoord x
,y
, height
, width
= grid
.GetColSize(col
) -20;
357 // for width, subtract 20 because ColSize includes a magin of 10 pixels
358 // that we do not want here and because we always start with an increment
359 // by 10 in the loop below.
360 int count
= 250; //Limit iterations..
362 wxRect
rect(0,0,width
,10);
364 // M is a nice large character 'y' gives descender!.
365 dc
.GetTextExtent(wxT("My"), &x
, &y
);
370 rect
.SetWidth(width
);
371 height
= y
* (wx_truncate_cast(wxCoord
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
).GetCount()));
373 // Search for a shape no taller than the golden ratio.
374 } while (count
&& (width
< (height
*1.68)) );
377 return wxSize(width
,height
);
381 // ----------------------------------------------------------------------------
382 // wxGridCellStringRenderer
383 // ----------------------------------------------------------------------------
385 void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid
& grid
,
386 const wxGridCellAttr
& attr
,
390 dc
.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT
);
392 // TODO some special colours for attr.IsReadOnly() case?
394 // different coloured text when the grid is disabled
395 if ( grid
.IsThisEnabled() )
400 if ( grid
.HasFocus() )
401 clr
= grid
.GetSelectionBackground();
403 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
);
404 dc
.SetTextBackground( clr
);
405 dc
.SetTextForeground( grid
.GetSelectionForeground() );
409 dc
.SetTextBackground( attr
.GetBackgroundColour() );
410 dc
.SetTextForeground( attr
.GetTextColour() );
415 dc
.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
));
416 dc
.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT
));
419 dc
.SetFont( attr
.GetFont() );
422 wxSize
wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr
& attr
,
424 const wxString
& text
)
426 wxCoord x
= 0, y
= 0, max_x
= 0;
427 dc
.SetFont(attr
.GetFont());
428 wxStringTokenizer
tk(text
, wxT('\n'));
429 while ( tk
.HasMoreTokens() )
431 dc
.GetTextExtent(tk
.GetNextToken(), &x
, &y
);
432 max_x
= wxMax(max_x
, x
);
435 y
*= 1 + text
.Freq(wxT('\n')); // multiply by the number of lines.
437 return wxSize(max_x
, y
);
440 wxSize
wxGridCellStringRenderer::GetBestSize(wxGrid
& grid
,
441 wxGridCellAttr
& attr
,
445 return DoGetBestSize(attr
, dc
, grid
.GetCellValue(row
, col
));
448 void wxGridCellStringRenderer::Draw(wxGrid
& grid
,
449 wxGridCellAttr
& attr
,
451 const wxRect
& rectCell
,
455 wxRect rect
= rectCell
;
458 // erase only this cells background, overflow cells should have been erased
459 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
462 attr
.GetAlignment(&hAlign
, &vAlign
);
464 int overflowCols
= 0;
466 if (attr
.GetOverflow())
468 int cols
= grid
.GetNumberCols();
469 int best_width
= GetBestSize(grid
,attr
,dc
,row
,col
).GetWidth();
470 int cell_rows
, cell_cols
;
471 attr
.GetSize( &cell_rows
, &cell_cols
); // shouldn't get here if <= 0
472 if ((best_width
> rectCell
.width
) && (col
< cols
) && grid
.GetTable())
474 int i
, c_cols
, c_rows
;
475 for (i
= col
+cell_cols
; i
< cols
; i
++)
477 bool is_empty
= true;
478 for (int j
=row
; j
< row
+ cell_rows
; j
++)
480 // check w/ anchor cell for multicell block
481 grid
.GetCellSize(j
, i
, &c_rows
, &c_cols
);
484 if (!grid
.GetTable()->IsEmptyCell(j
+ c_rows
, i
))
493 rect
.width
+= grid
.GetColSize(i
);
501 if (rect
.width
>= best_width
)
505 overflowCols
= i
- col
- cell_cols
+ 1;
506 if (overflowCols
>= cols
)
507 overflowCols
= cols
- 1;
510 if (overflowCols
> 0) // redraw overflow cells w/ proper hilight
512 hAlign
= wxALIGN_LEFT
; // if oveflowed then it's left aligned
514 clip
.x
+= rectCell
.width
;
515 // draw each overflow cell individually
516 int col_end
= col
+ cell_cols
+ overflowCols
;
517 if (col_end
>= grid
.GetNumberCols())
518 col_end
= grid
.GetNumberCols() - 1;
519 for (int i
= col
+ cell_cols
; i
<= col_end
; i
++)
521 clip
.width
= grid
.GetColSize(i
) - 1;
522 dc
.DestroyClippingRegion();
523 dc
.SetClippingRegion(clip
);
525 SetTextColoursAndFont(grid
, attr
, dc
,
526 grid
.IsInSelection(row
,i
));
528 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
529 rect
, hAlign
, vAlign
);
530 clip
.x
+= grid
.GetColSize(i
) - 1;
536 dc
.DestroyClippingRegion();
540 // now we only have to draw the text
541 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
543 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
544 rect
, hAlign
, vAlign
);
547 // ----------------------------------------------------------------------------
548 // wxGridCellNumberRenderer
549 // ----------------------------------------------------------------------------
551 wxString
wxGridCellNumberRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
553 wxGridTableBase
*table
= grid
.GetTable();
555 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
557 text
.Printf(wxT("%ld"), table
->GetValueAsLong(row
, col
));
561 text
= table
->GetValue(row
, col
);
567 void wxGridCellNumberRenderer::Draw(wxGrid
& grid
,
568 wxGridCellAttr
& attr
,
570 const wxRect
& rectCell
,
574 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
576 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
578 // draw the text right aligned by default
579 int hAlign
= wxALIGN_RIGHT
,
580 vAlign
= wxALIGN_INVALID
;
581 attr
.GetNonDefaultAlignment(&hAlign
, &vAlign
);
583 wxRect rect
= rectCell
;
586 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
589 wxSize
wxGridCellNumberRenderer::GetBestSize(wxGrid
& grid
,
590 wxGridCellAttr
& attr
,
594 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
597 // ----------------------------------------------------------------------------
598 // wxGridCellFloatRenderer
599 // ----------------------------------------------------------------------------
601 wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width
,
606 SetPrecision(precision
);
610 wxGridCellRenderer
*wxGridCellFloatRenderer::Clone() const
612 wxGridCellFloatRenderer
*renderer
= new wxGridCellFloatRenderer
;
613 renderer
->m_width
= m_width
;
614 renderer
->m_precision
= m_precision
;
615 renderer
->m_style
= m_style
;
616 renderer
->m_format
= m_format
;
621 wxString
wxGridCellFloatRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
623 wxGridTableBase
*table
= grid
.GetTable();
628 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_FLOAT
) )
630 val
= table
->GetValueAsDouble(row
, col
);
635 text
= table
->GetValue(row
, col
);
636 hasDouble
= text
.ToDouble(&val
);
645 if ( m_precision
== -1 )
647 // default width/precision
652 m_format
.Printf(wxT("%%.%d"), m_precision
);
655 else if ( m_precision
== -1 )
658 m_format
.Printf(wxT("%%%d."), m_width
);
662 m_format
.Printf(wxT("%%%d.%d"), m_width
, m_precision
);
665 bool isUpper
= ( ( m_style
& wxGRID_FLOAT_FORMAT_UPPER
) == wxGRID_FLOAT_FORMAT_UPPER
);
666 if ( ( m_style
& wxGRID_FLOAT_FORMAT_SCIENTIFIC
) == wxGRID_FLOAT_FORMAT_SCIENTIFIC
)
667 m_format
+= isUpper
? wxT('E') : wxT('e');
668 else if ( ( m_style
& wxGRID_FLOAT_FORMAT_COMPACT
) == wxGRID_FLOAT_FORMAT_COMPACT
)
669 m_format
+= isUpper
? wxT('G') : wxT('g');
671 m_format
+= wxT('f');
674 text
.Printf(m_format
, val
);
677 //else: text already contains the string
682 void wxGridCellFloatRenderer::Draw(wxGrid
& grid
,
683 wxGridCellAttr
& attr
,
685 const wxRect
& rectCell
,
689 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
691 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
693 // draw the text right aligned by default
694 int hAlign
= wxALIGN_RIGHT
,
695 vAlign
= wxALIGN_INVALID
;
696 attr
.GetNonDefaultAlignment(&hAlign
, &vAlign
);
698 wxRect rect
= rectCell
;
701 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
704 wxSize
wxGridCellFloatRenderer::GetBestSize(wxGrid
& grid
,
705 wxGridCellAttr
& attr
,
709 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
712 void wxGridCellFloatRenderer::SetParameters(const wxString
& params
)
719 SetFormat(wxGRID_FLOAT_FORMAT_DEFAULT
);
724 wxString tmp
= params
.BeforeFirst(wxT(','), &rest
);
728 if ( tmp
.ToLong(&width
) )
730 SetWidth((int)width
);
734 wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params
.c_str());
738 tmp
= rest
.BeforeFirst(wxT(','));
742 if ( tmp
.ToLong(&precision
) )
744 SetPrecision((int)precision
);
748 wxLogDebug(wxT("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params
.c_str());
752 tmp
= rest
.AfterFirst(wxT(','));
755 if ( tmp
[0] == wxT('f') )
757 SetFormat(wxGRID_FLOAT_FORMAT_FIXED
);
759 else if ( tmp
[0] == wxT('e') )
761 SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC
);
763 else if ( tmp
[0] == wxT('g') )
765 SetFormat(wxGRID_FLOAT_FORMAT_COMPACT
);
767 else if ( tmp
[0] == wxT('E') )
769 SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC
|
770 wxGRID_FLOAT_FORMAT_UPPER
);
772 else if ( tmp
[0] == wxT('F') )
774 SetFormat(wxGRID_FLOAT_FORMAT_FIXED
|
775 wxGRID_FLOAT_FORMAT_UPPER
);
777 else if ( tmp
[0] == wxT('G') )
779 SetFormat(wxGRID_FLOAT_FORMAT_COMPACT
|
780 wxGRID_FLOAT_FORMAT_UPPER
);
784 wxLogDebug("Invalid wxGridCellFloatRenderer format "
785 "parameter string '%s ignored", params
);
791 // ----------------------------------------------------------------------------
792 // wxGridCellBoolRenderer
793 // ----------------------------------------------------------------------------
795 wxSize
wxGridCellBoolRenderer::ms_sizeCheckMark
;
797 wxSize
wxGridCellBoolRenderer::GetBestSize(wxGrid
& grid
,
798 wxGridCellAttr
& WXUNUSED(attr
),
803 // compute it only once (no locks for MT safeness in GUI thread...)
804 if ( !ms_sizeCheckMark
.x
)
806 ms_sizeCheckMark
= wxRendererNative::Get().GetCheckBoxSize(&grid
);
809 return ms_sizeCheckMark
;
812 void wxGridCellBoolRenderer::Draw(wxGrid
& grid
,
813 wxGridCellAttr
& attr
,
819 wxGridCellRenderer::Draw(grid
, attr
, dc
, rect
, row
, col
, isSelected
);
821 // draw a check mark in the centre (ignoring alignment - TODO)
822 wxSize size
= GetBestSize(grid
, attr
, dc
, row
, col
);
824 // don't draw outside the cell
825 wxCoord minSize
= wxMin(rect
.width
, rect
.height
);
826 if ( size
.x
>= minSize
|| size
.y
>= minSize
)
828 // and even leave (at least) 1 pixel margin
829 size
.x
= size
.y
= minSize
;
832 // draw a border around checkmark
834 attr
.GetAlignment(&hAlign
, &vAlign
);
837 if (hAlign
== wxALIGN_CENTRE
)
839 rectBorder
.x
= rect
.x
+ rect
.width
/ 2 - size
.x
/ 2;
840 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
841 rectBorder
.width
= size
.x
;
842 rectBorder
.height
= size
.y
;
844 else if (hAlign
== wxALIGN_LEFT
)
846 rectBorder
.x
= rect
.x
+ 2;
847 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
848 rectBorder
.width
= size
.x
;
849 rectBorder
.height
= size
.y
;
851 else if (hAlign
== wxALIGN_RIGHT
)
853 rectBorder
.x
= rect
.x
+ rect
.width
- size
.x
- 2;
854 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
855 rectBorder
.width
= size
.x
;
856 rectBorder
.height
= size
.y
;
860 if ( grid
.GetTable()->CanGetValueAs(row
, col
, wxGRID_VALUE_BOOL
) )
862 value
= grid
.GetTable()->GetValueAsBool(row
, col
);
866 wxString
cellval( grid
.GetTable()->GetValue(row
, col
) );
867 value
= wxGridCellBoolEditor::IsTrueValue(cellval
);
872 flags
|= wxCONTROL_CHECKED
;
874 wxRendererNative::Get().DrawCheckBox( &grid
, dc
, rectBorder
, flags
);