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