+void wxListItemData::SetPosition( int x, int y )
+{
+ wxCHECK_RET( m_rect, _T("unexpected SetPosition() call") );
+
+ m_rect->x = x;
+ m_rect->y = y;
+}
+
+void wxListItemData::SetSize( int width, int height )
+{
+ wxCHECK_RET( m_rect, _T("unexpected SetSize() call") );
+
+ if ( width != -1 )
+ m_rect->width = width;
+ if ( height != -1 )
+ m_rect->height = height;
+}
+
+bool wxListItemData::IsHit( int x, int y ) const
+{
+ wxCHECK_MSG( m_rect, false, _T("can't be called in this mode") );
+
+ return wxRect(GetX(), GetY(), GetWidth(), GetHeight()).Inside(x, y);
+}
+
+int wxListItemData::GetX() const
+{
+ wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+ return m_rect->x;
+}
+
+int wxListItemData::GetY() const
+{
+ wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+ return m_rect->y;
+}
+
+int wxListItemData::GetWidth() const
+{
+ wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+ return m_rect->width;
+}
+
+int wxListItemData::GetHeight() const
+{
+ wxCHECK_MSG( m_rect, 0, _T("can't be called in this mode") );
+
+ return m_rect->height;
+}
+
+void wxListItemData::GetItem( wxListItem &info ) const
+{
+ info.m_text = m_text;
+ info.m_image = m_image;
+ info.m_data = m_data;
+
+ if ( m_attr )
+ {
+ if ( m_attr->HasTextColour() )
+ info.SetTextColour(m_attr->GetTextColour());
+ if ( m_attr->HasBackgroundColour() )
+ info.SetBackgroundColour(m_attr->GetBackgroundColour());
+ if ( m_attr->HasFont() )
+ info.SetFont(m_attr->GetFont());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// wxListHeaderData
+//-----------------------------------------------------------------------------
+
+void wxListHeaderData::Init()
+{
+ m_mask = 0;
+ m_image = -1;
+ m_format = 0;
+ m_width = 0;
+ m_xpos = 0;
+ m_ypos = 0;
+ m_height = 0;
+}
+
+wxListHeaderData::wxListHeaderData()
+{
+ Init();
+}
+
+wxListHeaderData::wxListHeaderData( const wxListItem &item )
+{
+ Init();
+
+ SetItem( item );
+}
+
+void wxListHeaderData::SetItem( const wxListItem &item )
+{
+ m_mask = item.m_mask;
+
+ if ( m_mask & wxLIST_MASK_TEXT )
+ m_text = item.m_text;
+
+ if ( m_mask & wxLIST_MASK_IMAGE )
+ m_image = item.m_image;
+
+ if ( m_mask & wxLIST_MASK_FORMAT )
+ m_format = item.m_format;
+
+ if ( m_mask & wxLIST_MASK_WIDTH )
+ SetWidth(item.m_width);
+}
+
+void wxListHeaderData::SetPosition( int x, int y )
+{
+ m_xpos = x;
+ m_ypos = y;
+}
+
+void wxListHeaderData::SetHeight( int h )
+{
+ m_height = h;
+}
+
+void wxListHeaderData::SetWidth( int w )
+{
+ m_width = w;
+ if (m_width < 0)
+ m_width = WIDTH_COL_DEFAULT;
+ else if (m_width < WIDTH_COL_MIN)
+ m_width = WIDTH_COL_MIN;
+}
+
+void wxListHeaderData::SetFormat( int format )
+{
+ m_format = format;
+}
+
+bool wxListHeaderData::HasImage() const
+{
+ return m_image != -1;
+}
+
+bool wxListHeaderData::IsHit( int x, int y ) const
+{
+ return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height));
+}
+
+void wxListHeaderData::GetItem( wxListItem& item )
+{
+ item.m_mask = m_mask;
+ item.m_text = m_text;
+ item.m_image = m_image;
+ item.m_format = m_format;
+ item.m_width = m_width;
+}
+
+int wxListHeaderData::GetImage() const
+{
+ return m_image;
+}
+
+int wxListHeaderData::GetWidth() const
+{
+ return m_width;
+}
+
+int wxListHeaderData::GetFormat() const
+{
+ return m_format;
+}
+
+//-----------------------------------------------------------------------------
+// wxListLineData
+//-----------------------------------------------------------------------------
+
+inline int wxListLineData::GetMode() const
+{
+ return m_owner->GetListCtrl()->GetWindowStyleFlag() & wxLC_MASK_TYPE;
+}
+
+inline bool wxListLineData::InReportView() const
+{
+ return m_owner->HasFlag(wxLC_REPORT);
+}
+
+inline bool wxListLineData::IsVirtual() const
+{
+ return m_owner->IsVirtual();
+}
+
+wxListLineData::wxListLineData( wxListMainWindow *owner )
+{
+ m_owner = owner;
+
+ if ( InReportView() )
+ {
+ m_gi = NULL;
+ }
+ else // !report
+ {
+ m_gi = new GeometryInfo;
+ }
+
+ m_highlighted = false;
+
+ InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 );
+}
+
+void wxListLineData::CalculateSize( wxDC *dc, int spacing )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ wxCHECK_RET( node, _T("no subitems at all??") );
+
+ wxListItemData *item = node->GetData();
+
+ wxString s;
+ wxCoord lw, lh;
+
+ switch ( GetMode() )
+ {
+ case wxLC_ICON:
+ case wxLC_SMALL_ICON:
+ m_gi->m_rectAll.width = spacing;
+
+ s = item->GetText();
+
+ if ( s.empty() )
+ {
+ lh =
+ m_gi->m_rectLabel.width =
+ m_gi->m_rectLabel.height = 0;
+ }
+ else // has label
+ {
+ dc->GetTextExtent( s, &lw, &lh );
+ lw += EXTRA_WIDTH;
+ lh += EXTRA_HEIGHT;
+
+ m_gi->m_rectAll.height = spacing + lh;
+ if (lw > spacing)
+ m_gi->m_rectAll.width = lw;
+
+ m_gi->m_rectLabel.width = lw;
+ m_gi->m_rectLabel.height = lh;
+ }
+
+ if (item->HasImage())
+ {
+ int w, h;
+ m_owner->GetImageSize( item->GetImage(), w, h );
+ m_gi->m_rectIcon.width = w + 8;
+ m_gi->m_rectIcon.height = h + 8;
+
+ if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width )
+ m_gi->m_rectAll.width = m_gi->m_rectIcon.width;
+ if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 )
+ m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4;
+ }
+
+ if ( item->HasText() )
+ {
+ m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width;
+ m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height;
+ }
+ else // no text, highlight the icon
+ {
+ m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width;
+ m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height;
+ }
+ break;
+
+ case wxLC_LIST:
+ s = item->GetTextForMeasuring();
+
+ dc->GetTextExtent( s, &lw, &lh );
+ lw += EXTRA_WIDTH;
+ lh += EXTRA_HEIGHT;
+
+ m_gi->m_rectLabel.width = lw;
+ m_gi->m_rectLabel.height = lh;
+
+ m_gi->m_rectAll.width = lw;
+ m_gi->m_rectAll.height = lh;
+
+ if (item->HasImage())
+ {
+ int w, h;
+ m_owner->GetImageSize( item->GetImage(), w, h );
+ m_gi->m_rectIcon.width = w;
+ m_gi->m_rectIcon.height = h;
+
+ m_gi->m_rectAll.width += 4 + w;
+ if (h > m_gi->m_rectAll.height)
+ m_gi->m_rectAll.height = h;
+ }
+
+ m_gi->m_rectHighlight.width = m_gi->m_rectAll.width;
+ m_gi->m_rectHighlight.height = m_gi->m_rectAll.height;
+ break;
+
+ case wxLC_REPORT:
+ wxFAIL_MSG( _T("unexpected call to SetSize") );
+ break;
+
+ default:
+ wxFAIL_MSG( _T("unknown mode") );
+ }
+}
+
+void wxListLineData::SetPosition( int x, int y, int spacing )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ wxCHECK_RET( node, _T("no subitems at all??") );
+
+ wxListItemData *item = node->GetData();
+
+ switch ( GetMode() )
+ {
+ case wxLC_ICON:
+ case wxLC_SMALL_ICON:
+ m_gi->m_rectAll.x = x;
+ m_gi->m_rectAll.y = y;
+
+ if ( item->HasImage() )
+ {
+ m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 +
+ (m_gi->m_rectAll.width - m_gi->m_rectIcon.width) / 2;
+ m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 4;
+ }
+
+ if ( item->HasText() )
+ {
+ if (m_gi->m_rectAll.width > spacing)
+ m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 2;
+ else
+ m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 2 + (spacing/2) - (m_gi->m_rectLabel.width/2);
+ m_gi->m_rectLabel.y = m_gi->m_rectAll.y + m_gi->m_rectAll.height + 2 - m_gi->m_rectLabel.height;
+ m_gi->m_rectHighlight.x = m_gi->m_rectLabel.x - 2;
+ m_gi->m_rectHighlight.y = m_gi->m_rectLabel.y - 2;
+ }
+ else // no text, highlight the icon
+ {
+ m_gi->m_rectHighlight.x = m_gi->m_rectIcon.x - 4;
+ m_gi->m_rectHighlight.y = m_gi->m_rectIcon.y - 4;
+ }
+ break;
+
+ case wxLC_LIST:
+ m_gi->m_rectAll.x = x;
+ m_gi->m_rectAll.y = y;
+
+ m_gi->m_rectHighlight.x = m_gi->m_rectAll.x;
+ m_gi->m_rectHighlight.y = m_gi->m_rectAll.y;
+ m_gi->m_rectLabel.y = m_gi->m_rectAll.y + 2;
+
+ if (item->HasImage())
+ {
+ m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 2;
+ m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 2;
+ m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 6 + m_gi->m_rectIcon.width;
+ }
+ else
+ {
+ m_gi->m_rectLabel.x = m_gi->m_rectAll.x + 2;
+ }
+ break;
+
+ case wxLC_REPORT:
+ wxFAIL_MSG( _T("unexpected call to SetPosition") );
+ break;
+
+ default:
+ wxFAIL_MSG( _T("unknown mode") );
+ }
+}
+
+void wxListLineData::InitItems( int num )
+{
+ for (int i = 0; i < num; i++)
+ m_items.Append( new wxListItemData(m_owner) );
+}
+
+void wxListLineData::SetItem( int index, const wxListItem &info )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ wxCHECK_RET( node, _T("invalid column index in SetItem") );
+
+ wxListItemData *item = node->GetData();
+ item->SetItem( info );
+}
+
+void wxListLineData::GetItem( int index, wxListItem &info )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ if (node)
+ {
+ wxListItemData *item = node->GetData();
+ item->GetItem( info );
+ }
+}
+
+wxString wxListLineData::GetText(int index) const
+{
+ wxString s;
+
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ if (node)
+ {
+ wxListItemData *item = node->GetData();
+ s = item->GetText();
+ }
+
+ return s;
+}
+
+void wxListLineData::SetText( int index, const wxString s )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ if (node)
+ {
+ wxListItemData *item = node->GetData();
+ item->SetText( s );
+ }
+}
+
+void wxListLineData::SetImage( int index, int image )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ wxCHECK_RET( node, _T("invalid column index in SetImage()") );
+
+ wxListItemData *item = node->GetData();
+ item->SetImage(image);
+}
+
+int wxListLineData::GetImage( int index ) const
+{
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
+ wxCHECK_MSG( node, -1, _T("invalid column index in GetImage()") );
+
+ wxListItemData *item = node->GetData();
+ return item->GetImage();
+}
+
+wxListItemAttr *wxListLineData::GetAttr() const
+{
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") );
+
+ wxListItemData *item = node->GetData();
+ return item->GetAttr();
+}
+
+void wxListLineData::SetAttr(wxListItemAttr *attr)
+{
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ wxCHECK_RET( node, _T("invalid column index in SetAttr()") );
+
+ wxListItemData *item = node->GetData();
+ item->SetAttr(attr);
+}
+
+bool wxListLineData::SetAttributes(wxDC *dc,
+ const wxListItemAttr *attr,
+ bool highlighted)
+{
+ wxWindow *listctrl = m_owner->GetParent();
+
+ // fg colour
+
+ // don't use foreground colour for drawing highlighted items - this might
+ // make them completely invisible (and there is no way to do bit
+ // arithmetics on wxColour, unfortunately)
+ wxColour colText;
+ if ( highlighted )
+ {
+ colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+ }
+ else
+ {
+ if ( attr && attr->HasTextColour() )
+ {
+ colText = attr->GetTextColour();
+ }
+ else
+ {
+ colText = listctrl->GetForegroundColour();
+ }
+ }
+
+ dc->SetTextForeground(colText);
+
+ // font
+ wxFont font;
+ if ( attr && attr->HasFont() )
+ {
+ font = attr->GetFont();
+ }
+ else
+ {
+ font = listctrl->GetFont();
+ }
+
+ dc->SetFont(font);
+
+ // bg colour
+ bool hasBgCol = attr && attr->HasBackgroundColour();
+ if ( highlighted || hasBgCol )
+ {
+ if ( highlighted )
+ {
+ dc->SetBrush( *m_owner->GetHighlightBrush() );
+ }
+ else
+ {
+ dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID));
+ }
+
+ dc->SetPen( *wxTRANSPARENT_PEN );
+
+ return true;
+ }
+
+ return false;
+}
+
+void wxListLineData::Draw( wxDC *dc )
+{
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ wxCHECK_RET( node, _T("no subitems at all??") );
+
+ bool highlighted = IsHighlighted();
+
+ wxListItemAttr *attr = GetAttr();
+
+ if ( SetAttributes(dc, attr, highlighted) )
+ {
+ dc->DrawRectangle( m_gi->m_rectHighlight );
+ }
+
+ // just for debugging to better see where the items are
+#if 0
+ dc->SetPen(*wxRED_PEN);
+ dc->SetBrush(*wxTRANSPARENT_BRUSH);
+ dc->DrawRectangle( m_gi->m_rectAll );
+ dc->SetPen(*wxGREEN_PEN);
+ dc->DrawRectangle( m_gi->m_rectIcon );
+#endif // 0
+
+ wxListItemData *item = node->GetData();
+ if (item->HasImage())
+ {
+ // centre the image inside our rectangle, this looks nicer when items
+ // ae aligned in a row
+ const wxRect& rectIcon = m_gi->m_rectIcon;
+
+ m_owner->DrawImage(item->GetImage(), dc, rectIcon.x, rectIcon.y);
+ }
+
+ if (item->HasText())
+ {
+ const wxRect& rectLabel = m_gi->m_rectLabel;
+
+ wxDCClipper clipper(*dc, rectLabel);
+ dc->DrawText(item->GetText(), rectLabel.x, rectLabel.y);
+ }
+}
+
+void wxListLineData::DrawInReportMode( wxDC *dc,
+ const wxRect& rect,
+ const wxRect& rectHL,
+ bool highlighted )
+{
+ // TODO: later we should support setting different attributes for
+ // different columns - to do it, just add "col" argument to
+ // GetAttr() and move these lines into the loop below
+ wxListItemAttr *attr = GetAttr();
+ if ( SetAttributes(dc, attr, highlighted) )
+ {
+ dc->DrawRectangle( rectHL );
+ }
+
+ wxCoord x = rect.x + HEADER_OFFSET_X,
+ y = rect.y + (LINE_SPACING + EXTRA_HEIGHT) / 2;
+
+ size_t col = 0;
+ for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+ node;
+ node = node->GetNext(), col++ )
+ {
+ wxListItemData *item = node->GetData();
+
+ int width = m_owner->GetColumnWidth(col);
+ int xOld = x;
+ x += width;
+
+ if ( item->HasImage() )
+ {
+ int ix, iy;
+ m_owner->DrawImage( item->GetImage(), dc, xOld, y );
+ m_owner->GetImageSize( item->GetImage(), ix, iy );
+
+ ix += IMAGE_MARGIN_IN_REPORT_MODE;
+
+ xOld += ix;
+ width -= ix;
+ }
+
+ wxDCClipper clipper(*dc, xOld, y, width - 8, rect.height);
+
+ if ( item->HasText() )
+ {
+ DrawTextFormatted(dc, item->GetText(), col, xOld, y, width - 8);
+ }
+ }
+}
+
+void wxListLineData::DrawTextFormatted(wxDC *dc,
+ const wxString &text,
+ int col,
+ int x,
+ int y,
+ int width)
+{
+ wxString drawntext, ellipsis;
+ wxCoord w, h, base_w;
+ wxListItem item;
+
+ // determine if the string can fit inside the current width
+ dc->GetTextExtent(text, &w, &h);
+ if (w <= width)
+ {
+ // it can, draw it using the items alignment
+ m_owner->GetColumn(col, item);
+ switch ( item.GetAlign() )
+ {
+ default:
+ wxFAIL_MSG( _T("unknown list item format") );
+ // fall through
+
+ case wxLIST_FORMAT_LEFT:
+ // nothing to do
+ break;
+
+ case wxLIST_FORMAT_RIGHT:
+ x += width - w;
+ break;
+
+ case wxLIST_FORMAT_CENTER:
+ x += (width - w) / 2;
+ break;
+ }
+
+ dc->DrawText(text, x, y);
+ }
+ else // otherwise, truncate and add an ellipsis if possible
+ {
+ // determine the base width
+ ellipsis = wxString(wxT("..."));
+ dc->GetTextExtent(ellipsis, &base_w, &h);
+
+ // continue until we have enough space or only one character left
+ wxCoord w_c, h_c;
+ size_t len = text.Length();
+ drawntext = text.Left(len);
+ while (len > 1)
+ {
+ dc->GetTextExtent(drawntext.Last(), &w_c, &h_c);
+ drawntext.RemoveLast();
+ len--;
+ w -= w_c;
+ if (w + base_w <= width)
+ break;
+ }
+
+ // if still not enough space, remove ellipsis characters
+ while (ellipsis.Length() > 0 && w + base_w > width)
+ {
+ ellipsis = ellipsis.Left(ellipsis.Length() - 1);
+ dc->GetTextExtent(ellipsis, &base_w, &h);
+ }
+
+ // now draw the text
+ dc->DrawText(drawntext, x, y);
+ dc->DrawText(ellipsis, x + w, y);
+ }
+}
+
+bool wxListLineData::Highlight( bool on )
+{
+ wxCHECK_MSG( !IsVirtual(), false, _T("unexpected call to Highlight") );
+
+ if ( on == m_highlighted )
+ return false;
+
+ m_highlighted = on;
+
+ return true;
+}
+
+void wxListLineData::ReverseHighlight( void )
+{
+ Highlight(!IsHighlighted());
+}
+
+//-----------------------------------------------------------------------------
+// wxListHeaderWindow
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxListHeaderWindow,wxWindow)
+
+BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
+ EVT_PAINT (wxListHeaderWindow::OnPaint)
+ EVT_MOUSE_EVENTS (wxListHeaderWindow::OnMouse)
+ EVT_SET_FOCUS (wxListHeaderWindow::OnSetFocus)
+END_EVENT_TABLE()
+
+void wxListHeaderWindow::Init()
+{
+ m_currentCursor = (wxCursor *) NULL;
+ m_isDragging = false;
+ m_dirty = false;
+}
+
+wxListHeaderWindow::wxListHeaderWindow()
+{
+ Init();
+
+ m_owner = (wxListMainWindow *) NULL;
+ m_resizeCursor = (wxCursor *) NULL;
+}
+
+wxListHeaderWindow::wxListHeaderWindow( wxWindow *win,
+ wxWindowID id,
+ wxListMainWindow *owner,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString &name )
+ : wxWindow( win, id, pos, size, style, name )
+{
+ Init();
+
+ m_owner = owner;
+ m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
+
+#if _USE_VISATTR
+ wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
+ SetOwnForegroundColour( attr.colFg );
+ SetOwnBackgroundColour( attr.colBg );
+ if (!m_hasFont)
+ SetOwnFont( attr.font );
+#else
+ SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+ SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
+ if (!m_hasFont)
+ SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
+#endif
+}
+
+wxListHeaderWindow::~wxListHeaderWindow()
+{
+ delete m_resizeCursor;
+}
+
+#ifdef __WXUNIVERSAL__
+#include "wx/univ/renderer.h"
+#include "wx/univ/theme.h"
+#endif
+
+// shift the DC origin to match the position of the main window horz
+// scrollbar: this allows us to always use logical coords
+void wxListHeaderWindow::AdjustDC(wxDC& dc)
+{
+ int xpix;
+ m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
+
+ int x;
+ m_owner->GetViewStart( &x, NULL );
+
+ // account for the horz scrollbar offset
+ dc.SetDeviceOrigin( -x * xpix, 0 );
+}
+
+void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+ wxPaintDC dc( this );
+
+ PrepareDC( dc );
+ AdjustDC( dc );
+
+ dc.BeginDrawing();
+
+ dc.SetFont( GetFont() );
+
+ // width and height of the entire header window
+ int w, h;
+ GetClientSize( &w, &h );
+ m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
+
+ dc.SetBackgroundMode(wxTRANSPARENT);
+
+ dc.SetTextForeground(GetForegroundColour());
+
+ int x = HEADER_OFFSET_X;
+
+ int numColumns = m_owner->GetColumnCount();
+ wxListItem item;
+ for ( int i = 0; i < numColumns && x < w; i++ )
+ {
+ m_owner->GetColumn( i, item );
+ int wCol = item.m_width;
+
+ // the width of the rect to draw: make it smaller to fit entirely
+ // inside the column rect
+ int cw = wCol - 2;
+
+ wxRendererNative::Get().DrawHeaderButton
+ (
+ this,
+ dc,
+ wxRect(x, HEADER_OFFSET_Y, cw, h - 2),
+ m_parent->IsEnabled() ? 0
+ : wxCONTROL_DISABLED
+ );
+
+ // see if we have enough space for the column label
+
+ // for this we need the width of the text
+ wxCoord wLabel;
+ wxCoord hLabel;
+ dc.GetTextExtent(item.GetText(), &wLabel, &hLabel);
+ wLabel += 2*EXTRA_WIDTH;
+
+ // and the width of the icon, if any
+ static const int MARGIN_BETWEEN_TEXT_AND_ICON = 2;
+ int ix = 0, // init them just to suppress the compiler warnings
+ iy = 0;
+ const int image = item.m_image;
+ wxImageListType *imageList;
+ if ( image != -1 )
+ {
+ imageList = m_owner->m_small_image_list;
+ if ( imageList )
+ {
+ imageList->GetSize(image, ix, iy);
+ wLabel += ix + MARGIN_BETWEEN_TEXT_AND_ICON;
+ }
+ }
+ else
+ {
+ imageList = NULL;
+ }
+
+ // ignore alignment if there is not enough space anyhow
+ int xAligned;
+ switch ( wLabel < cw ? item.GetAlign() : wxLIST_FORMAT_LEFT )
+ {
+ default:
+ wxFAIL_MSG( _T("unknown list item format") );
+ // fall through
+
+ case wxLIST_FORMAT_LEFT:
+ xAligned = x;
+ break;
+
+ case wxLIST_FORMAT_RIGHT:
+ xAligned = x + cw - wLabel;
+ break;
+
+ case wxLIST_FORMAT_CENTER:
+ xAligned = x + (cw - wLabel) / 2;
+ break;
+ }
+
+
+ // if we have an image, draw it on the right of the label
+ if ( imageList )
+ {
+ imageList->Draw
+ (
+ image,
+ dc,
+ xAligned + wLabel - ix - MARGIN_BETWEEN_TEXT_AND_ICON,
+ HEADER_OFFSET_Y + (h - 4 - iy)/2,
+ wxIMAGELIST_DRAW_TRANSPARENT
+ );
+
+ cw -= ix + MARGIN_BETWEEN_TEXT_AND_ICON;
+ }
+
+ // draw the text clipping it so that it doesn't overwrite the column
+ // boundary
+ wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 );
+
+ dc.DrawText( item.GetText(),
+ xAligned + EXTRA_WIDTH, h / 2 - hLabel / 2 ); //HEADER_OFFSET_Y + EXTRA_HEIGHT );
+
+ x += wCol;
+ }
+
+ dc.EndDrawing();
+}
+
+void wxListHeaderWindow::DrawCurrent()
+{
+ int x1 = m_currentX;
+ int y1 = 0;
+ m_owner->ClientToScreen( &x1, &y1 );
+
+ int x2 = m_currentX;
+ int y2 = 0;
+ m_owner->GetClientSize( NULL, &y2 );
+ m_owner->ClientToScreen( &x2, &y2 );
+
+ wxScreenDC dc;
+ dc.SetLogicalFunction( wxINVERT );
+ dc.SetPen( wxPen( *wxBLACK, 2, wxSOLID ) );
+ dc.SetBrush( *wxTRANSPARENT_BRUSH );
+
+ AdjustDC(dc);
+
+ dc.DrawLine( x1, y1, x2, y2 );
+
+ dc.SetLogicalFunction( wxCOPY );
+
+ dc.SetPen( wxNullPen );
+ dc.SetBrush( wxNullBrush );
+}
+
+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 occured
+ 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, 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();
+}
+
+//-----------------------------------------------------------------------------
+// wxListTextCtrl (internal)
+//-----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(wxListTextCtrl,wxTextCtrl)
+ EVT_CHAR (wxListTextCtrl::OnChar)
+ EVT_KEY_UP (wxListTextCtrl::OnKeyUp)
+ EVT_KILL_FOCUS (wxListTextCtrl::OnKillFocus)
+END_EVENT_TABLE()
+
+wxListTextCtrl::wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit)
+ : m_startValue(owner->GetItemText(itemEdit)),
+ m_itemEdited(itemEdit)
+{
+ m_owner = owner;
+ m_finished = false;
+
+ wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
+
+ m_owner->CalcScrolledPosition(rectLabel.x, rectLabel.y,
+ &rectLabel.x, &rectLabel.y);
+
+ (void)Create(owner, wxID_ANY, m_startValue,
+ wxPoint(rectLabel.x-4,rectLabel.y-4),
+ wxSize(rectLabel.width+11,rectLabel.height+8));
+}
+
+void wxListTextCtrl::Finish()
+{
+ if ( !m_finished )
+ {
+ wxPendingDelete.Append(this);
+
+ m_finished = true;
+
+ m_owner->SetFocus();
+ }
+}
+
+bool wxListTextCtrl::AcceptChanges()
+{
+ const wxString value = 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 wxListTextCtrl::OnChar( wxKeyEvent &event )
+{
+ switch ( event.m_keyCode )
+ {
+ case WXK_RETURN:
+ if ( AcceptChanges() )
+ {
+ // Close the text control, changes were accepted
+ Finish();
+ }
+ // else do nothing, do not accept and do not close
+
+ break;
+
+ case WXK_ESCAPE:
+ Finish();
+ m_owner->OnRenameCancelled( m_itemEdited );
+ break;
+
+ default:
+ event.Skip();
+ }
+}
+
+void wxListTextCtrl::OnKeyUp( wxKeyEvent &event )
+{
+ if (m_finished)
+ {
+ event.Skip();
+ return;
+ }
+
+ // auto-grow the textctrl:
+ wxSize parentSize = m_owner->GetSize();
+ wxPoint myPos = GetPosition();
+ wxSize mySize = GetSize();
+ int sx, sy;
+ GetTextExtent(GetValue() + _T("MM"), &sx, &sy);
+ if (myPos.x + sx > parentSize.x)
+ sx = parentSize.x - myPos.x;
+ if (mySize.x > sx)
+ sx = mySize.x;
+ SetSize(sx, wxDefaultCoord);
+
+ event.Skip();
+}
+
+void wxListTextCtrl::OnKillFocus( wxFocusEvent &event )
+{
+ if ( !m_finished )
+ {
+ // 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_current =
+ m_lineLastClicked =
+ 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
+ );
+
+ wxSize sz = size;
+ sz.y = 25;
+
+ 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);
+
+ 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(listctrl->OnGetItemImage(line));
+ 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;
+ int 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
+{
+ if ( IsVirtual() )
+ {
+ return m_selStore.IsSelected(line);
+ }
+ else // !virtual
+ {
+ wxListLineData *ld = GetLine(line);
+ wxCHECK_MSG( ld, false, _T("invalid index in IsHighlighted") );
+
+ return ld->IsHighlighted();
+ }
+}
+
+void wxListMainWindow::HighlightLines( size_t lineFrom,
+ size_t lineTo,
+ bool highlight )
+{
+ if ( IsVirtual() )
+ {
+ wxArrayInt linesChanged;
+ if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight,
+ &linesChanged) )
+ {
+ // meny items changed state, refresh everything
+ RefreshLines(lineFrom, lineTo);
+ }
+ else // only a few items changed state, refresh only them
+ {
+ size_t count = linesChanged.GetCount();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ RefreshLine(linesChanged[n]);
+ }
+ }
+ }
+ else // iterate over all items in non report view
+ {
+ for ( size_t line = lineFrom; line <= lineTo; line++ )
+ {
+ if ( HighlightLine(line, highlight) )
+ {
+ RefreshLine(line);
+ }
+ }
+ }
+}
+
+bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
+{
+ bool changed;
+
+ if ( IsVirtual() )
+ {
+ changed = m_selStore.SelectItem(line, highlight);
+ }
+ else // !virtual
+ {
+ wxListLineData *ld = GetLine(line);
+ wxCHECK_MSG( ld, false, _T("invalid index in HighlightLine") );
+
+ changed = ld->Highlight(highlight);
+ }
+
+ if ( changed )
+ {
+ SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED
+ : wxEVT_COMMAND_LIST_ITEM_DESELECTED );
+ }
+
+ return changed;