split wxGrid implementation in grideditors.cpp (for wxGridCellEditor-derived classes...
[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 return;
75
76 // accept changes
77 m_grid->DisableCellEditControl();
78
79 event.Skip();
80 }
81
82 void 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
107 void 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
219 wxGridCellEditor::wxGridCellEditor()
220 {
221 m_control = NULL;
222 m_attr = NULL;
223 }
224
225 wxGridCellEditor::~wxGridCellEditor()
226 {
227 Destroy();
228 }
229
230 void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent),
231 wxWindowID WXUNUSED(id),
232 wxEvtHandler* evtHandler)
233 {
234 if ( evtHandler )
235 m_control->PushEventHandler(evtHandler);
236 }
237
238 void 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
255 void 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
266 void 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
319 void 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
326 void wxGridCellEditor::HandleReturn(wxKeyEvent& event)
327 {
328 event.Skip();
329 }
330
331 bool 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
363 void wxGridCellEditor::StartingKey(wxKeyEvent& event)
364 {
365 event.Skip();
366 }
367
368 void wxGridCellEditor::StartingClick()
369 {
370 }
371
372 #if wxUSE_TEXTCTRL
373
374 // ----------------------------------------------------------------------------
375 // wxGridCellTextEditor
376 // ----------------------------------------------------------------------------
377
378 wxGridCellTextEditor::wxGridCellTextEditor()
379 {
380 m_maxChars = 0;
381 }
382
383 void wxGridCellTextEditor::Create(wxWindow* parent,
384 wxWindowID id,
385 wxEvtHandler* evtHandler)
386 {
387 DoCreate(parent, id, evtHandler);
388 }
389
390 void 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
410 void 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
417 void 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
464 void 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
473 void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
474 {
475 Text()->SetValue(startValue);
476 Text()->SetInsertionPointEnd();
477 Text()->SetSelection(-1, -1);
478 Text()->SetFocus();
479 }
480
481 bool 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
499 void wxGridCellTextEditor::ApplyEdit(int row, int col, wxGrid* grid)
500 {
501 grid->GetTable()->SetValue(row, col, m_value);
502 m_value.clear();
503 }
504
505 void wxGridCellTextEditor::Reset()
506 {
507 wxASSERT_MSG( m_control, "wxGridCellTextEditor must be created first!" );
508
509 DoReset(m_value);
510 }
511
512 void wxGridCellTextEditor::DoReset(const wxString& startValue)
513 {
514 Text()->SetValue(startValue);
515 Text()->SetInsertionPointEnd();
516 }
517
518 bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
519 {
520 return wxGridCellEditor::IsAcceptedKey(event);
521 }
522
523 void 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
564 void 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
581 void 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
603 wxString wxGridCellTextEditor::GetValue() const
604 {
605 return Text()->GetValue();
606 }
607
608 // ----------------------------------------------------------------------------
609 // wxGridCellNumberEditor
610 // ----------------------------------------------------------------------------
611
612 wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max)
613 {
614 m_min = min;
615 m_max = max;
616 }
617
618 void 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
645 void 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
677 bool 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
720 void 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
729 void 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
743 bool 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
758 void 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
787 void 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)
816 wxString 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
839 wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision)
840 {
841 m_width = width;
842 m_precision = precision;
843 }
844
845 void 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
856 void 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
882 bool 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
913 void 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
923 void wxGridCellFloatEditor::Reset()
924 {
925 DoReset(GetString());
926 }
927
928 void 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
955 void 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
983 wxString 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
1009 bool 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()
1052 wxString wxGridCellBoolEditor::ms_stringValues[2] = { _T(""), _T("1") };
1053
1054 void 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
1065 void 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
1136 void 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
1147 void 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
1178 bool 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
1193 void 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
1202 void wxGridCellBoolEditor::Reset()
1203 {
1204 wxASSERT_MSG(m_control,
1205 wxT("The wxGridCellEditor must be created first!"));
1206
1207 CBox()->SetValue(m_value);
1208 }
1209
1210 void wxGridCellBoolEditor::StartingClick()
1211 {
1212 CBox()->SetValue(!CBox()->GetValue());
1213 }
1214
1215 bool 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
1232 void 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
1251 wxString wxGridCellBoolEditor::GetValue() const
1252 {
1253 return ms_stringValues[CBox()->GetValue()];
1254 }
1255
1256 /* static */ void
1257 wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue,
1258 const wxString& valueFalse)
1259 {
1260 ms_stringValues[false] = valueFalse;
1261 ms_stringValues[true] = valueTrue;
1262 }
1263
1264 /* static */ bool
1265 wxGridCellBoolEditor::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
1278 wxGridCellChoiceEditor::wxGridCellChoiceEditor(const wxArrayString& choices,
1279 bool allowOthers)
1280 : m_choices(choices),
1281 m_allowOthers(allowOthers) { }
1282
1283 wxGridCellChoiceEditor::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
1298 wxGridCellEditor *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
1307 void 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
1325 void 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
1337 void 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
1366 bool 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
1381 void wxGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
1382 {
1383 grid->GetTable()->SetValue(row, col, m_value);
1384 }
1385
1386 void 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
1403 void 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
1421 wxString 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
1438 wxGridCellEnumEditor::wxGridCellEnumEditor(const wxString& choices)
1439 :wxGridCellChoiceEditor()
1440 {
1441 m_index = -1;
1442
1443 if (!choices.empty())
1444 SetParameters(choices);
1445 }
1446
1447 wxGridCellEditor *wxGridCellEnumEditor::Clone() const
1448 {
1449 wxGridCellEnumEditor *editor = new wxGridCellEnumEditor();
1450 editor->m_index = m_index;
1451 return editor;
1452 }
1453
1454 void 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
1484 bool 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
1499 void 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
1514 void
1515 wxGridCellAutoWrapStringEditor::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