5 * Created by Toshi Nagata on 2022/09/11.
6 * Copyright 2022 Toshi Nagata. All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
18 #include "MyListCtrl.h"
21 #include "wx/dcclient.h"
22 #include "wx/scrolwin.h"
23 #include "wx/glcanvas.h"
26 #include "wx/toplevel.h"
28 #if wxCHECK_VERSION(3,1,0)
29 #define FromFrameDIP(frame, x) frame->FromDIP(x)
31 #define FromFrameDIP(frame, x) (x)
34 const wxEventType MyListCtrlEvent = wxNewEventType();
36 IMPLEMENT_DYNAMIC_CLASS(MyListCtrl, wxWindow)
38 BEGIN_EVENT_TABLE(MyListCtrl, wxWindow)
39 //EVT_COMMAND(MyListCtrlEvent_tableSelectionChanged, MyListCtrlEvent, MyListCtrl::OnTableSelectionChanged)
40 //EVT_COMMAND(MyListCtrlEvent_enableTableSelectionNotification, MyListCtrlEvent, MyListCtrl::OnEnableTableSelectionNotification)
43 MyListCtrl::MyListCtrl()
50 headerHeight = rowHeight = 0;
51 pageWidth = pageHeight = 0;
52 editRow = editColumn = -1;
58 selectionChangeNotificationRequired = false;
59 selectionChangeNotificationEnabled = true;
60 lastPopUpColumn = lastPopUpRow = -1;
62 #if defined(__WXMAC__)
63 // On OSX, the default font seems to be 14-point, which is too big.
64 // wxFont font = this->GetFont();
65 // font.SetPointSize(12);
66 // this->SetFont(font);
70 MyListCtrl::~MyListCtrl()
72 if (editText != NULL) {
73 /* editText->Destroy(); */ /* May be unnecessary */
79 MyListCtrl::Create(wxWindow* parent, wxWindowID wid, const wxPoint& pos, const wxSize& size)
81 this->wxWindow::Create(parent, wid, pos, size);
83 header = new wxWindow(this, 1001, wxPoint(0, 0), wxSize(size.x, 16), wxWANTS_CHARS);
84 scroll = new wxScrolledWindow(this, 1002, wxPoint(0, 16), wxSize(size.x, (size.y <= 16 ? -1 : size.y - 16)));
87 header->Bind(wxEVT_PAINT, &MyListCtrl::OnPaintHeader, this);
88 scroll->Bind(wxEVT_PAINT, &MyListCtrl::OnPaint, this);
89 scroll->Bind(wxEVT_LEFT_DOWN, &MyListCtrl::OnLeftDown, this);
90 scroll->Bind(wxEVT_LEFT_UP, &MyListCtrl::OnLeftUp, this);
91 scroll->Bind(wxEVT_LEFT_DCLICK, &MyListCtrl::OnLeftDClick, this);
92 scroll->Bind(wxEVT_MOTION, &MyListCtrl::OnMotion, this);
93 scroll->Bind(wxEVT_SCROLLWIN_BOTTOM, &MyListCtrl::OnScrollWin, this);
94 scroll->Bind(wxEVT_SCROLLWIN_TOP, &MyListCtrl::OnScrollWin, this);
95 scroll->Bind(wxEVT_SCROLLWIN_LINEDOWN, &MyListCtrl::OnScrollWin, this);
96 scroll->Bind(wxEVT_SCROLLWIN_LINEUP, &MyListCtrl::OnScrollWin, this);
97 scroll->Bind(wxEVT_SCROLLWIN_PAGEDOWN, &MyListCtrl::OnScrollWin, this);
98 scroll->Bind(wxEVT_SCROLLWIN_PAGEUP, &MyListCtrl::OnScrollWin, this);
99 scroll->Bind(wxEVT_SCROLLWIN_THUMBRELEASE, &MyListCtrl::OnScrollWin, this);
100 scroll->Bind(wxEVT_SCROLLWIN_THUMBTRACK, &MyListCtrl::OnScrollWin, this);
101 scroll->Bind(wxEVT_CHAR, &MyListCtrl::OnCharInScroll, this);
102 scroll->Bind(wxEVT_SET_FOCUS, &MyListCtrl::OnSetFocusInScroll, this);
103 scroll->Bind(wxEVT_KILL_FOCUS, &MyListCtrl::OnKillFocusInScroll, this);
106 cellFont = GetFont();
107 headerFont = cellFont.Smaller();
108 header->SetFont(headerFont);
110 // Measure line height
112 int w, h, descent, leading;
113 dc.GetTextExtent(_T("M"), &w, &h, &descent, &leading, &cellFont);
114 rowHeight = h + FromFrameDIP(scroll, 2);
115 dc.GetTextExtent(_T("M"), &w, &h, &descent, &leading, &headerFont);
116 headerHeight = h + FromFrameDIP(scroll, 2);
117 header->SetSize(wxSize(size.x, headerHeight));
118 pageHeight = rowHeight;
119 pageWidth = rowHeight;
120 scroll->SetScrollbars(rowHeight, rowHeight, 1, 1, true);
124 wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
125 vsizer->Add(header, wxSizerFlags(0).Expand().Border(wxALL, 0));
126 vsizer->Add(scroll, wxSizerFlags(1).Expand().Border(wxALL, 0));
127 this->SetSizer(vsizer);
129 selectionChangeNotificationRequired = false;
130 selectionChangeNotificationEnabled = true;
135 MyListCtrl::SetDataSource(MyListCtrlDataSource *source)
142 MyListCtrl::GetColumnCount()
144 return colNames.Count();
148 MyListCtrl::DeleteColumn(int col)
150 if (col < 0 || col >= GetColumnCount())
152 colNames.RemoveAt(col);
153 colWidths.erase(colWidths.begin() + col);
154 colFormats.erase(colFormats.begin() + col);
160 MyListCtrl::InsertColumn(int col, const wxString &heading, int format, int width)
162 if (col < 0 || col > GetColumnCount())
164 colNames.Insert(heading, col);
165 colWidths.insert(colWidths.begin() + col, width);
166 colFormats.insert(colFormats.begin() + col, format);
172 MyListCtrl::SetColumnWidth(int col, int width)
174 if (col < 0 || col > GetColumnCount())
176 colWidths[col] = width;
181 MyListCtrl::RefreshTable(bool refreshWindow)
183 if (dataSource == NULL) {
187 wxSize sz = header->GetSize();
188 nrows = dataSource->GetItemCount(this);
189 ncols = GetColumnCount();
191 for (i = 0; i < ncols; i++) {
192 pageWidth += colWidths[i];
194 // rowHeight = dataSource->GetRowHeight(this);
195 // "+4" is for drawing marker during cell dragging
196 pageHeight = rowHeight * nrows + FromFrameDIP(this, 4);
197 // Set the subwindow infos
199 header->SetMinSize(sz);
200 scroll->SetVirtualSize(pageWidth, pageHeight);
201 int pageSize = floor((pageWidth + rowHeight - 1) / rowHeight);
202 if (scroll->GetScrollPageSize(wxHORIZONTAL) != pageSize)
203 scroll->SetScrollPageSize(wxHORIZONTAL, pageSize);
204 if (scroll->GetScrollPageSize(wxVERTICAL) != pageSize)
205 scroll->SetScrollPageSize(wxVERTICAL, nrows);
214 MyListCtrl::SetNeedsReload(bool flag)
221 static wxBrush lightBlueBrush(wxColour(128, 128, 255));
224 MyListCtrl::OnPaint(wxPaintEvent &event)
226 if (dataSource == NULL)
234 isActive = scroll->HasFocus();
235 isPaintActive = isActive;
236 wxPaintDC dc(scroll);
237 scroll->DoPrepareDC(dc);
239 scroll->CalcUnscrolledPosition(0, 0, &ox, &oy);
240 int col = -1, row, basex;
241 wxSize sz = scroll->GetClientSize();
242 bool showDragTarget = (draggingRows && (dragTargetRow != mouseRow && dragTargetRow != mouseRow + 1));
244 dc.SetPen(*wxTRANSPARENT_PEN);
245 dc.SetBrush(*wxWHITE_BRUSH);
246 dc.DrawRectangle(ox, oy, sz.x, sz.y);
249 for (i = 0; i < ncols; i++) {
250 basex += colWidths[i];
253 basex -= colWidths[i];
261 int mg = FromFrameDIP(this, 2);
262 row = floor(oy / rowHeight);
263 for (j = row; j < nrows; j++) {
264 float fg0[4], bg0[4];
265 int n0 = dataSource->SetItemColor(this, j, -1, fg0, bg0);
267 if (showDragTarget && j >= dragTargetRow) {
273 for (i = col; i < ncols; i++) {
276 str = dataSource->GetItemText(this, j, i);
277 n = dataSource->SetItemColor(this, j, i, fg, bg);
278 if (IsRowSelected(j)) {
279 if (showDragTarget) {
281 bg[0] = bg[0] * 0.5 + 0.25;
282 bg[1] = bg[1] * 0.5 + 0.25;
283 bg[2] = bg[2] * 0.5 + 0.5;
285 bg[0] = bg0[0] * 0.5 + 0.25;
286 bg[1] = bg0[1] * 0.5 + 0.25;
287 bg[2] = bg0[2] * 0.5 + 0.5;
293 float bgbase[3] = { 0, 0, 1 };
300 bg[0] = (bg[0] + bgbase[0]) * 0.5;
301 bg[1] = (bg[1] + bgbase[1]) * 0.5;
302 bg[2] = (bg[2] + bgbase[2]) * 0.5;
304 bg[0] = (bg0[0] + bgbase[0]) * 0.5;
305 bg[1] = (bg0[1] + bgbase[1]) * 0.5;
306 bg[2] = (bg0[2] + bgbase[2]) * 0.5;
314 // Leave fg[] as they are
320 fg[0] = fg[1] = fg[2] = 1.0;
324 // Leave bg[] as they are
330 bg[0] = bg[1] = bg[2] = 1.0;
333 // Leave fg[] as they are
339 fg[0] = fg[1] = fg[2] = 0.0;
342 colour.Set(bg[0] * 255, bg[1] * 255, bg[2] * 255);
343 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(colour));
344 dc.SetPen(*wxTRANSPARENT_PEN);
345 colour.Set(fg[0] * 255, fg[1] * 255, fg[2] * 255);
346 dc.SetTextForeground(colour);
347 dc.DrawRectangle(x, y, colWidths[i], rowHeight - 1);
348 dc.SetPen(*wxLIGHT_GREY_PEN);
349 dc.DrawLine(x, y + rowHeight - 1, x + colWidths[i], y + rowHeight - 1);
350 if (i == ncols - 1) {
351 dc.DrawLine(x + colWidths[i], y, x + colWidths[i], y + rowHeight - 1);
353 dc.SetClippingRegion(x + mg, y, colWidths[i] - mg * 2, rowHeight - 1);
354 dc.DrawText(str, x + mg, y);
355 dc.DestroyClippingRegion();
360 if (showDragTarget) {
361 y = dragTargetRow * rowHeight + 1;
362 dc.SetBrush(*wxRED_BRUSH);
363 dc.SetPen(wxNullPen);
364 dc.DrawRectangle(basex, y, x - basex, 3);
371 MyListCtrl::OnPaintHeader(wxPaintEvent &event)
373 wxPaintDC dc(header);
374 dc.SetPen(*wxGREY_PEN);
375 dc.SetBrush(*wxLIGHT_GREY_BRUSH);
376 wxSize sz = header->GetSize();
377 dc.DrawRectangle(0, 0, sz.x, sz.y);
383 int mg = FromFrameDIP(this, 2);
384 scroll->CalcUnscrolledPosition(0, 0, &ox, &oy);
386 for (i = 0; i < ncols; i++) {
387 x1 = x + colWidths[i];
389 wxString str = colNames[i];
390 dc.DrawLine(x + colWidths[i], 0, x + colWidths[i], sz.y - 1);
391 dc.SetClippingRegion(x + mg, 0, colWidths[i] - mg * 2, sz.y);
392 dc.DrawText(str, x + mg, 0);
393 dc.DestroyClippingRegion();
401 MyListCtrl::PrepareSelectionChangeNotification()
404 if (selectionChangeNotificationRequired || !selectionChangeNotificationEnabled)
405 return; // Do nothing
406 oldSelection.resize(selection.size());
407 for (i = 0; i < selection.size(); i++)
408 oldSelection[i] = selection[i];
409 selectionChangeNotificationRequired = true;
413 MyListCtrl::GetItemState(int item, int stateMask)
415 if (stateMask & wxLIST_STATE_SELECTED)
416 return IsRowSelected(item) ? wxLIST_STATE_SELECTED : 0;
421 MyListCtrl::SetItemState(int item, int state, int stateMask)
423 if (stateMask & wxLIST_STATE_SELECTED) {
424 if (state & wxLIST_STATE_SELECTED)
433 MyListCtrl::IsRowSelected(int row)
436 for (i = 0; i < selection.size(); i++) {
437 if (selection[i] == row)
444 MyListCtrl::SelectRow(int row)
447 if (!dataSource->IsRowSelectable(this, row))
449 for (i = 0; i < selection.size(); i++) {
450 if (selection[i] == row)
452 if (selection[i] > row)
455 selection.insert(selection.begin() + i, row);
461 MyListCtrl::UnselectRow(int row)
464 for (i = 0; i < selection.size(); i++) {
465 if (selection[i] == row) {
466 selection.erase(selection.begin() + i);
475 MyListCtrl::UnselectAllRows()
482 // 1. On a selected row with no modifier
483 // If dragging starts, then start dragging the selected cells (if dragging is enabled)
484 // If mouse up on another row, then try to reorder the selected cells (if dragging is enabled)
485 // (the moved cells are left selected)
486 // If mouse up on the same row, then deselect all other rows
487 // (and leave only this row selected)
488 // 2. On a selected row with shift
489 // Same as 1, except that if mouse up on the same row then do nothing
490 // 3. On a selected row with command (Mac) or control (Win)
491 // Unselect this row, and do nothing on drag or mouse-up
492 // 4. On an unselected row with no modifier
493 // Unselect all other rows and select this row (in this handler)
494 // And do the same way as 2
495 // 5. On an unselected row with shift
496 // Select all cells between the last-clicked row and this row (in this handler)
497 // And do the same way as 2
498 // 6. On an unselected row with command or control key
499 // Select this row (in this handler), and do the same way as 2
501 MyListCtrl::OnLeftDown(wxMouseEvent &event)
503 int ux, uy, row, col, modifiers, i, n;
506 bool isRowSelected, selectionChanged = false;
509 if (!isPaintActive) {
510 // The scroll is still displayed in inactive state;
511 // Set focus to scroll, and do nothing else.
512 // This flag is used because we actually want to intercept the mouseDown
513 // for 'just activate the listview', but wxWidgets may not allow processing
514 // mouseDown event _before_ the scroll view gets focus.
519 pos = event.GetPosition();
520 if (FindItemAtPosition(pos, &row, &col) && dataSource != NULL && (n = dataSource->HasPopUpMenu(this, row, col, &items)) > 0) {
522 for (i = 0; i < n; i++) {
528 mnu.AppendSeparator();
537 wxString itemStr(p, WX_DEFAULT_CONV);
538 mnu.Append(i + 1, itemStr);
540 mnu.Enable(i + 1, false);
546 lastPopUpColumn = col;
548 mnu.Bind(wxEVT_COMMAND_MENU_SELECTED, &MyListCtrl::OnPopUpMenuSelected, this);
550 n = dataSource->GetItemCount(this);
551 for (i = 0; i < n; i++)
552 SetItemState(i, (i == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
553 PostSelectionChangeNotification();
557 scroll->CalcUnscrolledPosition(pos.x, pos.y, &ux, &uy);
558 row = floor(uy / rowHeight);
559 isRowSelected = IsRowSelected(row);
560 modifiers = event.GetModifiers();
561 PrepareSelectionChangeNotification();
562 if ((modifiers & wxMOD_SHIFT) != 0)
563 mouseMode = (isRowSelected ? 2 : 5);
564 #if defined(__WXMAC__) || defined(__WXOSX__)
565 else if ((modifiers & wxMOD_CMD) != 0)
566 mouseMode = (isRowSelected ? 3 : 6);
568 else if ((modifiers & wxMOD_CONTROL) != 0)
569 mouseMode = (isRowSelected ? 3 : 6);
572 mouseMode = (isRowSelected ? 1 : 4);
574 if (mouseMode == 3) {
576 selectionChanged = true;
577 } else if (mouseMode == 4) {
580 selectionChanged = true;
581 } else if (mouseMode == 5) {
582 if (lastMouseRow >= 0) {
584 if (lastMouseRow < row) {
591 for (i = rs; i <= re; i++) {
594 selectionChanged = true;
596 } else if (mouseMode == 6) {
598 selectionChanged = true;
600 if (selectionChanged) {
601 PostSelectionChangeNotification();
603 // Actually no change occurred
604 selectionChangeNotificationRequired = false;
610 MyListCtrl::DragRows(int x, int y)
612 wxSize sz = scroll->GetClientSize();
618 scroll->CalcUnscrolledPosition(x, y, &ux, &uy);
619 dragTargetRow = floor(uy / rowHeight + 0.5);
620 if (dragTargetRow < 0)
622 else if (dragTargetRow > nrows)
623 dragTargetRow = nrows;
628 MyListCtrl::OnMotion(wxMouseEvent &event)
630 if (event.LeftIsDown()) {
632 if (mouseMode > 0 && !scroll->HasCapture()) {
634 scroll->CaptureMouse();
635 if (mouseMode != 3 && dataSource->IsDragAndDropEnabled(this, mouseRow)) {
640 wxPoint cp = scroll->ScreenToClient(wxGetMousePosition());
641 DragRows(cp.x, cp.y);
647 MyListCtrl::OnLeftDClick(wxMouseEvent &event)
649 // Start editing this cell
650 int x, y, ux, uy, row, col, cx, i;
653 scroll->CalcUnscrolledPosition(x, y, &ux, &uy);
654 row = floor(uy / rowHeight);
656 for (i = 0; i < ncols; i++) {
657 if ((ux >= cx && ux < cx + colWidths[i]) || i == ncols - 1) {
663 if (!dataSource->IsItemEditable(this, row, col))
665 StartEditText(row, col);
669 MyListCtrl::OnLeftUp(wxMouseEvent &event)
671 int x, y, ux, uy, row;
672 bool dragged = false;
673 bool selectionChanged = selectionChangeNotificationRequired;
676 scroll->CalcUnscrolledPosition(x, y, &ux, &uy);
677 row = floor(uy / rowHeight);
678 PrepareSelectionChangeNotification();
679 if (scroll->HasCapture()) {
680 scroll->ReleaseMouse();
682 if (row != mouseRow) {
684 dataSource->DragSelectionToRow(this, dragTargetRow);
685 selectionChanged = true;
688 lastMouseRow = dragTargetRow;
691 if (mouseMode == 1 || mouseMode == 4) {
692 if (selection.size() != 1 || !IsRowSelected(mouseRow)) {
695 selectionChanged = true;
700 if (selectionChanged)
701 PostSelectionChangeNotification();
703 selectionChangeNotificationRequired = false;
706 draggingRows = false;
711 MyListCtrl::OnScrollWin(wxScrollWinEvent &event)
713 wxPoint cp = scroll->ScreenToClient(wxGetMousePosition());
714 wxSize sz = scroll->GetClientSize();
715 wxEventType etype = event.GetEventType();
717 int step = rowHeight;
718 scroll->CalcUnscrolledPosition(0, 0, &vx, &vy);
719 wxSize vs = scroll->GetVirtualSize();
720 int orient = event.GetOrientation();
722 if (scroll->IsAutoScrolling()) {
724 if (mouseMode == 3) {
725 return; // Mouse is captured but do not autoscroll
727 if (etype == wxEVT_SCROLLWIN_LINEUP || etype == wxEVT_SCROLLWIN_LINEDOWN) {
728 if (orient == wxHORIZONTAL) {
729 // Is the mouse outside the client width?
731 etype = wxEVT_SCROLLWIN_LINEUP;
734 } else if (cp.x > sz.x) {
735 etype = wxEVT_SCROLLWIN_LINEDOWN;
736 if (vx > vs.x - sz.x)
740 // Is the mouse outsize the client height?
742 etype = wxEVT_SCROLLWIN_LINEUP;
745 } else if (cp.y > sz.y) {
746 etype = wxEVT_SCROLLWIN_LINEDOWN;
747 if (vy > vs.y - sz.y)
753 return; // Pause scrolling
754 event.SetEventType(etype);
758 // Handle dragging rows
759 DragRows(cp.x, cp.y);
761 header->Refresh(); // Adjust the header display
765 MyListCtrl::PostSelectionChangeNotification()
767 dataSource->OnSelectionChanged(this);
768 if (selectionChangeNotificationRequired && selectionChangeNotificationEnabled) {
769 wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_tableSelectionChanged);
770 wxPostEvent(this, myEvent);
771 selectionChangeNotificationRequired = false;
775 // Find item on list control
776 // Pos is the *client* coordinate in scroll (i.e. scrolled position)
778 MyListCtrl::FindItemAtPosition(const wxPoint &pos, int *row, int *col)
781 wxPoint p = scroll->CalcUnscrolledPosition(pos);
782 r = floor(p.y / rowHeight);
791 for (i = 0; i < ncols; i++) {
792 if (p.x >= cx && p.x < cx + colWidths[i])
801 return (r >= 0 && r < nrows && i >= 0 && i < ncols);
804 // The return rect is the *client* coordinate in scroll (i.e. scrolled position)
806 MyListCtrl::GetItemRectForRowAndColumn(wxRect &rect, int row, int col)
808 int i, tx, ty, cx, cy;
809 if (col < 0 || col >= ncols || row < 0 || row >= nrows)
811 cy = rowHeight * row;
813 for (i = 0; i < col; i++) {
816 scroll->CalcScrolledPosition(cx, cy, &tx, &ty);
819 rect.width = colWidths[col];
820 rect.height = rowHeight;
824 // Get the left-top position in scroll unit (= rowHeight)
826 MyListCtrl::GetScrollPosition(int *xpos, int *ypos)
828 *xpos = scroll->GetScrollPos(wxHORIZONTAL);
829 *ypos = scroll->GetScrollPos(wxVERTICAL);
832 // Scroll so that (xpos, ypos) position is left-top
833 // Return false if the position is outside the scrolling limit
835 MyListCtrl::SetScrollPosition(int xpos, int ypos)
838 int xlim = scroll->GetScrollLines(wxHORIZONTAL) - scroll->GetScrollPageSize(wxHORIZONTAL);
839 int ylim = scroll->GetScrollLines(wxVERTICAL) - scroll->GetScrollPageSize(wxVERTICAL);
840 // int xlim = (vsz.x - sz.x) / rowHeight;
841 // int ylim = (vsz.y - sz.y) / rowHeight;
858 // TextCtrl may be moved during the scroll, so amend the position
859 scroll->Scroll(xpos, ypos);
862 int delta = FromFrameDIP(scroll, 2);
863 if (GetItemRectForRowAndColumn(r, editRow, editColumn)) {
864 editText->SetPosition(wxPoint(r.x - delta, r.y - delta));
872 MyListCtrl::EnsureVisible(int row, int col)
875 if (!GetItemRectForRowAndColumn(r, row, (col == -1 ? 0 : col)))
877 wxSize sz = scroll->GetClientSize();
878 int scx = -1, scy = -1;
880 scroll->CalcUnscrolledPosition(r.x, r.y, &ux, &uy);
883 scx = floor(ux / rowHeight);
884 } else if (r.x + r.width > sz.x) {
885 scx = ceil((ux + r.width - sz.x) / rowHeight);
889 } // If col is negative, then do not scroll horizontally
891 scy = floor(uy / rowHeight);
892 } else if (r.y + r.height > sz.y) {
893 scy = ceil((uy + r.height - sz.y) / rowHeight);
897 if (scx >= 0 || scy >= 0) {
898 scroll->Scroll(scx, scy);
905 MyListCtrl::StartEditText(int row, int col)
908 int delta = FromFrameDIP(scroll, 2);
909 EnsureVisible(row, col);
910 if (!GetItemRectForRowAndColumn(r, row, col))
912 if (editText == NULL) {
913 editText = new wxTextCtrl(scroll, -1, "", wxPoint(r.x - delta, r.y - delta), wxSize(r.width + delta * 2, r.height + delta * 2), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxWANTS_CHARS);
914 editText->Bind(wxEVT_CHAR, &MyListCtrl::OnCharInText, this);
915 editText->Bind(wxEVT_CHAR_HOOK, &MyListCtrl::OnCharHookInText, this);
918 editText->SetPosition(wxPoint(r.x - delta, r.y - delta));
919 editText->SetSize(wxSize(r.width + delta * 2, r.height + delta * 2));
921 if (selection.size() != 1 || !IsRowSelected(row)) {
924 PostSelectionChangeNotification();
926 wxString str = dataSource->GetItemText(this, row, col);
927 editText->SetValue(str);
929 editText->SetFocus();
930 editText->SelectAll();
936 MyListCtrl::FinalizeEdit()
938 if (editText != NULL) {
939 wxString sval = editText->GetValue();
941 dataSource->SetItemText(this, editRow, editColumn, sval);
946 MyListCtrl::EndEditText(bool setValueFlag)
959 MyListCtrl::EndEditTextAndRestart(bool setValueFlag, int newRow, int newCol)
961 EndEditText(setValueFlag);
962 StartEditText(newRow, newCol);
966 MyListCtrl::OnCharInText(wxKeyEvent &event)
968 int kc = event.GetKeyCode();
969 bool shiftDown = event.ShiftDown();
970 bool altDown = event.AltDown();
972 int col = editColumn;
973 if (kc == WXK_RETURN) {
984 if (row >= nrows - 1)
989 } while (row >= 0 && !dataSource->IsItemEditable(this, row, col));
991 StartEditText(row, col);
996 } else if (kc == WXK_NUMPAD_ENTER) {
998 } else if (kc == WXK_TAB) {
1017 } while (row >= 0 && !dataSource->IsItemEditable(this, row, col));
1019 StartEditText(row, col);
1027 MyListCtrl::OnCharHookInText(wxKeyEvent &event)
1029 #if defined(__WXMAC__) || defined(__WXOSX__)
1030 // On macOS, shift-TAB is consumed by wxWidgets even when wxTE_PROCESS_TAB and wxWANTS_CHARS
1031 // are specified (why?); so, we intercept the TAB and shift-TAB here
1032 int kc = event.GetKeyCode();
1033 if (kc == WXK_TAB || kc == WXK_NUMPAD_TAB) {
1034 OnCharInText(event);
1035 } else event.Skip();
1042 MyListCtrl::OnCharInScroll(wxKeyEvent &event)
1044 int kc = event.GetKeyCode();
1045 if (kc == WXK_DOWN || kc == WXK_UP) {
1047 if (selection.size() > 0) {
1048 if (selection.size() > 1) {
1049 if (lastMouseRow >= 0 && lastMouseRow < nrows)
1051 else if (kc == WXK_DOWN)
1052 row = selection[selection.size() - 1];
1055 } else row = selection[0];
1056 if (kc == WXK_UP && row > 0)
1058 else if (kc == WXK_DOWN && row < nrows - 1)
1060 else return; // Ignore key
1063 PostSelectionChangeNotification();
1065 lastMouseRow = row; // Fake as if this row was clicked
1067 } else event.Skip();
1071 MyListCtrl::OnSetFocusInScroll(wxFocusEvent &event)
1077 MyListCtrl::OnKillFocusInScroll(wxFocusEvent &event)
1083 MyListCtrl::OnPopUpMenuSelected(wxCommandEvent &event)
1085 if (dataSource != NULL)
1086 dataSource->OnPopUpMenuSelected(this, lastPopUpRow, lastPopUpColumn, event.GetId() - 1);