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