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"
29 #include "wx/tokenzr.h"
30 #include "wx/renderer.h"
32 // ----------------------------------------------------------------------------
33 // wxGridCellDateTimeRenderer
34 // ----------------------------------------------------------------------------
38 // Enables a grid cell to display a formatted date and or time
40 wxGridCellDateTimeRenderer
::wxGridCellDateTimeRenderer(const wxString
& outformat
, const wxString
& informat
)
43 m_oformat
= outformat
;
44 m_tz
= wxDateTime
::Local
;
45 m_dateDef
= wxDefaultDateTime
;
48 wxGridCellRenderer
*wxGridCellDateTimeRenderer
::Clone() const
50 wxGridCellDateTimeRenderer
*renderer
= new wxGridCellDateTimeRenderer
;
51 renderer
->m_iformat
= m_iformat
;
52 renderer
->m_oformat
= m_oformat
;
53 renderer
->m_dateDef
= m_dateDef
;
54 renderer
->m_tz
= m_tz
;
59 wxString wxGridCellDateTimeRenderer
::GetString(const wxGrid
& grid
, int row
, int col
)
61 wxGridTableBase
*table
= grid
.GetTable();
63 bool hasDatetime
= false;
66 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_DATETIME
) )
68 void * tempval
= table
->GetValueAsCustom(row
, col
,wxGRID_VALUE_DATETIME
);
72 val
= *((wxDateTime
*)tempval
);
74 delete (wxDateTime
*)tempval
;
81 text
= table
->GetValue(row
, col
);
82 const char * const end
= val
.ParseFormat(text
, m_iformat
, m_dateDef
);
83 hasDatetime
= end
&& !*end
;
87 text
= val
.Format(m_oformat
, m_tz
);
89 // If we failed to parse string just show what we where given?
93 void wxGridCellDateTimeRenderer
::Draw(wxGrid
& grid
,
96 const wxRect
& rectCell
,
100 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
102 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
104 // draw the text right aligned by default
106 attr
.GetAlignment(&hAlign
, &vAlign
);
109 wxRect rect
= rectCell
;
112 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
115 wxSize wxGridCellDateTimeRenderer
::GetBestSize(wxGrid
& grid
,
116 wxGridCellAttr
& attr
,
120 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
123 void wxGridCellDateTimeRenderer
::SetParameters(const wxString
& params
)
129 #endif // wxUSE_DATETIME
131 // ----------------------------------------------------------------------------
132 // wxGridCellChoiceNumberRenderer
133 // ----------------------------------------------------------------------------
134 // Renders a number as a textual equivalent.
135 // eg data in cell is 0,1,2 ... n the cell could be rendered as "John","Fred"..."Bob"
138 wxGridCellEnumRenderer
::wxGridCellEnumRenderer(const wxString
& choices
)
140 if (!choices
.empty())
141 SetParameters(choices
);
144 wxGridCellRenderer
*wxGridCellEnumRenderer
::Clone() const
146 wxGridCellEnumRenderer
*renderer
= new wxGridCellEnumRenderer
;
147 renderer
->m_choices
= m_choices
;
151 wxString wxGridCellEnumRenderer
::GetString(const wxGrid
& grid
, int row
, int col
)
153 wxGridTableBase
*table
= grid
.GetTable();
155 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
157 int choiceno
= table
->GetValueAsLong(row
, col
);
158 text
.Printf(_T("%s"), m_choices
[ choiceno
].c_str() );
162 text
= table
->GetValue(row
, col
);
166 //If we faild to parse string just show what we where given?
170 void wxGridCellEnumRenderer
::Draw(wxGrid
& grid
,
171 wxGridCellAttr
& attr
,
173 const wxRect
& rectCell
,
177 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
179 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
181 // draw the text right aligned by default
183 attr
.GetAlignment(&hAlign
, &vAlign
);
186 wxRect rect
= rectCell
;
189 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
192 wxSize wxGridCellEnumRenderer
::GetBestSize(wxGrid
& grid
,
193 wxGridCellAttr
& attr
,
197 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
200 void wxGridCellEnumRenderer
::SetParameters(const wxString
& params
)
210 wxStringTokenizer
tk(params
, _T(','));
211 while ( tk
.HasMoreTokens() )
213 m_choices
.Add(tk
.GetNextToken());
218 // ----------------------------------------------------------------------------
219 // wxGridCellAutoWrapStringRenderer
220 // ----------------------------------------------------------------------------
224 wxGridCellAutoWrapStringRenderer
::Draw(wxGrid
& grid
,
225 wxGridCellAttr
& attr
,
227 const wxRect
& rectCell
,
232 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
234 // now we only have to draw the text
235 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
237 int horizAlign
, vertAlign
;
238 attr
.GetAlignment(&horizAlign
, &vertAlign
);
240 wxRect rect
= rectCell
;
243 grid
.DrawTextRectangle(dc
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
),
244 rect
, horizAlign
, vertAlign
);
249 wxGridCellAutoWrapStringRenderer
::GetTextLines(wxGrid
& grid
,
251 const wxGridCellAttr
& attr
,
255 wxString data
= grid
.GetCellValue(row
, col
);
258 dc
.SetFont(attr
.GetFont());
260 //Taken from wxGrid again!
261 wxCoord x
= 0, y
= 0, curr_x
= 0;
262 wxCoord max_x
= rect
.GetWidth();
264 dc
.SetFont(attr
.GetFont());
265 wxStringTokenizer
tk(data
, _T(" \n\t\r"));
266 wxString thisline
= wxEmptyString
;
268 while ( tk
.HasMoreTokens() )
270 wxString tok
= tk
.GetNextToken();
271 //FIXME: this causes us to print an extra unnecesary
272 // space at the end of the line. But it
273 // is invisible , simplifies the size calculation
274 // and ensures tokens are separated in the display
277 dc
.GetTextExtent(tok
, &x
, &y
);
278 if ( curr_x
+ x
> max_x
)
280 lines
.Add( wxString(thisline
) );
291 lines
.Add( wxString(thisline
) );
298 wxGridCellAutoWrapStringRenderer
::GetBestSize(wxGrid
& grid
,
299 wxGridCellAttr
& attr
,
303 wxCoord x
,y
, height
, width
= grid
.GetColSize(col
) -20;
304 // for width, subtract 20 because ColSize includes a magin of 10 pixels
305 // that we do not want here and because we always start with an increment
306 // by 10 in the loop below.
307 int count
= 250; //Limit iterations..
309 wxRect
rect(0,0,width
,10);
311 // M is a nice large character 'y' gives descender!.
312 dc
.GetTextExtent(wxT("My"), &x
, &y
);
317 rect
.SetWidth(width
);
318 height
= y
* (wx_truncate_cast(wxCoord
, GetTextLines(grid
,dc
,attr
,rect
,row
,col
).GetCount()));
320 // Search for a shape no taller than the golden ratio.
321 } while (count
&& (width
< (height
*1.68)) );
324 return wxSize(width
,height
);
328 // ----------------------------------------------------------------------------
329 // wxGridCellRenderer
330 // ----------------------------------------------------------------------------
332 void wxGridCellRenderer
::Draw(wxGrid
& grid
,
333 wxGridCellAttr
& attr
,
336 int WXUNUSED(row
), int WXUNUSED(col
),
339 dc
.SetBackgroundMode( wxBRUSHSTYLE_SOLID
);
342 if ( grid
.IsEnabled() )
346 if ( grid
.HasFocus() )
347 clr
= grid
.GetSelectionBackground();
349 clr
= wxSystemSettings
::GetColour(wxSYS_COLOUR_BTNSHADOW
);
353 clr
= attr
.GetBackgroundColour();
356 else // grey out fields if the grid is disabled
358 clr
= wxSystemSettings
::GetColour(wxSYS_COLOUR_BTNFACE
);
362 dc
.SetPen( *wxTRANSPARENT_PEN
);
363 dc
.DrawRectangle(rect
);
366 // ----------------------------------------------------------------------------
367 // wxGridCellStringRenderer
368 // ----------------------------------------------------------------------------
370 void wxGridCellStringRenderer
::SetTextColoursAndFont(const wxGrid
& grid
,
371 const wxGridCellAttr
& attr
,
375 dc
.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT
);
377 // TODO some special colours for attr.IsReadOnly() case?
379 // different coloured text when the grid is disabled
380 if ( grid
.IsEnabled() )
385 if ( grid
.HasFocus() )
386 clr
= grid
.GetSelectionBackground();
388 clr
= wxSystemSettings
::GetColour(wxSYS_COLOUR_BTNSHADOW
);
389 dc
.SetTextBackground( clr
);
390 dc
.SetTextForeground( grid
.GetSelectionForeground() );
394 dc
.SetTextBackground( attr
.GetBackgroundColour() );
395 dc
.SetTextForeground( attr
.GetTextColour() );
400 dc
.SetTextBackground(wxSystemSettings
::GetColour(wxSYS_COLOUR_BTNFACE
));
401 dc
.SetTextForeground(wxSystemSettings
::GetColour(wxSYS_COLOUR_GRAYTEXT
));
404 dc
.SetFont( attr
.GetFont() );
407 wxSize wxGridCellStringRenderer
::DoGetBestSize(const wxGridCellAttr
& attr
,
409 const wxString
& text
)
411 wxCoord x
= 0, y
= 0, max_x
= 0;
412 dc
.SetFont(attr
.GetFont());
413 wxStringTokenizer
tk(text
, _T('\n'));
414 while ( tk
.HasMoreTokens() )
416 dc
.GetTextExtent(tk
.GetNextToken(), &x
, &y
);
417 max_x
= wxMax(max_x
, x
);
420 y
*= 1 + text
.Freq(wxT('\n')); // multiply by the number of lines.
422 return wxSize(max_x
, y
);
425 wxSize wxGridCellStringRenderer
::GetBestSize(wxGrid
& grid
,
426 wxGridCellAttr
& attr
,
430 return DoGetBestSize(attr
, dc
, grid
.GetCellValue(row
, col
));
433 void wxGridCellStringRenderer
::Draw(wxGrid
& grid
,
434 wxGridCellAttr
& attr
,
436 const wxRect
& rectCell
,
440 wxRect rect
= rectCell
;
443 // erase only this cells background, overflow cells should have been erased
444 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
447 attr
.GetAlignment(&hAlign
, &vAlign
);
449 int overflowCols
= 0;
451 if (attr
.GetOverflow())
453 int cols
= grid
.GetNumberCols();
454 int best_width
= GetBestSize(grid
,attr
,dc
,row
,col
).GetWidth();
455 int cell_rows
, cell_cols
;
456 attr
.GetSize( &cell_rows
, &cell_cols
); // shouldn't get here if <= 0
457 if ((best_width
> rectCell
.width
) && (col
< cols
) && grid
.GetTable())
459 int i
, c_cols
, c_rows
;
460 for (i
= col
+cell_cols
; i
< cols
; i
++)
462 bool is_empty
= true;
463 for (int j
=row
; j
< row
+ cell_rows
; j
++)
465 // check w/ anchor cell for multicell block
466 grid
.GetCellSize(j
, i
, &c_rows
, &c_cols
);
469 if (!grid
.GetTable()->IsEmptyCell(j
+ c_rows
, i
))
478 rect
.width
+= grid
.GetColSize(i
);
486 if (rect
.width
>= best_width
)
490 overflowCols
= i
- col
- cell_cols
+ 1;
491 if (overflowCols
>= cols
)
492 overflowCols
= cols
- 1;
495 if (overflowCols
> 0) // redraw overflow cells w/ proper hilight
497 hAlign
= wxALIGN_LEFT
; // if oveflowed then it's left aligned
499 clip
.x
+= rectCell
.width
;
500 // draw each overflow cell individually
501 int col_end
= col
+ cell_cols
+ overflowCols
;
502 if (col_end
>= grid
.GetNumberCols())
503 col_end
= grid
.GetNumberCols() - 1;
504 for (int i
= col
+ cell_cols
; i
<= col_end
; i
++)
506 clip
.width
= grid
.GetColSize(i
) - 1;
507 dc
.DestroyClippingRegion();
508 dc
.SetClippingRegion(clip
);
510 SetTextColoursAndFont(grid
, attr
, dc
,
511 grid
.IsInSelection(row
,i
));
513 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
514 rect
, hAlign
, vAlign
);
515 clip
.x
+= grid
.GetColSize(i
) - 1;
521 dc
.DestroyClippingRegion();
525 // now we only have to draw the text
526 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
528 grid
.DrawTextRectangle(dc
, grid
.GetCellValue(row
, col
),
529 rect
, hAlign
, vAlign
);
532 // ----------------------------------------------------------------------------
533 // wxGridCellNumberRenderer
534 // ----------------------------------------------------------------------------
536 wxString wxGridCellNumberRenderer
::GetString(const wxGrid
& grid
, int row
, int col
)
538 wxGridTableBase
*table
= grid
.GetTable();
540 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_NUMBER
) )
542 text
.Printf(_T("%ld"), table
->GetValueAsLong(row
, col
));
546 text
= table
->GetValue(row
, col
);
552 void wxGridCellNumberRenderer
::Draw(wxGrid
& grid
,
553 wxGridCellAttr
& attr
,
555 const wxRect
& rectCell
,
559 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
561 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
563 // draw the text right aligned by default
565 attr
.GetAlignment(&hAlign
, &vAlign
);
566 hAlign
= wxALIGN_RIGHT
;
568 wxRect rect
= rectCell
;
571 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
574 wxSize wxGridCellNumberRenderer
::GetBestSize(wxGrid
& grid
,
575 wxGridCellAttr
& attr
,
579 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
582 // ----------------------------------------------------------------------------
583 // wxGridCellFloatRenderer
584 // ----------------------------------------------------------------------------
586 wxGridCellFloatRenderer
::wxGridCellFloatRenderer(int width
, int precision
)
589 SetPrecision(precision
);
592 wxGridCellRenderer
*wxGridCellFloatRenderer
::Clone() const
594 wxGridCellFloatRenderer
*renderer
= new wxGridCellFloatRenderer
;
595 renderer
->m_width
= m_width
;
596 renderer
->m_precision
= m_precision
;
597 renderer
->m_format
= m_format
;
602 wxString wxGridCellFloatRenderer
::GetString(const wxGrid
& grid
, int row
, int col
)
604 wxGridTableBase
*table
= grid
.GetTable();
609 if ( table
->CanGetValueAs(row
, col
, wxGRID_VALUE_FLOAT
) )
611 val
= table
->GetValueAsDouble(row
, col
);
616 text
= table
->GetValue(row
, col
);
617 hasDouble
= text
.ToDouble(&val
);
626 if ( m_precision
== -1 )
628 // default width/precision
633 m_format
.Printf(_T("%%.%df"), m_precision
);
636 else if ( m_precision
== -1 )
639 m_format
.Printf(_T("%%%d.f"), m_width
);
643 m_format
.Printf(_T("%%%d.%df"), m_width
, m_precision
);
647 text
.Printf(m_format
, val
);
650 //else: text already contains the string
655 void wxGridCellFloatRenderer
::Draw(wxGrid
& grid
,
656 wxGridCellAttr
& attr
,
658 const wxRect
& rectCell
,
662 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rectCell
, row
, col
, isSelected
);
664 SetTextColoursAndFont(grid
, attr
, dc
, isSelected
);
666 // draw the text right aligned by default
668 attr
.GetAlignment(&hAlign
, &vAlign
);
669 hAlign
= wxALIGN_RIGHT
;
671 wxRect rect
= rectCell
;
674 grid
.DrawTextRectangle(dc
, GetString(grid
, row
, col
), rect
, hAlign
, vAlign
);
677 wxSize wxGridCellFloatRenderer
::GetBestSize(wxGrid
& grid
,
678 wxGridCellAttr
& attr
,
682 return DoGetBestSize(attr
, dc
, GetString(grid
, row
, col
));
685 void wxGridCellFloatRenderer
::SetParameters(const wxString
& params
)
695 wxString tmp
= params
.BeforeFirst(_T(','));
699 if ( tmp
.ToLong(&width
) )
701 SetWidth((int)width
);
705 wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params
.c_str());
709 tmp
= params
.AfterFirst(_T(','));
713 if ( tmp
.ToLong(&precision
) )
715 SetPrecision((int)precision
);
719 wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params
.c_str());
725 // ----------------------------------------------------------------------------
726 // wxGridCellBoolRenderer
727 // ----------------------------------------------------------------------------
729 wxSize wxGridCellBoolRenderer
::ms_sizeCheckMark
;
731 // FIXME these checkbox size calculations are really ugly...
733 // between checkmark and box
734 static const wxCoord wxGRID_CHECKMARK_MARGIN
= 2;
736 wxSize wxGridCellBoolRenderer
::GetBestSize(wxGrid
& grid
,
737 wxGridCellAttr
& WXUNUSED(attr
),
742 // compute it only once (no locks for MT safeness in GUI thread...)
743 if ( !ms_sizeCheckMark
.x
)
746 wxCheckBox
*checkbox
= new wxCheckBox(&grid
, wxID_ANY
, wxEmptyString
);
747 wxSize size
= checkbox
->GetBestSize();
748 wxCoord checkSize
= size
.y
+ 2 * wxGRID_CHECKMARK_MARGIN
;
750 #if defined(__WXMOTIF__)
751 checkSize
-= size
.y
/ 2;
756 ms_sizeCheckMark
.x
= ms_sizeCheckMark
.y
= checkSize
;
759 return ms_sizeCheckMark
;
762 void wxGridCellBoolRenderer
::Draw(wxGrid
& grid
,
763 wxGridCellAttr
& attr
,
769 wxGridCellRenderer
::Draw(grid
, attr
, dc
, rect
, row
, col
, isSelected
);
771 // draw a check mark in the centre (ignoring alignment - TODO)
772 wxSize size
= GetBestSize(grid
, attr
, dc
, row
, col
);
774 // don't draw outside the cell
775 wxCoord minSize
= wxMin(rect
.width
, rect
.height
);
776 if ( size
.x
>= minSize
|| size
.y
>= minSize
)
778 // and even leave (at least) 1 pixel margin
779 size
.x
= size
.y
= minSize
;
782 // draw a border around checkmark
784 attr
.GetAlignment(&hAlign
, &vAlign
);
787 if (hAlign
== wxALIGN_CENTRE
)
789 rectBorder
.x
= rect
.x
+ rect
.width
/ 2 - size
.x
/ 2;
790 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
791 rectBorder
.width
= size
.x
;
792 rectBorder
.height
= size
.y
;
794 else if (hAlign
== wxALIGN_LEFT
)
796 rectBorder
.x
= rect
.x
+ 2;
797 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
798 rectBorder
.width
= size
.x
;
799 rectBorder
.height
= size
.y
;
801 else if (hAlign
== wxALIGN_RIGHT
)
803 rectBorder
.x
= rect
.x
+ rect
.width
- size
.x
- 2;
804 rectBorder
.y
= rect
.y
+ rect
.height
/ 2 - size
.y
/ 2;
805 rectBorder
.width
= size
.x
;
806 rectBorder
.height
= size
.y
;
810 if ( grid
.GetTable()->CanGetValueAs(row
, col
, wxGRID_VALUE_BOOL
) )
812 value
= grid
.GetTable()->GetValueAsBool(row
, col
);
816 wxString
cellval( grid
.GetTable()->GetValue(row
, col
) );
817 value
= wxGridCellBoolEditor
::IsTrueValue(cellval
);
822 flags
|= wxCONTROL_CHECKED
;
824 wxRendererNative
::Get().DrawCheckBox( &grid
, dc
, rectBorder
, flags
);