Fix non-ASCII key handling in wxGrid editors.
[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;
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 #else // !__WXMAC__
348 alt = event.AltDown();
349 #endif // __WXMAC__/!__WXMAC__
350
351 // Assume it's not a valid char if ctrl or alt is down, but if both are
352 // down then it may be because of an AltGr key combination, so let them
353 // through in that case.
354 if ((ctrl || alt) && !(ctrl && alt))
355 return false;
356
357 #if wxUSE_UNICODE
358 if ( event.GetUnicodeKey() == WXK_NONE )
359 return false;
360 #else
361 if ( event.GetKeyCode() > WXK_START )
362 return false;
363 #endif
364
365 return true;
366 }
367
368 void wxGridCellEditor::StartingKey(wxKeyEvent& event)
369 {
370 event.Skip();
371 }
372
373 void wxGridCellEditor::StartingClick()
374 {
375 }
376
377 #if wxUSE_TEXTCTRL
378
379 // ----------------------------------------------------------------------------
380 // wxGridCellTextEditor
381 // ----------------------------------------------------------------------------
382
383 wxGridCellTextEditor::wxGridCellTextEditor()
384 {
385 m_maxChars = 0;
386 }
387
388 void wxGridCellTextEditor::Create(wxWindow* parent,
389 wxWindowID id,
390 wxEvtHandler* evtHandler)
391 {
392 DoCreate(parent, id, evtHandler);
393 }
394
395 void wxGridCellTextEditor::DoCreate(wxWindow* parent,
396 wxWindowID id,
397 wxEvtHandler* evtHandler,
398 long style)
399 {
400 // Use of wxTE_RICH2 is a strange hack to work around the bug #11681: a
401 // plain text control seems to lose its caret somehow when we hide it and
402 // show it again for a different cell.
403 style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER | wxTE_RICH2;
404
405 m_control = new wxTextCtrl(parent, id, wxEmptyString,
406 wxDefaultPosition, wxDefaultSize,
407 style);
408
409 // set max length allowed in the textctrl, if the parameter was set
410 if ( m_maxChars != 0 )
411 {
412 Text()->SetMaxLength(m_maxChars);
413 }
414
415 wxGridCellEditor::Create(parent, id, evtHandler);
416 }
417
418 void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell),
419 wxGridCellAttr * WXUNUSED(attr))
420 {
421 // as we fill the entire client area,
422 // don't do anything here to minimize flicker
423 }
424
425 void wxGridCellTextEditor::SetSize(const wxRect& rectOrig)
426 {
427 wxRect rect(rectOrig);
428
429 // Make the edit control large enough to allow for internal margins
430 //
431 // TODO: remove this if the text ctrl sizing is improved esp. for unix
432 //
433 #if defined(__WXGTK__)
434 if (rect.x != 0)
435 {
436 rect.x += 1;
437 rect.y += 1;
438 rect.width -= 1;
439 rect.height -= 1;
440 }
441 #elif defined(__WXMSW__)
442 if ( rect.x == 0 )
443 rect.x += 2;
444 else
445 rect.x += 3;
446
447 if ( rect.y == 0 )
448 rect.y += 2;
449 else
450 rect.y += 3;
451
452 rect.width -= 2;
453 rect.height -= 2;
454 #else
455 int extra_x = ( rect.x > 2 ) ? 2 : 1;
456 int extra_y = ( rect.y > 2 ) ? 2 : 1;
457
458 #if defined(__WXMOTIF__)
459 extra_x *= 2;
460 extra_y *= 2;
461 #endif
462
463 rect.SetLeft( wxMax(0, rect.x - extra_x) );
464 rect.SetTop( wxMax(0, rect.y - extra_y) );
465 rect.SetRight( rect.GetRight() + 2 * extra_x );
466 rect.SetBottom( rect.GetBottom() + 2 * extra_y );
467 #endif
468
469 wxGridCellEditor::SetSize(rect);
470 }
471
472 void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid)
473 {
474 wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
475
476 m_value = grid->GetTable()->GetValue(row, col);
477
478 DoBeginEdit(m_value);
479 }
480
481 void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
482 {
483 Text()->SetValue(startValue);
484 Text()->SetInsertionPointEnd();
485 Text()->SetSelection(-1, -1);
486 Text()->SetFocus();
487 }
488
489 bool wxGridCellTextEditor::EndEdit(int WXUNUSED(row),
490 int WXUNUSED(col),
491 const wxGrid* WXUNUSED(grid),
492 const wxString& WXUNUSED(oldval),
493 wxString *newval)
494 {
495 wxCHECK_MSG( m_control, false,
496 "wxGridCellTextEditor must be created first!" );
497
498 const wxString value = Text()->GetValue();
499 if ( value == m_value )
500 return false;
501
502 m_value = value;
503
504 if ( newval )
505 *newval = m_value;
506
507 return true;
508 }
509
510 void wxGridCellTextEditor::ApplyEdit(int row, int col, wxGrid* grid)
511 {
512 grid->GetTable()->SetValue(row, col, m_value);
513 m_value.clear();
514 }
515
516 void wxGridCellTextEditor::Reset()
517 {
518 wxASSERT_MSG( m_control, "wxGridCellTextEditor must be created first!" );
519
520 DoReset(m_value);
521 }
522
523 void wxGridCellTextEditor::DoReset(const wxString& startValue)
524 {
525 Text()->SetValue(startValue);
526 Text()->SetInsertionPointEnd();
527 }
528
529 bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
530 {
531 switch ( event.GetKeyCode() )
532 {
533 case WXK_DELETE:
534 case WXK_BACK:
535 return true;
536
537 default:
538 return wxGridCellEditor::IsAcceptedKey(event);
539 }
540 }
541
542 void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
543 {
544 // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
545 // longer an appropriate way to get the character into the text control.
546 // Do it ourselves instead. We know that if we get this far that we have
547 // a valid character, so not a whole lot of testing needs to be done.
548
549 wxTextCtrl* tc = Text();
550 wxChar ch;
551 long pos;
552
553 bool isPrintable;
554
555 #if wxUSE_UNICODE
556 ch = event.GetUnicodeKey();
557 if ( ch != WXK_NONE )
558 isPrintable = true;
559 else
560 #endif // wxUSE_UNICODE
561 {
562 ch = (wxChar)event.GetKeyCode();
563 isPrintable = ch >= WXK_SPACE && ch < WXK_START;
564 }
565
566 switch (ch)
567 {
568 case WXK_DELETE:
569 // delete the character at the cursor
570 pos = tc->GetInsertionPoint();
571 if (pos < tc->GetLastPosition())
572 tc->Remove(pos, pos + 1);
573 break;
574
575 case WXK_BACK:
576 // delete the character before the cursor
577 pos = tc->GetInsertionPoint();
578 if (pos > 0)
579 tc->Remove(pos - 1, pos);
580 break;
581
582 default:
583 if ( isPrintable )
584 tc->WriteText(ch);
585 break;
586 }
587 }
588
589 void wxGridCellTextEditor::HandleReturn( wxKeyEvent&
590 WXUNUSED_GTK(WXUNUSED_MOTIF(event)) )
591 {
592 #if defined(__WXMOTIF__) || defined(__WXGTK__)
593 // wxMotif needs a little extra help...
594 size_t pos = (size_t)( Text()->GetInsertionPoint() );
595 wxString s( Text()->GetValue() );
596 s = s.Left(pos) + wxT("\n") + s.Mid(pos);
597 Text()->SetValue(s);
598 Text()->SetInsertionPoint( pos );
599 #else
600 // the other ports can handle a Return key press
601 //
602 event.Skip();
603 #endif
604 }
605
606 void wxGridCellTextEditor::SetParameters(const wxString& params)
607 {
608 if ( !params )
609 {
610 // reset to default
611 m_maxChars = 0;
612 }
613 else
614 {
615 long tmp;
616 if ( params.ToLong(&tmp) )
617 {
618 m_maxChars = (size_t)tmp;
619 }
620 else
621 {
622 wxLogDebug( wxT("Invalid wxGridCellTextEditor parameter string '%s' ignored"), params.c_str() );
623 }
624 }
625 }
626
627 // return the value in the text control
628 wxString wxGridCellTextEditor::GetValue() const
629 {
630 return Text()->GetValue();
631 }
632
633 // ----------------------------------------------------------------------------
634 // wxGridCellNumberEditor
635 // ----------------------------------------------------------------------------
636
637 wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max)
638 {
639 m_min = min;
640 m_max = max;
641 }
642
643 void wxGridCellNumberEditor::Create(wxWindow* parent,
644 wxWindowID id,
645 wxEvtHandler* evtHandler)
646 {
647 #if wxUSE_SPINCTRL
648 if ( HasRange() )
649 {
650 // create a spin ctrl
651 m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString,
652 wxDefaultPosition, wxDefaultSize,
653 wxSP_ARROW_KEYS,
654 m_min, m_max);
655
656 wxGridCellEditor::Create(parent, id, evtHandler);
657 }
658 else
659 #endif
660 {
661 // just a text control
662 wxGridCellTextEditor::Create(parent, id, evtHandler);
663
664 #if wxUSE_VALIDATORS
665 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
666 #endif
667 }
668 }
669
670 void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
671 {
672 // first get the value
673 wxGridTableBase *table = grid->GetTable();
674 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
675 {
676 m_value = table->GetValueAsLong(row, col);
677 }
678 else
679 {
680 m_value = 0;
681 wxString sValue = table->GetValue(row, col);
682 if (! sValue.ToLong(&m_value) && ! sValue.empty())
683 {
684 wxFAIL_MSG( wxT("this cell doesn't have numeric value") );
685 return;
686 }
687 }
688
689 #if wxUSE_SPINCTRL
690 if ( HasRange() )
691 {
692 Spin()->SetValue((int)m_value);
693 Spin()->SetFocus();
694 }
695 else
696 #endif
697 {
698 DoBeginEdit(GetString());
699 }
700 }
701
702 bool wxGridCellNumberEditor::EndEdit(int WXUNUSED(row),
703 int WXUNUSED(col),
704 const wxGrid* WXUNUSED(grid),
705 const wxString& oldval, wxString *newval)
706 {
707 long value = 0;
708 wxString text;
709
710 #if wxUSE_SPINCTRL
711 if ( HasRange() )
712 {
713 value = Spin()->GetValue();
714 if ( value == m_value )
715 return false;
716
717 text.Printf(wxT("%ld"), value);
718 }
719 else // using unconstrained input
720 #endif // wxUSE_SPINCTRL
721 {
722 text = Text()->GetValue();
723 if ( text.empty() )
724 {
725 if ( oldval.empty() )
726 return false;
727 }
728 else // non-empty text now (maybe 0)
729 {
730 if ( !text.ToLong(&value) )
731 return false;
732
733 // if value == m_value == 0 but old text was "" and new one is
734 // "0" something still did change
735 if ( value == m_value && (value || !oldval.empty()) )
736 return false;
737 }
738 }
739
740 m_value = value;
741
742 if ( newval )
743 *newval = text;
744
745 return true;
746 }
747
748 void wxGridCellNumberEditor::ApplyEdit(int row, int col, wxGrid* grid)
749 {
750 wxGridTableBase * const table = grid->GetTable();
751 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
752 table->SetValueAsLong(row, col, m_value);
753 else
754 table->SetValue(row, col, wxString::Format("%ld", m_value));
755 }
756
757 void wxGridCellNumberEditor::Reset()
758 {
759 #if wxUSE_SPINCTRL
760 if ( HasRange() )
761 {
762 Spin()->SetValue((int)m_value);
763 }
764 else
765 #endif
766 {
767 DoReset(GetString());
768 }
769 }
770
771 bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event)
772 {
773 if ( wxGridCellEditor::IsAcceptedKey(event) )
774 {
775 int keycode = event.GetKeyCode();
776 if ( (keycode < 128) &&
777 (wxIsdigit(keycode) || keycode == '+' || keycode == '-'))
778 {
779 return true;
780 }
781 }
782
783 return false;
784 }
785
786 void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event)
787 {
788 int keycode = event.GetKeyCode();
789 if ( !HasRange() )
790 {
791 if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-')
792 {
793 wxGridCellTextEditor::StartingKey(event);
794
795 // skip Skip() below
796 return;
797 }
798 }
799 #if wxUSE_SPINCTRL
800 else
801 {
802 if ( wxIsdigit(keycode) )
803 {
804 wxSpinCtrl* spin = (wxSpinCtrl*)m_control;
805 spin->SetValue(keycode - '0');
806 spin->SetSelection(1,1);
807 return;
808 }
809 }
810 #endif
811
812 event.Skip();
813 }
814
815 void wxGridCellNumberEditor::SetParameters(const wxString& params)
816 {
817 if ( !params )
818 {
819 // reset to default
820 m_min =
821 m_max = -1;
822 }
823 else
824 {
825 long tmp;
826 if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
827 {
828 m_min = (int)tmp;
829
830 if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
831 {
832 m_max = (int)tmp;
833
834 // skip the error message below
835 return;
836 }
837 }
838
839 wxLogDebug(wxT("Invalid wxGridCellNumberEditor parameter string '%s' ignored"), params.c_str());
840 }
841 }
842
843 // return the value in the spin control if it is there (the text control otherwise)
844 wxString wxGridCellNumberEditor::GetValue() const
845 {
846 wxString s;
847
848 #if wxUSE_SPINCTRL
849 if ( HasRange() )
850 {
851 long value = Spin()->GetValue();
852 s.Printf(wxT("%ld"), value);
853 }
854 else
855 #endif
856 {
857 s = Text()->GetValue();
858 }
859
860 return s;
861 }
862
863 // ----------------------------------------------------------------------------
864 // wxGridCellFloatEditor
865 // ----------------------------------------------------------------------------
866
867 wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision)
868 {
869 m_width = width;
870 m_precision = precision;
871 }
872
873 void wxGridCellFloatEditor::Create(wxWindow* parent,
874 wxWindowID id,
875 wxEvtHandler* evtHandler)
876 {
877 wxGridCellTextEditor::Create(parent, id, evtHandler);
878
879 #if wxUSE_VALIDATORS
880 Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
881 #endif
882 }
883
884 void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
885 {
886 // first get the value
887 wxGridTableBase * const table = grid->GetTable();
888 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
889 {
890 m_value = table->GetValueAsDouble(row, col);
891 }
892 else
893 {
894 m_value = 0.0;
895
896 const wxString value = table->GetValue(row, col);
897 if ( !value.empty() )
898 {
899 if ( !value.ToDouble(&m_value) )
900 {
901 wxFAIL_MSG( wxT("this cell doesn't have float value") );
902 return;
903 }
904 }
905 }
906
907 DoBeginEdit(GetString());
908 }
909
910 bool wxGridCellFloatEditor::EndEdit(int WXUNUSED(row),
911 int WXUNUSED(col),
912 const wxGrid* WXUNUSED(grid),
913 const wxString& oldval, wxString *newval)
914 {
915 const wxString text(Text()->GetValue());
916
917 double value;
918 if ( !text.empty() )
919 {
920 if ( !text.ToDouble(&value) )
921 return false;
922 }
923 else // new value is empty string
924 {
925 if ( oldval.empty() )
926 return false; // nothing changed
927
928 value = 0.;
929 }
930
931 // the test for empty strings ensures that we don't skip the value setting
932 // when "" is replaced by "0" or vice versa as "" numeric value is also 0.
933 if ( wxIsSameDouble(value, m_value) && !text.empty() && !oldval.empty() )
934 return false; // nothing changed
935
936 m_value = value;
937
938 if ( newval )
939 *newval = text;
940
941 return true;
942 }
943
944 void wxGridCellFloatEditor::ApplyEdit(int row, int col, wxGrid* grid)
945 {
946 wxGridTableBase * const table = grid->GetTable();
947
948 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) )
949 table->SetValueAsDouble(row, col, m_value);
950 else
951 table->SetValue(row, col, Text()->GetValue());
952 }
953
954 void wxGridCellFloatEditor::Reset()
955 {
956 DoReset(GetString());
957 }
958
959 void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event)
960 {
961 int keycode = event.GetKeyCode();
962 char tmpbuf[2];
963 tmpbuf[0] = (char) keycode;
964 tmpbuf[1] = '\0';
965 wxString strbuf(tmpbuf, *wxConvCurrent);
966
967 #if wxUSE_INTL
968 bool is_decimal_point = ( strbuf ==
969 wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER) );
970 #else
971 bool is_decimal_point = ( strbuf == wxT(".") );
972 #endif
973
974 if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-'
975 || is_decimal_point )
976 {
977 wxGridCellTextEditor::StartingKey(event);
978
979 // skip Skip() below
980 return;
981 }
982
983 event.Skip();
984 }
985
986 void wxGridCellFloatEditor::SetParameters(const wxString& params)
987 {
988 if ( !params )
989 {
990 // reset to default
991 m_width =
992 m_precision = -1;
993 }
994 else
995 {
996 long tmp;
997 if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
998 {
999 m_width = (int)tmp;
1000
1001 if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
1002 {
1003 m_precision = (int)tmp;
1004
1005 // skip the error message below
1006 return;
1007 }
1008 }
1009
1010 wxLogDebug(wxT("Invalid wxGridCellFloatEditor parameter string '%s' ignored"), params.c_str());
1011 }
1012 }
1013
1014 wxString wxGridCellFloatEditor::GetString() const
1015 {
1016 wxString fmt;
1017 if ( m_precision == -1 && m_width != -1)
1018 {
1019 // default precision
1020 fmt.Printf(wxT("%%%d.f"), m_width);
1021 }
1022 else if ( m_precision != -1 && m_width == -1)
1023 {
1024 // default width
1025 fmt.Printf(wxT("%%.%df"), m_precision);
1026 }
1027 else if ( m_precision != -1 && m_width != -1 )
1028 {
1029 fmt.Printf(wxT("%%%d.%df"), m_width, m_precision);
1030 }
1031 else
1032 {
1033 // default width/precision
1034 fmt = wxT("%f");
1035 }
1036
1037 return wxString::Format(fmt, m_value);
1038 }
1039
1040 bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
1041 {
1042 if ( wxGridCellEditor::IsAcceptedKey(event) )
1043 {
1044 const int keycode = event.GetKeyCode();
1045 if ( wxIsascii(keycode) )
1046 {
1047 #if wxUSE_INTL
1048 const wxString decimalPoint =
1049 wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
1050 #else
1051 const wxString decimalPoint(wxT('.'));
1052 #endif
1053
1054 // accept digits, 'e' as in '1e+6', also '-', '+', and '.'
1055 if ( wxIsdigit(keycode) ||
1056 tolower(keycode) == 'e' ||
1057 keycode == decimalPoint ||
1058 keycode == '+' ||
1059 keycode == '-' )
1060 {
1061 return true;
1062 }
1063 }
1064 }
1065
1066 return false;
1067 }
1068
1069 #endif // wxUSE_TEXTCTRL
1070
1071 #if wxUSE_CHECKBOX
1072
1073 // ----------------------------------------------------------------------------
1074 // wxGridCellBoolEditor
1075 // ----------------------------------------------------------------------------
1076
1077 // the default values for GetValue()
1078 wxString wxGridCellBoolEditor::ms_stringValues[2] = { wxT(""), wxT("1") };
1079
1080 void wxGridCellBoolEditor::Create(wxWindow* parent,
1081 wxWindowID id,
1082 wxEvtHandler* evtHandler)
1083 {
1084 m_control = new wxCheckBox(parent, id, wxEmptyString,
1085 wxDefaultPosition, wxDefaultSize,
1086 wxNO_BORDER);
1087
1088 wxGridCellEditor::Create(parent, id, evtHandler);
1089 }
1090
1091 void wxGridCellBoolEditor::SetSize(const wxRect& r)
1092 {
1093 bool resize = false;
1094 wxSize size = m_control->GetSize();
1095 wxCoord minSize = wxMin(r.width, r.height);
1096
1097 // check if the checkbox is not too big/small for this cell
1098 wxSize sizeBest = m_control->GetBestSize();
1099 if ( !(size == sizeBest) )
1100 {
1101 // reset to default size if it had been made smaller
1102 size = sizeBest;
1103
1104 resize = true;
1105 }
1106
1107 if ( size.x >= minSize || size.y >= minSize )
1108 {
1109 // leave 1 pixel margin
1110 size.x = size.y = minSize - 2;
1111
1112 resize = true;
1113 }
1114
1115 if ( resize )
1116 {
1117 m_control->SetSize(size);
1118 }
1119
1120 // position it in the centre of the rectangle (TODO: support alignment?)
1121
1122 #if defined(__WXGTK__) || defined (__WXMOTIF__)
1123 // the checkbox without label still has some space to the right in wxGTK,
1124 // so shift it to the right
1125 size.x -= 8;
1126 #elif defined(__WXMSW__)
1127 // here too, but in other way
1128 size.x += 1;
1129 size.y -= 2;
1130 #endif
1131
1132 int hAlign = wxALIGN_CENTRE;
1133 int vAlign = wxALIGN_CENTRE;
1134 if (GetCellAttr())
1135 GetCellAttr()->GetAlignment(& hAlign, & vAlign);
1136
1137 int x = 0, y = 0;
1138 if (hAlign == wxALIGN_LEFT)
1139 {
1140 x = r.x + 2;
1141
1142 #ifdef __WXMSW__
1143 x += 2;
1144 #endif
1145
1146 y = r.y + r.height / 2 - size.y / 2;
1147 }
1148 else if (hAlign == wxALIGN_RIGHT)
1149 {
1150 x = r.x + r.width - size.x - 2;
1151 y = r.y + r.height / 2 - size.y / 2;
1152 }
1153 else if (hAlign == wxALIGN_CENTRE)
1154 {
1155 x = r.x + r.width / 2 - size.x / 2;
1156 y = r.y + r.height / 2 - size.y / 2;
1157 }
1158
1159 m_control->Move(x, y);
1160 }
1161
1162 void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr)
1163 {
1164 m_control->Show(show);
1165
1166 if ( show )
1167 {
1168 wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY;
1169 CBox()->SetBackgroundColour(colBg);
1170 }
1171 }
1172
1173 void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
1174 {
1175 wxASSERT_MSG(m_control,
1176 wxT("The wxGridCellEditor must be created first!"));
1177
1178 if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
1179 {
1180 m_value = grid->GetTable()->GetValueAsBool(row, col);
1181 }
1182 else
1183 {
1184 wxString cellval( grid->GetTable()->GetValue(row, col) );
1185
1186 if ( cellval == ms_stringValues[false] )
1187 m_value = false;
1188 else if ( cellval == ms_stringValues[true] )
1189 m_value = true;
1190 else
1191 {
1192 // do not try to be smart here and convert it to true or false
1193 // because we'll still overwrite it with something different and
1194 // this risks to be very surprising for the user code, let them
1195 // know about it
1196 wxFAIL_MSG( wxT("invalid value for a cell with bool editor!") );
1197 }
1198 }
1199
1200 CBox()->SetValue(m_value);
1201 CBox()->SetFocus();
1202 }
1203
1204 bool wxGridCellBoolEditor::EndEdit(int WXUNUSED(row),
1205 int WXUNUSED(col),
1206 const wxGrid* WXUNUSED(grid),
1207 const wxString& WXUNUSED(oldval),
1208 wxString *newval)
1209 {
1210 bool value = CBox()->GetValue();
1211 if ( value == m_value )
1212 return false;
1213
1214 m_value = value;
1215
1216 if ( newval )
1217 *newval = GetValue();
1218
1219 return true;
1220 }
1221
1222 void wxGridCellBoolEditor::ApplyEdit(int row, int col, wxGrid* grid)
1223 {
1224 wxGridTableBase * const table = grid->GetTable();
1225 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
1226 table->SetValueAsBool(row, col, m_value);
1227 else
1228 table->SetValue(row, col, GetValue());
1229 }
1230
1231 void wxGridCellBoolEditor::Reset()
1232 {
1233 wxASSERT_MSG(m_control,
1234 wxT("The wxGridCellEditor must be created first!"));
1235
1236 CBox()->SetValue(m_value);
1237 }
1238
1239 void wxGridCellBoolEditor::StartingClick()
1240 {
1241 CBox()->SetValue(!CBox()->GetValue());
1242 }
1243
1244 bool wxGridCellBoolEditor::IsAcceptedKey(wxKeyEvent& event)
1245 {
1246 if ( wxGridCellEditor::IsAcceptedKey(event) )
1247 {
1248 int keycode = event.GetKeyCode();
1249 switch ( keycode )
1250 {
1251 case WXK_SPACE:
1252 case '+':
1253 case '-':
1254 return true;
1255 }
1256 }
1257
1258 return false;
1259 }
1260
1261 void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event)
1262 {
1263 int keycode = event.GetKeyCode();
1264 switch ( keycode )
1265 {
1266 case WXK_SPACE:
1267 CBox()->SetValue(!CBox()->GetValue());
1268 break;
1269
1270 case '+':
1271 CBox()->SetValue(true);
1272 break;
1273
1274 case '-':
1275 CBox()->SetValue(false);
1276 break;
1277 }
1278 }
1279
1280 wxString wxGridCellBoolEditor::GetValue() const
1281 {
1282 return ms_stringValues[CBox()->GetValue()];
1283 }
1284
1285 /* static */ void
1286 wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue,
1287 const wxString& valueFalse)
1288 {
1289 ms_stringValues[false] = valueFalse;
1290 ms_stringValues[true] = valueTrue;
1291 }
1292
1293 /* static */ bool
1294 wxGridCellBoolEditor::IsTrueValue(const wxString& value)
1295 {
1296 return value == ms_stringValues[true];
1297 }
1298
1299 #endif // wxUSE_CHECKBOX
1300
1301 #if wxUSE_COMBOBOX
1302
1303 // ----------------------------------------------------------------------------
1304 // wxGridCellChoiceEditor
1305 // ----------------------------------------------------------------------------
1306
1307 wxGridCellChoiceEditor::wxGridCellChoiceEditor(const wxArrayString& choices,
1308 bool allowOthers)
1309 : m_choices(choices),
1310 m_allowOthers(allowOthers) { }
1311
1312 wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count,
1313 const wxString choices[],
1314 bool allowOthers)
1315 : m_allowOthers(allowOthers)
1316 {
1317 if ( count )
1318 {
1319 m_choices.Alloc(count);
1320 for ( size_t n = 0; n < count; n++ )
1321 {
1322 m_choices.Add(choices[n]);
1323 }
1324 }
1325 }
1326
1327 wxGridCellEditor *wxGridCellChoiceEditor::Clone() const
1328 {
1329 wxGridCellChoiceEditor *editor = new wxGridCellChoiceEditor;
1330 editor->m_allowOthers = m_allowOthers;
1331 editor->m_choices = m_choices;
1332
1333 return editor;
1334 }
1335
1336 void wxGridCellChoiceEditor::Create(wxWindow* parent,
1337 wxWindowID id,
1338 wxEvtHandler* evtHandler)
1339 {
1340 int style = wxTE_PROCESS_ENTER |
1341 wxTE_PROCESS_TAB |
1342 wxBORDER_NONE;
1343
1344 if ( !m_allowOthers )
1345 style |= wxCB_READONLY;
1346 m_control = new wxComboBox(parent, id, wxEmptyString,
1347 wxDefaultPosition, wxDefaultSize,
1348 m_choices,
1349 style);
1350
1351 wxGridCellEditor::Create(parent, id, evtHandler);
1352 }
1353
1354 void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell,
1355 wxGridCellAttr * attr)
1356 {
1357 // as we fill the entire client area, don't do anything here to minimize
1358 // flicker
1359
1360 // TODO: It doesn't actually fill the client area since the height of a
1361 // combo always defaults to the standard. Until someone has time to
1362 // figure out the right rectangle to paint, just do it the normal way.
1363 wxGridCellEditor::PaintBackground(rectCell, attr);
1364 }
1365
1366 void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
1367 {
1368 wxASSERT_MSG(m_control,
1369 wxT("The wxGridCellEditor must be created first!"));
1370
1371 wxGridCellEditorEvtHandler* evtHandler = NULL;
1372 if (m_control)
1373 evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);
1374
1375 // Don't immediately end if we get a kill focus event within BeginEdit
1376 if (evtHandler)
1377 evtHandler->SetInSetFocus(true);
1378
1379 m_value = grid->GetTable()->GetValue(row, col);
1380
1381 Reset(); // this updates combo box to correspond to m_value
1382
1383 Combo()->SetFocus();
1384
1385 if (evtHandler)
1386 {
1387 // When dropping down the menu, a kill focus event
1388 // happens after this point, so we can't reset the flag yet.
1389 #if !defined(__WXGTK20__)
1390 evtHandler->SetInSetFocus(false);
1391 #endif
1392 }
1393 }
1394
1395 bool wxGridCellChoiceEditor::EndEdit(int WXUNUSED(row),
1396 int WXUNUSED(col),
1397 const wxGrid* WXUNUSED(grid),
1398 const wxString& WXUNUSED(oldval),
1399 wxString *newval)
1400 {
1401 const wxString value = Combo()->GetValue();
1402 if ( value == m_value )
1403 return false;
1404
1405 m_value = value;
1406
1407 if ( newval )
1408 *newval = value;
1409
1410 return true;
1411 }
1412
1413 void wxGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
1414 {
1415 grid->GetTable()->SetValue(row, col, m_value);
1416 }
1417
1418 void wxGridCellChoiceEditor::Reset()
1419 {
1420 if (m_allowOthers)
1421 {
1422 Combo()->SetValue(m_value);
1423 Combo()->SetInsertionPointEnd();
1424 }
1425 else // the combobox is read-only
1426 {
1427 // find the right position, or default to the first if not found
1428 int pos = Combo()->FindString(m_value);
1429 if (pos == wxNOT_FOUND)
1430 pos = 0;
1431 Combo()->SetSelection(pos);
1432 }
1433 }
1434
1435 void wxGridCellChoiceEditor::SetParameters(const wxString& params)
1436 {
1437 if ( !params )
1438 {
1439 // what can we do?
1440 return;
1441 }
1442
1443 m_choices.Empty();
1444
1445 wxStringTokenizer tk(params, wxT(','));
1446 while ( tk.HasMoreTokens() )
1447 {
1448 m_choices.Add(tk.GetNextToken());
1449 }
1450 }
1451
1452 // return the value in the text control
1453 wxString wxGridCellChoiceEditor::GetValue() const
1454 {
1455 return Combo()->GetValue();
1456 }
1457
1458 #endif // wxUSE_COMBOBOX
1459
1460 #if wxUSE_COMBOBOX
1461
1462 // ----------------------------------------------------------------------------
1463 // wxGridCellEnumEditor
1464 // ----------------------------------------------------------------------------
1465
1466 // A cell editor which displays an enum number as a textual equivalent. eg
1467 // data in cell is 0,1,2 ... n the cell could be displayed as
1468 // "John","Fred"..."Bob" in the combo choice box
1469
1470 wxGridCellEnumEditor::wxGridCellEnumEditor(const wxString& choices)
1471 :wxGridCellChoiceEditor()
1472 {
1473 m_index = -1;
1474
1475 if (!choices.empty())
1476 SetParameters(choices);
1477 }
1478
1479 wxGridCellEditor *wxGridCellEnumEditor::Clone() const
1480 {
1481 wxGridCellEnumEditor *editor = new wxGridCellEnumEditor();
1482 editor->m_index = m_index;
1483 return editor;
1484 }
1485
1486 void wxGridCellEnumEditor::BeginEdit(int row, int col, wxGrid* grid)
1487 {
1488 wxASSERT_MSG(m_control,
1489 wxT("The wxGridCellEnumEditor must be Created first!"));
1490
1491 wxGridTableBase *table = grid->GetTable();
1492
1493 if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
1494 {
1495 m_index = table->GetValueAsLong(row, col);
1496 }
1497 else
1498 {
1499 wxString startValue = table->GetValue(row, col);
1500 if (startValue.IsNumber() && !startValue.empty())
1501 {
1502 startValue.ToLong(&m_index);
1503 }
1504 else
1505 {
1506 m_index = -1;
1507 }
1508 }
1509
1510 Combo()->SetSelection(m_index);
1511 Combo()->SetInsertionPointEnd();
1512 Combo()->SetFocus();
1513
1514 }
1515
1516 bool wxGridCellEnumEditor::EndEdit(int WXUNUSED(row),
1517 int WXUNUSED(col),
1518 const wxGrid* WXUNUSED(grid),
1519 const wxString& WXUNUSED(oldval),
1520 wxString *newval)
1521 {
1522 long idx = Combo()->GetSelection();
1523 if ( idx == m_index )
1524 return false;
1525
1526 m_index = idx;
1527
1528 if ( newval )
1529 newval->Printf("%ld", m_index);
1530
1531 return true;
1532 }
1533
1534 void wxGridCellEnumEditor::ApplyEdit(int row, int col, wxGrid* grid)
1535 {
1536 wxGridTableBase * const table = grid->GetTable();
1537 if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
1538 table->SetValueAsLong(row, col, m_index);
1539 else
1540 table->SetValue(row, col, wxString::Format("%ld", m_index));
1541 }
1542
1543 #endif // wxUSE_COMBOBOX
1544
1545 // ----------------------------------------------------------------------------
1546 // wxGridCellAutoWrapStringEditor
1547 // ----------------------------------------------------------------------------
1548
1549 void
1550 wxGridCellAutoWrapStringEditor::Create(wxWindow* parent,
1551 wxWindowID id,
1552 wxEvtHandler* evtHandler)
1553 {
1554 wxGridCellTextEditor::DoCreate(parent, id, evtHandler,
1555 wxTE_MULTILINE | wxTE_RICH);
1556 }
1557
1558
1559 #endif // wxUSE_GRID