]> git.saurik.com Git - wxWidgets.git/blob - src/generic/grideditors.cpp
Allow passing multi-line strings to wxDC::DrawText(), even under MSW.
[wxWidgets.git] / src / generic / grideditors.cpp
1 ///////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/grideditors.cpp
3 // Purpose: wxGridCellEditorEvtHandler and wxGrid editors
4 // Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by: Robin Dunn, Vadim Zeitlin, Santiago Palacios
6 // Created: 1/08/1999
7 // RCS-ID: $Id$
8 // Copyright: (c) Michael Bedward (mbedward@ozemail.com.au)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_GRID
20
21 #include "wx/grid.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/utils.h"
25 #include "wx/dcclient.h"
26 #include "wx/settings.h"
27 #include "wx/log.h"
28 #include "wx/textctrl.h"
29 #include "wx/checkbox.h"
30 #include "wx/combobox.h"
31 #include "wx/valtext.h"
32 #include "wx/intl.h"
33 #include "wx/math.h"
34 #include "wx/listbox.h"
35 #endif
36
37 #include "wx/textfile.h"
38 #include "wx/spinctrl.h"
39 #include "wx/tokenzr.h"
40 #include "wx/renderer.h"
41 #include "wx/headerctrl.h"
42
43 #include "wx/generic/gridsel.h"
44 #include "wx/generic/grideditors.h"
45 #include "wx/generic/private/grid.h"
46
47 #if defined(__WXMOTIF__)
48 #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier)
49 #else
50 #define WXUNUSED_MOTIF(identifier) identifier
51 #endif
52
53 #if defined(__WXGTK__)
54 #define WXUNUSED_GTK(identifier) WXUNUSED(identifier)
55 #else
56 #define WXUNUSED_GTK(identifier) identifier
57 #endif
58
59 // Required for wxIs... functions
60 #include <ctype.h>
61
62 // ============================================================================
63 // implementation
64 // ============================================================================
65
66 // ----------------------------------------------------------------------------
67 // wxGridCellEditorEvtHandler
68 // ----------------------------------------------------------------------------
69
70 void wxGridCellEditorEvtHandler::OnKillFocus(wxFocusEvent& event)
71 {
72 // Don't disable the cell if we're just starting to edit it
73 if ( m_inSetFocus )
74 {
75 event.Skip();
76 return;
77 }
78
79 // accept changes
80 m_grid->DisableCellEditControl();
81
82 // notice that we must not skip the event here because the call above may
83 // delete the control which received the kill focus event in the first
84 // place and if we pretend not having processed the event, the search for a
85 // handler for it will continue using the now deleted object resulting in a
86 // crash
87 }
88
89 void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event)
90 {
91 switch ( event.GetKeyCode() )
92 {
93 case WXK_ESCAPE:
94 m_editor->Reset();
95 m_grid->DisableCellEditControl();
96 break;
97
98 case WXK_TAB:
99 m_grid->GetEventHandler()->ProcessEvent( event );
100 break;
101
102 case WXK_RETURN:
103 case WXK_NUMPAD_ENTER:
104 if (!m_grid->GetEventHandler()->ProcessEvent(event))
105 m_editor->HandleReturn(event);
106 break;
107
108 default:
109 event.Skip();
110 break;
111 }
112 }
113
114 void wxGridCellEditorEvtHandler::OnChar(wxKeyEvent& event)
115 {
116 int row = m_grid->GetGridCursorRow();
117 int col = m_grid->GetGridCursorCol();
118 wxRect rect = m_grid->CellToRect( row, col );
119 int cw, ch;
120 m_grid->GetGridWindow()->GetClientSize( &cw, &ch );
121
122 // if cell width is smaller than grid client area, cell is wholly visible
123 bool wholeCellVisible = (rect.GetWidth() < cw);
124
125 switch ( event.GetKeyCode() )
126 {
127 case WXK_ESCAPE:
128 case WXK_TAB:
129 case WXK_RETURN:
130 case WXK_NUMPAD_ENTER:
131 break;
132
133 case WXK_HOME:
134 {
135 if ( wholeCellVisible )
136 {
137 // no special processing needed...
138 event.Skip();
139 break;
140 }
141
142 // do special processing for partly visible cell...
143
144 // get the widths of all cells previous to this one
145 int colXPos = 0;
146 for ( int i = 0; i < col; i++ )
147 {
148 colXPos += m_grid->GetColSize(i);
149 }
150
151 int xUnit = 1, yUnit = 1;
152 m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit);
153 if (col != 0)
154 {
155 m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL));
156 }
157 else
158 {
159 m_grid->Scroll(colXPos / xUnit, m_grid->GetScrollPos(wxVERTICAL));
160 }
161 event.Skip();
162 break;
163 }
164
165 case WXK_END:
166 {
167 if ( wholeCellVisible )
168 {
169 // no special processing needed...
170 event.Skip();
171 break;
172 }
173
174 // do special processing for partly visible cell...
175
176 int textWidth = 0;
177 wxString value = m_grid->GetCellValue(row, col);
178 if ( wxEmptyString != value )
179 {
180 // get width of cell CONTENTS (text)
181 int y;
182 wxFont font = m_grid->GetCellFont(row, col);
183 m_grid->GetTextExtent(value, &textWidth, &y, NULL, NULL, &font);
184
185 // try to RIGHT align the text by scrolling
186 int client_right = m_grid->GetGridWindow()->GetClientSize().GetWidth();
187
188 // (m_grid->GetScrollLineX()*2) is a factor for not scrolling to far,
189 // otherwise the last part of the cell content might be hidden below the scroll bar
190 // FIXME: maybe there is a more suitable correction?
191 textWidth -= (client_right - (m_grid->GetScrollLineX() * 2));
192 if ( textWidth < 0 )
193 {
194 textWidth = 0;
195 }
196 }
197
198 // get the widths of all cells previous to this one
199 int colXPos = 0;
200 for ( int i = 0; i < col; i++ )
201 {
202 colXPos += m_grid->GetColSize(i);
203 }
204
205 // and add the (modified) text width of the cell contents
206 // as we'd like to see the last part of the cell contents
207 colXPos += textWidth;
208
209 int xUnit = 1, yUnit = 1;
210 m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit);
211 m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL));
212 event.Skip();
213 break;
214 }
215
216 default:
217 event.Skip();
218 break;
219 }
220 }
221
222 // ----------------------------------------------------------------------------
223 // wxGridCellEditor
224 // ----------------------------------------------------------------------------
225
226 wxGridCellEditor::wxGridCellEditor()
227 {
228 m_control = NULL;
229 m_attr = NULL;
230 }
231
232 wxGridCellEditor::~wxGridCellEditor()
233 {
234 Destroy();
235 }
236
237 void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent),
238 wxWindowID WXUNUSED(id),
239 wxEvtHandler* evtHandler)
240 {
241 if ( evtHandler )
242 m_control->PushEventHandler(evtHandler);
243 }
244
245 void wxGridCellEditor::PaintBackground(const wxRect& rectCell,
246 wxGridCellAttr *attr)
247 {
248 // erase the background because we might not fill the cell
249 wxClientDC dc(m_control->GetParent());
250 wxGridWindow* gridWindow = wxDynamicCast(m_control->GetParent(), wxGridWindow);
251 if (gridWindow)
252 gridWindow->GetOwner()->PrepareDC(dc);
253
254 dc.SetPen(*wxTRANSPARENT_PEN);
255 dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
256 dc.DrawRectangle(rectCell);
257
258 // redraw the control we just painted over
259 m_control->Refresh();
260 }
261
262 void wxGridCellEditor::Destroy()
263 {
264 if (m_control)
265 {
266 m_control->PopEventHandler( true /* delete it*/ );
267
268 m_control->Destroy();
269 m_control = NULL;
270 }
271 }
272
273 void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr)
274 {
275 wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
276
277 m_control->Show(show);
278
279 if ( show )
280 {
281 // set the colours/fonts if we have any
282 if ( attr )
283 {
284 m_colFgOld = m_control->GetForegroundColour();
285 m_control->SetForegroundColour(attr->GetTextColour());
286
287 m_colBgOld = m_control->GetBackgroundColour();
288 m_control->SetBackgroundColour(attr->GetBackgroundColour());
289
290 // Workaround for GTK+1 font setting problem on some platforms
291 #if !defined(__WXGTK__) || defined(__WXGTK20__)
292 m_fontOld = m_control->GetFont();
293 m_control->SetFont(attr->GetFont());
294 #endif
295
296 // can't do anything more in the base class version, the other
297 // attributes may only be used by the derived classes
298 }
299 }
300 else
301 {
302 // restore the standard colours fonts
303 if ( m_colFgOld.Ok() )
304 {
305 m_control->SetForegroundColour(m_colFgOld);
306 m_colFgOld = wxNullColour;
307 }
308
309 if ( m_colBgOld.Ok() )
310 {
311 m_control->SetBackgroundColour(m_colBgOld);
312 m_colBgOld = wxNullColour;
313 }
314
315 // Workaround for GTK+1 font setting problem on some platforms
316 #if !defined(__WXGTK__) || defined(__WXGTK20__)
317 if ( m_fontOld.Ok() )
318 {
319 m_control->SetFont(m_fontOld);
320 m_fontOld = wxNullFont;
321 }
322 #endif
323 }
324 }
325
326 void wxGridCellEditor::SetSize(const wxRect& rect)
327 {
328 wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
329
330 m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
331 }
332
333 void wxGridCellEditor::HandleReturn(wxKeyEvent& event)
334 {
335 event.Skip();
336 }
337
338 bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event)
339 {
340 bool ctrl = event.ControlDown();
341 bool alt = event.AltDown();
342
343 #ifdef __WXMAC__
344 // On the Mac the Alt key is more like shift and is used for entry of
345 // valid characters, so check for Ctrl and Meta instead.
346 alt = event.MetaDown();
347 #endif
348
349 // Assume it's not a valid char if ctrl or alt is down, but if both are
350 // down then it may be because of an AltGr key combination, so let them
351 // through in that case.
352 if ((ctrl || alt) && !(ctrl && alt))
353 return false;
354
355 #if wxUSE_UNICODE
356 // if the unicode key code is not really a unicode character (it may
357 // be a function key or etc., the platforms appear to always give us a
358 // small value in this case) then fallback to the ASCII key code but
359 // don't do anything for function keys or etc.
360 if ( event.GetUnicodeKey() > 127 && event.GetKeyCode() > 127 )
361 return false;
362 #else
363 if ( event.GetKeyCode() > 255 )
364 return false;
365 #endif
366
367 return true;
368 }
369
370 void wxGridCellEditor::StartingKey(wxKeyEvent& event)
371 {
372 event.Skip();
373 }
374
375 void wxGridCellEditor::StartingClick()
376 {
377 }
378
379 #if wxUSE_TEXTCTRL
380
381 // ----------------------------------------------------------------------------
382 // wxGridCellTextEditor
383 // ----------------------------------------------------------------------------
384
385 wxGridCellTextEditor::wxGridCellTextEditor()
386 {
387 m_maxChars = 0;
388 }
389
390 void wxGridCellTextEditor::Create(wxWindow* parent,
391 wxWindowID id,
392 wxEvtHandler* evtHandler)
393 {
394 DoCreate(parent, id, evtHandler);
395 }
396
397 void wxGridCellTextEditor::DoCreate(wxWindow* parent,
398 wxWindowID id,
399 wxEvtHandler* evtHandler,
400 long style)
401 {
402 // Use of wxTE_RICH2 is a strange hack to work around the bug #11681: a
403 // plain text control seems to lose its caret somehow when we hide it and
404 // show it again for a different cell.
405 style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER | wxTE_RICH2;
406
407 m_control = new wxTextCtrl(parent, id, wxEmptyString,
408 wxDefaultPosition, wxDefaultSize,
409 style);
410
411 // set max length allowed in the textctrl, if the parameter was set
412 if ( m_maxChars != 0 )
413 {
414 Text()->SetMaxLength(m_maxChars);
415 }
416
417 wxGridCellEditor::Create(parent, id, evtHandler);
418 }
419
420 void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell),
421 wxGridCellAttr * WXUNUSED(attr))
422 {
423 // as we fill the entire client area,
424 // don't do anything here to minimize flicker
425 }
426
427 void wxGridCellTextEditor::SetSize(const wxRect& rectOrig)
428 {
429 wxRect rect(rectOrig);
430
431 // Make the edit control large enough to allow for internal margins
432 //
433 // TODO: remove this if the text ctrl sizing is improved esp. for unix
434 //
435 #if defined(__WXGTK__)
436 if (rect.x != 0)
437 {
438 rect.x += 1;
439 rect.y += 1;
440 rect.width -= 1;
441 rect.height -= 1;
442 }
443 #elif defined(__WXMSW__)
444 if ( rect.x == 0 )
445 rect.x += 2;
446 else
447 rect.x += 3;
448
449 if ( rect.y == 0 )
450 rect.y += 2;
451 else
452 rect.y += 3;
453
454 rect.width -= 2;
455 rect.height -= 2;
456 #else
457 int extra_x = ( rect.x > 2 ) ? 2 : 1;
458 int extra_y = ( rect.y > 2 ) ? 2 : 1;
459
460 #if defined(__WXMOTIF__)
461 extra_x *= 2;
462 extra_y *= 2;
463 #endif
464
465 rect.SetLeft( wxMax(0, rect.x - extra_x) );
466 rect.SetTop( wxMax(0, rect.y - extra_y) );
467 rect.SetRight( rect.GetRight() + 2 * extra_x );
468 rect.SetBottom( rect.GetBottom() + 2 * extra_y );
469 #endif
470
471 wxGridCellEditor::SetSize(rect);
472 }
473
474 void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid)
475 {
476 wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
477
478 m_value = grid->GetTable()->GetValue(row, col);
479
480 DoBeginEdit(m_value);
481 }
482
483 void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
484 {
485 Text()->SetValue(startValue);
486 Text()->SetInsertionPointEnd();
487 Text()->SetSelection(-1, -1);
488 Text()->SetFocus();
489 }
490
491 bool wxGridCellTextEditor::EndEdit(int WXUNUSED(row),
492 int WXUNUSED(col),
493 const wxGrid* WXUNUSED(grid),
494 const wxString& WXUNUSED(oldval),
495 wxString *newval)
496 {
497 wxCHECK_MSG( m_control, false,
498 "wxGridCellTextEditor must be created first!" );
499
500 const wxString value = Text()->GetValue();
501 if ( value == m_value )
502 return false;
503
504 m_value = value;
505
506 if ( newval )
507 *newval = m_value;
508
509 return true;
510 }
511
512 void wxGridCellTextEditor::ApplyEdit(int row, int col, wxGrid* grid)
513 {
514 grid->GetTable()->SetValue(row, col, m_value);
515 m_value.clear();
516 }
517
518 void wxGridCellTextEditor::Reset()
519 {
520 wxASSERT_MSG( m_control, "wxGridCellTextEditor must be created first!" );
521
522 DoReset(m_value);
523 }
524
525 void wxGridCellTextEditor::DoReset(const wxString& startValue)
526 {
527 Text()->SetValue(startValue);
528 Text()->SetInsertionPointEnd();
529 }
530
531 bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
532 {
533 return wxGridCellEditor::IsAcceptedKey(event);
534 }
535
536 void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
537 {
538 // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
539 // longer an appropriate way to get the character into the text control.
540 // Do it ourselves instead. We know that if we get this far that we have
541 // a valid character, so not a whole lot of testing needs to be done.
542
543 wxTextCtrl* tc = Text();
544 wxChar ch;
545 long pos;
546
547 #if wxUSE_UNICODE
548 ch = event.GetUnicodeKey();
549 if (ch <= 127)
550 ch = (wxChar)event.GetKeyCode();
551 #else
552 ch = (wxChar)event.GetKeyCode();
553 #endif
554
555 switch (ch)
556 {
557 case WXK_DELETE:
558 // delete the character at the cursor
559 pos = tc->GetInsertionPoint();
560 if (pos < tc->GetLastPosition())
561 tc->Remove(pos, pos + 1);
562 break;
563
564 case WXK_BACK:
565 // delete the character before the cursor
566 pos = tc->GetInsertionPoint();
567 if (pos > 0)
568 tc->Remove(pos - 1, pos);
569 break;
570
571 default:
572 tc->WriteText(ch);
573 break;
574 }
575 }
576
577 void wxGridCellTextEditor::HandleReturn( wxKeyEvent&
578 WXUNUSED_GTK(WXUNUSED_MOTIF(event)) )
579 {
580 #if defined(__WXMOTIF__) || defined(__WXGTK__)
581 // wxMotif needs a little extra help...
582 size_t pos = (size_t)( Text()->GetInsertionPoint() );
583 wxString s( Text()->GetValue() );
584 s = s.Left(pos) + wxT("\n") + s.Mid(pos);
585 Text()->SetValue(s);
586 Text()->SetInsertionPoint( pos );
587 #else
588 // the other ports can handle a Return key press
589 //
590 event.Skip();
591 #endif
592 }
593
594 void wxGridCellTextEditor::SetParameters(const wxString& params)
595 {
596 if ( !params )
597 {
598 // reset to default
599 m_maxChars = 0;
600 }
601 else
602 {
603 long tmp;
604 if ( params.ToLong(&tmp) )
605 {
606 m_maxChars = (size_t)tmp;
607 }
608 else
609 {
610 wxLogDebug( wxT("Invalid wxGridCellTextEditor parameter string '%s' ignored"), params.c_str() );
611 }
612 }
613 }
614
615 // return the value in the text control
616 wxString wxGridCellTextEditor::GetValue() const
617 {
618 return Text()->GetValue();
619 }
620
621 // ----------------------------------------------------------------------------
622 // wxGridCellNumberEditor
623 // ----------------------------------------------------------------------------
624
625 wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max)
626 {
627 m_min = min;
628 m_max = max;
629 }
630
631 void wxGridCellNumberEditor::Create(wxWindow* parent,
632 wxWindowID id,
633 wxEvtHandler* evtHandler)
634 {
635 #if wxUSE_SPINCTRL
636 if ( HasRange() )
637 {
638 // create a spin ctrl
639 m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString,
640 wxDefaultPosition, wxDefaultSize,
641 wxSP_ARROW_KEYS,
642 m_min, m_max);
643
644 wxGridCellEditor::Create(parent, id, evtHandler);
645 }
646 else
647 #endif
648 {
649 // just a text control
650 wxGridCellTextEditor::Create(parent, id, evtHandler);
651
652 #if wxUSE_VALIDATORS
653 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
654 #endif
655 }
656 }
657
658 void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
659 {
660 // first get the value
661 wxGridTableBase *table = grid->GetTable();
662 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
663 {
664 m_value = table->GetValueAsLong(row, col);
665 }
666 else
667 {
668 m_value = 0;
669 wxString sValue = table->GetValue(row, col);
670 if (! sValue.ToLong(&m_value) && ! sValue.empty())
671 {
672 wxFAIL_MSG( wxT("this cell doesn't have numeric value") );
673 return;
674 }
675 }
676
677 #if wxUSE_SPINCTRL
678 if ( HasRange() )
679 {
680 Spin()->SetValue((int)m_value);
681 Spin()->SetFocus();
682 }
683 else
684 #endif
685 {
686 DoBeginEdit(GetString());
687 }
688 }
689
690 bool wxGridCellNumberEditor::EndEdit(int WXUNUSED(row),
691 int WXUNUSED(col),
692 const wxGrid* WXUNUSED(grid),
693 const wxString& oldval, wxString *newval)
694 {
695 long value = 0;
696 wxString text;
697
698 #if wxUSE_SPINCTRL
699 if ( HasRange() )
700 {
701 value = Spin()->GetValue();
702 if ( value == m_value )
703 return false;
704
705 text.Printf(wxT("%ld"), value);
706 }
707 else // using unconstrained input
708 #endif // wxUSE_SPINCTRL
709 {
710 text = Text()->GetValue();
711 if ( text.empty() )
712 {
713 if ( oldval.empty() )
714 return false;
715 }
716 else // non-empty text now (maybe 0)
717 {
718 if ( !text.ToLong(&value) )
719 return false;
720
721 // if value == m_value == 0 but old text was "" and new one is
722 // "0" something still did change
723 if ( value == m_value && (value || !oldval.empty()) )
724 return false;
725 }
726 }
727
728 m_value = value;
729
730 if ( newval )
731 *newval = text;
732
733 return true;
734 }
735
736 void wxGridCellNumberEditor::ApplyEdit(int row, int col, wxGrid* grid)
737 {
738 wxGridTableBase * const table = grid->GetTable();
739 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
740 table->SetValueAsLong(row, col, m_value);
741 else
742 table->SetValue(row, col, wxString::Format("%ld", m_value));
743 }
744
745 void wxGridCellNumberEditor::Reset()
746 {
747 #if wxUSE_SPINCTRL
748 if ( HasRange() )
749 {
750 Spin()->SetValue((int)m_value);
751 }
752 else
753 #endif
754 {
755 DoReset(GetString());
756 }
757 }
758
759 bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event)
760 {
761 if ( wxGridCellEditor::IsAcceptedKey(event) )
762 {
763 int keycode = event.GetKeyCode();
764 if ( (keycode < 128) &&
765 (wxIsdigit(keycode) || keycode == '+' || keycode == '-'))
766 {
767 return true;
768 }
769 }
770
771 return false;
772 }
773
774 void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event)
775 {
776 int keycode = event.GetKeyCode();
777 if ( !HasRange() )
778 {
779 if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-')
780 {
781 wxGridCellTextEditor::StartingKey(event);
782
783 // skip Skip() below
784 return;
785 }
786 }
787 #if wxUSE_SPINCTRL
788 else
789 {
790 if ( wxIsdigit(keycode) )
791 {
792 wxSpinCtrl* spin = (wxSpinCtrl*)m_control;
793 spin->SetValue(keycode - '0');
794 spin->SetSelection(1,1);
795 return;
796 }
797 }
798 #endif
799
800 event.Skip();
801 }
802
803 void wxGridCellNumberEditor::SetParameters(const wxString& params)
804 {
805 if ( !params )
806 {
807 // reset to default
808 m_min =
809 m_max = -1;
810 }
811 else
812 {
813 long tmp;
814 if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
815 {
816 m_min = (int)tmp;
817
818 if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
819 {
820 m_max = (int)tmp;
821
822 // skip the error message below
823 return;
824 }
825 }
826
827 wxLogDebug(wxT("Invalid wxGridCellNumberEditor parameter string '%s' ignored"), params.c_str());
828 }
829 }
830
831 // return the value in the spin control if it is there (the text control otherwise)
832 wxString wxGridCellNumberEditor::GetValue() const
833 {
834 wxString s;
835
836 #if wxUSE_SPINCTRL
837 if ( HasRange() )
838 {
839 long value = Spin()->GetValue();
840 s.Printf(wxT("%ld"), value);
841 }
842 else
843 #endif
844 {
845 s = Text()->GetValue();
846 }
847
848 return s;
849 }
850
851 // ----------------------------------------------------------------------------
852 // wxGridCellFloatEditor
853 // ----------------------------------------------------------------------------
854
855 wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision)
856 {
857 m_width = width;
858 m_precision = precision;
859 }
860
861 void wxGridCellFloatEditor::Create(wxWindow* parent,
862 wxWindowID id,
863 wxEvtHandler* evtHandler)
864 {
865 wxGridCellTextEditor::Create(parent, id, evtHandler);
866
867 #if wxUSE_VALIDATORS
868 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
869 #endif
870 }
871
872 void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
873 {
874 // first get the value
875 wxGridTableBase * const table = grid->GetTable();
876 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
877 {
878 m_value = table->GetValueAsDouble(row, col);
879 }
880 else
881 {
882 m_value = 0.0;
883
884 const wxString value = table->GetValue(row, col);
885 if ( !value.empty() )
886 {
887 if ( !value.ToDouble(&m_value) )
888 {
889 wxFAIL_MSG( wxT("this cell doesn't have float value") );
890 return;
891 }
892 }
893 }
894
895 DoBeginEdit(GetString());
896 }
897
898 bool wxGridCellFloatEditor::EndEdit(int WXUNUSED(row),
899 int WXUNUSED(col),
900 const wxGrid* WXUNUSED(grid),
901 const wxString& oldval, wxString *newval)
902 {
903 const wxString text(Text()->GetValue());
904
905 double value;
906 if ( !text.empty() )
907 {
908 if ( !text.ToDouble(&value) )
909 return false;
910 }
911 else // new value is empty string
912 {
913 if ( oldval.empty() )
914 return false; // nothing changed
915
916 value = 0.;
917 }
918
919 // the test for empty strings ensures that we don't skip the value setting
920 // when "" is replaced by "0" or vice versa as "" numeric value is also 0.
921 if ( wxIsSameDouble(value, m_value) && !text.empty() && !oldval.empty() )
922 return false; // nothing changed
923
924 m_value = value;
925
926 if ( newval )
927 *newval = text;
928
929 return true;
930 }
931
932 void wxGridCellFloatEditor::ApplyEdit(int row, int col, wxGrid* grid)
933 {
934 wxGridTableBase * const table = grid->GetTable();
935
936 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) )
937 table->SetValueAsDouble(row, col, m_value);
938 else
939 table->SetValue(row, col, Text()->GetValue());
940 }
941
942 void wxGridCellFloatEditor::Reset()
943 {
944 DoReset(GetString());
945 }
946
947 void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event)
948 {
949 int keycode = event.GetKeyCode();
950 char tmpbuf[2];
951 tmpbuf[0] = (char) keycode;
952 tmpbuf[1] = '\0';
953 wxString strbuf(tmpbuf, *wxConvCurrent);
954
955 #if wxUSE_INTL
956 bool is_decimal_point = ( strbuf ==
957 wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER) );
958 #else
959 bool is_decimal_point = ( strbuf == wxT(".") );
960 #endif
961
962 if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-'
963 || is_decimal_point )
964 {
965 wxGridCellTextEditor::StartingKey(event);
966
967 // skip Skip() below
968 return;
969 }
970
971 event.Skip();
972 }
973
974 void wxGridCellFloatEditor::SetParameters(const wxString& params)
975 {
976 if ( !params )
977 {
978 // reset to default
979 m_width =
980 m_precision = -1;
981 }
982 else
983 {
984 long tmp;
985 if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
986 {
987 m_width = (int)tmp;
988
989 if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
990 {
991 m_precision = (int)tmp;
992
993 // skip the error message below
994 return;
995 }
996 }
997
998 wxLogDebug(wxT("Invalid wxGridCellFloatEditor parameter string '%s' ignored"), params.c_str());
999 }
1000 }
1001
1002 wxString wxGridCellFloatEditor::GetString() const
1003 {
1004 wxString fmt;
1005 if ( m_precision == -1 && m_width != -1)
1006 {
1007 // default precision
1008 fmt.Printf(wxT("%%%d.f"), m_width);
1009 }
1010 else if ( m_precision != -1 && m_width == -1)
1011 {
1012 // default width
1013 fmt.Printf(wxT("%%.%df"), m_precision);
1014 }
1015 else if ( m_precision != -1 && m_width != -1 )
1016 {
1017 fmt.Printf(wxT("%%%d.%df"), m_width, m_precision);
1018 }
1019 else
1020 {
1021 // default width/precision
1022 fmt = wxT("%f");
1023 }
1024
1025 return wxString::Format(fmt, m_value);
1026 }
1027
1028 bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
1029 {
1030 if ( wxGridCellEditor::IsAcceptedKey(event) )
1031 {
1032 const int keycode = event.GetKeyCode();
1033 if ( wxIsascii(keycode) )
1034 {
1035 #if wxUSE_INTL
1036 const wxString decimalPoint =
1037 wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
1038 #else
1039 const wxString decimalPoint(wxT('.'));
1040 #endif
1041
1042 // accept digits, 'e' as in '1e+6', also '-', '+', and '.'
1043 if ( wxIsdigit(keycode) ||
1044 tolower(keycode) == 'e' ||
1045 keycode == decimalPoint ||
1046 keycode == '+' ||
1047 keycode == '-' )
1048 {
1049 return true;
1050 }
1051 }
1052 }
1053
1054 return false;
1055 }
1056
1057 #endif // wxUSE_TEXTCTRL
1058
1059 #if wxUSE_CHECKBOX
1060
1061 // ----------------------------------------------------------------------------
1062 // wxGridCellBoolEditor
1063 // ----------------------------------------------------------------------------
1064
1065 // the default values for GetValue()
1066 wxString wxGridCellBoolEditor::ms_stringValues[2] = { wxT(""), wxT("1") };
1067
1068 void wxGridCellBoolEditor::Create(wxWindow* parent,
1069 wxWindowID id,
1070 wxEvtHandler* evtHandler)
1071 {
1072 m_control = new wxCheckBox(parent, id, wxEmptyString,
1073 wxDefaultPosition, wxDefaultSize,
1074 wxNO_BORDER);
1075
1076 wxGridCellEditor::Create(parent, id, evtHandler);
1077 }
1078
1079 void wxGridCellBoolEditor::SetSize(const wxRect& r)
1080 {
1081 bool resize = false;
1082 wxSize size = m_control->GetSize();
1083 wxCoord minSize = wxMin(r.width, r.height);
1084
1085 // check if the checkbox is not too big/small for this cell
1086 wxSize sizeBest = m_control->GetBestSize();
1087 if ( !(size == sizeBest) )
1088 {
1089 // reset to default size if it had been made smaller
1090 size = sizeBest;
1091
1092 resize = true;
1093 }
1094
1095 if ( size.x >= minSize || size.y >= minSize )
1096 {
1097 // leave 1 pixel margin
1098 size.x = size.y = minSize - 2;
1099
1100 resize = true;
1101 }
1102
1103 if ( resize )
1104 {
1105 m_control->SetSize(size);
1106 }
1107
1108 // position it in the centre of the rectangle (TODO: support alignment?)
1109
1110 #if defined(__WXGTK__) || defined (__WXMOTIF__)
1111 // the checkbox without label still has some space to the right in wxGTK,
1112 // so shift it to the right
1113 size.x -= 8;
1114 #elif defined(__WXMSW__)
1115 // here too, but in other way
1116 size.x += 1;
1117 size.y -= 2;
1118 #endif
1119
1120 int hAlign = wxALIGN_CENTRE;
1121 int vAlign = wxALIGN_CENTRE;
1122 if (GetCellAttr())
1123 GetCellAttr()->GetAlignment(& hAlign, & vAlign);
1124
1125 int x = 0, y = 0;
1126 if (hAlign == wxALIGN_LEFT)
1127 {
1128 x = r.x + 2;
1129
1130 #ifdef __WXMSW__
1131 x += 2;
1132 #endif
1133
1134 y = r.y + r.height / 2 - size.y / 2;
1135 }
1136 else if (hAlign == wxALIGN_RIGHT)
1137 {
1138 x = r.x + r.width - size.x - 2;
1139 y = r.y + r.height / 2 - size.y / 2;
1140 }
1141 else if (hAlign == wxALIGN_CENTRE)
1142 {
1143 x = r.x + r.width / 2 - size.x / 2;
1144 y = r.y + r.height / 2 - size.y / 2;
1145 }
1146
1147 m_control->Move(x, y);
1148 }
1149
1150 void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr)
1151 {
1152 m_control->Show(show);
1153
1154 if ( show )
1155 {
1156 wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY;
1157 CBox()->SetBackgroundColour(colBg);
1158 }
1159 }
1160
1161 void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
1162 {
1163 wxASSERT_MSG(m_control,
1164 wxT("The wxGridCellEditor must be created first!"));
1165
1166 if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
1167 {
1168 m_value = grid->GetTable()->GetValueAsBool(row, col);
1169 }
1170 else
1171 {
1172 wxString cellval( grid->GetTable()->GetValue(row, col) );
1173
1174 if ( cellval == ms_stringValues[false] )
1175 m_value = false;
1176 else if ( cellval == ms_stringValues[true] )
1177 m_value = true;
1178 else
1179 {
1180 // do not try to be smart here and convert it to true or false
1181 // because we'll still overwrite it with something different and
1182 // this risks to be very surprising for the user code, let them
1183 // know about it
1184 wxFAIL_MSG( wxT("invalid value for a cell with bool editor!") );
1185 }
1186 }
1187
1188 CBox()->SetValue(m_value);
1189 CBox()->SetFocus();
1190 }
1191
1192 bool wxGridCellBoolEditor::EndEdit(int WXUNUSED(row),
1193 int WXUNUSED(col),
1194 const wxGrid* WXUNUSED(grid),
1195 const wxString& WXUNUSED(oldval),
1196 wxString *newval)
1197 {
1198 bool value = CBox()->GetValue();
1199 if ( value == m_value )
1200 return false;
1201
1202 m_value = value;
1203
1204 if ( newval )
1205 *newval = GetValue();
1206
1207 return true;
1208 }
1209
1210 void wxGridCellBoolEditor::ApplyEdit(int row, int col, wxGrid* grid)
1211 {
1212 wxGridTableBase * const table = grid->GetTable();
1213 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
1214 table->SetValueAsBool(row, col, m_value);
1215 else
1216 table->SetValue(row, col, GetValue());
1217 }
1218
1219 void wxGridCellBoolEditor::Reset()
1220 {
1221 wxASSERT_MSG(m_control,
1222 wxT("The wxGridCellEditor must be created first!"));
1223
1224 CBox()->SetValue(m_value);
1225 }
1226
1227 void wxGridCellBoolEditor::StartingClick()
1228 {
1229 CBox()->SetValue(!CBox()->GetValue());
1230 }
1231
1232 bool wxGridCellBoolEditor::IsAcceptedKey(wxKeyEvent& event)
1233 {
1234 if ( wxGridCellEditor::IsAcceptedKey(event) )
1235 {
1236 int keycode = event.GetKeyCode();
1237 switch ( keycode )
1238 {
1239 case WXK_SPACE:
1240 case '+':
1241 case '-':
1242 return true;
1243 }
1244 }
1245
1246 return false;
1247 }
1248
1249 void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event)
1250 {
1251 int keycode = event.GetKeyCode();
1252 switch ( keycode )
1253 {
1254 case WXK_SPACE:
1255 CBox()->SetValue(!CBox()->GetValue());
1256 break;
1257
1258 case '+':
1259 CBox()->SetValue(true);
1260 break;
1261
1262 case '-':
1263 CBox()->SetValue(false);
1264 break;
1265 }
1266 }
1267
1268 wxString wxGridCellBoolEditor::GetValue() const
1269 {
1270 return ms_stringValues[CBox()->GetValue()];
1271 }
1272
1273 /* static */ void
1274 wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue,
1275 const wxString& valueFalse)
1276 {
1277 ms_stringValues[false] = valueFalse;
1278 ms_stringValues[true] = valueTrue;
1279 }
1280
1281 /* static */ bool
1282 wxGridCellBoolEditor::IsTrueValue(const wxString& value)
1283 {
1284 return value == ms_stringValues[true];
1285 }
1286
1287 #endif // wxUSE_CHECKBOX
1288
1289 #if wxUSE_COMBOBOX
1290
1291 // ----------------------------------------------------------------------------
1292 // wxGridCellChoiceEditor
1293 // ----------------------------------------------------------------------------
1294
1295 wxGridCellChoiceEditor::wxGridCellChoiceEditor(const wxArrayString& choices,
1296 bool allowOthers)
1297 : m_choices(choices),
1298 m_allowOthers(allowOthers) { }
1299
1300 wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count,
1301 const wxString choices[],
1302 bool allowOthers)
1303 : m_allowOthers(allowOthers)
1304 {
1305 if ( count )
1306 {
1307 m_choices.Alloc(count);
1308 for ( size_t n = 0; n < count; n++ )
1309 {
1310 m_choices.Add(choices[n]);
1311 }
1312 }
1313 }
1314
1315 wxGridCellEditor *wxGridCellChoiceEditor::Clone() const
1316 {
1317 wxGridCellChoiceEditor *editor = new wxGridCellChoiceEditor;
1318 editor->m_allowOthers = m_allowOthers;
1319 editor->m_choices = m_choices;
1320
1321 return editor;
1322 }
1323
1324 void wxGridCellChoiceEditor::Create(wxWindow* parent,
1325 wxWindowID id,
1326 wxEvtHandler* evtHandler)
1327 {
1328 int style = wxTE_PROCESS_ENTER |
1329 wxTE_PROCESS_TAB |
1330 wxBORDER_NONE;
1331
1332 if ( !m_allowOthers )
1333 style |= wxCB_READONLY;
1334 m_control = new wxComboBox(parent, id, wxEmptyString,
1335 wxDefaultPosition, wxDefaultSize,
1336 m_choices,
1337 style);
1338
1339 wxGridCellEditor::Create(parent, id, evtHandler);
1340 }
1341
1342 void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell,
1343 wxGridCellAttr * attr)
1344 {
1345 // as we fill the entire client area, don't do anything here to minimize
1346 // flicker
1347
1348 // TODO: It doesn't actually fill the client area since the height of a
1349 // combo always defaults to the standard. Until someone has time to
1350 // figure out the right rectangle to paint, just do it the normal way.
1351 wxGridCellEditor::PaintBackground(rectCell, attr);
1352 }
1353
1354 void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
1355 {
1356 wxASSERT_MSG(m_control,
1357 wxT("The wxGridCellEditor must be created first!"));
1358
1359 wxGridCellEditorEvtHandler* evtHandler = NULL;
1360 if (m_control)
1361 evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);
1362
1363 // Don't immediately end if we get a kill focus event within BeginEdit
1364 if (evtHandler)
1365 evtHandler->SetInSetFocus(true);
1366
1367 m_value = grid->GetTable()->GetValue(row, col);
1368
1369 Reset(); // this updates combo box to correspond to m_value
1370
1371 Combo()->SetFocus();
1372
1373 if (evtHandler)
1374 {
1375 // When dropping down the menu, a kill focus event
1376 // happens after this point, so we can't reset the flag yet.
1377 #if !defined(__WXGTK20__)
1378 evtHandler->SetInSetFocus(false);
1379 #endif
1380 }
1381 }
1382
1383 bool wxGridCellChoiceEditor::EndEdit(int WXUNUSED(row),
1384 int WXUNUSED(col),
1385 const wxGrid* WXUNUSED(grid),
1386 const wxString& WXUNUSED(oldval),
1387 wxString *newval)
1388 {
1389 const wxString value = Combo()->GetValue();
1390 if ( value == m_value )
1391 return false;
1392
1393 m_value = value;
1394
1395 if ( newval )
1396 *newval = value;
1397
1398 return true;
1399 }
1400
1401 void wxGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
1402 {
1403 grid->GetTable()->SetValue(row, col, m_value);
1404 }
1405
1406 void wxGridCellChoiceEditor::Reset()
1407 {
1408 if (m_allowOthers)
1409 {
1410 Combo()->SetValue(m_value);
1411 Combo()->SetInsertionPointEnd();
1412 }
1413 else // the combobox is read-only
1414 {
1415 // find the right position, or default to the first if not found
1416 int pos = Combo()->FindString(m_value);
1417 if (pos == wxNOT_FOUND)
1418 pos = 0;
1419 Combo()->SetSelection(pos);
1420 }
1421 }
1422
1423 void wxGridCellChoiceEditor::SetParameters(const wxString& params)
1424 {
1425 if ( !params )
1426 {
1427 // what can we do?
1428 return;
1429 }
1430
1431 m_choices.Empty();
1432
1433 wxStringTokenizer tk(params, wxT(','));
1434 while ( tk.HasMoreTokens() )
1435 {
1436 m_choices.Add(tk.GetNextToken());
1437 }
1438 }
1439
1440 // return the value in the text control
1441 wxString wxGridCellChoiceEditor::GetValue() const
1442 {
1443 return Combo()->GetValue();
1444 }
1445
1446 #endif // wxUSE_COMBOBOX
1447
1448 #if wxUSE_COMBOBOX
1449
1450 // ----------------------------------------------------------------------------
1451 // wxGridCellEnumEditor
1452 // ----------------------------------------------------------------------------
1453
1454 // A cell editor which displays an enum number as a textual equivalent. eg
1455 // data in cell is 0,1,2 ... n the cell could be displayed as
1456 // "John","Fred"..."Bob" in the combo choice box
1457
1458 wxGridCellEnumEditor::wxGridCellEnumEditor(const wxString& choices)
1459 :wxGridCellChoiceEditor()
1460 {
1461 m_index = -1;
1462
1463 if (!choices.empty())
1464 SetParameters(choices);
1465 }
1466
1467 wxGridCellEditor *wxGridCellEnumEditor::Clone() const
1468 {
1469 wxGridCellEnumEditor *editor = new wxGridCellEnumEditor();
1470 editor->m_index = m_index;
1471 return editor;
1472 }
1473
1474 void wxGridCellEnumEditor::BeginEdit(int row, int col, wxGrid* grid)
1475 {
1476 wxASSERT_MSG(m_control,
1477 wxT("The wxGridCellEnumEditor must be Created first!"));
1478
1479 wxGridTableBase *table = grid->GetTable();
1480
1481 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
1482 {
1483 m_index = table->GetValueAsLong(row, col);
1484 }
1485 else
1486 {
1487 wxString startValue = table->GetValue(row, col);
1488 if (startValue.IsNumber() && !startValue.empty())
1489 {
1490 startValue.ToLong(&m_index);
1491 }
1492 else
1493 {
1494 m_index = -1;
1495 }
1496 }
1497
1498 Combo()->SetSelection(m_index);
1499 Combo()->SetInsertionPointEnd();
1500 Combo()->SetFocus();
1501
1502 }
1503
1504 bool wxGridCellEnumEditor::EndEdit(int WXUNUSED(row),
1505 int WXUNUSED(col),
1506 const wxGrid* WXUNUSED(grid),
1507 const wxString& WXUNUSED(oldval),
1508 wxString *newval)
1509 {
1510 long idx = Combo()->GetSelection();
1511 if ( idx == m_index )
1512 return false;
1513
1514 m_index = idx;
1515
1516 if ( newval )
1517 newval->Printf("%ld", m_index);
1518
1519 return true;
1520 }
1521
1522 void wxGridCellEnumEditor::ApplyEdit(int row, int col, wxGrid* grid)
1523 {
1524 wxGridTableBase * const table = grid->GetTable();
1525 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
1526 table->SetValueAsLong(row, col, m_index);
1527 else
1528 table->SetValue(row, col, wxString::Format("%ld", m_index));
1529 }
1530
1531 #endif // wxUSE_COMBOBOX
1532
1533 // ----------------------------------------------------------------------------
1534 // wxGridCellAutoWrapStringEditor
1535 // ----------------------------------------------------------------------------
1536
1537 void
1538 wxGridCellAutoWrapStringEditor::Create(wxWindow* parent,
1539 wxWindowID id,
1540 wxEvtHandler* evtHandler)
1541 {
1542 wxGridCellTextEditor::DoCreate(parent, id, evtHandler,
1543 wxTE_MULTILINE | wxTE_RICH);
1544 }
1545
1546
1547 #endif // wxUSE_GRID