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