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