1 ///////////////////////////////////////////////////////////////////////////
2 // Name:        src/generic/gridctrl.cpp
3 // Purpose:     wxGrid controls
4 // Author:      Paul Gammans, Roger Gammans
5 // Modified by:
6 // Created:     11/04/2001
7 // Copyright:   (c) The Computer Surgery (paul@compsurg.co.uk)
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "wx/wxprec.h"
12 
13 
14 #if wxUSE_GRID
15 
16 #include "wx/generic/gridctrl.h"
17 #include "wx/generic/grideditors.h"
18 
19 #ifndef WX_PRECOMP
20     #include "wx/textctrl.h"
21     #include "wx/dc.h"
22     #include "wx/combobox.h"
23     #include "wx/settings.h"
24     #include "wx/log.h"
25     #include "wx/checkbox.h"
26 #endif // WX_PRECOMP
27 
28 #include "wx/tokenzr.h"
29 #include "wx/renderer.h"
30 
31 #include "wx/generic/private/grid.h"
32 #include "wx/private/window.h"
33 
34 // ----------------------------------------------------------------------------
35 // wxGridCellRenderer
36 // ----------------------------------------------------------------------------
37 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rect,int WXUNUSED (row),int WXUNUSED (col),bool isSelected)38 void wxGridCellRenderer::Draw(wxGrid& grid,
39                               wxGridCellAttr& attr,
40                               wxDC& dc,
41                               const wxRect& rect,
42                               int WXUNUSED(row), int WXUNUSED(col),
43                               bool isSelected)
44 {
45     dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID );
46 
47     wxColour clr;
48     if ( grid.IsThisEnabled() )
49     {
50         if ( isSelected )
51         {
52             if ( grid.HasFocus() )
53                 clr = grid.GetSelectionBackground();
54             else
55                 clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW);
56         }
57         else
58         {
59             clr = attr.GetBackgroundColour();
60         }
61     }
62     else // grey out fields if the grid is disabled
63     {
64         clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
65     }
66 
67     dc.SetBrush(clr);
68     dc.SetPen( *wxTRANSPARENT_PEN );
69     dc.DrawRectangle(rect);
70 }
71 
SetTextColoursAndFont(const wxGrid & grid,const wxGridCellAttr & attr,wxDC & dc,bool isSelected)72 void wxGridCellRenderer::SetTextColoursAndFont(const wxGrid& grid,
73                                                const wxGridCellAttr& attr,
74                                                wxDC& dc,
75                                                bool isSelected)
76 {
77     dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT );
78 
79     // TODO some special colours for attr.IsReadOnly() case?
80 
81     // different coloured text when the grid is disabled
82     if ( grid.IsThisEnabled() )
83     {
84         if ( isSelected )
85         {
86             wxColour clr;
87             if ( grid.HasFocus() )
88                 clr = grid.GetSelectionBackground();
89             else
90                 clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW);
91             dc.SetTextBackground( clr );
92             dc.SetTextForeground( grid.GetSelectionForeground() );
93         }
94         else
95         {
96             dc.SetTextBackground( attr.GetBackgroundColour() );
97             dc.SetTextForeground( attr.GetTextColour() );
98         }
99     }
100     else
101     {
102         dc.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
103         dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
104     }
105 
106     dc.SetFont( attr.GetFont() );
107 }
108 
109 // ----------------------------------------------------------------------------
110 // wxGridCellDateTimeRenderer
111 // ----------------------------------------------------------------------------
112 
113 #if wxUSE_DATETIME
114 
115 bool
TryGetValueAsDate(wxDateTime & result,const DateParseParams & params,const wxGrid & grid,int row,int col)116 wxGridPrivate::TryGetValueAsDate(wxDateTime& result,
117                                  const DateParseParams& params,
118                                  const wxGrid& grid,
119                                  int row, int col)
120 {
121     wxGridTableBase *table = grid.GetTable();
122 
123     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_DATETIME) )
124     {
125         void * tempval = table->GetValueAsCustom(row, col,wxGRID_VALUE_DATETIME);
126 
127         if (tempval)
128         {
129             result = *((wxDateTime *)tempval);
130             delete (wxDateTime *)tempval;
131 
132             return true;
133         }
134 
135     }
136 
137     const wxString text = table->GetValue(row, col);
138 
139     wxString::const_iterator end;
140 
141     if ( result.ParseFormat(text, params.format, &end) && end == text.end() )
142         return true;
143 
144     // Check if we can fall back to free-form parsing, which notably allows us
145     // to parse strings such as "today" or "tomorrow" which would be never
146     // accepted by ParseFormat().
147     if ( params.fallbackParseDate &&
148             result.ParseDate(text, &end) && end == text.end() )
149         return true;
150 
151     return false;
152 }
153 
154 using namespace wxGridPrivate;
155 
156 // Enables a grid cell to display a formatted date
157 
wxGridCellDateRenderer(const wxString & outformat)158 wxGridCellDateRenderer::wxGridCellDateRenderer(const wxString& outformat)
159 {
160     if ( outformat.empty() )
161     {
162         m_oformat = "%x"; // Localized date representation.
163     }
164     else
165     {
166         m_oformat = outformat;
167     }
168     m_tz = wxDateTime::Local;
169 }
170 
Clone() const171 wxGridCellRenderer *wxGridCellDateRenderer::Clone() const
172 {
173     return new wxGridCellDateRenderer(*this);
174 }
175 
GetString(const wxGrid & grid,int row,int col)176 wxString wxGridCellDateRenderer::GetString(const wxGrid& grid, int row, int col)
177 {
178     wxString text;
179 
180     DateParseParams params;
181     GetDateParseParams(params);
182 
183     wxDateTime val;
184     if ( TryGetValueAsDate(val, params, grid, row, col) )
185         text = val.Format(m_oformat, m_tz );
186 
187     // If we failed to parse string just show what we where given?
188     return text;
189 }
190 
191 void
GetDateParseParams(DateParseParams & params) const192 wxGridCellDateRenderer::GetDateParseParams(DateParseParams& params) const
193 {
194     params = DateParseParams::WithFallback(m_oformat);
195 }
196 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)197 void wxGridCellDateRenderer::Draw(wxGrid& grid,
198                                   wxGridCellAttr& attr,
199                                   wxDC& dc,
200                                   const wxRect& rectCell,
201                                   int row, int col,
202                                   bool isSelected)
203 {
204     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
205 
206     SetTextColoursAndFont(grid, attr, dc, isSelected);
207 
208     wxRect rect = rectCell;
209     rect.Inflate(-1);
210 
211     // draw the text right aligned by default
212     grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
213                            wxALIGN_RIGHT);
214 }
215 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)216 wxSize wxGridCellDateRenderer::GetBestSize(wxGrid& grid,
217                                            wxGridCellAttr& attr,
218                                            wxDC& dc,
219                                            int row, int col)
220 {
221     return DoGetBestSize(attr, dc, GetString(grid, row, col));
222 }
223 
GetMaxBestSize(wxGrid & WXUNUSED (grid),wxGridCellAttr & attr,wxDC & dc)224 wxSize wxGridCellDateRenderer::GetMaxBestSize(wxGrid& WXUNUSED(grid),
225                                               wxGridCellAttr& attr,
226                                               wxDC& dc)
227 {
228     wxSize size;
229 
230     // Try to produce the longest string in the current format: as we don't
231     // know which month is the longest, we need to try all of them.
232     for ( int m = wxDateTime::Jan; m <= wxDateTime::Dec; ++m )
233     {
234         const wxDateTime d(28, static_cast<wxDateTime::Month>(m), 9999);
235 
236         size.IncTo(DoGetBestSize(attr, dc, d.Format(m_oformat, m_tz)));
237     }
238 
239     return size;
240 }
241 
SetParameters(const wxString & params)242 void wxGridCellDateRenderer::SetParameters(const wxString& params)
243 {
244     if (!params.empty())
245         m_oformat=params;
246 }
247 
248 
249 // Enables a grid cell to display a formatted date and or time
250 
wxGridCellDateTimeRenderer(const wxString & outformat,const wxString & informat)251 wxGridCellDateTimeRenderer::wxGridCellDateTimeRenderer(const wxString& outformat, const wxString& informat)
252     : wxGridCellDateRenderer(outformat)
253     , m_iformat(informat)
254 {
255 }
256 
Clone() const257 wxGridCellRenderer *wxGridCellDateTimeRenderer::Clone() const
258 {
259     return new wxGridCellDateTimeRenderer(*this);
260 }
261 
262 void
GetDateParseParams(DateParseParams & params) const263 wxGridCellDateTimeRenderer::GetDateParseParams(DateParseParams& params) const
264 {
265     params = DateParseParams::WithoutFallback(m_iformat);
266 }
267 
268 #endif // wxUSE_DATETIME
269 
270 // ----------------------------------------------------------------------------
271 // wxGridCellChoiceRenderer
272 // ----------------------------------------------------------------------------
273 
GetMaxBestSize(wxGrid & WXUNUSED (grid),wxGridCellAttr & attr,wxDC & dc)274 wxSize wxGridCellChoiceRenderer::GetMaxBestSize(wxGrid& WXUNUSED(grid),
275                                                 wxGridCellAttr& attr,
276                                                 wxDC& dc)
277 {
278     wxSize size;
279 
280     for ( size_t n = 0; n < m_choices.size(); ++n )
281     {
282         size.IncTo(DoGetBestSize(attr, dc, m_choices[n]));
283     }
284 
285     return size;
286 }
287 
SetParameters(const wxString & params)288 void wxGridCellChoiceRenderer::SetParameters(const wxString& params)
289 {
290     m_choices.clear();
291 
292     if ( params.empty() )
293         return;
294 
295     wxStringTokenizer tk(params, wxT(','));
296     while ( tk.HasMoreTokens() )
297     {
298         m_choices.Add(tk.GetNextToken());
299     }
300 }
301 
302 // ----------------------------------------------------------------------------
303 // wxGridCellEnumRenderer
304 // ----------------------------------------------------------------------------
305 // Renders a number as a textual equivalent.
306 // eg data in cell is 0,1,2 ... n the cell could be rendered as "John","Fred"..."Bob"
307 
308 
wxGridCellEnumRenderer(const wxString & choices)309 wxGridCellEnumRenderer::wxGridCellEnumRenderer(const wxString& choices)
310 {
311     if (!choices.empty())
312         SetParameters(choices);
313 }
314 
Clone() const315 wxGridCellRenderer *wxGridCellEnumRenderer::Clone() const
316 {
317     wxGridCellEnumRenderer *renderer = new wxGridCellEnumRenderer;
318     renderer->m_choices = m_choices;
319     return renderer;
320 }
321 
GetString(const wxGrid & grid,int row,int col)322 wxString wxGridCellEnumRenderer::GetString(const wxGrid& grid, int row, int col)
323 {
324     wxGridTableBase *table = grid.GetTable();
325     wxString text;
326     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
327     {
328         int choiceno = table->GetValueAsLong(row, col);
329         text.Printf(wxT("%s"), m_choices[ choiceno ].c_str() );
330     }
331     else
332     {
333         text = table->GetValue(row, col);
334     }
335 
336 
337     //If we faild to parse string just show what we where given?
338     return text;
339 }
340 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)341 void wxGridCellEnumRenderer::Draw(wxGrid& grid,
342                                    wxGridCellAttr& attr,
343                                    wxDC& dc,
344                                    const wxRect& rectCell,
345                                    int row, int col,
346                                    bool isSelected)
347 {
348     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
349 
350     SetTextColoursAndFont(grid, attr, dc, isSelected);
351 
352     wxRect rect = rectCell;
353     rect.Inflate(-1);
354 
355     // draw the text right aligned by default
356     grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
357                            wxALIGN_RIGHT);
358 }
359 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)360 wxSize wxGridCellEnumRenderer::GetBestSize(wxGrid& grid,
361                                             wxGridCellAttr& attr,
362                                             wxDC& dc,
363                                             int row, int col)
364 {
365     return DoGetBestSize(attr, dc, GetString(grid, row, col));
366 }
367 
368 // ----------------------------------------------------------------------------
369 // wxGridCellAutoWrapStringRenderer
370 // ----------------------------------------------------------------------------
371 
372 
373 void
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)374 wxGridCellAutoWrapStringRenderer::Draw(wxGrid& grid,
375                       wxGridCellAttr& attr,
376                       wxDC& dc,
377                       const wxRect& rectCell,
378                       int row, int col,
379                       bool isSelected) {
380 
381 
382     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
383 
384     // now we only have to draw the text
385     SetTextColoursAndFont(grid, attr, dc, isSelected);
386 
387     int horizAlign, vertAlign;
388     attr.GetAlignment(&horizAlign, &vertAlign);
389 
390     wxRect rect = rectCell;
391     rect.Inflate(-1);
392 
393     // Do not use here the overload taking the attribute, as this would
394     // ellipsize the text, which is never necessary with this renderer.
395     grid.DrawTextRectangle(dc, GetTextLines(grid,dc,attr,rect,row,col),
396                            rect, horizAlign, vertAlign);
397 }
398 
399 
400 wxArrayString
GetTextLines(wxGrid & grid,wxDC & dc,const wxGridCellAttr & attr,const wxRect & rect,int row,int col)401 wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid,
402                                                wxDC& dc,
403                                                const wxGridCellAttr& attr,
404                                                const wxRect& rect,
405                                                int row, int col)
406 {
407     dc.SetFont(attr.GetFont());
408     const wxCoord maxWidth = rect.GetWidth();
409 
410     // Transform logical lines into physical ones, wrapping the longer ones.
411     const wxArrayString
412         logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
413 
414     // Trying to do anything if the column is hidden anyhow doesn't make sense
415     // and we run into problems in BreakLine() in this case.
416     if ( maxWidth <= 0 )
417         return logicalLines;
418 
419     wxArrayString physicalLines;
420     for ( wxArrayString::const_iterator it = logicalLines.begin();
421           it != logicalLines.end();
422           ++it )
423     {
424         const wxString& line = *it;
425 
426         if ( dc.GetTextExtent(line).x > maxWidth )
427         {
428             // Line does not fit, break it up.
429             BreakLine(dc, line, maxWidth, physicalLines);
430         }
431         else // The entire line fits as is
432         {
433             physicalLines.push_back(line);
434         }
435     }
436 
437     return physicalLines;
438 }
439 
440 void
BreakLine(wxDC & dc,const wxString & logicalLine,wxCoord maxWidth,wxArrayString & lines)441 wxGridCellAutoWrapStringRenderer::BreakLine(wxDC& dc,
442                                             const wxString& logicalLine,
443                                             wxCoord maxWidth,
444                                             wxArrayString& lines)
445 {
446     wxCoord lineWidth = 0;
447     wxString line;
448 
449     // For each word
450     wxStringTokenizer wordTokenizer(logicalLine, wxS(" \t"), wxTOKEN_RET_DELIMS);
451     while ( wordTokenizer.HasMoreTokens() )
452     {
453         const wxString word = wordTokenizer.GetNextToken();
454         const wxCoord wordWidth = dc.GetTextExtent(word).x;
455         if ( lineWidth + wordWidth < maxWidth )
456         {
457             // Word fits, just add it to this line.
458             line += word;
459             lineWidth += wordWidth;
460         }
461         else
462         {
463             // Word does not fit, check whether the word is itself wider that
464             // available width
465             if ( wordWidth < maxWidth )
466             {
467                 // Word can fit in a new line, put it at the beginning
468                 // of the new line.
469                 lines.push_back(line);
470                 line = word;
471                 lineWidth = wordWidth;
472             }
473             else // Word cannot fit in available width at all.
474             {
475                 if ( !line.empty() )
476                 {
477                     lines.push_back(line);
478                     line.clear();
479                     lineWidth = 0;
480                 }
481 
482                 // Break it up in several lines.
483                 lineWidth = BreakWord(dc, word, maxWidth, lines, line);
484             }
485         }
486     }
487 
488     if ( !line.empty() )
489         lines.push_back(line);
490 }
491 
492 
493 wxCoord
BreakWord(wxDC & dc,const wxString & word,wxCoord maxWidth,wxArrayString & lines,wxString & line)494 wxGridCellAutoWrapStringRenderer::BreakWord(wxDC& dc,
495                                             const wxString& word,
496                                             wxCoord maxWidth,
497                                             wxArrayString& lines,
498                                             wxString& line)
499 {
500     wxArrayInt widths;
501     dc.GetPartialTextExtents(word, widths);
502 
503     // TODO: Use binary search to find the first element > maxWidth.
504     const unsigned count = widths.size();
505     unsigned n;
506     for ( n = 0; n < count; n++ )
507     {
508         if ( widths[n] > maxWidth )
509             break;
510     }
511 
512     if ( n == 0 )
513     {
514         // This is a degenerate case: the first character of the word is
515         // already wider than the available space, so we just can't show it
516         // completely and have to put the first character in this line.
517         n = 1;
518     }
519 
520     lines.push_back(word.substr(0, n));
521 
522     // Check if the remainder of the string fits in one line.
523     //
524     // Unfortunately we can't use the existing partial text extents as the
525     // extent of the remainder may be different when it's rendered in a
526     // separate line instead of as part of the same one, so we have to
527     // recompute it.
528     const wxString rest = word.substr(n);
529     const wxCoord restWidth = dc.GetTextExtent(rest).x;
530     if ( restWidth <= maxWidth )
531     {
532         line = rest;
533         return restWidth;
534     }
535 
536     // Break the rest of the word into lines.
537     //
538     // TODO: Perhaps avoid recursion? The code is simpler like this but using a
539     // loop in this function would probably be more efficient.
540     return BreakWord(dc, rest, maxWidth, lines, line);
541 }
542 
543 wxSize
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)544 wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid,
545                                               wxGridCellAttr& attr,
546                                               wxDC& dc,
547                                               int row, int col)
548 {
549     // We have to make a choice here and fix either width or height because we
550     // don't have any naturally best size. This choice is mostly arbitrary, but
551     // we need to be consistent about it, otherwise wxGrid auto-sizing code
552     // would get confused. For now we decide to use a single line of text and
553     // compute the width needed to fully display everything.
554     const int height = dc.GetCharHeight();
555 
556     return wxSize(GetBestWidth(grid, attr, dc, row, col, height), height);
557 }
558 
559 static const int AUTOWRAP_Y_MARGIN = 4;
560 
561 int
GetBestHeight(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col,int width)562 wxGridCellAutoWrapStringRenderer::GetBestHeight(wxGrid& grid,
563                                                 wxGridCellAttr& attr,
564                                                 wxDC& dc,
565                                                 int row, int col,
566                                                 int width)
567 {
568     const int lineHeight = dc.GetCharHeight();
569 
570     // Use as many lines as we need for this width and add a small border to
571     // improve the appearance.
572     return GetTextLines(grid, dc, attr, wxSize(width, lineHeight),
573                         row, col).size() * lineHeight + AUTOWRAP_Y_MARGIN;
574 }
575 
576 int
GetBestWidth(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col,int height)577 wxGridCellAutoWrapStringRenderer::GetBestWidth(wxGrid& grid,
578                                                wxGridCellAttr& attr,
579                                                wxDC& dc,
580                                                int row, int col,
581                                                int height)
582 {
583     const int lineHeight = dc.GetCharHeight();
584 
585     // Base the maximal number of lines either on how many fit or how many
586     // (new)lines the cell's text contains, whichever results in the most lines.
587     //
588     // It's important to take the newlines into account as GetTextLines() splits
589     // based on them and the number of lines returned can never drop below that,
590     // resulting in the while loop below never exiting if there are already more
591     // lines in the text than can fit in the available height.
592     const size_t maxLines = wxMax(
593                               (height - AUTOWRAP_Y_MARGIN)/lineHeight,
594                               1 + grid.GetCellValue(row, col).Freq(wxS('\n')));
595 
596     // Increase width until all the text fits.
597     //
598     // TODO: this is not the most efficient to do it for the long strings.
599     const int charWidth = dc.GetCharWidth();
600     int width = 2*charWidth;
601     while ( GetTextLines(grid, dc, attr, wxSize(width, height),
602                          row, col).size() > maxLines )
603         width += charWidth;
604 
605     return width;
606 }
607 
608 // ----------------------------------------------------------------------------
609 // wxGridCellStringRenderer
610 // ----------------------------------------------------------------------------
611 
DoGetBestSize(const wxGridCellAttr & attr,wxDC & dc,const wxString & text)612 wxSize wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr& attr,
613                                                wxDC& dc,
614                                                const wxString& text)
615 {
616     dc.SetFont(attr.GetFont());
617     return dc.GetMultiLineTextExtent(text);
618 }
619 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)620 wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid,
621                                              wxGridCellAttr& attr,
622                                              wxDC& dc,
623                                              int row, int col)
624 {
625     return DoGetBestSize(attr, dc, grid.GetCellValue(row, col));
626 }
627 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)628 void wxGridCellStringRenderer::Draw(wxGrid& grid,
629                                     wxGridCellAttr& attr,
630                                     wxDC& dc,
631                                     const wxRect& rectCell,
632                                     int row, int col,
633                                     bool isSelected)
634 {
635     wxRect rect = rectCell;
636     rect.Inflate(-1);
637 
638     // erase only this cells background, overflow cells should have been erased
639     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
640 
641     if ( attr.CanOverflow() )
642     {
643         int hAlign, vAlign;
644         attr.GetAlignment(&hAlign, &vAlign);
645 
646         int overflowCols = 0;
647         int cols = grid.GetNumberCols();
648         int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth();
649         int cell_rows, cell_cols;
650         attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <= 0
651         if ((best_width > rectCell.width) && (col < cols) && grid.GetTable())
652         {
653             int i, c_cols, c_rows;
654             for (i = col+cell_cols; i < cols; i++)
655             {
656                 bool is_empty = true;
657                 for (int j=row; j < row + cell_rows; j++)
658                 {
659                     // check w/ anchor cell for multicell block
660                     grid.GetCellSize(j, i, &c_rows, &c_cols);
661                     if (c_rows > 0)
662                         c_rows = 0;
663                     if (!grid.GetTable()->IsEmptyCell(j + c_rows, i))
664                     {
665                         is_empty = false;
666                         break;
667                     }
668                 }
669 
670                 if (is_empty)
671                 {
672                     rect.width += grid.GetColSize(i);
673                 }
674                 else
675                 {
676                     i--;
677                     break;
678                 }
679 
680                 if (rect.width >= best_width)
681                     break;
682             }
683 
684             overflowCols = i - col - cell_cols + 1;
685             if (overflowCols >= cols)
686                 overflowCols = cols - 1;
687         }
688 
689         if (overflowCols > 0) // redraw overflow cells w/ proper hilight
690         {
691             hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned
692             wxRect clip = rect;
693             clip.x += rectCell.width;
694             // draw each overflow cell individually
695             int col_end = col + cell_cols + overflowCols;
696             if (col_end >= grid.GetNumberCols())
697                 col_end = grid.GetNumberCols() - 1;
698             for (int i = col + cell_cols; i <= col_end; i++)
699             {
700                 // redraw the cell to update the background
701                 wxGridCellCoords coords(row, i);
702                 grid.DrawCell(dc, coords);
703 
704                 clip.width = grid.GetColSize(i) - 1;
705                 wxDCClipper clipper(dc, clip);
706 
707                 SetTextColoursAndFont(grid, attr, dc,
708                         grid.IsInSelection(row,i));
709 
710                 grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
711                         rect, hAlign, vAlign);
712                 clip.x += grid.GetColSize(i) - 1;
713             }
714 
715             rect = rectCell;
716             rect.Inflate(-1);
717             rect.width++;
718         }
719     }
720 
721     // now we only have to draw the text
722     SetTextColoursAndFont(grid, attr, dc, isSelected);
723 
724     grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
725                            rect, attr);
726 }
727 
728 // ----------------------------------------------------------------------------
729 // wxGridCellNumberRenderer
730 // ----------------------------------------------------------------------------
731 
GetString(const wxGrid & grid,int row,int col)732 wxString wxGridCellNumberRenderer::GetString(const wxGrid& grid, int row, int col)
733 {
734     wxGridTableBase *table = grid.GetTable();
735     wxString text;
736     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
737     {
738         text.Printf(wxT("%ld"), table->GetValueAsLong(row, col));
739     }
740     else
741     {
742         text = table->GetValue(row, col);
743     }
744 
745     return text;
746 }
747 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)748 void wxGridCellNumberRenderer::Draw(wxGrid& grid,
749                                     wxGridCellAttr& attr,
750                                     wxDC& dc,
751                                     const wxRect& rectCell,
752                                     int row, int col,
753                                     bool isSelected)
754 {
755     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
756 
757     SetTextColoursAndFont(grid, attr, dc, isSelected);
758 
759     wxRect rect = rectCell;
760     rect.Inflate(-1);
761 
762     // draw the text right aligned by default
763     grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
764                            wxALIGN_RIGHT);
765 }
766 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)767 wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid,
768                                              wxGridCellAttr& attr,
769                                              wxDC& dc,
770                                              int row, int col)
771 {
772     return DoGetBestSize(attr, dc, GetString(grid, row, col));
773 }
774 
GetMaxBestSize(wxGrid & WXUNUSED (grid),wxGridCellAttr & attr,wxDC & dc)775 wxSize wxGridCellNumberRenderer::GetMaxBestSize(wxGrid& WXUNUSED(grid),
776                                                 wxGridCellAttr& attr,
777                                                 wxDC& dc)
778 {
779     // In theory, it's possible that there is a value in min..max range which
780     // is longer than both min and max, e.g. we could conceivably have "88" be
781     // wider than both "87" and "91" with some fonts, but it seems something
782     // too exotic to worry about in practice.
783     wxSize size = DoGetBestSize(attr, dc, wxString::Format("%ld", m_minValue));
784     size.IncTo(DoGetBestSize(attr, dc, wxString::Format("%ld", m_maxValue)));
785 
786     return size;
787 }
788 
SetParameters(const wxString & params)789 void wxGridCellNumberRenderer::SetParameters(const wxString& params)
790 {
791     if ( params.empty() )
792         return;
793 
794     wxString maxStr;
795     const wxString minStr = params.BeforeFirst(',', &maxStr);
796 
797     if ( !minStr.ToLong(&m_minValue) || !maxStr.ToLong(&m_maxValue) )
798     {
799         wxLogDebug("Invalid wxGridCellNumberRenderer parameters \"%s\"", params);
800     }
801 }
802 
803 // ----------------------------------------------------------------------------
804 // wxGridCellFloatRenderer
805 // ----------------------------------------------------------------------------
806 
wxGridCellFloatRenderer(int width,int precision,int format)807 wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width,
808                                                  int precision,
809                                                  int format)
810 {
811     SetWidth(width);
812     SetPrecision(precision);
813     SetFormat(format);
814 }
815 
Clone() const816 wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const
817 {
818     wxGridCellFloatRenderer *renderer = new wxGridCellFloatRenderer;
819     renderer->m_width = m_width;
820     renderer->m_precision = m_precision;
821     renderer->m_style = m_style;
822     renderer->m_format = m_format;
823 
824     return renderer;
825 }
826 
GetString(const wxGrid & grid,int row,int col)827 wxString wxGridCellFloatRenderer::GetString(const wxGrid& grid, int row, int col)
828 {
829     wxGridTableBase *table = grid.GetTable();
830 
831     bool hasDouble;
832     double val;
833     wxString text;
834     if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
835     {
836         val = table->GetValueAsDouble(row, col);
837         hasDouble = true;
838     }
839     else
840     {
841         text = table->GetValue(row, col);
842         hasDouble = text.ToDouble(&val);
843     }
844 
845     if ( hasDouble )
846     {
847         if ( !m_format )
848         {
849             if ( m_width == -1 )
850             {
851                 if ( m_precision == -1 )
852                 {
853                     // default width/precision
854                     m_format = wxT("%");
855                 }
856                 else
857                 {
858                     m_format.Printf(wxT("%%.%d"), m_precision);
859                 }
860             }
861             else if ( m_precision == -1 )
862             {
863                 // default precision
864                 m_format.Printf(wxT("%%%d."), m_width);
865             }
866             else
867             {
868                 m_format.Printf(wxT("%%%d.%d"), m_width, m_precision);
869             }
870 
871             bool isUpper = ( ( m_style & wxGRID_FLOAT_FORMAT_UPPER ) == wxGRID_FLOAT_FORMAT_UPPER);
872             if ( ( m_style & wxGRID_FLOAT_FORMAT_SCIENTIFIC ) == wxGRID_FLOAT_FORMAT_SCIENTIFIC)
873                 m_format += isUpper ? wxT('E') : wxT('e');
874             else if ( ( m_style & wxGRID_FLOAT_FORMAT_COMPACT ) == wxGRID_FLOAT_FORMAT_COMPACT)
875                 m_format += isUpper ? wxT('G') : wxT('g');
876             else
877                 m_format += wxT('f');
878         }
879 
880         text.Printf(m_format, val);
881 
882     }
883     //else: text already contains the string
884 
885     return text;
886 }
887 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rectCell,int row,int col,bool isSelected)888 void wxGridCellFloatRenderer::Draw(wxGrid& grid,
889                                    wxGridCellAttr& attr,
890                                    wxDC& dc,
891                                    const wxRect& rectCell,
892                                    int row, int col,
893                                    bool isSelected)
894 {
895     wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
896 
897     SetTextColoursAndFont(grid, attr, dc, isSelected);
898 
899     wxRect rect = rectCell;
900     rect.Inflate(-1);
901 
902     // draw the text right aligned by default
903     grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
904                            wxALIGN_RIGHT);
905 }
906 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int row,int col)907 wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid,
908                                             wxGridCellAttr& attr,
909                                             wxDC& dc,
910                                             int row, int col)
911 {
912     return DoGetBestSize(attr, dc, GetString(grid, row, col));
913 }
914 
SetParameters(const wxString & params)915 void wxGridCellFloatRenderer::SetParameters(const wxString& params)
916 {
917     if ( !params )
918     {
919         // reset to defaults
920         SetWidth(-1);
921         SetPrecision(-1);
922         SetFormat(wxGRID_FLOAT_FORMAT_DEFAULT);
923     }
924     else
925     {
926         wxString rest;
927         wxString tmp = params.BeforeFirst(wxT(','), &rest);
928         if ( !tmp.empty() )
929         {
930             long width;
931             if ( tmp.ToLong(&width) )
932             {
933                 SetWidth((int)width);
934             }
935             else
936             {
937                 wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
938             }
939         }
940 
941         tmp = rest.BeforeFirst(wxT(','));
942         if ( !tmp.empty() )
943         {
944             long precision;
945             if ( tmp.ToLong(&precision) )
946             {
947                 SetPrecision((int)precision);
948             }
949             else
950             {
951                 wxLogDebug(wxT("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str());
952             }
953         }
954 
955         tmp = rest.AfterFirst(wxT(','));
956         if ( !tmp.empty() )
957         {
958             if ( tmp[0] == wxT('f') )
959             {
960                 SetFormat(wxGRID_FLOAT_FORMAT_FIXED);
961             }
962             else if ( tmp[0] == wxT('e') )
963             {
964                 SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC);
965             }
966             else if ( tmp[0] == wxT('g') )
967             {
968                 SetFormat(wxGRID_FLOAT_FORMAT_COMPACT);
969             }
970             else if ( tmp[0] == wxT('E') )
971             {
972                 SetFormat(wxGRID_FLOAT_FORMAT_SCIENTIFIC |
973                           wxGRID_FLOAT_FORMAT_UPPER);
974             }
975             else if ( tmp[0] == wxT('F') )
976             {
977                 SetFormat(wxGRID_FLOAT_FORMAT_FIXED |
978                           wxGRID_FLOAT_FORMAT_UPPER);
979             }
980             else if ( tmp[0] == wxT('G') )
981             {
982                 SetFormat(wxGRID_FLOAT_FORMAT_COMPACT |
983                           wxGRID_FLOAT_FORMAT_UPPER);
984             }
985             else
986             {
987                 wxLogDebug("Invalid wxGridCellFloatRenderer format "
988                            "parameter string '%s ignored", params);
989             }
990         }
991     }
992 }
993 
994 // ----------------------------------------------------------------------------
995 // wxGridCellBoolRenderer
996 // ----------------------------------------------------------------------------
997 
GetBestSize(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,int WXUNUSED (row),int WXUNUSED (col))998 wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid,
999                                            wxGridCellAttr& attr,
1000                                            wxDC& dc,
1001                                            int WXUNUSED(row),
1002                                            int WXUNUSED(col))
1003 {
1004     return GetMaxBestSize(grid, attr, dc);
1005 }
1006 
GetMaxBestSize(wxGrid & grid,wxGridCellAttr & WXUNUSED (attr),wxDC & WXUNUSED (dc))1007 wxSize wxGridCellBoolRenderer::GetMaxBestSize(wxGrid& grid,
1008                                               wxGridCellAttr& WXUNUSED(attr),
1009                                               wxDC& WXUNUSED(dc))
1010 {
1011     static wxPrivate::DpiDependentValue<wxSize> s_sizeCheckMark;
1012 
1013     // Get the check mark size in pixels if it hadn't been done yet or if the
1014     // DPI has changed.
1015     if ( s_sizeCheckMark.HasChanged(&grid) )
1016     {
1017         s_sizeCheckMark.SetAtNewDPI
1018             (
1019                 wxRendererNative::Get().GetCheckBoxSize(&grid, wxCONTROL_CELL)
1020             );
1021     }
1022 
1023     return s_sizeCheckMark.Get();
1024 }
1025 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rect,int row,int col,bool isSelected)1026 void wxGridCellBoolRenderer::Draw(wxGrid& grid,
1027                                   wxGridCellAttr& attr,
1028                                   wxDC& dc,
1029                                   const wxRect& rect,
1030                                   int row, int col,
1031                                   bool isSelected)
1032 {
1033     wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
1034 
1035     int hAlign = wxALIGN_LEFT;
1036     int vAlign = wxALIGN_CENTRE_VERTICAL;
1037     attr.GetNonDefaultAlignment(&hAlign, &vAlign);
1038 
1039     const wxRect checkBoxRect =
1040         wxGetContentRect(GetBestSize(grid, attr, dc, row, col),
1041                          rect, hAlign, vAlign);
1042 
1043     bool value;
1044     if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) )
1045     {
1046         value = grid.GetTable()->GetValueAsBool(row, col);
1047     }
1048     else
1049     {
1050         wxString cellval( grid.GetTable()->GetValue(row, col) );
1051         value = wxGridCellBoolEditor::IsTrueValue(cellval);
1052     }
1053 
1054     int flags = wxCONTROL_CELL;
1055     if (value)
1056         flags |= wxCONTROL_CHECKED;
1057 
1058     wxRendererNative::Get().DrawCheckBox( &grid, dc, checkBoxRect, flags );
1059 }
1060 
1061 #endif // wxUSE_GRID
1062 
1063