// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "listctrl.h"
#pragma implementation "listctrlbase.h"
#endif
#include "wx/textctrl.h"
#endif
-#include "wx/imaglist.h"
-#include "wx/listctrl.h"
-
-#if defined(__WXGTK__)
- #include <gtk/gtk.h>
- #include "wx/gtk/win_gtk.h"
+// under Win32 we always use the native version and also may use the generic
+// one, however some things should be done only if we use only the generic
+// version
+#if defined(__WIN32__) && !defined(__WXUNIVERSAL__)
+ #define HAVE_NATIVE_LISTCTRL
#endif
+// if we have the native control, wx/listctrl.h declares it and not this one
+#ifdef HAVE_NATIVE_LISTCTRL
+ #include "wx/generic/listctrl.h"
+#else // !HAVE_NATIVE_LISTCTRL
+ #include "wx/listctrl.h"
+
+ // if we have a native version, its implementation file does all this
+ IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
+ IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
+ IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
+
+ IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl)
+#endif // HAVE_NATIVE_LISTCTRL/!HAVE_NATIVE_LISTCTRL
+
+#include "wx/selstore.h"
+
+#include "wx/renderer.h"
+
// ----------------------------------------------------------------------------
// events
// ----------------------------------------------------------------------------
// private classes
// ============================================================================
-// ----------------------------------------------------------------------------
-// wxSelectionStore
-// ----------------------------------------------------------------------------
-
-int CMPFUNC_CONV wxSizeTCmpFn(size_t n1, size_t n2) { return n1 - n2; }
-
-WX_DEFINE_SORTED_EXPORTED_ARRAY_LONG(size_t, wxIndexArray);
-
-// this class is used to store the selected items in the virtual list control
-// (but it is not tied to list control and so can be used with other controls
-// such as wxListBox in wxUniv)
-//
-// the idea is to make it really smart later (i.e. store the selections as an
-// array of ranes + individual items) but, as I don't have time to do it now
-// (this would require writing code to merge/break ranges and much more) keep
-// it simple but define a clean interface to it which allows it to be made
-// smarter later
-class WXDLLEXPORT wxSelectionStore
-{
-public:
- wxSelectionStore() : m_itemsSel(wxSizeTCmpFn) { Init(); }
-
- // set the total number of items we handle
- void SetItemCount(size_t count) { m_count = count; }
-
- // special case of SetItemCount(0)
- void Clear() { m_itemsSel.Clear(); m_count = 0; }
-
- // must be called when a new item is inserted/added
- void OnItemAdd(size_t item) { wxFAIL_MSG( _T("TODO") ); }
-
- // must be called when an item is deleted
- void OnItemDelete(size_t item);
-
- // select one item, use SelectRange() insted if possible!
- //
- // returns true if the items selection really changed
- bool SelectItem(size_t item, bool select = TRUE);
-
- // select the range of items
- //
- // return true and fill the itemsChanged array with the indices of items
- // which have changed state if "few" of them did, otherwise return false
- // (meaning that too many items changed state to bother counting them
- // individually)
- bool SelectRange(size_t itemFrom, size_t itemTo,
- bool select = TRUE,
- wxArrayInt *itemsChanged = NULL);
-
- // return true if the given item is selected
- bool IsSelected(size_t item) const;
-
- // return the total number of selected items
- size_t GetSelectedCount() const
- {
- return m_defaultState ? m_count - m_itemsSel.GetCount()
- : m_itemsSel.GetCount();
- }
-
-private:
- // (re)init
- void Init() { m_defaultState = FALSE; }
-
- // the total number of items we handle
- size_t m_count;
-
- // the default state: normally, FALSE (i.e. off) but maybe set to TRUE if
- // there are more selected items than non selected ones - this allows to
- // handle selection of all items efficiently
- bool m_defaultState;
-
- // the array of items whose selection state is different from default
- wxIndexArray m_itemsSel;
-
- DECLARE_NO_COPY_CLASS(wxSelectionStore)
-};
-
//-----------------------------------------------------------------------------
// wxListItemData (internal)
//-----------------------------------------------------------------------------
#include "wx/listimpl.cpp"
WX_DEFINE_LIST(wxListItemDataList);
-class WXDLLEXPORT wxListLineData
+class wxListLineData
{
public:
// the list of subitems: only may have more than one item in report mode
public:
wxListLineData(wxListMainWindow *owner);
- ~wxListLineData() { delete m_gi; }
+ ~wxListLineData()
+ {
+ WX_CLEAR_LIST(wxListItemDataList, m_items);
+ delete m_gi;
+ }
// are we in report mode?
inline bool InReportView() const;
const wxListItemAttr *attr,
bool highlight);
+ // draw the text on the DC with the correct justification; also add an
+ // ellipsis if the text is too large to fit in the current width
+ void DrawTextFormatted(wxDC *dc, const wxString &text, int col, int x, int y, int width);
+
// these are only used by GetImage/SetImage above, we don't support images
// with subitems at the public API level yet
void SetImage( int index, int image );
const wxPoint &pos = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = 0,
- const wxString &name = "wxlistctrlcolumntitles" );
+ const wxString &name = wxT("wxlistctrlcolumntitles") );
virtual ~wxListHeaderWindow();
- void DoDrawRect( wxDC *dc, int x, int y, int w, int h );
void DrawCurrent();
void AdjustDC(wxDC& dc);
// common part of all ctors
void Init();
- void SendListEvent(wxEventType type, wxPoint pos);
+ // generate and process the list event of the given type, return true if
+ // it wasn't vetoed, i.e. if we should proceed
+ bool SendListEvent(wxEventType type, wxPoint pos);
DECLARE_DYNAMIC_CLASS(wxListHeaderWindow)
DECLARE_EVENT_TABLE()
#include "wx/listimpl.cpp"
WX_DEFINE_LIST(wxListHeaderDataList);
-class WXDLLEXPORT wxListMainWindow : public wxScrolledWindow
+class wxListMainWindow : public wxScrolledWindow
{
public:
wxListMainWindow();
void OnRenameTimer();
bool OnRenameAccept(size_t itemEdit, const wxString& value);
+ void OnRenameCancelled(size_t itemEdit);
void OnMouse( wxMouseEvent &event );
void GetImageSize( int index, int &width, int &height ) const;
int GetTextLength( const wxString &s ) const;
- void SetImageList( wxImageList *imageList, int which );
+ void SetImageList( wxImageListType *imageList, int which );
void SetItemSpacing( int spacing, bool isSmall = FALSE );
int GetItemSpacing( bool isSmall = FALSE );
wxColour *m_highlightColour;
int m_xScroll,
m_yScroll;
- wxImageList *m_small_image_list;
- wxImageList *m_normal_image_list;
+ wxImageListType *m_small_image_list;
+ wxImageListType *m_normal_image_list;
int m_small_spacing;
int m_normal_spacing;
bool m_hasFocus;
DECLARE_DYNAMIC_CLASS(wxListMainWindow)
DECLARE_EVENT_TABLE()
+
+ friend class wxGenericListCtrl;
};
// ============================================================================
// implementation
// ============================================================================
-// ----------------------------------------------------------------------------
-// wxSelectionStore
-// ----------------------------------------------------------------------------
-
-bool wxSelectionStore::IsSelected(size_t item) const
-{
- bool isSel = m_itemsSel.Index(item) != wxNOT_FOUND;
-
- // if the default state is to be selected, being in m_itemsSel means that
- // the item is not selected, so we have to inverse the logic
- return m_defaultState ? !isSel : isSel;
-}
-
-bool wxSelectionStore::SelectItem(size_t item, bool select)
-{
- // search for the item ourselves as like this we get the index where to
- // insert it later if needed, so we do only one search in the array instead
- // of two (adding item to a sorted array requires a search)
- size_t index = m_itemsSel.IndexForInsert(item);
- bool isSel = index < m_itemsSel.GetCount() && m_itemsSel[index] == item;
-
- if ( select != m_defaultState )
- {
- if ( !isSel )
- {
- m_itemsSel.AddAt(item, index);
-
- return TRUE;
- }
- }
- else // reset to default state
- {
- if ( isSel )
- {
- m_itemsSel.RemoveAt(index);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-bool wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo,
- bool select,
- wxArrayInt *itemsChanged)
-{
- // 100 is hardcoded but it shouldn't matter much: the important thing is
- // that we don't refresh everything when really few (e.g. 1 or 2) items
- // change state
- static const size_t MANY_ITEMS = 100;
-
- wxASSERT_MSG( itemFrom <= itemTo, _T("should be in order") );
-
- // are we going to have more [un]selected items than the other ones?
- if ( itemTo - itemFrom > m_count/2 )
- {
- if ( select != m_defaultState )
- {
- // the default state now becomes the same as 'select'
- m_defaultState = select;
-
- // so all the old selections (which had state select) shouldn't be
- // selected any more, but all the other ones should
- wxIndexArray selOld = m_itemsSel;
- m_itemsSel.Empty();
-
- // TODO: it should be possible to optimize the searches a bit
- // knowing the possible range
-
- size_t item;
- for ( item = 0; item < itemFrom; item++ )
- {
- if ( selOld.Index(item) == wxNOT_FOUND )
- m_itemsSel.Add(item);
- }
-
- for ( item = itemTo + 1; item < m_count; item++ )
- {
- if ( selOld.Index(item) == wxNOT_FOUND )
- m_itemsSel.Add(item);
- }
-
- // many items (> half) changed state
- itemsChanged = NULL;
- }
- else // select == m_defaultState
- {
- // get the inclusive range of items between itemFrom and itemTo
- size_t count = m_itemsSel.GetCount(),
- start = m_itemsSel.IndexForInsert(itemFrom),
- end = m_itemsSel.IndexForInsert(itemTo);
-
- if ( start == count || m_itemsSel[start] < itemFrom )
- {
- start++;
- }
-
- if ( end == count || m_itemsSel[end] > itemTo )
- {
- end--;
- }
-
- if ( start <= end )
- {
- // delete all of them (from end to avoid changing indices)
- for ( int i = end; i >= (int)start; i-- )
- {
- if ( itemsChanged )
- {
- if ( itemsChanged->GetCount() > MANY_ITEMS )
- {
- // stop counting (see comment below)
- itemsChanged = NULL;
- }
- else
- {
- itemsChanged->Add(m_itemsSel[i]);
- }
- }
-
- m_itemsSel.RemoveAt(i);
- }
- }
- }
- }
- else // "few" items change state
- {
- if ( itemsChanged )
- {
- itemsChanged->Empty();
- }
-
- // just add the items to the selection
- for ( size_t item = itemFrom; item <= itemTo; item++ )
- {
- if ( SelectItem(item, select) && itemsChanged )
- {
- itemsChanged->Add(item);
-
- if ( itemsChanged->GetCount() > MANY_ITEMS )
- {
- // stop counting them, we'll just eat gobs of memory
- // for nothing at all - faster to refresh everything in
- // this case
- itemsChanged = NULL;
- }
- }
- }
- }
-
- // we set it to NULL if there are many items changing state
- return itemsChanged != NULL;
-}
-
-void wxSelectionStore::OnItemDelete(size_t item)
-{
- size_t count = m_itemsSel.GetCount(),
- i = m_itemsSel.IndexForInsert(item);
-
- if ( i < count && m_itemsSel[i] == item )
- {
- // this item itself was in m_itemsSel, remove it from there
- m_itemsSel.RemoveAt(i);
-
- count--;
- }
-
- // and adjust the index of all which follow it
- while ( i < count )
- {
- // all following elements must be greater than the one we deleted
- wxASSERT_MSG( m_itemsSel[i] > item, _T("logic error") );
-
- m_itemsSel[i++]--;
- }
-}
-
//-----------------------------------------------------------------------------
// wxListItemData
//-----------------------------------------------------------------------------
wxListLineData::wxListLineData( wxListMainWindow *owner )
{
m_owner = owner;
- m_items.DeleteContents( TRUE );
if ( InReportView() )
{
void wxListLineData::CalculateSize( wxDC *dc, int spacing )
{
- wxListItemDataList::Node *node = m_items.GetFirst();
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
wxCHECK_RET( node, _T("no subitems at all??") );
wxListItemData *item = node->GetData();
}
void wxListLineData::SetPosition( int x, int y,
- int window_width,
+ int WXUNUSED(window_width),
int spacing )
{
- wxListItemDataList::Node *node = m_items.GetFirst();
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
wxCHECK_RET( node, _T("no subitems at all??") );
wxListItemData *item = node->GetData();
void wxListLineData::SetItem( int index, const wxListItem &info )
{
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
wxCHECK_RET( node, _T("invalid column index in SetItem") );
wxListItemData *item = node->GetData();
void wxListLineData::GetItem( int index, wxListItem &info )
{
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
if (node)
{
wxListItemData *item = node->GetData();
{
wxString s;
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
if (node)
{
wxListItemData *item = node->GetData();
void wxListLineData::SetText( int index, const wxString s )
{
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
if (node)
{
wxListItemData *item = node->GetData();
void wxListLineData::SetImage( int index, int image )
{
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
wxCHECK_RET( node, _T("invalid column index in SetImage()") );
wxListItemData *item = node->GetData();
int wxListLineData::GetImage( int index ) const
{
- wxListItemDataList::Node *node = m_items.Item( index );
+ wxListItemDataList::compatibility_iterator node = m_items.Item( index );
wxCHECK_MSG( node, -1, _T("invalid column index in GetImage()") );
wxListItemData *item = node->GetData();
wxListItemAttr *wxListLineData::GetAttr() const
{
- wxListItemDataList::Node *node = m_items.GetFirst();
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") );
wxListItemData *item = node->GetData();
void wxListLineData::SetAttr(wxListItemAttr *attr)
{
- wxListItemDataList::Node *node = m_items.GetFirst();
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
wxCHECK_RET( node, _T("invalid column index in SetAttr()") );
wxListItemData *item = node->GetData();
void wxListLineData::Draw( wxDC *dc )
{
- wxListItemDataList::Node *node = m_items.GetFirst();
+ wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
wxCHECK_RET( node, _T("no subitems at all??") );
bool highlighted = IsHighlighted();
y = rect.y + (LINE_SPACING + EXTRA_HEIGHT) / 2;
size_t col = 0;
- for ( wxListItemDataList::Node *node = m_items.GetFirst();
+ for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
node;
node = node->GetNext(), col++ )
{
width -= ix;
}
- wxDCClipper clipper(*dc, xOld, y, width, rect.height);
+ wxDCClipper clipper(*dc, xOld, y, width - 8, rect.height);
if ( item->HasText() )
{
- dc->DrawText( item->GetText(), xOld, y );
+ 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);
}
}
delete m_resizeCursor;
}
-void wxListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h )
-{
-#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
- GtkStateType state = m_parent->IsEnabled() ? GTK_STATE_NORMAL
- : GTK_STATE_INSENSITIVE;
-
- x = dc->XLOG2DEV( x );
-
- gtk_paint_box (m_wxwindow->style, GTK_PIZZA(m_wxwindow)->bin_window,
- state, GTK_SHADOW_OUT,
- (GdkRectangle*) NULL, m_wxwindow,
- (char *)"button", // const_cast
- x-1, y-1, w+2, h+2);
-#elif defined( __WXMAC__ )
- const int m_corner = 1;
-
- dc->SetBrush( *wxTRANSPARENT_BRUSH );
-
- dc->SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNSHADOW ) , 1 , wxSOLID ) );
- dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer)
- dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer)
-
- wxPen pen( wxColour( 0x88 , 0x88 , 0x88 ), 1, wxSOLID );
-
- dc->SetPen( pen );
- dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner)
- dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner)
-
- dc->SetPen( *wxWHITE_PEN );
- dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer)
- dc->DrawRectangle( x, y, 1, h ); // left (outer)
- dc->DrawLine( x, y+h-1, x+1, y+h-1 );
- dc->DrawLine( x+w-1, y, x+w-1, y+1 );
-#else // !GTK, !Mac
- const int m_corner = 1;
-
- dc->SetBrush( *wxTRANSPARENT_BRUSH );
-
- dc->SetPen( *wxBLACK_PEN );
- dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer)
- dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer)
-
- wxPen pen( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID );
-
- dc->SetPen( pen );
- dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner)
- dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner)
-
- dc->SetPen( *wxWHITE_PEN );
- dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer)
- dc->DrawRectangle( x, y, 1, h ); // left (outer)
- dc->DrawLine( x, y+h-1, x+1, y+h-1 );
- dc->DrawLine( x+w-1, y, x+w-1, y+1 );
+#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::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
-#if defined(__WXGTK__)
- wxClientDC dc( this );
-#else
wxPaintDC dc( this );
-#endif
PrepareDC( dc );
AdjustDC( dc );
// inside the column rect
int cw = wCol - 2;
- dc.SetPen( *wxWHITE_PEN );
-
- DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 );
-
- // if we have an image, draw it on the right of the label
- int image = item.m_image;
+ 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;
+ dc.GetTextExtent(item.GetText(), &wLabel, NULL);
+ 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 )
{
- wxImageList *imageList = m_owner->m_small_image_list;
+ imageList = m_owner->m_small_image_list;
if ( imageList )
{
- int ix, iy;
imageList->GetSize(image, ix, iy);
+ wLabel += ix + MARGIN_BETWEEN_TEXT_AND_ICON;
+ }
+ }
+ else
+ {
+ imageList = NULL;
+ }
- imageList->Draw
- (
- image,
- dc,
- x + cw - ix - 1,
- HEADER_OFFSET_Y + (h - 4 - iy)/2,
- wxIMAGELIST_DRAW_TRANSPARENT
- );
+ // 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
- cw -= ix + 2;
- }
- //else: ignore the column image
+ 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
wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 );
dc.DrawText( item.GetText(),
- x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT );
+ xAligned + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT );
x += wCol;
}
if (m_isDragging)
{
- SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING,
- event.GetPosition());
+ SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition());
// we don't draw the line beyond our window, but we allow dragging it
// there
m_isDragging = FALSE;
m_dirty = TRUE;
m_owner->SetColumnWidth( m_column, m_currentX - m_minX );
- SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG,
- event.GetPosition());
+ SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition());
}
else
{
{
if (hit_border && event.LeftDown())
{
- m_isDragging = TRUE;
- m_currentX = x;
- DrawCurrent();
- CaptureMouse();
- SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
- event.GetPosition());
+ if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
+ event.GetPosition()) )
+ {
+ m_isDragging = TRUE;
+ m_currentX = x;
+ DrawCurrent();
+ CaptureMouse();
+ }
+ //else: column resizing was vetoed by the user code
}
else // click on a column
{
m_owner->SetFocus();
}
-void wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos)
+bool wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos)
{
wxWindow *parent = GetParent();
wxListEvent le( type, parent->GetId() );
le.m_pointDrag.y -= GetSize().y;
le.m_col = m_column;
- parent->GetEventHandler()->ProcessEvent( le );
+ return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
}
//-----------------------------------------------------------------------------
case WXK_ESCAPE:
Finish();
+ m_owner->OnRenameCancelled( m_itemEdited );
break;
default:
{
if ( !m_finished )
{
- (void)AcceptChanges();
-
+ // We must finish regardless of success, otherwise we'll get focus problems
Finish();
+
+ if ( !AcceptChanges() )
+ m_owner->OnRenameCancelled( m_itemEdited );
}
-
+
event.Skip();
}
void wxListMainWindow::Init()
{
- m_columns.DeleteContents( TRUE );
m_dirty = TRUE;
m_countVirt = 0;
m_lineFrom =
m_headerWidth =
m_lineHeight = 0;
- m_small_image_list = (wxImageList *) NULL;
- m_normal_image_list = (wxImageList *) NULL;
+ m_small_image_list = (wxImageListType *) NULL;
+ m_normal_image_list = (wxImageListType *) NULL;
m_small_spacing = 30;
m_normal_spacing = 40;
wxListMainWindow::~wxListMainWindow()
{
DoDeleteAllItems();
+ WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
delete m_highlightBrush;
delete m_highlightUnfocusedBrush;
if ( y < SCROLL_UNIT_Y )
y = SCROLL_UNIT_Y;
- y += EXTRA_HEIGHT;
+ 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;
}
{
if ( HasFlag(wxLC_REPORT) )
{
- size_t visibleFrom;
- GetVisibleLinesRange(&visibleFrom, NULL);
+ size_t visibleFrom, visibleTo;
+ GetVisibleLinesRange(&visibleFrom, &visibleTo);
if ( lineFrom < visibleFrom )
lineFrom = visibleFrom;
+ else if ( lineFrom > visibleTo )
+ return;
wxRect rect;
rect.x = 0;
rect.y = GetLineY(lineFrom);
+ CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
wxSize size = GetClientSize();
rect.width = size.x;
// refresh till the bottom of the window
rect.height = size.y - rect.y;
- CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
RefreshRect( rect );
}
else // !report
wxPen pen(GetRuleColour(), 1, wxSOLID);
wxSize clientSize = GetClientSize();
- for ( size_t i = visibleFrom; i <= visibleTo; i++ )
+ // Don't draw the first one
+ for ( size_t i = visibleFrom+1; i <= visibleTo; i++ )
{
dc.SetPen(pen);
dc.SetBrush( *wxTRANSPARENT_BRUSH );
}
// Draw last horizontal rule
- if ( visibleTo > visibleFrom )
+ if ( visibleTo == GetItemCount() - 1 )
{
dc.SetPen(pen);
dc.SetBrush( *wxTRANSPARENT_BRUSH );
- dc.DrawLine(0 - dev_x, m_lineTo*lineHeight,
- clientSize.x - dev_x , m_lineTo*lineHeight );
+ dc.DrawLine(0 - dev_x, (m_lineTo+1)*lineHeight,
+ clientSize.x - dev_x , (m_lineTo+1)*lineHeight );
}
}
int col = 0;
wxRect firstItemRect;
wxRect lastItemRect;
- GetItemRect(0, firstItemRect);
- GetItemRect(GetItemCount() - 1, lastItemRect);
+ GetItemRect(visibleFrom, firstItemRect);
+ GetItemRect(visibleTo, lastItemRect);
int x = firstItemRect.GetX();
dc.SetPen(pen);
dc.SetBrush(* wxTRANSPARENT_BRUSH);
{
int colWidth = GetColumnWidth(col);
x += colWidth;
- dc.DrawLine(x - dev_x, firstItemRect.GetY() - 1 - dev_y,
- x - dev_x, lastItemRect.GetBottom() + 1 - dev_y);
+ dc.DrawLine(x - dev_x - 2, firstItemRect.GetY() - 1 - dev_y,
+ x - dev_x - 2, lastItemRect.GetBottom() + 1 - dev_y);
}
}
}
le.IsAllowed();
}
+#ifdef __VMS__ // Ignore unreacheable code
+# pragma message disable initnotreach
+#endif
+
+void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
+{
+ // wxMSW seems not to notify the program about
+ // cancelled label edits.
+ return;
+
+ // let owner know that the edit was cancelled
+ wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
+
+ // These only exist for wxTreeCtrl, which should probably be changed
+ // le.m_editCancelled = TRUE;
+ // le.m_label = wxEmptyString;
+
+ le.SetEventObject( GetParent() );
+ le.m_itemIndex = itemEdit;
+
+ wxListLineData *data = GetLine(itemEdit);
+ wxCHECK_RET( data, _T("invalid index in OnRenameCancelled()") );
+
+ data->GetItem( 0, le.m_item );
+
+ GetEventHandler()->ProcessEvent( le );
+}
+#ifdef __VMS__
+# pragma message enable initnotreach
+#endif
+
void wxListMainWindow::OnMouse( wxMouseEvent &event )
{
event.SetEventObject( GetParent() );
wxListEvent le( command, GetParent()->GetId() );
le.SetEventObject( GetParent() );
+ le.m_itemIndex = current;
le.m_pointDrag = m_dragStart;
GetParent()->GetEventHandler()->ProcessEvent( le );
m_renameTimer->Stop();
m_lastOnSame = FALSE;
-#ifdef __WXGTK__
- // FIXME: wxGTK generates bad sequence of events prior to doubleclick
- // ("down, up, down, double, up" while other ports
- // do "down, up, double, up"). We have to have this hack
- // in place till somebody fixes wxGTK...
- if ( current == m_lineBeforeLastClicked )
-#else
if ( current == m_lineLastClicked )
-#endif
{
SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetParent()->GetId() );
le.m_itemIndex = m_current;
GetLine(m_current)->GetItem( 0, le.m_item );
- le.m_code = (int)event.KeyCode();
+ le.m_code = event.GetKeyCode();
le.SetEventObject( parent );
parent->GetEventHandler()->ProcessEvent( le );
}
ke.SetEventObject( parent );
if (parent->GetEventHandler()->ProcessEvent( ke )) return;
- if (event.KeyCode() == WXK_TAB)
+ if (event.GetKeyCode() == WXK_TAB)
{
wxNavigationKeyEvent nevent;
nevent.SetWindowChange( event.ControlDown() );
return;
}
- switch (event.KeyCode())
+ switch (event.GetKeyCode())
{
case WXK_UP:
if ( m_current > 0 )
return lw + AUTOSIZE_COL_MARGIN;
}
-void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
+void wxListMainWindow::SetImageList( wxImageListType *imageList, int which )
{
m_dirty = TRUE;
{
m_small_image_list = imageList;
m_small_spacing = width + 14;
+ m_lineHeight = 0; // ensure that the line height will be recalc'd
}
}
void wxListMainWindow::SetColumn( int col, wxListItem &item )
{
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
wxCHECK_RET( node, _T("invalid column index in SetColumn") );
if ( headerWin )
headerWin->m_dirty = TRUE;
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
wxCHECK_RET( node, _T("no column?") );
wxListHeaderData *column = node->GetData();
for ( size_t i = 0; i < count; i++ )
{
wxListLineData *line = GetLine(i);
- wxListItemDataList::Node *n = line->m_items.Item( col );
+ wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
wxCHECK_RET( n, _T("no subitem?") );
void wxListMainWindow::GetColumn( int col, wxListItem &item ) const
{
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
wxCHECK_RET( node, _T("invalid column index in GetColumn") );
wxListHeaderData *column = node->GetData();
int wxListMainWindow::GetColumnWidth( int col ) const
{
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
wxCHECK_MSG( node, 0, _T("invalid column index") );
wxListHeaderData *column = node->GetData();
line->SetItem( item.m_col, item );
}
- if ( InReportView() )
- {
- // just refresh the line to show the new value of the text/image
- RefreshLine((size_t)id);
- }
- else // !report
- {
- // refresh everything (resulting in horrible flicker - FIXME!)
- m_dirty = TRUE;
- }
+ // update the item on screen
+ wxRect rectItem;
+ GetItemRect(id, rectItem);
+ RefreshRect(rectItem);
}
void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
void wxListMainWindow::DeleteColumn( int col )
{
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") );
m_dirty = TRUE;
- m_columns.DeleteNode( node );
+ delete node->GetData();
+ m_columns.Erase( node );
+
+ if ( !IsVirtual() )
+ {
+ // update all the items
+ for ( size_t i = 0; i < m_lines.GetCount(); i++ )
+ {
+ wxListLineData * const line = GetLine(i);
+ wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
+ delete n->GetData();
+ line->m_items.Erase(n);
+ }
+ }
// invalidate it as it has to be recalculated
m_headerWidth = 0;
void wxListMainWindow::DeleteEverything()
{
- m_columns.Clear();
+ WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
DeleteAllItems();
}
int mode = 0;
if ( HasFlag(wxLC_REPORT) )
+ {
mode = wxLC_REPORT;
+ ResetVisibleLinesRange();
+ }
else if ( HasFlag(wxLC_LIST) )
mode = wxLC_LIST;
else if ( HasFlag(wxLC_ICON) )
m_lines.Insert( line, id );
m_dirty = TRUE;
+
+ SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM);
+
RefreshLines(id, GetItemCount() - 1);
}
{
if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
item.m_width = GetTextLength( item.m_text );
+
wxListHeaderData *column = new wxListHeaderData( item );
- if ((col >= 0) && (col < (int)m_columns.GetCount()))
+ bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
+ if ( insert )
{
- wxListHeaderDataList::Node *node = m_columns.Item( col );
+ wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
m_columns.Insert( node, column );
}
else
m_columns.Append( column );
}
+ if ( !IsVirtual() )
+ {
+ // update all the items
+ for ( size_t i = 0; i < m_lines.GetCount(); i++ )
+ {
+ wxListLineData * const line = GetLine(i);
+ wxListItemData * const data = new wxListItemData(this);
+ if ( insert )
+ line->m_items.Insert(col, data);
+ else
+ line->m_items.Append(data);
+ }
+ }
+
// invalidate it as it has to be recalculated
m_headerWidth = 0;
}
*to = m_lineTo;
}
-// -------------------------------------------------------------------------------------
-// wxListItem
-// -------------------------------------------------------------------------------------
-
-#if !defined(__WIN32__)
-IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
-#endif
-
// -------------------------------------------------------------------------------------
// wxGenericListCtrl
// -------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl)
-#if !defined(__WIN32__)
-IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
-
-IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
-#endif
-
BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl)
EVT_SIZE(wxGenericListCtrl::OnSize)
- EVT_IDLE(wxGenericListCtrl::OnIdle)
END_EVENT_TABLE()
-#if !defined(__WXMSW__) || defined(__WIN16__) || defined(__WXUNIVERSAL__)
-/*
- * wxListCtrl has to be a real class or we have problems with
- * the run-time information.
- */
-
-IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl)
-#endif
-
wxGenericListCtrl::wxGenericListCtrl()
{
- m_imageListNormal = (wxImageList *) NULL;
- m_imageListSmall = (wxImageList *) NULL;
- m_imageListState = (wxImageList *) NULL;
+ m_imageListNormal = (wxImageListType *) NULL;
+ m_imageListSmall = (wxImageListType *) NULL;
+ m_imageListState = (wxImageListType *) NULL;
m_ownsImageListNormal =
m_ownsImageListSmall =
{
m_imageListNormal =
m_imageListSmall =
- m_imageListState = (wxImageList *) NULL;
+ m_imageListState = (wxImageListType *) NULL;
m_ownsImageListNormal =
m_ownsImageListSmall =
m_ownsImageListState = FALSE;
return FALSE;
// don't create the inner window with the border
- style &= ~wxSUNKEN_BORDER;
+ style &= ~wxBORDER_MASK;
m_mainWin = new wxListMainWindow( this, -1, wxPoint(0,0), size, style );
bool wxGenericListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const
{
m_mainWin->GetItemRect( item, rect );
+ if ( m_mainWin->HasHeader() )
+ rect.y += HEADER_HEIGHT + 1;
return TRUE;
}
long wxGenericListCtrl::GetTopItem() const
{
- return 0;
+ size_t top;
+ m_mainWin->GetVisibleLinesRange(&top, NULL);
+ return (long)top;
}
long wxGenericListCtrl::GetNextItem( long item, int geom, int state ) const
return m_mainWin->GetNextItem( item, geom, state );
}
-wxImageList *wxGenericListCtrl::GetImageList(int which) const
+wxImageListType *wxGenericListCtrl::GetImageList(int which) const
{
if (which == wxIMAGE_LIST_NORMAL)
{
{
return m_imageListState;
}
- return (wxImageList *) NULL;
+ return (wxImageListType *) NULL;
}
-void wxGenericListCtrl::SetImageList( wxImageList *imageList, int which )
+void wxGenericListCtrl::SetImageList( wxImageListType *imageList, int which )
{
if ( which == wxIMAGE_LIST_NORMAL )
{
m_mainWin->SetImageList( imageList, which );
}
-void wxGenericListCtrl::AssignImageList(wxImageList *imageList, int which)
+void wxGenericListCtrl::AssignImageList(wxImageListType *imageList, int which)
{
SetImageList(imageList, which);
if ( which == wxIMAGE_LIST_NORMAL )
// if we hadn't had header before and have it now we need to relayout the
// window
- if ( GetColumnCount() == 1 )
+ if ( GetColumnCount() == 1 && m_mainWin->HasHeader() )
{
ResizeReportView(TRUE /* have header */);
}
}
}
-void wxGenericListCtrl::OnIdle( wxIdleEvent & event )
+void wxGenericListCtrl::OnInternalIdle()
{
- event.Skip();
-
+ wxWindow::OnInternalIdle();
+
// do it only if needed
if ( !m_mainWin->m_dirty )
return;
m_mainWin->RefreshLines(itemFrom, itemTo);
}
+/*
+ * Generic wxListCtrl is more or less a container for two other
+ * windows which drawings are done upon. These are namely
+ * 'm_headerWin' and 'm_mainWin'.
+ * Here we override 'virtual wxWindow::Refresh()' to mimic the
+ * behaviour wxListCtrl has under wxMSW.
+ */
+void wxGenericListCtrl::Refresh(bool eraseBackground, const wxRect *rect)
+{
+ if (!rect)
+ {
+ // The easy case, no rectangle specified.
+ if (m_headerWin)
+ m_headerWin->Refresh(eraseBackground);
+
+ if (m_mainWin)
+ m_mainWin->Refresh(eraseBackground);
+ }
+ else
+ {
+ // Refresh the header window
+ if (m_headerWin)
+ {
+ wxRect rectHeader = m_headerWin->GetRect();
+ rectHeader.Intersect(*rect);
+ if (rectHeader.GetWidth() && rectHeader.GetHeight())
+ {
+ int x, y;
+ m_headerWin->GetPosition(&x, &y);
+ rectHeader.Offset(-x, -y);
+ m_headerWin->Refresh(eraseBackground, &rectHeader);
+ }
+
+ }
+
+ // Refresh the main window
+ if (m_mainWin)
+ {
+ wxRect rectMain = m_mainWin->GetRect();
+ rectMain.Intersect(*rect);
+ if (rectMain.GetWidth() && rectMain.GetHeight())
+ {
+ int x, y;
+ m_mainWin->GetPosition(&x, &y);
+ rectMain.Offset(-x, -y);
+ m_mainWin->Refresh(eraseBackground, &rectMain);
+ }
+ }
+ }
+}
+
void wxGenericListCtrl::Freeze()
{
m_mainWin->Freeze();