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 selectionChangeNotificationSent = false;
67 selectionChangeNotificationEnabled = true;
68 subTitleRowAttr = new wxListItemAttr;
74 MyListCtrl::SetDataSource(MyListCtrlDataSource *source)
81 MyListCtrl::RefreshTable()
83 if (dataSource != NULL) {
84 int nrows = dataSource->GetItemCount(this);
87 RefreshItems(0, nrows - 1);
92 // Define the repainting behaviour
94 MyListCtrl::OnPaintCallback(wxDC *dc)
96 if (dragTargetRow >= 0) {
98 wxPen pen = *wxCYAN_PEN;
100 #if defined(__WXMSW__)
101 static const int offset_y = -6;
103 static const int offset_y = 0;
105 CalcScrolledPosition(0, 0, &dx, &dy);
108 if (dragTargetRow == GetItemCount()) {
109 GetItemRect(dragTargetRow - 1, r);
112 GetItemRect(dragTargetRow, r);
113 y = r.y - dy - r.height;
116 // 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);
117 dc->DrawLine(r.x - dx, y, r.x - dx + r.width, y);
121 // Callback function that is called from wxListMainWindow::OnPaint().
122 // This is a very ugly hack, but I can think of no alternative...
124 wxListCtrl_onPaintCallback(wxGenericListCtrl *listctrl, wxDC *dc)
126 MyListCtrl *ctrl = wxStaticCast(listctrl, MyListCtrl);
127 if (ctrl != NULL && dc != NULL)
128 ctrl->OnPaintCallback(dc);
132 MyListCtrl::OnGetItemText(long item, long column) const
134 if (dataSource == NULL)
135 return wxEmptyString;
136 return dataSource->GetItemText((MyListCtrl *)this, item, column);
140 MyListCtrl::OnGetItemAttr(long item) const
145 row = item % 1000000;
146 col = item / 1000000 - 1; // -1 for all rows
147 ret = dataSource->SetItemColor((MyListCtrl *)this, row, col, fg, bg);
151 wxColour fgcol((int)(fg[0] * 255), (int)(fg[1] * 255), (int)(fg[2] * 255));
152 subTitleRowAttr->SetTextColour(fgcol);
153 } else subTitleRowAttr->SetTextColour(*wxBLACK);
155 wxColour bgcol((int)(bg[0] * 255), (int)(bg[1] * 255), (int)(bg[2] * 255));
156 subTitleRowAttr->SetBackgroundColour(bgcol);
157 } else subTitleRowAttr->SetBackgroundColour(*wxWHITE);
158 return subTitleRowAttr;
162 MyListCtrl::PostSelectionChangeNotification()
164 if (!selectionChangeNotificationSent && selectionChangeNotificationEnabled) {
165 selectionChangeNotificationSent = true;
166 wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_tableSelectionChanged);
167 wxPostEvent(this, myEvent);
172 MyListCtrl::OnBeginLabelEdit(wxListEvent &event)
174 // printf("OnBeginLabelEdit: item index = %d\n", event.GetIndex());
178 MyListCtrl::OnEndLabelEdit(wxListEvent &event)
180 // printf("OnEndLabelEdit: item index = %d\n", event.GetIndex());
184 MyListCtrl::OnItemActivated(wxListEvent &event)
186 // printf("OnItemActivated: item index = %d, col = %d\n", event.GetIndex(), event.GetItem().m_col);
190 MyListCtrl::OnBeginDrag(wxListEvent &event)
192 int count = GetItemCount();
194 if (dataSource == NULL || !dataSource->IsDragAndDropEnabled(this))
200 wxMouseState mstate = wxGetMouseState();
201 wxPoint pt(mstate.GetX(), mstate.GetY());
202 pt = ScreenToClient(pt);
203 long newRow = FindItem(-1, pt, 0);
204 if (newRow != dragTargetRow) {
206 EnsureVisible(newRow);
212 GetItemRect(count - 1, r);
214 EnsureVisible(count - 1);
215 if (pt.y < r.y + r.height)
217 } else if (count > 0 && pt.y >= r.y - r.height)
222 if (newRow != dragTargetRow) {
224 if (newRow == count) {
225 GetItemRect(newRow - 1, r);
228 GetItemRect(newRow, r);
233 if (dragTargetRow >= 0) {
234 if (dragTargetRow == count) {
235 GetItemRect(dragTargetRow - 1, r);
238 GetItemRect(dragTargetRow, r);
243 dragTargetRow = newRow;
246 if (!mstate.LeftIsDown()) {
247 // If the mouse cursor is outside the item rect, then dragging should be discarded
248 if (dragTargetRow >= 0) {
256 if (dragTargetRow >= 0)
257 dataSource->DragSelectionToRow(this, dragTargetRow);
263 MyListCtrl::GetItemRectForRowAndColumn(wxRect &rect, int row, int column)
265 int i, xpos, width, xunit, yunit;
266 if (!GetItemRect(row, rect))
268 GetScrollPixelsPerUnit(&xunit, &yunit);
269 xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
270 for (i = 0; i < column; i++) {
271 width = GetColumnWidth(i);
275 rect.SetWidth(GetColumnWidth(column));
280 MyListCtrl::GetScrollPixelsPerUnit(int *xunit, int *yunit)
282 wxGenericListCtrl::GetScrollPixelsPerUnit(xunit, yunit);
284 // /* m_mainWin is a protected member in wxGenericListCtrl */
285 // /*((wxScrolledWindow *)m_mainWin)->*/GetScrollPixelsPerUnit(&x, &y);
286 // if (xunit != NULL)
288 // if (yunit != NULL)
293 MyListCtrl::FindItemAtPosition(const wxPoint &pos, int *row, int *column)
295 int i, r, ncols, width, xpos, flags, xunit, yunit;
296 r = (int)HitTest(pos, flags, NULL);
297 if (r == wxNOT_FOUND)
299 ncols = GetColumnCount();
300 GetScrollPixelsPerUnit(&xunit, &yunit);
301 xpos = -GetScrollPos(wxHORIZONTAL) * xunit;
302 for (i = 0; i < ncols; i++) {
303 width = GetColumnWidth(i);
316 MyListCtrl::StartEditText(int row, int column)
319 int x0, x1, dx, size, xpos, ypos, xunit, yunit;
320 if (editText != NULL && editText->IsShown())
322 if (dataSource == NULL || !dataSource->IsItemEditable(this, row, column))
325 /* Select only this row */
327 for (x0 = 0; x0 < x1; x0++)
328 SetItemState(x0, (x0 == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
330 // Call the event handler directly
331 // (Otherwise, the table selection may be updated from the "current" selection in the molecule
332 // before the selection is updated from the table)
333 wxCommandEvent dummyEvent;
334 OnTableSelectionChanged(dummyEvent);
336 /* Scroll the list so that the editing item is visible */
338 GetItemRectForRowAndColumn(rect, row, column);
342 GetScrollPixelsPerUnit(&xunit, &yunit);
343 xpos = GetScrollPos(wxHORIZONTAL);
344 ypos = GetScrollPos(wxVERTICAL);
346 x1 = x0 + rect.GetWidth();
352 } else if (x1 > (size = GetSize().GetWidth())) {
354 dx = ((x1 - size) / xunit) + 1;
357 Scroll(xpos + dx, -1);
359 rect.x += dx * xunit;
362 /* Reposition the rect relative to the origin of the scrolling area */
363 GetItemRectForRowAndColumn(rect, row, column);
365 ClientToScreen(&rect.x, &rect.y);
366 ((wxWindow *)m_mainWin)->ScreenToClient(&rect.x, &rect.y);
368 #if defined(__WXMSW__)
369 // wxMSW seems to require that rect.y >= 0
374 wxString str = dataSource->GetItemText(this, editRow, editColumn);
375 if (editText == NULL) {
376 /* m_mainWin is a protected member in wxGenericListCtrl */
377 editText = new wxTextCtrl((wxWindow *)m_mainWin, -1, wxT(""), rect.GetPosition(), rect.GetSize(), wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB);
378 editText->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(MyListCtrl::OnKeyDownOnEditText), NULL, this);
379 editText->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(MyListCtrl::OnKillFocusOnEditText), NULL, this);
381 editText->SetSize(rect.x, rect.y, rect.width, rect.height, wxSIZE_ALLOW_MINUS_ONE);
385 editText->AppendText(str);
386 editText->SetFocus();
387 // editText->Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(MyListCtrl::OnIdle), NULL, this);
389 editText->SetSelection(-1, -1); // Select all text
393 MyListCtrl::EndEditTextAndRestart(bool setValueFlag, int newRow, int newColumn)
396 if (editText != NULL && editText->IsShown()) {
397 if (setValueFlag && dataSource) {
398 sval = editText->GetValue();
400 // if (wxWindow::FindFocus() == editText) {
403 #if defined(__WXMAC__)
405 /* Erase the focus ring */
406 wxRect rect = editText->GetRect();
407 rect = rect.Inflate(5, 5);
408 // Refresh(true, &rect); /* This somehow leaves lower side of the focus ring to remain */
412 // Temporarily hide until new editing starts
413 // (editText is set to NULL to avoid recursive calling of EndEditText())
414 wxTextCtrl *saveEditText = editText;
416 saveEditText->Hide();
417 editText = saveEditText;
419 if (setValueFlag && dataSource)
420 dataSource->SetItemText(this, editRow, editColumn, sval);
424 if (newRow >= 0 && newColumn >= 0) {
425 StartEditText(newRow, newColumn);
427 editRow = editColumn = -1;
428 #if 0 && defined(__WXMAC__)
429 if (editText != NULL) {
430 editText->Disconnect(wxID_ANY);
435 if (editText != NULL) {
436 editText->Move(-1000, -1000);
445 MyListCtrl::EndEditText(bool setValueFlag)
447 EndEditTextAndRestart(setValueFlag, -1, -1);
451 MyListCtrl::OnKillFocusOnEditText(wxFocusEvent &event)
453 if (editText != NULL && editText->IsShown()) {
459 MyListCtrl::OnIdle(wxIdleEvent &event)
463 if (editText != NULL && (wp = wxWindow::FindFocus()) != editText) {
470 MyListCtrl::OnKeyDownOnEditText(wxKeyEvent &event)
472 int keyCode, ncols, nrows, ecol, erow;
474 if (editText == NULL || !editText->IsShown()) {
478 keyCode = event.GetKeyCode();
479 ncols = GetColumnCount();
480 nrows = GetItemCount();
481 shiftDown = (event.GetModifiers() == wxMOD_SHIFT);
497 if (ecol == ncols - 1) {
498 if (erow >= nrows - 1)
506 if (dataSource == NULL || dataSource->IsItemEditable(this, erow, ecol))
509 EndEditTextAndRestart(true, erow, ecol);
512 if (event.GetModifiers() == wxMOD_ALT) {
513 printf("alt-return pressed\n"); fflush(stdout);
515 printf("EndEditText completed\n"); fflush(stdout);
526 if (erow == nrows - 1)
530 if (dataSource == NULL || dataSource->IsItemEditable(this, erow, ecol))
533 EndEditTextAndRestart(true, erow, ecol);
545 MyListCtrl::DeleteColumn(int col)
548 return wxGenericListCtrl::DeleteColumn(col);
552 MyListCtrl::InsertColumn(long col, const wxString &heading, int format, int width)
555 return wxGenericListCtrl::InsertColumn(col, heading, format, width);
559 MyListCtrl::OnPopUpMenuSelected(wxCommandEvent &event)
561 if (dataSource != NULL)
562 dataSource->OnPopUpMenuSelected(this, lastPopUpRow, lastPopUpColumn, event.GetId() - 1);
566 MyListCtrl::OnLeftDClick(wxMouseEvent &event)
569 wxPoint pos = event.GetPosition();
570 if (!FindItemAtPosition(pos, &row, &col))
572 if (editText != NULL) {
573 if (editRow == row && editColumn == col) {
577 EndEditTextAndRestart(true, row, col);
579 StartEditText(row, col);
584 MyListCtrl::OnMouseDown(wxMouseEvent &event)
589 if (editText != NULL && editText->IsShown()) {
590 // During the text edit, mouse down outside the textctrl will terminate the editing
594 wxPoint pos = event.GetPosition();
595 if (FindItemAtPosition(pos, &row, &col) && dataSource != NULL && (n = dataSource->HasPopUpMenu(this, row, col, &items)) > 0) {
597 for (i = 0; i < n; i++) {
603 mnu.AppendSeparator();
612 wxString itemStr(p, WX_DEFAULT_CONV);
613 mnu.Append(i + 1, itemStr);
615 mnu.Enable(i + 1, false);
621 lastPopUpColumn = col;
623 mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyListCtrl::OnPopUpMenuSelected), NULL, this);
625 n = dataSource->GetItemCount(this);
626 for (i = 0; i < n; i++)
627 SetItemState(i, (i == row ? wxLIST_STATE_SELECTED : 0), wxLIST_STATE_SELECTED);
628 PostSelectionChangeNotification();
632 // Intercept mouse down event and post selection change notification
633 // (a workaround of wxMSW problem where EVT_LIST_ITEM_SELECTED is not sent in some occasions)
634 PostSelectionChangeNotification();
639 MyListCtrl::OnChar(wxKeyEvent &event)
641 // See comments on OnMouseUp()
642 PostSelectionChangeNotification();
647 MyListCtrl::OnItemSelectionChanged(wxListEvent &event)
649 PostSelectionChangeNotification();
653 MyListCtrl::OnTableSelectionChanged(wxCommandEvent &event)
655 selectionChangeNotificationSent = false;
656 if (dataSource == NULL)
658 dataSource->OnSelectionChanged(this);
662 MyListCtrl::OnEnableTableSelectionNotification(wxCommandEvent &event)
664 selectionChangeNotificationEnabled = true;