5 * Created by Toshi Nagata on 08/12/09.
6 * Copyright 2008 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 const wxEventType MyListCtrlEvent = wxNewEventType();
28 IMPLEMENT_DYNAMIC_CLASS(MyListCtrl, wxGenericListCtrl)
30 BEGIN_EVENT_TABLE(MyListCtrl, wxGenericListCtrl)
31 EVT_LIST_ITEM_SELECTED(-1, MyListCtrl::OnItemSelectionChanged)
32 EVT_LIST_ITEM_DESELECTED(-1, MyListCtrl::OnItemSelectionChanged)
33 EVT_COMMAND(MyListCtrlEvent_tableSelectionChanged, MyListCtrlEvent, MyListCtrl::OnTableSelectionChanged)
34 EVT_COMMAND(MyListCtrlEvent_enableTableSelectionNotification, MyListCtrlEvent, MyListCtrl::OnEnableTableSelectionNotification)
35 EVT_LIST_BEGIN_DRAG(-1, MyListCtrl::OnBeginDrag)
36 EVT_LEFT_DCLICK(MyListCtrl::OnLeftDClick)
37 EVT_CHAR(MyListCtrl::OnChar)
38 EVT_LEFT_DOWN(MyListCtrl::OnMouseDown)
41 MyListCtrl::MyListCtrl()
44 #if defined(__WXMAC__)
45 // On OSX, the default font seems to be 14-point, which is too big.
46 wxFont font = this->GetFont();
47 font.SetPointSize(12);
52 MyListCtrl::~MyListCtrl()
54 if (editText != NULL) {
55 /* editText->Destroy(); */ /* May be unnecessary */
61 MyListCtrl::Create(wxWindow* parent, wxWindowID wid, const wxPoint& pos, const wxSize& size)
63 this->wxGenericListCtrl::Create(parent, wid, pos, size, wxLC_REPORT | wxLC_VIRTUAL | wxBORDER_SIMPLE);
66 /* Create a TextCtrl for editing Text */
67 /* m_mainWin is a protected member in wxGenericListCtrl */
68 editText = new wxTextCtrl((wxWindow *)m_mainWin, -1, wxT(""), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB);
70 editText->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(MyListCtrl::OnKeyDownOnEditText), NULL, this);
71 editText->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(MyListCtrl::OnKillFocusOnEditText), NULL, this);
73 selectionChangeNotificationSent = false;
74 selectionChangeNotificationEnabled = true;
75 subTitleRowAttr = new wxListItemAttr;
81 MyListCtrl::SetDataSource(MyListCtrlDataSource *source)
88 MyListCtrl::RefreshTable()
90 if (dataSource != NULL) {
91 int nrows = dataSource->GetItemCount(this);
94 RefreshItems(0, nrows - 1);
99 // Define the repainting behaviour
101 MyListCtrl::OnPaintCallback(wxDC *dc)
103 if (dragTargetRow >= 0) {
105 wxPen pen = *wxCYAN_PEN;
107 #if defined(__WXMSW__)
108 static const int offset_y = -6;
110 static const int offset_y = 0;
112 CalcScrolledPosition(0, 0, &dx, &dy);
115 if (dragTargetRow == GetItemCount()) {
116 GetItemRect(dragTargetRow - 1, r);
119 GetItemRect(dragTargetRow, r);
120 y = r.y - dy - r.height;
123 // printf("dragTargetRow = %d, r.y = %d, y = %d, r.x-dx = %d, r.width = %d\n", dragTargetRow, r.y, y, r.x - dx, r.width);
124 dc->DrawLine(r.x - dx, y, r.x - dx + r.width, y);
128 // Callback function that is called from wxListMainWindow::OnPaint().
129 // This is a very ugly hack, but I can think of no alternative...
131 wxListCtrl_onPaintCallback(wxGenericListCtrl *listctrl, wxDC *dc)
133 MyListCtrl *ctrl = wxStaticCast(listctrl, MyListCtrl);
134 if (ctrl != NULL && dc != NULL)
135 ctrl->OnPaintCallback(dc);
139 MyListCtrl::OnGetItemText(long item, long column) const
141 if (dataSource == NULL)
142 return wxEmptyString;
143 return dataSource->GetItemText((MyListCtrl *)this, item, column);
147 MyListCtrl::OnGetItemAttr(long item) const
152 row = item % 1000000;
153 col = item / 1000000 - 1; // -1 for all rows
154 ret = dataSource->SetItemColor((MyListCtrl *)this, row, col, fg, bg);
158 wxColour fgcol((int)(fg[0] * 255), (int)(fg[1] * 255), (int)(fg[2] * 255));
159 subTitleRowAttr->SetTextColour(fgcol);
160 } else subTitleRowAttr->SetTextColour(*wxBLACK);
162 wxColour bgcol((int)(bg[0] * 255), (int)(bg[1] * 255), (int)(bg[2] * 255));
163 subTitleRowAttr->SetBackgroundColour(bgcol);
164 } else subTitleRowAttr->SetBackgroundColour(*wxWHITE);
165 return subTitleRowAttr;
169 MyListCtrl::PostSelectionChangeNotification()
171 if (!selectionChangeNotificationSent && selectionChangeNotificationEnabled) {
172 selectionChangeNotificationSent = true;
173 wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_tableSelectionChanged);
174 wxPostEvent(this, myEvent);
179 MyListCtrl::OnBeginLabelEdit(wxListEvent &event)
181 // printf("OnBeginLabelEdit: item index = %d\n", event.GetIndex());
185 MyListCtrl::OnEndLabelEdit(wxListEvent &event)
187 // printf("OnEndLabelEdit: item index = %d\n", event.GetIndex());
191 MyListCtrl::OnItemActivated(wxListEvent &event)
193 // printf("OnItemActivated: item index = %d, col = %d\n", event.GetIndex(), event.GetItem().m_col);
197 MyListCtrl::OnBeginDrag(wxListEvent &event)
199 int count = GetItemCount();
201 if (dataSource == NULL || !dataSource->IsDragAndDropEnabled(this))
207 wxMouseState mstate = wxGetMouseState();
208 wxPoint pt(mstate.GetX(), mstate.GetY());
209 pt = ScreenToClient(pt);
210 long newRow = FindItem(-1, pt, 0);
211 if (newRow != dragTargetRow) {
213 EnsureVisible(newRow);
219 GetItemRect(count - 1, r);
221 EnsureVisible(count - 1);
222 if (pt.y < r.y + r.height)
224 } else if (count > 0 && pt.y >= r.y - r.height)
229 if (newRow != dragTargetRow) {
231 if (newRow == count) {
232 GetItemRect(newRow - 1, r);
235 GetItemRect(newRow, r);
240 if (dragTargetRow >= 0) {
241 if (dragTargetRow == count) {
242 GetItemRect(dragTargetRow - 1, r);
245 GetItemRect(dragTargetRow, r);
250 dragTargetRow = newRow;
253 if (!mstate.LeftIsDown()) {
254 // If the mouse cursor is outside the item rect, then dragging should be discarded
255 if (dragTargetRow >= 0) {
263 if (dragTargetRow >= 0)
264 dataSource->DragSelectionToRow(this, dragTargetRow);
270 MyListCtrl::GetItemRectForRowAndColumn(wxRect &rect, int row, int column)
272 int i, xpos, width, xunit, yunit;
273 if (!GetItemRect(row, rect))
275 GetScrollPixelsPerUnit(&xunit, &yunit);
276 xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
277 for (i = 0; i < column; i++) {
278 width = GetColumnWidth(i);
282 rect.SetWidth(GetColumnWidth(column));
287 MyListCtrl::GetScrollPixelsPerUnit(int *xunit, int *yunit)
289 wxGenericListCtrl::GetScrollPixelsPerUnit(xunit, yunit);
291 // /* m_mainWin is a protected member in wxGenericListCtrl */
292 // /*((wxScrolledWindow *)m_mainWin)->*/GetScrollPixelsPerUnit(&x, &y);
293 // if (xunit != NULL)
295 // if (yunit != NULL)
300 MyListCtrl::FindItemAtPosition(const wxPoint &pos, int *row, int *column)
302 int i, r, ncols, width, xpos, flags, xunit, yunit;
303 r = (int)HitTest(pos, flags, NULL);
304 if (r == wxNOT_FOUND)
306 ncols = GetColumnCount();
307 GetScrollPixelsPerUnit(&xunit, &yunit);
308 xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
309 for (i = 0; i < ncols; i++) {
310 width = GetColumnWidth(i);
323 MyListCtrl::StartEditText(int row, int column)
326 int x0, x1, dx, size, xpos, ypos, xunit, yunit;
327 if (editText != NULL && editText->IsShown())
329 if (dataSource == NULL || !dataSource->IsItemEditable(this, row, column))
332 /* Select only this row */
334 for (x0 = 0; x0 < x1; x0++)
335 SetItemState(x0, (x0 == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
337 // Call the event handler directly
338 // (Otherwise, the table selection may be updated from the "current" selection in the molecule
339 // before the selection is updated from the table)
340 wxCommandEvent dummyEvent;
341 OnTableSelectionChanged(dummyEvent);
343 /* Scroll the list so that the editing item is visible */
345 GetItemRectForRowAndColumn(rect, row, column);
349 GetScrollPixelsPerUnit(&xunit, &yunit);
350 xpos = GetScrollPos(wxHORIZONTAL);
351 ypos = GetScrollPos(wxVERTICAL);
353 x1 = x0 + rect.GetWidth();
359 } else if (x1 > (size = GetSize().GetWidth())) {
361 dx = ((x1 - size) / xunit) + 1;
364 Scroll(xpos + dx, -1);
366 rect.x += dx * xunit;
369 /* Reposition the rect relative to the origin of the scrolling area */
370 GetItemRectForRowAndColumn(rect, row, column);
372 ClientToScreen(&rect.x, &rect.y);
373 ((wxWindow *)m_mainWin)->ScreenToClient(&rect.x, &rect.y);
375 #if defined(__WXMSW__)
376 // wxMSW seems to require that rect.y >= 0
381 wxString str = dataSource->GetItemText(this, editRow, editColumn);
382 if (editText == NULL) {
383 /* m_mainWin is a protected member in wxGenericListCtrl */
384 editText = new wxTextCtrl((wxWindow *)m_mainWin, -1, wxT(""), rect.GetPosition(), rect.GetSize(), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB);
385 editText->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(MyListCtrl::OnKeyDownOnEditText), NULL, this);
386 editText->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(MyListCtrl::OnKillFocusOnEditText), NULL, this);
388 editText->SetSize(rect.x, rect.y, rect.width, rect.height, wxSIZE_ALLOW_MINUS_ONE);
392 editText->AppendText(str);
393 editText->SetFocus();
394 // editText->Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyListCtrl::OnIdle), NULL, this);
396 editText->SetSelection(-1, -1); // Select all text
400 MyListCtrl::EndEditTextAndRestart(bool setValueFlag, int newRow, int newColumn)
403 if (editText != NULL && editText->IsShown()) {
404 if (setValueFlag && dataSource) {
405 sval = editText->GetValue();
407 // if (wxWindow::FindFocus() == editText) {
410 #if defined(__WXMAC__)
412 /* Erase the focus ring */
413 wxRect rect = editText->GetRect();
414 rect = rect.Inflate(5, 5);
415 // Refresh(true, &rect); /* This somehow leaves lower side of the focus ring to remain */
420 // Should we destroy editText every time?
421 // (It would be safer anyway)
422 //editText->Destroy();
425 // Temporarily hide until new editing starts
426 // (editText is set to NULL to avoid recursive calling of EndEditText())
427 wxTextCtrl *saveEditText = editText;
429 saveEditText->Hide();
430 editText = saveEditText;
432 if (setValueFlag && dataSource)
433 dataSource->SetItemText(this, editRow, editColumn, sval);
437 if (newRow >= 0 && newColumn >= 0) {
438 StartEditText(newRow, newColumn);
440 editRow = editColumn = -1;
441 #if 0 && defined(__WXMAC__)
442 if (editText != NULL) {
443 editText->Disconnect(wxID_ANY);
448 if (editText != NULL) {
449 editText->Move(-1000, -1000);
458 MyListCtrl::EndEditText(bool setValueFlag)
460 EndEditTextAndRestart(setValueFlag, -1, -1);
464 MyListCtrl::OnKillFocusOnEditText(wxFocusEvent &event)
466 if (editText != NULL && editText->IsShown()) {
472 MyListCtrl::OnIdle(wxIdleEvent &event)
476 if (editText != NULL && (wp = wxWindow::FindFocus()) != editText) {
483 MyListCtrl::OnKeyDownOnEditText(wxKeyEvent &event)
485 int keyCode, ncols, nrows, ecol, erow;
487 if (editText == NULL || !editText->IsShown()) {
491 keyCode = event.GetKeyCode();
492 ncols = GetColumnCount();
493 nrows = GetItemCount();
494 shiftDown = (event.GetModifiers() == wxMOD_SHIFT);
510 if (ecol == ncols - 1) {
511 if (erow >= nrows - 1)
519 if (dataSource == NULL || dataSource->IsItemEditable(this, erow, ecol))
522 EndEditTextAndRestart(true, erow, ecol);
525 if (event.GetModifiers() == wxMOD_ALT) {
526 printf("alt-return pressed\n"); fflush(stdout);
528 printf("EndEditText completed\n"); fflush(stdout);
539 if (erow == nrows - 1)
543 if (dataSource == NULL || dataSource->IsItemEditable(this, erow, ecol))
546 EndEditTextAndRestart(true, erow, ecol);
558 MyListCtrl::DeleteColumn(int col)
561 return wxGenericListCtrl::DeleteColumn(col);
565 MyListCtrl::InsertColumn(long col, const wxString &heading, int format, int width)
568 return wxGenericListCtrl::InsertColumn(col, heading, format, width);
572 MyListCtrl::OnPopUpMenuSelected(wxCommandEvent &event)
574 if (dataSource != NULL)
575 dataSource->OnPopUpMenuSelected(this, lastPopUpRow, lastPopUpColumn, event.GetId() - 1);
579 MyListCtrl::OnLeftDClick(wxMouseEvent &event)
582 wxPoint pos = event.GetPosition();
583 if (!FindItemAtPosition(pos, &row, &col))
585 if (editText != NULL) {
586 if (editRow == row && editColumn == col) {
590 EndEditTextAndRestart(true, row, col);
592 StartEditText(row, col);
597 MyListCtrl::OnMouseDown(wxMouseEvent &event)
602 if (editText != NULL && editText->IsShown()) {
603 // During the text edit, mouse down outside the textctrl will terminate the editing
607 wxPoint pos = event.GetPosition();
608 if (FindItemAtPosition(pos, &row, &col) && dataSource != NULL && (n = dataSource->HasPopUpMenu(this, row, col, &items)) > 0) {
610 for (i = 0; i < n; i++) {
616 mnu.AppendSeparator();
625 wxString itemStr(p, WX_DEFAULT_CONV);
626 mnu.Append(i + 1, itemStr);
628 mnu.Enable(i + 1, false);
634 lastPopUpColumn = col;
636 mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyListCtrl::OnPopUpMenuSelected), NULL, this);
638 n = dataSource->GetItemCount(this);
639 for (i = 0; i < n; i++)
640 SetItemState(i, (i == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
641 PostSelectionChangeNotification();
645 // Intercept mouse down event and post selection change notification
646 // (a workaround of wxMSW problem where EVT_LIST_ITEM_SELECTED is not sent in some occasions)
647 PostSelectionChangeNotification();
652 MyListCtrl::OnChar(wxKeyEvent &event)
654 // See comments on OnMouseUp()
655 PostSelectionChangeNotification();
660 MyListCtrl::OnItemSelectionChanged(wxListEvent &event)
662 PostSelectionChangeNotification();
666 MyListCtrl::OnTableSelectionChanged(wxCommandEvent &event)
668 selectionChangeNotificationSent = false;
669 if (dataSource == NULL)
671 dataSource->OnSelectionChanged(this);
675 MyListCtrl::OnEnableTableSelectionNotification(wxCommandEvent &event)
677 selectionChangeNotificationEnabled = true;