+void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
+{
+ // we want to work with logical coords
+ int x;
+ m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
+ int y = event.GetY();
+
+ if (m_isDragging)
+ {
+ SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition());
+
+ // we don't draw the line beyond our window, but we allow dragging it
+ // there
+ int w = 0;
+ GetClientSize( &w, NULL );
+ m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
+ w -= 6;
+
+ // erase the line if it was drawn
+ if ( m_currentX < w )
+ DrawCurrent();
+
+ if (event.ButtonUp())
+ {
+ ReleaseMouse();
+ m_isDragging = false;
+ m_dirty = true;
+ m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
+ SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition());
+ }
+ else
+ {
+ if (x > m_minX + 7)
+ m_currentX = x;
+ else
+ m_currentX = m_minX + 7;
+
+ // draw in the new location
+ if ( m_currentX < w )
+ DrawCurrent();
+ }
+ }
+ else // not dragging
+ {
+ m_minX = 0;
+ bool hit_border = false;
+
+ // end of the current column
+ int xpos = 0;
+
+ // find the column where this event occurred
+ int col,
+ countCol = m_owner->GetColumnCount();
+ for (col = 0; col < countCol; col++)
+ {
+ xpos += m_owner->GetColumnWidth( col );
+ m_column = col;
+
+ if ( (abs(x-xpos) < 3) && (y < 22) )
+ {
+ // near the column border
+ hit_border = true;
+ break;
+ }
+
+ if ( x < xpos )
+ {
+ // inside the column
+ break;
+ }
+
+ m_minX = xpos;
+ }
+
+ if ( col == countCol )
+ m_column = -1;
+
+ if (event.LeftDown() || event.RightUp())
+ {
+ if (hit_border && event.LeftDown())
+ {
+ if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
+ event.GetPosition()) )
+ {
+ m_isDragging = true;
+ m_currentX = x;
+ CaptureMouse();
+ DrawCurrent();
+ }
+ //else: column resizing was vetoed by the user code
+ }
+ else // click on a column
+ {
+ SendListEvent( event.LeftDown()
+ ? wxEVT_COMMAND_LIST_COL_CLICK
+ : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
+ event.GetPosition());
+ }
+ }
+ else if (event.Moving())
+ {
+ bool setCursor;
+ if (hit_border)
+ {
+ setCursor = m_currentCursor == wxSTANDARD_CURSOR;
+ m_currentCursor = m_resizeCursor;
+ }
+ else
+ {
+ setCursor = m_currentCursor != wxSTANDARD_CURSOR;
+ m_currentCursor = wxSTANDARD_CURSOR;
+ }
+
+ if ( setCursor )
+ SetCursor(*m_currentCursor);
+ }
+ }
+}
+
+void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
+{
+ m_owner->SetFocus();
+ m_owner->Update();
+}
+
+bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
+{
+ wxWindow *parent = GetParent();
+ wxListEvent le( type, parent->GetId() );
+ le.SetEventObject( parent );
+ le.m_pointDrag = pos;
+
+ // the position should be relative to the parent window, not
+ // this one for compatibility with MSW and common sense: the
+ // user code doesn't know anything at all about this header
+ // window, so why should it get positions relative to it?
+ le.m_pointDrag.y -= GetSize().y;
+
+ le.m_col = m_column;
+ return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
+}
+
+//-----------------------------------------------------------------------------
+// wxListRenameTimer (internal)
+//-----------------------------------------------------------------------------
+
+wxListRenameTimer::wxListRenameTimer( wxListMainWindow *owner )
+{
+ m_owner = owner;
+}
+
+void wxListRenameTimer::Notify()
+{
+ m_owner->OnRenameTimer();
+}
+
+//-----------------------------------------------------------------------------
+// wxListTextCtrlWrapper (internal)
+//-----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(wxListTextCtrlWrapper, wxEvtHandler)
+ EVT_CHAR (wxListTextCtrlWrapper::OnChar)
+ EVT_KEY_UP (wxListTextCtrlWrapper::OnKeyUp)
+ EVT_KILL_FOCUS (wxListTextCtrlWrapper::OnKillFocus)
+END_EVENT_TABLE()
+
+wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner,
+ wxTextCtrl *text,
+ size_t itemEdit)
+ : m_startValue(owner->GetItemText(itemEdit)),
+ m_itemEdited(itemEdit)
+{
+ m_owner = owner;
+ m_text = text;
+ m_finished = false;
+ m_aboutToFinish = false;
+
+ wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
+
+ m_owner->CalcScrolledPosition(rectLabel.x, rectLabel.y,
+ &rectLabel.x, &rectLabel.y);
+
+ m_text->Create(owner, wxID_ANY, m_startValue,
+ wxPoint(rectLabel.x-4,rectLabel.y-4),
+ wxSize(rectLabel.width+11,rectLabel.height+8));
+ m_text->PushEventHandler(this);
+}
+
+void wxListTextCtrlWrapper::Finish()
+{
+ if ( !m_finished )
+ {
+ m_finished = true;
+
+ m_text->RemoveEventHandler(this);
+ m_owner->FinishEditing(m_text);
+
+ delete this;
+ }
+}
+
+bool wxListTextCtrlWrapper::AcceptChanges()
+{
+ const wxString value = m_text->GetValue();
+
+ if ( value == m_startValue )
+ // nothing changed, always accept
+ return true;
+
+ if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
+ // vetoed by the user
+ return false;
+
+ // accepted, do rename the item
+ m_owner->SetItemText(m_itemEdited, value);
+
+ return true;
+}
+
+void wxListTextCtrlWrapper::AcceptChangesAndFinish()
+{
+ m_aboutToFinish = true;
+
+ // Notify the owner about the changes
+ AcceptChanges();
+
+ // Even if vetoed, close the control (consistent with MSW)
+ Finish();
+}
+
+void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event )
+{
+ switch ( event.m_keyCode )
+ {
+ case WXK_RETURN:
+ AcceptChangesAndFinish();
+ break;
+
+ case WXK_ESCAPE:
+ Finish();
+ m_owner->OnRenameCancelled( m_itemEdited );
+ break;
+
+ default:
+ event.Skip();
+ }
+}
+
+void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
+{
+ if (m_finished)
+ {
+ event.Skip();
+ return;
+ }
+
+ // auto-grow the textctrl:
+ wxSize parentSize = m_owner->GetSize();
+ wxPoint myPos = m_text->GetPosition();
+ wxSize mySize = m_text->GetSize();
+ int sx, sy;
+ m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
+ if (myPos.x + sx > parentSize.x)
+ sx = parentSize.x - myPos.x;
+ if (mySize.x > sx)
+ sx = mySize.x;
+ m_text->SetSize(sx, wxDefaultCoord);
+
+ event.Skip();
+}
+
+void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
+{
+ if ( !m_finished && !m_aboutToFinish )
+ {
+ // We must finish regardless of success, otherwise we'll get
+ // focus problems:
+ Finish();
+
+ if ( !AcceptChanges() )
+ m_owner->OnRenameCancelled( m_itemEdited );
+ }
+
+ // We must let the native text control handle focus, too, otherwise
+ // it could have problems with the cursor (e.g., in wxGTK).
+ event.Skip();
+}
+
+//-----------------------------------------------------------------------------
+// wxListMainWindow
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxListMainWindow,wxScrolledWindow)
+
+BEGIN_EVENT_TABLE(wxListMainWindow,wxScrolledWindow)
+ EVT_PAINT (wxListMainWindow::OnPaint)
+ EVT_MOUSE_EVENTS (wxListMainWindow::OnMouse)
+ EVT_CHAR (wxListMainWindow::OnChar)
+ EVT_KEY_DOWN (wxListMainWindow::OnKeyDown)
+ EVT_SET_FOCUS (wxListMainWindow::OnSetFocus)
+ EVT_KILL_FOCUS (wxListMainWindow::OnKillFocus)
+ EVT_SCROLLWIN (wxListMainWindow::OnScroll)
+END_EVENT_TABLE()
+
+void wxListMainWindow::Init()
+{
+ m_dirty = true;
+ m_countVirt = 0;
+ m_lineFrom =
+ m_lineTo = (size_t)-1;
+ m_linesPerPage = 0;
+
+ m_headerWidth =
+ m_lineHeight = 0;
+
+ m_small_image_list = (wxImageListType *) NULL;
+ m_normal_image_list = (wxImageListType *) NULL;
+
+ m_small_spacing = 30;
+ m_normal_spacing = 40;
+
+ m_hasFocus = false;
+ m_dragCount = 0;
+ m_isCreated = false;
+
+ m_lastOnSame = false;
+ m_renameTimer = new wxListRenameTimer( this );
+ m_textctrlWrapper = NULL;
+
+ m_current =
+ m_lineLastClicked =
+ m_lineSelectSingleOnUp =
+ m_lineBeforeLastClicked = (size_t)-1;
+
+ m_freezeCount = 0;
+}
+
+wxListMainWindow::wxListMainWindow()
+{
+ Init();
+
+ m_highlightBrush =
+ m_highlightUnfocusedBrush = (wxBrush *) NULL;
+}
+
+wxListMainWindow::wxListMainWindow( wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString &name )
+ : wxScrolledWindow( parent, id, pos, size,
+ style | wxHSCROLL | wxVSCROLL, name )
+{
+ Init();
+
+ m_highlightBrush = new wxBrush
+ (
+ wxSystemSettings::GetColour
+ (
+ wxSYS_COLOUR_HIGHLIGHT
+ ),
+ wxSOLID
+ );
+
+ m_highlightUnfocusedBrush = new wxBrush
+ (
+ wxSystemSettings::GetColour
+ (
+ wxSYS_COLOUR_BTNSHADOW
+ ),
+ wxSOLID
+ );
+
+ SetScrollbars( 0, 0, 0, 0, 0, 0 );
+
+ wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
+ SetOwnForegroundColour( attr.colFg );
+ SetOwnBackgroundColour( attr.colBg );
+ if (!m_hasFont)
+ SetOwnFont( attr.font );
+}
+
+wxListMainWindow::~wxListMainWindow()
+{
+ DoDeleteAllItems();
+ WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
+ WX_CLEAR_ARRAY(m_aColWidths);
+
+ delete m_highlightBrush;
+ delete m_highlightUnfocusedBrush;
+ delete m_renameTimer;
+}
+
+void wxListMainWindow::CacheLineData(size_t line)
+{
+ wxGenericListCtrl *listctrl = GetListCtrl();
+
+ wxListLineData *ld = GetDummyLine();
+
+ size_t countCol = GetColumnCount();
+ for ( size_t col = 0; col < countCol; col++ )
+ {
+ ld->SetText(col, listctrl->OnGetItemText(line, col));
+ ld->SetImage(col, listctrl->OnGetItemColumnImage(line, col));
+ }
+
+ ld->SetAttr(listctrl->OnGetItemAttr(line));
+}
+
+wxListLineData *wxListMainWindow::GetDummyLine() const
+{
+ wxASSERT_MSG( !IsEmpty(), _T("invalid line index") );
+ wxASSERT_MSG( IsVirtual(), _T("GetDummyLine() shouldn't be called") );
+
+ wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
+
+ // we need to recreate the dummy line if the number of columns in the
+ // control changed as it would have the incorrect number of fields
+ // otherwise
+ if ( !m_lines.IsEmpty() &&
+ m_lines[0].m_items.GetCount() != (size_t)GetColumnCount() )
+ {
+ self->m_lines.Clear();
+ }
+
+ if ( m_lines.IsEmpty() )
+ {
+ wxListLineData *line = new wxListLineData(self);
+ self->m_lines.Add(line);
+
+ // don't waste extra memory -- there never going to be anything
+ // else/more in this array
+ self->m_lines.Shrink();
+ }
+
+ return &m_lines[0];
+}
+
+// ----------------------------------------------------------------------------
+// line geometry (report mode only)
+// ----------------------------------------------------------------------------
+
+wxCoord wxListMainWindow::GetLineHeight() const
+{
+ // we cache the line height as calling GetTextExtent() is slow
+ if ( !m_lineHeight )
+ {
+ wxListMainWindow *self = wxConstCast(this, wxListMainWindow);
+
+ wxClientDC dc( self );
+ dc.SetFont( GetFont() );
+
+ wxCoord y;
+ dc.GetTextExtent(_T("H"), NULL, &y);
+
+ if ( m_small_image_list && m_small_image_list->GetImageCount() )
+ {
+ int iw = 0, ih = 0;
+ m_small_image_list->GetSize(0, iw, ih);
+ y = wxMax(y, ih);
+ }
+
+ y += EXTRA_HEIGHT;
+ self->m_lineHeight = y + LINE_SPACING;
+ }
+
+ return m_lineHeight;
+}
+
+wxCoord wxListMainWindow::GetLineY(size_t line) const
+{
+ wxASSERT_MSG( InReportView(), _T("only works in report mode") );
+
+ return LINE_SPACING + line * GetLineHeight();
+}
+
+wxRect wxListMainWindow::GetLineRect(size_t line) const
+{
+ if ( !InReportView() )
+ return GetLine(line)->m_gi->m_rectAll;
+
+ wxRect rect;
+ rect.x = HEADER_OFFSET_X;
+ rect.y = GetLineY(line);
+ rect.width = GetHeaderWidth();
+ rect.height = GetLineHeight();
+
+ return rect;
+}
+
+wxRect wxListMainWindow::GetLineLabelRect(size_t line) const
+{
+ if ( !InReportView() )
+ return GetLine(line)->m_gi->m_rectLabel;
+
+ wxRect rect;
+ rect.x = HEADER_OFFSET_X;
+ rect.y = GetLineY(line);
+ rect.width = GetColumnWidth(0);
+ rect.height = GetLineHeight();
+
+ return rect;
+}
+
+wxRect wxListMainWindow::GetLineIconRect(size_t line) const
+{
+ if ( !InReportView() )
+ return GetLine(line)->m_gi->m_rectIcon;
+
+ wxListLineData *ld = GetLine(line);
+ wxASSERT_MSG( ld->HasImage(), _T("should have an image") );
+
+ wxRect rect;
+ rect.x = HEADER_OFFSET_X;
+ rect.y = GetLineY(line);
+ GetImageSize(ld->GetImage(), rect.width, rect.height);
+
+ return rect;
+}
+
+wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
+{
+ return InReportView() ? GetLineRect(line)
+ : GetLine(line)->m_gi->m_rectHighlight;
+}
+
+long wxListMainWindow::HitTestLine(size_t line, int x, int y) const
+{
+ wxASSERT_MSG( line < GetItemCount(), _T("invalid line in HitTestLine") );
+
+ wxListLineData *ld = GetLine(line);
+
+ if ( ld->HasImage() && GetLineIconRect(line).Inside(x, y) )
+ return wxLIST_HITTEST_ONITEMICON;
+
+ // VS: Testing for "ld->HasText() || InReportView()" instead of
+ // "ld->HasText()" is needed to make empty lines in report view
+ // possible
+ if ( ld->HasText() || InReportView() )
+ {
+ wxRect rect = InReportView() ? GetLineRect(line)
+ : GetLineLabelRect(line);
+
+ if ( rect.Inside(x, y) )
+ return wxLIST_HITTEST_ONITEMLABEL;
+ }
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// highlight (selection) handling
+// ----------------------------------------------------------------------------
+
+bool wxListMainWindow::IsHighlighted(size_t line) const