1 ///////////////////////////////////////////////////////////////////////////
2 // Name: 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"
35 // ----------------------------------------------------------------------------
36 // wxGridCellDateTimeRenderer
37 // ----------------------------------------------------------------------------
41 // Enables a grid cell to display a formatted date and or time
43 wxGridCellDateTimeRenderer::wxGridCellDateTimeRenderer(const wxString
& outformat
, const wxString
& informat
)
46 m_oformat
= outformat
;
47 m_tz
= wxDateTime::Local
;
48 m_dateDef
= wxDefaultDateTime
;
51 wxGridCellRenderer
*wxGridCellDateTimeRenderer::Clone() const
53 wxGridCellDateTimeRenderer
*renderer
= new wxGridCellDateTimeRenderer
;
54 renderer
->m_iformat
= m_iformat
;
55 renderer
->m_oformat
= m_oformat
;
56 renderer
->m_dateDef
= m_dateDef
;
57 renderer
->m_tz
= m_tz
;
62 wxString
wxGridCellDateTimeRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
64 wxGridTableBase
*table
= grid
.GetTable();
66 bool hasDatetime
= false;
69 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_DATETIME
) )
71 void * tempval
= table
->GetValueAsCustom(row
, col
,wxGRID_VALUE_DATETIME
);
75 val
= *((wxDateTime
*)tempval
);
77 delete (wxDateTime
*)tempval
;
84 text
= table
->GetValue(row
, col
);
85 const char * const end
= val
.ParseFormat(text
, m_iformat
, m_dateDef
);
86 hasDatetime
= end
&& !*end
;
90 text
= val
.Format(m_oformat
, m_tz
);
92 // If we failed to parse string just show what we where given?
96 void wxGridCellDateTimeRenderer::Draw(wxGrid
& grid
,
99 const wxRect
& rectCell
,
103 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
105 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
107 // draw the text right aligned by default
109 attr
.GetAlignment(&hAlign
, &vAlign
);
112 wxRect rect
= rectCell
;
115 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
118 wxSize
wxGridCellDateTimeRenderer::GetBestSize(wxGrid
& grid
,
119 wxGridCellAttr
& attr
,
123 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
126 void wxGridCellDateTimeRenderer::SetParameters(const wxString
& params
)
132 #endif // wxUSE_DATETIME
134 // ----------------------------------------------------------------------------
135 // wxGridCellChoiceNumberRenderer
136 // ----------------------------------------------------------------------------
137 // Renders a number as a textual equivalent.
138 // eg data in cell is 0,1,2 ... n the cell could be rendered as "John","Fred"..."Bob"
141 wxGridCellEnumRenderer::wxGridCellEnumRenderer(const wxString
& choices
)
143 if (!choices
.empty())
144 SetParameters(choices
);
147 wxGridCellRenderer
*wxGridCellEnumRenderer::Clone() const
149 wxGridCellEnumRenderer
*renderer
= new wxGridCellEnumRenderer
;
150 renderer
->m_choices
= m_choices
;
154 wxString
wxGridCellEnumRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
156 wxGridTableBase
*table
= grid
.GetTable();
158 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
160 int choiceno
= table
->GetValueAsLong(row
, col
);
161 text
.Printf(_T("%s"), m_choices
[ choiceno
].c_str() );
165 text
= table
->GetValue(row
, col
);
169 //If we faild to parse string just show what we where given?
173 void wxGridCellEnumRenderer::Draw(wxGrid
& grid
,
174 wxGridCellAttr
& attr
,
176 const wxRect
& rectCell
,
180 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
182 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
184 // draw the text right aligned by default
186 attr
.GetAlignment(&hAlign
, &vAlign
);
189 wxRect rect
= rectCell
;
192 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
195 wxSize
wxGridCellEnumRenderer::GetBestSize(wxGrid
& grid
,
196 wxGridCellAttr
& attr
,
200 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
203 void wxGridCellEnumRenderer::SetParameters(const wxString
& params
)
213 wxStringTokenizer
tk(params
, _T(','));
214 while ( tk
.HasMoreTokens() )
216 m_choices
.Add(tk
.GetNextToken());
221 // ----------------------------------------------------------------------------
222 // wxGridCellAutoWrapStringRenderer
223 // ----------------------------------------------------------------------------
227 wxGridCellAutoWrapStringRenderer::Draw(wxGrid
& grid
,
228 wxGridCellAttr
& attr
,
230 const wxRect
& rectCell
,
235 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
237 // now we only have to draw the text
238 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
240 int horizAlign
, vertAlign
;
241 attr
.GetAlignment(&horizAlign
, &vertAlign
);
243 wxRect rect
= rectCell
;
246 grid
.DrawTextRectangle(dc
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
),
247 rect
, horizAlign
, vertAlign
);
252 wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid
& grid
,
254 const wxGridCellAttr
& attr
,
258 wxString data
= grid
.GetCellValue(row
, col
);
261 dc
.SetFont(attr
.GetFont());
263 //Taken from wxGrid again!
264 wxCoord x
= 0, y
= 0, curr_x
= 0;
265 wxCoord max_x
= rect
.GetWidth();
267 dc
.SetFont(attr
.GetFont());
268 wxStringTokenizer
tk(data
, _T(" \n\t\r"));
269 wxString thisline
= wxEmptyString
;
271 while ( tk
.HasMoreTokens() )
273 wxString tok
= tk
.GetNextToken();
274 //FIXME: this causes us to print an extra unnecesary
275 // space at the end of the line. But it
276 // is invisible , simplifies the size calculation
277 // and ensures tokens are separated in the display
280 dc
.GetTextExtent(tok
, &x
, &y
);
281 if ( curr_x
+ x
> max_x
)
285 // this means that a single token is wider than the maximal
286 // width -- still use it as is as we need to show at least the
287 // part of it which fits
304 lines
.Add( wxString(thisline
) );
311 wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid
& grid
,
312 wxGridCellAttr
& attr
,
316 wxCoord x
,y
, height
, width
= grid
.GetColSize(col
) -20;
317 // for width, subtract 20 because ColSize includes a magin of 10 pixels
318 // that we do not want here and because we always start with an increment
319 // by 10 in the loop below.
320 int count
= 250; //Limit iterations..
322 wxRect
rect(0,0,width
,10);
324 // M is a nice large character 'y' gives descender!.
325 dc
.GetTextExtent(wxT("My"), &x
, &y
);
330 rect
.SetWidth(width
);
331 height
= y
* (wx_truncate_cast(wxCoord
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
).GetCount()));
333 // Search for a shape no taller than the golden ratio.
334 } while (count
&& (width
< (height
*1.68)) );
337 return wxSize(width
,height
);
341 // ----------------------------------------------------------------------------
342 // wxGridCellRenderer
343 // ----------------------------------------------------------------------------
345 void wxGridCellRenderer::Draw(wxGrid
& grid
,
346 wxGridCellAttr
& attr
,
349 int WXUNUSED(row
), int WXUNUSED(col
),
352 dc
.SetBackgroundMode( wxBRUSHSTYLE_SOLID
);
355 if ( grid
.IsEnabled() )
359 if ( grid
.HasFocus() )
360 clr
= grid
.GetSelectionBackground();
362 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
);
366 clr
= attr
.GetBackgroundColour();
369 else // grey out fields if the grid is disabled
371 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
);
375 dc
.SetPen( *wxTRANSPARENT_PEN
);
376 dc
.DrawRectangle(rect
);
379 // ----------------------------------------------------------------------------
380 // wxGridCellStringRenderer
381 // ----------------------------------------------------------------------------
383 void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid
& grid
,
384 const wxGridCellAttr
& attr
,
388 dc
.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT
);
390 // TODO some special colours for attr.IsReadOnly() case?
392 // different coloured text when the grid is disabled
393 if ( grid
.IsEnabled() )
398 if ( grid
.HasFocus() )
399 clr
= grid
.GetSelectionBackground();
401 clr
= wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW
);
402 dc
.SetTextBackground( clr
);
403 dc
.SetTextForeground( grid
.GetSelectionForeground() );
407 dc
.SetTextBackground( attr
.GetBackgroundColour() );
408 dc
.SetTextForeground( attr
.GetTextColour() );
413 dc
.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE
));
414 dc
.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT
));
417 dc
.SetFont( attr
.GetFont() );
420 wxSize
wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr
& attr
,
422 const wxString
& text
)
424 wxCoord x
= 0, y
= 0, max_x
= 0;
425 dc
.SetFont(attr
.GetFont());
426 wxStringTokenizer
tk(text
, _T('\n'));
427 while ( tk
.HasMoreTokens() )
429 dc
.GetTextExtent(tk
.GetNextToken(), &x
, &y
);
430 max_x
= wxMax(max_x
, x
);
433 y
*= 1 + text
.Freq(wxT('\n')); // multiply by the number of lines.
435 return wxSize(max_x
, y
);
438 wxSize
wxGridCellStringRenderer::GetBestSize(wxGrid
& grid
,
439 wxGridCellAttr
& attr
,
443 return DoGetBestSize(attr
, dc
, grid
.GetCellValue(row
, col
));
446 void wxGridCellStringRenderer::Draw(wxGrid
& grid
,
447 wxGridCellAttr
& attr
,
449 const wxRect
& rectCell
,
453 wxRect rect
= rectCell
;
456 // erase only this cells background, overflow cells should have been erased
457 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
460 attr
.GetAlignment(&hAlign
, &vAlign
);
462 int overflowCols
= 0;
464 if (attr
.GetOverflow())
466 int cols
= grid
.GetNumberCols();
467 int best_width
= GetBestSize(grid
,attr
,dc
,row
,col
).GetWidth();
468 int cell_rows
, cell_cols
;
469 attr
.GetSize( &cell_rows
, &cell_cols
); // shouldn't get here if <= 0
470 if ((best_width
> rectCell
.width
) && (col
< cols
) && grid
.GetTable())
472 int i
, c_cols
, c_rows
;
473 for (i
= col
+cell_cols
; i
< cols
; i
++)
475 bool is_empty
= true;
476 for (int j
=row
; j
< row
+ cell_rows
; j
++)
478 // check w/ anchor cell for multicell block
479 grid
.GetCellSize(j
, i
, &c_rows
, &c_cols
);
482 if (!grid
.GetTable()->IsEmptyCell(j
+ c_rows
, i
))
491 rect
.width
+= grid
.GetColSize(i
);
499 if (rect
.width
>= best_width
)
503 overflowCols
= i
- col
- cell_cols
+ 1;
504 if (overflowCols
>= cols
)
505 overflowCols
= cols
- 1;
508 if (overflowCols
> 0) // redraw overflow cells w/ proper hilight
510 hAlign
= wxALIGN_LEFT
; // if oveflowed then it's left aligned
512 clip
.x
+= rectCell
.width
;
513 // draw each overflow cell individually
514 int col_end
= col
+ cell_cols
+ overflowCols
;
515 if (col_end
>= grid
.GetNumberCols())
516 col_end
= grid
.GetNumberCols() - 1;
517 for (int i
= col
+ cell_cols
; i
<= col_end
; i
++)
519 clip
.width
= grid
.GetColSize(i
) - 1;
520 dc
.DestroyClippingRegion();
521 dc
.SetClippingRegion(clip
);
523 SetTextColoursAndFont(grid
, attr
, dc
,
524 grid
.IsInSelection(row
,i
));
526 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
527 rect
, hAlign
, vAlign
);
528 clip
.x
+= grid
.GetColSize(i
) - 1;
534 dc
.DestroyClippingRegion();
538 // now we only have to draw the text
539 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
541 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
542 rect
, hAlign
, vAlign
);
545 // ----------------------------------------------------------------------------
546 // wxGridCellNumberRenderer
547 // ----------------------------------------------------------------------------
549 wxString
wxGridCellNumberRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
551 wxGridTableBase
*table
= grid
.GetTable();
553 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
555 text
.Printf(_T("%ld"), table
->GetValueAsLong(row
, col
));
559 text
= table
->GetValue(row
, col
);
565 void wxGridCellNumberRenderer::Draw(wxGrid
& grid
,
566 wxGridCellAttr
& attr
,
568 const wxRect
& rectCell
,
572 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
574 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
576 // draw the text right aligned by default
578 attr
.GetAlignment(&hAlign
, &vAlign
);
579 hAlign
= wxALIGN_RIGHT
;
581 wxRect rect
= rectCell
;
584 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
587 wxSize
wxGridCellNumberRenderer::GetBestSize(wxGrid
& grid
,
588 wxGridCellAttr
& attr
,
592 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
595 // ----------------------------------------------------------------------------
596 // wxGridCellFloatRenderer
597 // ----------------------------------------------------------------------------
599 wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width
, int precision
)
602 SetPrecision(precision
);
605 wxGridCellRenderer
*wxGridCellFloatRenderer::Clone() const
607 wxGridCellFloatRenderer
*renderer
= new wxGridCellFloatRenderer
;
608 renderer
->m_width
= m_width
;
609 renderer
->m_precision
= m_precision
;
610 renderer
->m_format
= m_format
;
615 wxString
wxGridCellFloatRenderer::GetString(const wxGrid
& grid
, int row
, int col
)
617 wxGridTableBase
*table
= grid
.GetTable();
622 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_FLOAT
) )
624 val
= table
->GetValueAsDouble(row
, col
);
629 text
= table
->GetValue(row
, col
);
630 hasDouble
= text
.ToDouble(&val
);
639 if ( m_precision
== -1 )
641 // default width/precision
646 m_format
.Printf(_T("%%.%df"), m_precision
);
649 else if ( m_precision
== -1 )
652 m_format
.Printf(_T("%%%d.f"), m_width
);
656 m_format
.Printf(_T("%%%d.%df"), m_width
, m_precision
);
660 text
.Printf(m_format
, val
);
663 //else: text already contains the string
668 void wxGridCellFloatRenderer::Draw(wxGrid
& grid
,
669 wxGridCellAttr
& attr
,
671 const wxRect
& rectCell
,
675 wxGridCellRenderer::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
677 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
679 // draw the text right aligned by default
681 attr
.GetAlignment(&hAlign
, &vAlign
);
682 hAlign
= wxALIGN_RIGHT
;
684 wxRect rect
= rectCell
;
687 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
690 wxSize
wxGridCellFloatRenderer::GetBestSize(wxGrid
& grid
,
691 wxGridCellAttr
& attr
,
695 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
698 void wxGridCellFloatRenderer::SetParameters(const wxString
& params
)
708 wxString tmp
= params
.BeforeFirst(_T(','));
712 if ( tmp
.ToLong(&width
) )
714 SetWidth((int)width
);
718 wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params
.c_str());
722 tmp
= params
.AfterFirst(_T(','));
726 if ( tmp
.ToLong(&precision
) )
728 SetPrecision((int)precision
);
732 wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params
.c_str());
738 // ----------------------------------------------------------------------------
739 // wxGridCellBoolRenderer
740 // ----------------------------------------------------------------------------
742 wxSize
wxGridCellBoolRenderer::ms_sizeCheckMark
;
744 // FIXME these checkbox size calculations are really ugly...
746 // between checkmark and box
747 static const wxCoord wxGRID_CHECKMARK_MARGIN
= 2;
749 wxSize
wxGridCellBoolRenderer::GetBestSize(wxGrid
& grid
,
750 wxGridCellAttr
& WXUNUSED(attr
),
755 // compute it only once (no locks for MT safeness in GUI thread...)
756 if ( !ms_sizeCheckMark
.x
)
759 wxCheckBox
*checkbox
= new wxCheckBox(&grid
, wxID_ANY
, wxEmptyString
);
760 wxSize size
= checkbox
->GetBestSize();
761 wxCoord checkSize
= size
.y
+ 2 * wxGRID_CHECKMARK_MARGIN
;
763 #if defined(__WXMOTIF__)
764 checkSize
-= size
.y
/ 2;
769 ms_sizeCheckMark
.x
= ms_sizeCheckMark
.y
= checkSize
;
772 return ms_sizeCheckMark
;
775 void wxGridCellBoolRenderer::Draw(wxGrid
& grid
,
776 wxGridCellAttr
& attr
,
782 wxGridCellRenderer::Draw(grid
, attr
, dc
, rect
, row
, col
, isSelected
);
784 // draw a check mark in the centre (ignoring alignment - TODO)
785 wxSize size
= GetBestSize(grid
, attr
, dc
, row
, col
);
787 // don't draw outside the cell
788 wxCoord minSize
= wxMin(rect
.width
, rect
.height
);
789 if ( size
.x
>= minSize
|| size
.y
>= minSize
)
791 // and even leave (at least) 1 pixel margin
792 size
.x
= size
.y
= minSize
;
795 // draw a border around checkmark
797 attr
.GetAlignment(&hAlign
, &vAlign
);
800 if (hAlign
== wxALIGN_CENTRE
)
802 rectBorder
.x
= rect
.x
+ rect
.width
/ 2 - size
.x
/ 2;
803 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
804 rectBorder
.width
= size
.x
;
805 rectBorder
.height
= size
.y
;
807 else if (hAlign
== wxALIGN_LEFT
)
809 rectBorder
.x
= rect
.x
+ 2;
810 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
811 rectBorder
.width
= size
.x
;
812 rectBorder
.height
= size
.y
;
814 else if (hAlign
== wxALIGN_RIGHT
)
816 rectBorder
.x
= rect
.x
+ rect
.width
- size
.x
- 2;
817 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
818 rectBorder
.width
= size
.x
;
819 rectBorder
.height
= size
.y
;
823 if ( grid
.GetTable()->CanGetValueAs(row
, col
, wxGRID_VALUE_BOOL
) )
825 value
= grid
.GetTable()->GetValueAsBool(row
, col
);
829 wxString
cellval( grid
.GetTable()->GetValue(row
, col
) );
830 value
= wxGridCellBoolEditor::IsTrueValue(cellval
);
835 flags
|= wxCONTROL_CHECKED
;
837 wxRendererNative::Get().DrawCheckBox( &grid
, dc
, rectBorder
, flags
);