Fix wxListCtrl::HitTest() which was off roughly by 1 due to not taking into account the header height. Closes https://github.com/wxWidgets/wxWidgets/pull/1350
1833 lines
46 KiB
C++
1833 lines
46 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/qt/listctrl.cpp
|
|
// Author: Mariano Reingart, Peter Most
|
|
// Copyright: (c) 2010 wxWidgets dev team
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#include <QtWidgets/QHeaderView>
|
|
#include <QtWidgets/QTreeView>
|
|
#include <QtWidgets/QItemDelegate>
|
|
#include <QtWidgets/QStyledItemDelegate>
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/bitmap.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/app.h"
|
|
#include "wx/listctrl.h"
|
|
#include "wx/imaglist.h"
|
|
#include "wx/recguard.h"
|
|
|
|
#include "wx/qt/private/winevent.h"
|
|
#include "wx/qt/private/treeitemfactory.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
Qt::AlignmentFlag wxQtConvertTextAlign(wxListColumnFormat align)
|
|
{
|
|
switch (align)
|
|
{
|
|
case wxLIST_FORMAT_LEFT:
|
|
return Qt::AlignLeft;
|
|
case wxLIST_FORMAT_RIGHT:
|
|
return Qt::AlignRight;
|
|
case wxLIST_FORMAT_CENTRE:
|
|
return Qt::AlignCenter;
|
|
}
|
|
return Qt::AlignLeft;
|
|
}
|
|
|
|
wxListColumnFormat wxQtConvertAlignFlag(int align)
|
|
{
|
|
switch (align)
|
|
{
|
|
case Qt::AlignLeft:
|
|
return wxLIST_FORMAT_LEFT;
|
|
case Qt::AlignRight:
|
|
return wxLIST_FORMAT_RIGHT;
|
|
case Qt::AlignCenter:
|
|
return wxLIST_FORMAT_CENTRE;
|
|
}
|
|
return wxLIST_FORMAT_LEFT;
|
|
}
|
|
|
|
void InitListEvent(wxListEvent& event,
|
|
wxListCtrl* listctrl,
|
|
wxEventType eventType,
|
|
const QModelIndex& index = QModelIndex())
|
|
{
|
|
event.SetEventObject(listctrl);
|
|
event.SetEventType(eventType);
|
|
event.SetId(listctrl->GetId());
|
|
|
|
if ( index.isValid() )
|
|
{
|
|
event.m_itemIndex = index.row();
|
|
event.m_col = index.column();
|
|
event.m_item.SetId(event.m_itemIndex);
|
|
event.m_item.SetMask(wxLIST_MASK_TEXT |
|
|
wxLIST_MASK_IMAGE |
|
|
wxLIST_MASK_DATA);
|
|
listctrl->GetItem(event.m_item);
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
class wxQtStyledItemDelegate : public QStyledItemDelegate
|
|
{
|
|
public:
|
|
explicit wxQtStyledItemDelegate(wxWindow* parent)
|
|
: m_parent(parent),
|
|
m_textCtrl(NULL)
|
|
{
|
|
}
|
|
|
|
QWidget* createEditor(QWidget *parent,
|
|
const QStyleOptionViewItem &WXUNUSED(option),
|
|
const QModelIndex &index) const wxOVERRIDE
|
|
{
|
|
if (m_textCtrl != NULL)
|
|
destroyEditor(m_textCtrl->GetHandle(), m_currentModelIndex);
|
|
|
|
m_currentModelIndex = index;
|
|
m_textCtrl = new wxQtListTextCtrl(m_parent, parent);
|
|
m_textCtrl->SetFocus();
|
|
return m_textCtrl->GetHandle();
|
|
}
|
|
|
|
void destroyEditor(QWidget *WXUNUSED(editor),
|
|
const QModelIndex &WXUNUSED(index)) const wxOVERRIDE
|
|
{
|
|
if (m_textCtrl != NULL)
|
|
{
|
|
m_currentModelIndex = QModelIndex(); // invalidate the index
|
|
wxTheApp->ScheduleForDestruction(m_textCtrl);
|
|
m_textCtrl = NULL;
|
|
}
|
|
}
|
|
|
|
void setModelData(QWidget *WXUNUSED(editor),
|
|
QAbstractItemModel *WXUNUSED(model),
|
|
const QModelIndex &WXUNUSED(index)) const wxOVERRIDE
|
|
{
|
|
// Don't set model data until wx has had a chance to send out events
|
|
}
|
|
|
|
wxTextCtrl* GetEditControl() const
|
|
{
|
|
return m_textCtrl;
|
|
}
|
|
|
|
QModelIndex GetCurrentModelIndex() const
|
|
{
|
|
return m_currentModelIndex;
|
|
}
|
|
|
|
void AcceptModelData(QWidget *editor,
|
|
QAbstractItemModel *model,
|
|
const QModelIndex &index) const
|
|
{
|
|
QStyledItemDelegate::setModelData(editor, model, index);
|
|
}
|
|
|
|
private:
|
|
wxWindow* m_parent;
|
|
mutable wxTextCtrl* m_textCtrl;
|
|
mutable QModelIndex m_currentModelIndex;
|
|
};
|
|
|
|
class wxQtListModel : public QAbstractTableModel
|
|
{
|
|
public:
|
|
explicit wxQtListModel(wxListCtrl *listCtrl) :
|
|
m_view(NULL),
|
|
m_listCtrl(listCtrl)
|
|
{
|
|
}
|
|
|
|
int rowCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE
|
|
{
|
|
return static_cast<int>(m_rows.size());
|
|
}
|
|
int columnCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE
|
|
{
|
|
return static_cast<int>(m_headers.size());
|
|
}
|
|
|
|
QVariant data(const QModelIndex &index, int role) const wxOVERRIDE
|
|
{
|
|
const int row = index.row();
|
|
const int col = index.column();
|
|
|
|
wxCHECK_MSG(row >= 0 && static_cast<size_t>(row) < m_rows.size(),
|
|
QVariant(),
|
|
"Invalid row index"
|
|
);
|
|
|
|
const RowItem &rowItem = m_rows[row];
|
|
const ColumnItem &columnItem = rowItem[col];
|
|
|
|
const bool isSelected = IsSelected(index);
|
|
|
|
switch ( role )
|
|
{
|
|
case Qt::DisplayRole:
|
|
return QVariant::fromValue(columnItem.m_label);
|
|
|
|
case Qt::EditRole:
|
|
return QVariant::fromValue(columnItem.m_label);
|
|
|
|
case Qt::DecorationRole:
|
|
{
|
|
wxImageList *imageList = GetImageList();
|
|
if ( imageList == NULL )
|
|
return QVariant();
|
|
|
|
int imageIndex = -1;
|
|
|
|
if ( columnItem.m_selectedImage != -1 )
|
|
{
|
|
if ( isSelected )
|
|
imageIndex = columnItem.m_selectedImage;
|
|
}
|
|
|
|
if ( imageIndex == -1 )
|
|
imageIndex = columnItem.m_image;
|
|
|
|
if ( imageIndex == -1 )
|
|
return QVariant();
|
|
|
|
wxBitmap image = imageList->GetBitmap(imageIndex);
|
|
wxCHECK_MSG(image.IsOk(), QVariant(), "Invalid image");
|
|
return QVariant::fromValue(*image.GetHandle());
|
|
}
|
|
case Qt::FontRole:
|
|
return QVariant::fromValue(columnItem.m_font);
|
|
|
|
case Qt::BackgroundRole:
|
|
return columnItem.m_backgroundColour.isValid() && !isSelected
|
|
? QVariant::fromValue(columnItem.m_backgroundColour)
|
|
: QVariant();
|
|
|
|
case Qt::ForegroundRole:
|
|
return columnItem.m_textColour.isValid() && !isSelected
|
|
? QVariant::fromValue(columnItem.m_textColour)
|
|
: QVariant();
|
|
|
|
case Qt::TextAlignmentRole:
|
|
return columnItem.m_align;
|
|
|
|
case Qt::CheckStateRole:
|
|
return col == 0 && m_listCtrl->HasCheckBoxes()
|
|
? rowItem.m_checked
|
|
: QVariant();
|
|
|
|
default:
|
|
return QVariant();
|
|
}
|
|
}
|
|
|
|
bool setData(
|
|
const QModelIndex &index,
|
|
const QVariant &value,
|
|
int role
|
|
) wxOVERRIDE
|
|
{
|
|
const int row = index.row();
|
|
const int col = index.column();
|
|
|
|
wxCHECK_MSG(
|
|
row >= 0 && static_cast<size_t>(row) < m_rows.size(),
|
|
false,
|
|
"Invalid row index"
|
|
);
|
|
wxCHECK_MSG(
|
|
col >= 0 && static_cast<size_t>(col) < m_rows[row].m_columns.size(),
|
|
false,
|
|
"Invalid column index"
|
|
);
|
|
|
|
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
|
{
|
|
m_rows[row][col].m_label = value.toString();
|
|
return true;
|
|
}
|
|
|
|
if ( role == Qt::CheckStateRole && col == 0 )
|
|
{
|
|
m_rows[row].m_checked =
|
|
static_cast<Qt::CheckState>(value.toUInt()) == Qt::Checked;
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event,
|
|
m_listCtrl,
|
|
m_rows[row].m_checked ? wxEVT_LIST_ITEM_CHECKED
|
|
: wxEVT_LIST_ITEM_UNCHECKED,
|
|
index);
|
|
m_listCtrl->HandleWindowEvent(event);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QVariant headerData(
|
|
int section,
|
|
Qt::Orientation orientation,
|
|
int role
|
|
) const wxOVERRIDE
|
|
{
|
|
if ( orientation == Qt::Vertical )
|
|
return QVariant();
|
|
|
|
wxCHECK_MSG(static_cast<size_t>(section) < m_headers.size(),
|
|
QVariant(), "Invalid header index");
|
|
|
|
const ColumnItem& header = m_headers[section];
|
|
|
|
switch ( role )
|
|
{
|
|
case Qt::DisplayRole:
|
|
return header.m_label;
|
|
|
|
case Qt::TextAlignmentRole:
|
|
return header.m_align;
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags flags(const QModelIndex &index) const wxOVERRIDE
|
|
{
|
|
Qt::ItemFlags
|
|
itemFlags = Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
|
|
|
|
if ( m_listCtrl->HasFlag(wxLC_EDIT_LABELS) )
|
|
itemFlags |= Qt::ItemIsEditable;
|
|
|
|
if ( index.column() == 0 && m_listCtrl->HasCheckBoxes() )
|
|
itemFlags |= Qt::ItemIsUserCheckable;
|
|
|
|
return itemFlags | QAbstractTableModel::flags(index);
|
|
}
|
|
|
|
bool removeRows(int row, int count, const QModelIndex &parent) wxOVERRIDE
|
|
{
|
|
if ( count == 0 )
|
|
return true;
|
|
|
|
beginRemoveRows(parent, row, row + count - 1);
|
|
eraseFromContainer(m_rows, row, count);
|
|
endRemoveRows();
|
|
return true;
|
|
}
|
|
|
|
bool removeColumns(int column,
|
|
int count,
|
|
const QModelIndex &parent) wxOVERRIDE
|
|
{
|
|
if ( count == 0 )
|
|
return true;
|
|
|
|
beginRemoveColumns(parent, column, column + count - 1);
|
|
|
|
eraseFromContainer(m_headers, column, count);
|
|
|
|
const int nRows = this->m_rows.size();
|
|
for ( int i = 0; i < nRows; ++i )
|
|
{
|
|
eraseFromContainer(m_rows[i].m_columns, column, count);
|
|
}
|
|
|
|
endRemoveColumns();
|
|
return true;
|
|
}
|
|
|
|
bool GetColumn(int index, wxListItem &info) const
|
|
{
|
|
wxCHECK_MSG(static_cast<size_t>(index) < m_headers.size(),
|
|
false, "Invalid column");
|
|
|
|
const ColumnItem &column = m_headers[index];
|
|
info.SetText(wxQtConvertString(column.m_label));
|
|
info.SetAlign(wxQtConvertAlignFlag(column.m_align));
|
|
info.SetWidth(m_view->columnWidth(index));
|
|
return true;
|
|
}
|
|
|
|
bool SetColumn(int index, const wxListItem& info)
|
|
{
|
|
wxCHECK_MSG(static_cast<size_t>(index) < m_headers.size(),
|
|
false, "Invalid column");
|
|
|
|
ColumnItem &column = m_headers[index];
|
|
column.m_label = wxQtConvertString(info.GetText());
|
|
column.m_align = wxQtConvertTextAlign(info.GetAlign());
|
|
|
|
headerDataChanged(Qt::Horizontal, index, index);
|
|
return true;
|
|
|
|
}
|
|
|
|
virtual bool GetItem(wxListItem& info)
|
|
{
|
|
const int row = static_cast<int>(info.GetId());
|
|
const int col = info.m_col;
|
|
|
|
wxCHECK_MSG(
|
|
row >= 0 && static_cast<size_t>(row) < m_rows.size(),
|
|
false,
|
|
"Invalid row"
|
|
);
|
|
|
|
wxCHECK_MSG(
|
|
col >= 0 && static_cast<size_t>(col) < m_rows[row].m_columns.size(),
|
|
false,
|
|
"Invalid col"
|
|
);
|
|
|
|
const RowItem &rowItem = m_rows[row];
|
|
const ColumnItem &columnItem = rowItem[col];
|
|
|
|
if ( !info.m_mask )
|
|
// by default, get everything for backwards compatibility
|
|
info.m_mask = -1;
|
|
|
|
if ( info.m_mask & wxLIST_MASK_TEXT )
|
|
info.SetText(wxQtConvertString(columnItem.m_label));
|
|
|
|
if ( info.m_mask & wxLIST_MASK_DATA )
|
|
{
|
|
info.SetData(rowItem.m_data);
|
|
}
|
|
|
|
CopySelectStatusToItem(info, row, col);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetItem(wxListItem &info)
|
|
{
|
|
const int row = static_cast<int>(info.GetId());
|
|
const int col = info.m_col;
|
|
|
|
wxCHECK_MSG( static_cast<size_t>(row) < m_rows.size(),
|
|
false, "Invalid row");
|
|
wxCHECK_MSG( static_cast<size_t>(col) < m_headers.size(),
|
|
false, "Invalid col");
|
|
|
|
const QModelIndex modelIndex = index(row, col);
|
|
RowItem &rowItem = m_rows[row];
|
|
ColumnItem &columnItem = rowItem[col];
|
|
|
|
QVector<int> roles;
|
|
|
|
if ( (info.m_mask & wxLIST_MASK_TEXT) && !info.GetText().IsNull() )
|
|
{
|
|
columnItem.m_label = wxQtConvertString(info.GetText());
|
|
roles.push_back(Qt::DisplayRole);
|
|
}
|
|
|
|
if ( info.m_mask & wxLIST_MASK_FORMAT )
|
|
{
|
|
columnItem.m_align = wxQtConvertTextAlign(info.GetAlign());
|
|
roles.push_back(Qt::TextAlignmentRole);
|
|
}
|
|
|
|
if ( info.m_mask & wxLIST_MASK_DATA )
|
|
{
|
|
rowItem.m_data = (void*)(info.GetData());
|
|
roles.push_back(Qt::UserRole);
|
|
}
|
|
|
|
if ( info.m_mask & wxLIST_MASK_STATE )
|
|
{
|
|
if ( (info.m_stateMask & wxLIST_STATE_FOCUSED) &&
|
|
(info.m_state & wxLIST_STATE_FOCUSED) )
|
|
m_view->setCurrentIndex(modelIndex);
|
|
if ( info.m_stateMask & wxLIST_STATE_SELECTED )
|
|
{
|
|
QItemSelectionModel *selection = m_view->selectionModel();
|
|
const QItemSelectionModel::SelectionFlag flag =
|
|
info.m_state & wxLIST_STATE_SELECTED
|
|
? QItemSelectionModel::Select
|
|
: QItemSelectionModel::Deselect;
|
|
selection->select(modelIndex, flag|QItemSelectionModel::Rows);
|
|
}
|
|
}
|
|
|
|
if ( info.m_mask & wxLIST_MASK_IMAGE )
|
|
{
|
|
columnItem.m_image = info.m_image;
|
|
roles.push_back(Qt::DecorationRole);
|
|
}
|
|
|
|
if ( info.GetFont().IsOk() )
|
|
{
|
|
columnItem.m_font = info.GetFont().GetHandle();
|
|
roles.push_back(Qt::FontRole);
|
|
}
|
|
|
|
if ( info.GetTextColour().IsOk() )
|
|
{
|
|
columnItem.m_textColour = info.GetTextColour().GetQColor();
|
|
roles.push_back(Qt::BackgroundRole);
|
|
}
|
|
|
|
if ( info.GetBackgroundColour().IsOk() )
|
|
{
|
|
columnItem.m_backgroundColour = info.GetBackgroundColour().GetQColor();
|
|
roles.push_back(Qt::ForegroundRole);
|
|
}
|
|
|
|
dataChanged(modelIndex, modelIndex, roles);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SetView(QTreeView *view)
|
|
{
|
|
m_view = view;
|
|
}
|
|
|
|
wxColour GetItemTextColour(long item)
|
|
{
|
|
wxCHECK_MSG(item >= 0 && static_cast<size_t>(item) < m_rows.size(),
|
|
wxNullColour, "Invalid row");
|
|
|
|
wxCHECK_MSG(!m_rows[item].m_columns.empty(),
|
|
wxNullColour, "No columns in model");
|
|
|
|
return wxColour(m_rows[item][0].m_textColour);
|
|
}
|
|
|
|
wxColour GetItemBackgroundColour(long item)
|
|
{
|
|
wxCHECK_MSG(item >= 0 && static_cast<size_t>(item) < m_rows.size(),
|
|
wxNullColour, "Invalid row");
|
|
wxCHECK_MSG(!m_headers.empty(),
|
|
wxNullColour, "No columns in model");
|
|
|
|
return wxColour(m_rows[item][0].m_backgroundColour);
|
|
}
|
|
|
|
wxFont GetItemFont(long item)
|
|
{
|
|
wxCHECK_MSG(item >= 0 && static_cast<size_t>(item) < m_rows.size(),
|
|
wxNullFont, "Invalid row");
|
|
|
|
wxCHECK_MSG(!m_headers.empty(),
|
|
wxNullFont, "No columns in model");
|
|
|
|
return wxFont(m_rows[item][0].m_font);
|
|
}
|
|
|
|
long FindItem(long start, const QString& str, bool partial) const
|
|
{
|
|
if ( start < 0 )
|
|
start = 0;
|
|
|
|
const QString strUpper = str.toUpper();
|
|
|
|
const long numberOfRows = m_rows.size();
|
|
const long numberOfColumns = m_headers.size();
|
|
|
|
if ( partial )
|
|
{
|
|
for ( long i = start; i < numberOfRows; ++i )
|
|
{
|
|
for ( long j = 0; j < numberOfColumns; ++j )
|
|
{
|
|
const QString current = m_rows[i][j].m_label.toUpper();
|
|
if ( current.contains(strUpper) )
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
for ( long i = start; i < numberOfRows; ++i )
|
|
{
|
|
for ( long j = 0; j < numberOfColumns; ++j )
|
|
{
|
|
if ( m_rows[i][j].m_label.toUpper() == strUpper )
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
long FindItem(long start, wxUIntPtr data) const
|
|
{
|
|
if ( start < 0 )
|
|
start = 0;
|
|
|
|
const long count = m_rows.size();
|
|
|
|
for ( long i = start; i < count; ++i )
|
|
{
|
|
if ( m_rows[i].m_data == reinterpret_cast<void*>(data) )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
long InsertItem(const wxListItem& info)
|
|
{
|
|
const int column = info.GetColumn();
|
|
wxCHECK_MSG(column >= 0, -1, "Invalid column index");
|
|
|
|
if ( static_cast<size_t>(column) >= m_headers.size() )
|
|
{
|
|
beginInsertColumns(QModelIndex(), m_headers.size(), column);
|
|
|
|
const ColumnItem emptyColumn;
|
|
|
|
for ( int c = m_headers.size(); c <= column; ++c )
|
|
{
|
|
m_headers.push_back(emptyColumn);
|
|
for ( size_t r = 0; r < m_rows.size(); ++r )
|
|
{
|
|
m_rows[r].m_columns.push_back(emptyColumn);
|
|
}
|
|
}
|
|
|
|
endInsertColumns();
|
|
}
|
|
|
|
const long row = info.GetId();
|
|
long newRowIndex;
|
|
|
|
if ( row == -1 || static_cast<size_t>(row) >= m_rows.size() )
|
|
{
|
|
m_rows.push_back(RowItem(m_headers.size()));
|
|
newRowIndex = m_rows.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
std::vector<RowItem>::iterator i = m_rows.begin();
|
|
std::advance(i, row);
|
|
m_rows.insert(i, RowItem(m_headers.size()));
|
|
newRowIndex = row;
|
|
}
|
|
|
|
beginInsertRows(QModelIndex(), newRowIndex, newRowIndex);
|
|
|
|
wxListItem newItem = info;
|
|
newItem.SetId(newRowIndex);
|
|
SetItem(newItem);
|
|
|
|
endInsertRows();
|
|
|
|
return newRowIndex;
|
|
}
|
|
|
|
long InsertColumn(long col, const wxListItem& info)
|
|
{
|
|
long newColumnIndex;
|
|
|
|
ColumnItem newColumn;
|
|
if ( info.m_mask & wxLIST_MASK_FORMAT )
|
|
{
|
|
newColumn.m_align = wxQtConvertTextAlign(info.GetAlign());
|
|
}
|
|
newColumn.m_label = wxQtConvertString(info.GetText());
|
|
|
|
if ( col == -1 || static_cast<size_t>(col) >= m_headers.size() )
|
|
{
|
|
newColumnIndex = m_headers.empty() ? 0 : m_headers.size();
|
|
}
|
|
else
|
|
{
|
|
newColumnIndex = col;
|
|
}
|
|
|
|
beginInsertColumns(QModelIndex(), newColumnIndex, newColumnIndex);
|
|
|
|
std::vector<ColumnItem>::iterator i = m_headers.begin();
|
|
std::advance(i, newColumnIndex);
|
|
m_headers.insert(i, newColumn);
|
|
|
|
const int numberOfRows = m_rows.size();
|
|
|
|
for (int i = 0; i < numberOfRows; ++i )
|
|
{
|
|
std::vector<ColumnItem>::iterator it = m_rows[i].m_columns.begin();
|
|
std::advance(it, newColumnIndex);
|
|
m_rows[i].m_columns.insert(it, newColumn);
|
|
}
|
|
|
|
endInsertColumns();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SortItems(wxListCtrlCompare fn, wxIntPtr data)
|
|
{
|
|
CompareAdapter compare(fn, data);
|
|
std::sort(m_rows.begin(), m_rows.end(), compare);
|
|
}
|
|
|
|
bool IsItemChecked(long item) const
|
|
{
|
|
wxCHECK_MSG(item >= 0 && static_cast<size_t>(item) <= m_rows.size(),
|
|
false, "Invalid row");
|
|
|
|
return m_rows[item].m_checked;
|
|
}
|
|
|
|
void CheckItem(long item, bool check)
|
|
{
|
|
wxCHECK_RET(item >= 0 && static_cast<size_t>(item) <= m_rows.size(),
|
|
"Invalid row");
|
|
|
|
m_rows[item].m_checked = check;
|
|
|
|
QVector<int> roles;
|
|
roles.push_back(Qt::CheckStateRole);
|
|
const QModelIndex modelIndex = index(item, 0);
|
|
dataChanged(modelIndex, modelIndex, roles);
|
|
}
|
|
|
|
virtual bool IsVirtual() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual void SetVirtualItemCount(long WXUNUSED(count))
|
|
{
|
|
wxFAIL_MSG("Shouldn't be called if wxLC_VIRTUAL is not used");
|
|
}
|
|
|
|
protected:
|
|
wxImageList *GetImageList() const
|
|
{
|
|
const int requiredList = m_listCtrl->HasFlag(wxLC_SMALL_ICON)
|
|
? wxIMAGE_LIST_SMALL
|
|
: wxIMAGE_LIST_NORMAL;
|
|
return m_listCtrl->GetImageList(requiredList);
|
|
}
|
|
|
|
wxListCtrl *GetListCtrl() const
|
|
{
|
|
return m_listCtrl;
|
|
}
|
|
|
|
bool IsSelected(const QModelIndex& index) const
|
|
{
|
|
QModelIndexList indices = m_view->selectionModel()->selectedIndexes();
|
|
return indices.contains(index);
|
|
}
|
|
|
|
void CopySelectStatusToItem(wxListItem& info, int row, int col)
|
|
{
|
|
if ( info.m_mask & wxLIST_MASK_STATE )
|
|
{
|
|
info.m_state = wxLIST_STATE_DONTCARE;
|
|
if (info.m_stateMask & wxLIST_STATE_FOCUSED)
|
|
{
|
|
if (m_view->currentIndex().row() == row)
|
|
info.m_state |= wxLIST_STATE_FOCUSED;
|
|
}
|
|
if (info.m_stateMask & wxLIST_STATE_SELECTED)
|
|
{
|
|
if (IsSelected(index(row, col)))
|
|
info.m_state |= wxLIST_STATE_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
template <typename T>
|
|
static void eraseFromContainer(T &container, int start_index, int count)
|
|
{
|
|
|
|
typename T::iterator first = container.begin();
|
|
std::advance(first, start_index);
|
|
|
|
typename T::iterator last = first;
|
|
std::advance(last, count);
|
|
|
|
container.erase(first, last);
|
|
}
|
|
|
|
struct ColumnItem
|
|
{
|
|
ColumnItem() :
|
|
m_align(Qt::AlignLeft),
|
|
m_image(-1),
|
|
m_selectedImage(-1)
|
|
{
|
|
}
|
|
|
|
QString m_label;
|
|
QColor m_backgroundColour;
|
|
QColor m_textColour;
|
|
QFont m_font;
|
|
Qt::AlignmentFlag m_align;
|
|
int m_image;
|
|
int m_selectedImage;
|
|
};
|
|
|
|
struct RowItem
|
|
{
|
|
RowItem() :
|
|
m_data(NULL),
|
|
m_checked(false)
|
|
{
|
|
}
|
|
|
|
RowItem(int columnCount ) :
|
|
m_columns(columnCount),
|
|
m_data(NULL),
|
|
m_checked(false)
|
|
{
|
|
}
|
|
|
|
const ColumnItem &operator[](int index) const
|
|
{
|
|
return m_columns[index];
|
|
}
|
|
|
|
ColumnItem &operator[](int index)
|
|
{
|
|
return m_columns[index];
|
|
}
|
|
|
|
std::vector<ColumnItem> m_columns;
|
|
void *m_data;
|
|
bool m_checked;
|
|
|
|
};
|
|
|
|
struct CompareAdapter
|
|
{
|
|
CompareAdapter(wxListCtrlCompare fn, wxIntPtr data) :
|
|
m_fn(fn),
|
|
m_data(data)
|
|
{
|
|
}
|
|
|
|
bool operator()(const RowItem &lhs, const RowItem &rhs) const
|
|
{
|
|
return m_fn(
|
|
reinterpret_cast<wxIntPtr>(lhs.m_data),
|
|
reinterpret_cast<wxIntPtr>(rhs.m_data),
|
|
m_data) < 0;
|
|
}
|
|
|
|
wxListCtrlCompare m_fn;
|
|
wxIntPtr m_data;
|
|
};
|
|
|
|
std::vector<ColumnItem> m_headers;
|
|
std::vector<RowItem> m_rows;
|
|
QTreeView *m_view;
|
|
wxListCtrl *m_listCtrl;
|
|
};
|
|
|
|
class wxQtVirtualListModel : public wxQtListModel
|
|
{
|
|
public:
|
|
wxQtVirtualListModel(wxListCtrl *listCtrl) :
|
|
wxQtListModel(listCtrl),
|
|
m_rowCount(0)
|
|
{
|
|
}
|
|
|
|
int rowCount(const QModelIndex& WXUNUSED(parent)) const wxOVERRIDE
|
|
{
|
|
return m_rowCount;
|
|
}
|
|
|
|
QVariant data(const QModelIndex &index, int role) const wxOVERRIDE
|
|
{
|
|
wxListCtrl *listCtrl = GetListCtrl();
|
|
|
|
const int row = index.row();
|
|
const int col = index.column();
|
|
|
|
if ( role == Qt::DisplayRole || role == Qt::EditRole )
|
|
{
|
|
const wxString text = listCtrl->OnGetItemText(row, col);
|
|
return QVariant::fromValue(wxQtConvertString(text));
|
|
}
|
|
|
|
if ( role == Qt::DecorationRole )
|
|
{
|
|
wxImageList *imageList = GetImageList();
|
|
if ( imageList == NULL )
|
|
return QVariant();
|
|
|
|
const int imageIndex = listCtrl->OnGetItemColumnImage(row, col);
|
|
if ( imageIndex == -1 )
|
|
return QVariant();
|
|
wxBitmap image = imageList->GetBitmap(imageIndex);
|
|
wxCHECK_MSG(image.IsOk(), QVariant(), "Invalid Bitmap");
|
|
return QVariant::fromValue(*image.GetHandle());
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool GetItem(wxListItem& info) wxOVERRIDE
|
|
{
|
|
const int row = static_cast<int>(info.GetId());
|
|
const int col = info.m_col;
|
|
|
|
if ( info.m_mask & wxLIST_MASK_TEXT )
|
|
info.SetText(GetListCtrl()->OnGetItemText(row, col));
|
|
|
|
CopySelectStatusToItem(info, row, col);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsVirtual() const wxOVERRIDE
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void SetVirtualItemCount(long count) wxOVERRIDE
|
|
{
|
|
beginResetModel();
|
|
m_rowCount = static_cast<int>(count);
|
|
endResetModel();
|
|
}
|
|
private:
|
|
int m_rowCount;
|
|
};
|
|
|
|
|
|
class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl >
|
|
{
|
|
public:
|
|
wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler );
|
|
|
|
void EmitListEvent(wxEventType typ, const QModelIndex &index) const;
|
|
|
|
void closeEditor(
|
|
QWidget *editor,
|
|
QAbstractItemDelegate::EndEditHint hint
|
|
) wxOVERRIDE
|
|
{
|
|
// Close process can re-signal closeEditor so we need to guard against
|
|
// reentrant calls.
|
|
wxRecursionGuard guard(m_closingEditor);
|
|
|
|
if (guard.IsInside())
|
|
return;
|
|
|
|
// There can be multiple calls to close editor when the item loses focus
|
|
const QModelIndex current_index = m_itemDelegate.GetCurrentModelIndex();
|
|
if (!current_index.isValid())
|
|
return;
|
|
|
|
const wxString editedText = m_itemDelegate.GetEditControl()->GetLineText(0);
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event,
|
|
GetHandler(),
|
|
wxEVT_LIST_END_LABEL_EDIT,
|
|
current_index);
|
|
event.m_item.SetText(editedText);
|
|
|
|
if (hint == QAbstractItemDelegate::RevertModelCache)
|
|
{
|
|
event.SetEditCanceled(true);
|
|
EmitEvent(event);
|
|
}
|
|
else
|
|
{
|
|
// Allow event handlers to decide whether to accept edited text
|
|
if (!GetHandler()->HandleWindowEvent(event) || event.IsAllowed())
|
|
m_itemDelegate.AcceptModelData(editor, model(), current_index);
|
|
}
|
|
|
|
// wx doesn't have hints to edit next/previous item
|
|
if ( hint == QAbstractItemDelegate::EditNextItem ||
|
|
hint == QAbstractItemDelegate::EditPreviousItem )
|
|
{
|
|
hint = QAbstractItemDelegate::SubmitModelCache;
|
|
}
|
|
|
|
QTreeView::closeEditor(editor, hint);
|
|
closePersistentEditor(current_index);
|
|
}
|
|
|
|
wxTextCtrl *GetEditControl()
|
|
{
|
|
return m_itemDelegate.GetEditControl();
|
|
}
|
|
|
|
virtual void paintEvent(QPaintEvent *event) wxOVERRIDE
|
|
{
|
|
QTreeView::paintEvent(event);
|
|
}
|
|
|
|
int GetHeaderHeight() const
|
|
{
|
|
return header() != NULL ? header()->height() : 0;
|
|
}
|
|
|
|
private:
|
|
void itemClicked(const QModelIndex &index);
|
|
void itemActivated(const QModelIndex &index);
|
|
void itemPressed(const QModelIndex &index);
|
|
|
|
wxQtStyledItemDelegate m_itemDelegate;
|
|
wxRecursionGuardFlag m_closingEditor;
|
|
};
|
|
|
|
wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler )
|
|
: wxQtEventSignalHandler< QTreeView, wxListCtrl >( parent, handler ),
|
|
m_itemDelegate(handler),
|
|
m_closingEditor(0)
|
|
{
|
|
connect(this, &QTreeView::clicked, this, &wxQtListTreeWidget::itemClicked);
|
|
connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed);
|
|
connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated);
|
|
|
|
setItemDelegate(&m_itemDelegate);
|
|
setEditTriggers(NoEditTriggers);
|
|
}
|
|
|
|
void wxQtListTreeWidget::EmitListEvent(wxEventType typ,
|
|
const QModelIndex &index) const
|
|
{
|
|
wxListCtrl *handler = GetHandler();
|
|
if ( handler )
|
|
{
|
|
// prepare the event
|
|
// -----------------
|
|
wxListEvent event;
|
|
InitListEvent(event, handler, typ, index);
|
|
EmitEvent(event);
|
|
}
|
|
}
|
|
|
|
void wxQtListTreeWidget::itemClicked(const QModelIndex &index)
|
|
{
|
|
EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index);
|
|
}
|
|
|
|
void wxQtListTreeWidget::itemPressed(const QModelIndex &index)
|
|
{
|
|
EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index);
|
|
}
|
|
|
|
void wxQtListTreeWidget::itemActivated(const QModelIndex &index)
|
|
{
|
|
EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index);
|
|
}
|
|
|
|
|
|
wxListCtrl::wxListCtrl()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
wxListCtrl::wxListCtrl(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
Init();
|
|
Create( parent, id, pos, size, style, validator, name );
|
|
}
|
|
|
|
|
|
bool wxListCtrl::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
m_model = style & wxLC_VIRTUAL
|
|
? new wxQtVirtualListModel(this)
|
|
: new wxQtListModel(this);
|
|
|
|
m_qtTreeWidget = new wxQtListTreeWidget(parent, this);
|
|
m_qtTreeWidget->setModel(m_model);
|
|
m_model->SetView(m_qtTreeWidget);
|
|
|
|
if (style & wxLC_NO_HEADER)
|
|
m_qtTreeWidget->setHeaderHidden(true);
|
|
|
|
m_qtTreeWidget->setRootIsDecorated(false);
|
|
m_qtTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
if ( !QtCreateControl(parent, id, pos, size, style, validator, name) )
|
|
return false;
|
|
|
|
SetWindowStyleFlag(style);
|
|
return true;
|
|
}
|
|
|
|
void wxListCtrl::Init()
|
|
{
|
|
m_hasCheckBoxes = false;
|
|
m_model = NULL;
|
|
m_imageListNormal = NULL;
|
|
m_ownsImageListNormal = false;
|
|
m_imageListSmall = NULL;
|
|
m_ownsImageListSmall = false;
|
|
m_imageListState = NULL;
|
|
m_ownsImageListState = false;
|
|
m_qtTreeWidget = NULL;
|
|
}
|
|
|
|
wxListCtrl::~wxListCtrl()
|
|
{
|
|
if ( m_ownsImageListNormal )
|
|
delete m_imageListNormal;
|
|
if ( m_ownsImageListSmall )
|
|
delete m_imageListSmall;
|
|
if ( m_ownsImageListState )
|
|
delete m_imageListState;
|
|
|
|
m_imageListNormal = NULL;
|
|
m_imageListSmall = NULL;
|
|
m_imageListState = NULL;
|
|
|
|
m_qtTreeWidget->setModel(NULL);
|
|
m_model->deleteLater();
|
|
}
|
|
|
|
bool wxListCtrl::SetForegroundColour(const wxColour& col)
|
|
{
|
|
return wxListCtrlBase::SetForegroundColour(col);
|
|
}
|
|
|
|
bool wxListCtrl::SetBackgroundColour(const wxColour& col)
|
|
{
|
|
return wxListCtrlBase::SetBackgroundColour(col);
|
|
}
|
|
|
|
bool wxListCtrl::GetColumn(int col, wxListItem& info) const
|
|
{
|
|
return m_model->GetColumn(col, info);
|
|
}
|
|
|
|
bool wxListCtrl::SetColumn(int col, const wxListItem& info)
|
|
{
|
|
if ( !m_model->SetColumn(col, info) )
|
|
return false;
|
|
|
|
if ( info.GetMask() & wxLIST_MASK_WIDTH )
|
|
SetColumnWidth(col, info.GetWidth());
|
|
|
|
return true;
|
|
}
|
|
|
|
int wxListCtrl::GetColumnWidth(int col) const
|
|
{
|
|
return m_qtTreeWidget->columnWidth(col);
|
|
}
|
|
|
|
bool wxListCtrl::SetColumnWidth(int col, int width)
|
|
{
|
|
if ( width < 0 )
|
|
{
|
|
m_qtTreeWidget->resizeColumnToContents(width);
|
|
return true;
|
|
}
|
|
|
|
m_qtTreeWidget->setColumnWidth(col, width);
|
|
return true;
|
|
}
|
|
|
|
int wxListCtrl::GetColumnOrder(int col) const
|
|
{
|
|
return col;
|
|
}
|
|
|
|
int wxListCtrl::GetColumnIndexFromOrder(int order) const
|
|
{
|
|
return order;
|
|
}
|
|
|
|
wxArrayInt wxListCtrl::GetColumnsOrder() const
|
|
{
|
|
return wxArrayInt();
|
|
}
|
|
|
|
bool wxListCtrl::SetColumnsOrder(const wxArrayInt& WXUNUSED(orders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int wxListCtrl::GetCountPerPage() const
|
|
{
|
|
// this may not be exact but should be a good approximation:
|
|
const int h = m_qtTreeWidget->visualRect(m_model->index(0, 0)).height();
|
|
if ( h )
|
|
return m_qtTreeWidget->height() / h;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
wxRect wxListCtrl::GetViewRect() const
|
|
{
|
|
// this may not be exact but should be a good approximation:
|
|
wxRect rect = wxQtConvertRect(m_qtTreeWidget->rect());
|
|
const int h = m_qtTreeWidget->header()->defaultSectionSize();
|
|
rect.SetTop(h);
|
|
rect.SetHeight(rect.GetHeight() - h);
|
|
return rect;
|
|
}
|
|
|
|
wxTextCtrl* wxListCtrl::GetEditControl() const
|
|
{
|
|
return m_qtTreeWidget->GetEditControl();
|
|
}
|
|
|
|
bool wxListCtrl::GetItem(wxListItem& info) const
|
|
{
|
|
return m_model->GetItem(info);
|
|
}
|
|
|
|
bool wxListCtrl::SetItem(wxListItem& info)
|
|
{
|
|
return m_model->SetItem(info);
|
|
}
|
|
|
|
long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId)
|
|
{
|
|
wxListItem info;
|
|
info.m_text = label;
|
|
info.m_mask = wxLIST_MASK_TEXT;
|
|
info.m_itemId = index;
|
|
info.m_col = col;
|
|
if ( imageId > -1 )
|
|
{
|
|
info.m_image = imageId;
|
|
info.m_mask |= wxLIST_MASK_IMAGE;
|
|
}
|
|
return SetItem(info);
|
|
}
|
|
|
|
int wxListCtrl::GetItemState(long item, long stateMask) const
|
|
{
|
|
wxListItem info;
|
|
|
|
info.m_mask = wxLIST_MASK_STATE;
|
|
info.m_stateMask = stateMask;
|
|
info.m_itemId = item;
|
|
|
|
if ( !GetItem(info) )
|
|
return 0;
|
|
|
|
return info.m_state;
|
|
}
|
|
|
|
bool wxListCtrl::SetItemState(long item, long state, long stateMask)
|
|
{
|
|
wxListItem info;
|
|
|
|
info.m_mask = wxLIST_MASK_STATE;
|
|
info.m_stateMask = stateMask;
|
|
info.m_state = state;
|
|
info.m_itemId = item;
|
|
|
|
return SetItem(info);
|
|
}
|
|
|
|
bool wxListCtrl::SetItemImage(long item, int image, int WXUNUSED(selImage))
|
|
{
|
|
return SetItemColumnImage(item, 0, image);
|
|
}
|
|
|
|
bool wxListCtrl::SetItemColumnImage(long item, long column, int image)
|
|
{
|
|
wxListItem info;
|
|
|
|
info.m_mask = wxLIST_MASK_IMAGE;
|
|
info.m_image = image;
|
|
info.m_itemId = item;
|
|
info.m_col = column;
|
|
|
|
return SetItem(info);
|
|
}
|
|
|
|
wxString wxListCtrl::GetItemText(long item, int col) const
|
|
{
|
|
wxListItem info;
|
|
info.SetId(item);
|
|
info.m_col = col;
|
|
info.m_mask = wxLIST_MASK_TEXT;
|
|
GetItem(info);
|
|
return info.GetText();
|
|
}
|
|
|
|
void wxListCtrl::SetItemText(long item, const wxString& str)
|
|
{
|
|
wxListItem info;
|
|
info.SetId(item);
|
|
info.m_mask = wxLIST_MASK_TEXT;
|
|
info.SetText(str);
|
|
SetItem(info);
|
|
}
|
|
|
|
wxUIntPtr wxListCtrl::GetItemData(long item) const
|
|
{
|
|
wxListItem info;
|
|
info.SetId(item);
|
|
info.m_mask = wxLIST_MASK_DATA;
|
|
GetItem(info);
|
|
return info.GetData();
|
|
}
|
|
|
|
bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data)
|
|
{
|
|
wxListItem info;
|
|
info.SetId(item);
|
|
info.m_mask = wxLIST_MASK_DATA;
|
|
info.SetData(data);
|
|
return SetItem(info);
|
|
}
|
|
|
|
bool wxListCtrl::SetItemData(long item, long data)
|
|
{
|
|
return SetItemPtrData(item, static_cast<wxUIntPtr>(data));
|
|
}
|
|
|
|
bool wxListCtrl::GetItemRect(long item, wxRect& rect, int WXUNUSED(code)) const
|
|
{
|
|
wxCHECK_MSG(item >= 0 && (item < GetItemCount()), false,
|
|
"invalid item in GetSubItemRect");
|
|
|
|
const int columnCount = m_model->columnCount(QModelIndex());
|
|
if ( columnCount == 0 )
|
|
return false;
|
|
|
|
// Calculate the union of the bounds of the items in the first and last
|
|
// column for the given row.
|
|
QRect first = m_qtTreeWidget->visualRect(m_model->index(item, 0));
|
|
QRect last = m_qtTreeWidget->visualRect(m_model->index(item, columnCount-1));
|
|
rect = wxQtConvertRect(first.united(last));
|
|
rect.Offset(0, m_qtTreeWidget->GetHeaderHeight());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::GetSubItemRect(long item,
|
|
long subItem,
|
|
wxRect& rect,
|
|
int WXUNUSED(code)) const
|
|
{
|
|
wxCHECK_MSG(item >= 0 && item < GetItemCount(),
|
|
false, "invalid row index in GetSubItemRect");
|
|
|
|
wxCHECK_MSG(subItem >= 0 && subItem < GetColumnCount(),
|
|
false, "invalid column index in GetSubItemRect");
|
|
|
|
const QModelIndex index = m_qtTreeWidget->model()->index(item, subItem);
|
|
rect = wxQtConvertRect(m_qtTreeWidget->visualRect(index));
|
|
rect.Offset(0, m_qtTreeWidget->GetHeaderHeight());
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const
|
|
{
|
|
wxRect rect;
|
|
if ( !GetItemRect(item, rect) )
|
|
return false;
|
|
|
|
pos.x = rect.x;
|
|
pos.y = rect.y;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::SetItemPosition(long WXUNUSED(item),
|
|
const wxPoint& WXUNUSED(pos))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int wxListCtrl::GetItemCount() const
|
|
{
|
|
return m_model->rowCount(QModelIndex());
|
|
}
|
|
|
|
int wxListCtrl::GetColumnCount() const
|
|
{
|
|
return m_model->columnCount(QModelIndex());
|
|
}
|
|
|
|
wxSize wxListCtrl::GetItemSpacing() const
|
|
{
|
|
return wxSize();
|
|
}
|
|
|
|
void wxListCtrl::SetItemTextColour(long item, const wxColour& col)
|
|
{
|
|
const int columnCount = m_model->columnCount(QModelIndex());
|
|
|
|
wxListItem listItem;
|
|
listItem.SetId(item);
|
|
listItem.SetTextColour(col);
|
|
|
|
for ( int i = 0; i < columnCount; ++i )
|
|
{
|
|
listItem.m_col = i;
|
|
SetItem(listItem);
|
|
}
|
|
}
|
|
|
|
wxColour wxListCtrl::GetItemTextColour( long item ) const
|
|
{
|
|
return m_model->GetItemTextColour(item);
|
|
}
|
|
|
|
void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col)
|
|
{
|
|
wxListItem listItem;
|
|
listItem.SetId(item);
|
|
listItem.SetBackgroundColour(col);
|
|
|
|
const int columnCount = m_model->columnCount(QModelIndex());
|
|
|
|
for ( int i = 0; i < columnCount; ++i )
|
|
{
|
|
listItem.m_col = i;
|
|
SetItem(listItem);
|
|
}
|
|
}
|
|
|
|
wxColour wxListCtrl::GetItemBackgroundColour( long item ) const
|
|
{
|
|
return m_model->GetItemBackgroundColour(item);
|
|
}
|
|
|
|
void wxListCtrl::SetItemFont( long item, const wxFont &f)
|
|
{
|
|
const int columnCount = m_model->columnCount(QModelIndex());
|
|
|
|
wxListItem listItem;
|
|
listItem.SetId(item);
|
|
listItem.SetFont(f);
|
|
|
|
for ( int i = 0; i < columnCount; ++i )
|
|
{
|
|
listItem.m_col = i;
|
|
SetItem(listItem);
|
|
}
|
|
}
|
|
|
|
wxFont wxListCtrl::GetItemFont( long item ) const
|
|
{
|
|
return m_model->GetItemFont(item);
|
|
}
|
|
|
|
int wxListCtrl::GetSelectedItemCount() const
|
|
{
|
|
QItemSelectionModel *selectionModel = m_qtTreeWidget->selectionModel();
|
|
QModelIndexList selectedRows = selectionModel->selectedRows();
|
|
return selectedRows.length();
|
|
}
|
|
|
|
wxColour wxListCtrl::GetTextColour() const
|
|
{
|
|
const QPalette palette = m_qtTreeWidget->palette();
|
|
const QColor color = palette.color(QPalette::WindowText);
|
|
return wxColour(color);
|
|
}
|
|
|
|
void wxListCtrl::SetTextColour(const wxColour& col)
|
|
{
|
|
QPalette palette = m_qtTreeWidget->palette();
|
|
palette.setColor(QPalette::WindowText, col.GetQColor());
|
|
m_qtTreeWidget->setPalette(palette);
|
|
}
|
|
|
|
long wxListCtrl::GetTopItem() const
|
|
{
|
|
const long itemCount = GetItemCount();
|
|
for ( long i = 0; i < itemCount; ++i )
|
|
{
|
|
wxRect itemRect;
|
|
GetItemRect(i, itemRect);
|
|
if ( itemRect.GetY() >= 0 )
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool wxListCtrl::HasCheckBoxes() const
|
|
{
|
|
return m_hasCheckBoxes;
|
|
}
|
|
|
|
bool wxListCtrl::EnableCheckBoxes(bool enable /*= true*/)
|
|
{
|
|
m_hasCheckBoxes = enable;
|
|
QVector<int> roles;
|
|
roles.push_back(Qt::CheckStateRole);
|
|
m_model->dataChanged(m_model->index(0, 0),
|
|
m_model->index(GetItemCount() - 1, GetColumnCount() - 1),
|
|
roles);
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::IsItemChecked(long item) const
|
|
{
|
|
return m_model->IsItemChecked(item);
|
|
}
|
|
|
|
void wxListCtrl::CheckItem(long item, bool check)
|
|
{
|
|
m_model->CheckItem(item, check);
|
|
}
|
|
|
|
void wxListCtrl::SetSingleStyle(long WXUNUSED(style), bool WXUNUSED(add))
|
|
{
|
|
}
|
|
|
|
void wxListCtrl::SetWindowStyleFlag(long style)
|
|
{
|
|
m_windowStyle = style;
|
|
m_qtTreeWidget->setHeaderHidden((style & wxLC_NO_HEADER) != 0);
|
|
m_qtTreeWidget->setSelectionMode((style & wxLC_SINGLE_SEL) != 0
|
|
? QAbstractItemView::SingleSelection
|
|
: QAbstractItemView::ExtendedSelection
|
|
);
|
|
const bool needVirtual = (style & wxLC_VIRTUAL) != 0;
|
|
|
|
if ( needVirtual != m_model->IsVirtual() )
|
|
{
|
|
wxQtListModel *oldModel = m_model;
|
|
m_model = needVirtual
|
|
? new wxQtVirtualListModel(this)
|
|
: new wxQtListModel(this);
|
|
m_model->SetView(m_qtTreeWidget);
|
|
m_qtTreeWidget->setModel(m_model);
|
|
delete oldModel;
|
|
}
|
|
}
|
|
|
|
long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const
|
|
{
|
|
wxListItem info;
|
|
long ret = item;
|
|
const long max = GetItemCount();
|
|
wxCHECK_MSG((ret >= -1) || (ret < max), -1,
|
|
"invalid listctrl index in GetNextItem()");
|
|
|
|
// notice that we start with the next item (or the first one if item == -1)
|
|
// and this is intentional to allow writing a simple loop to iterate over
|
|
// all selected items
|
|
ret++;
|
|
if ( ret == max )
|
|
// this is not an error because the index was OK initially,
|
|
// just no such item
|
|
return -1;
|
|
|
|
if ( state == wxLIST_STATE_DONTCARE )
|
|
return ret;
|
|
|
|
for ( long line = ret; line < max; line++ )
|
|
{
|
|
if ( GetItemState(line, state) )
|
|
return line;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
wxImageList *wxListCtrl::GetImageList(int which) const
|
|
{
|
|
if ( which == wxIMAGE_LIST_NORMAL )
|
|
{
|
|
return m_imageListNormal;
|
|
}
|
|
else if ( which == wxIMAGE_LIST_SMALL )
|
|
{
|
|
return m_imageListSmall;
|
|
}
|
|
else if ( which == wxIMAGE_LIST_STATE )
|
|
{
|
|
return m_imageListState;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void wxListCtrl::SetImageList(wxImageList *imageList, int which)
|
|
{
|
|
if ( which == wxIMAGE_LIST_NORMAL )
|
|
{
|
|
if ( m_ownsImageListNormal )
|
|
delete m_imageListNormal;
|
|
m_imageListNormal = imageList;
|
|
m_ownsImageListNormal = false;
|
|
}
|
|
else if ( which == wxIMAGE_LIST_SMALL )
|
|
{
|
|
if ( m_ownsImageListSmall )
|
|
delete m_imageListSmall;
|
|
m_imageListSmall = imageList;
|
|
m_ownsImageListSmall = false;
|
|
}
|
|
else if ( which == wxIMAGE_LIST_STATE )
|
|
{
|
|
if ( m_ownsImageListState )
|
|
delete m_imageListState;
|
|
m_imageListState = imageList;
|
|
m_ownsImageListState = false;
|
|
}
|
|
}
|
|
|
|
void wxListCtrl::AssignImageList(wxImageList *imageList, int which)
|
|
{
|
|
SetImageList(imageList, which);
|
|
if ( which == wxIMAGE_LIST_NORMAL )
|
|
m_ownsImageListNormal = true;
|
|
else if ( which == wxIMAGE_LIST_SMALL )
|
|
m_ownsImageListSmall = true;
|
|
else if ( which == wxIMAGE_LIST_STATE )
|
|
m_ownsImageListState = true;
|
|
}
|
|
|
|
void wxListCtrl::RefreshItem(long item)
|
|
{
|
|
RefreshItems(item, item);
|
|
}
|
|
|
|
void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
|
|
{
|
|
const int columnCount = GetColumnCount();
|
|
const QModelIndex start = m_model->index(itemFrom, 0);
|
|
const QModelIndex end = m_model->index(itemTo, columnCount - 1);
|
|
m_model->dataChanged(start, end);
|
|
}
|
|
|
|
bool wxListCtrl::Arrange(int WXUNUSED(flag))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxListCtrl::DeleteItem(long item)
|
|
{
|
|
if ( item < 0 || item >= GetItemCount() )
|
|
return false;
|
|
|
|
m_model->removeRow(item, QModelIndex());
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event, this, wxEVT_LIST_DELETE_ITEM);
|
|
event.m_item.SetId(item);
|
|
|
|
HandleWindowEvent(event);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::DeleteAllItems()
|
|
{
|
|
if ( GetItemCount() == 0 )
|
|
return true;
|
|
|
|
m_model->removeRows(0, GetItemCount(), QModelIndex());
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event, this, wxEVT_LIST_DELETE_ALL_ITEMS);
|
|
|
|
HandleWindowEvent(event);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::DeleteColumn(int col)
|
|
{
|
|
if ( col < 0 || col >= m_model->columnCount(QModelIndex()) )
|
|
return false;
|
|
|
|
m_model->removeColumn(0, QModelIndex());
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::DeleteAllColumns()
|
|
{
|
|
m_model->removeColumns(0,
|
|
m_model->columnCount(QModelIndex()),
|
|
QModelIndex());
|
|
return true;
|
|
}
|
|
|
|
void wxListCtrl::ClearAll()
|
|
{
|
|
DeleteAllColumns();
|
|
DeleteAllItems();
|
|
}
|
|
|
|
wxTextCtrl* wxListCtrl::EditLabel(long item,
|
|
wxClassInfo* WXUNUSED(textControlClass))
|
|
{
|
|
// Open the editor first so that it's available when handling events as per
|
|
// wx standard.
|
|
const QModelIndex index = m_model->index(item, 0);
|
|
m_qtTreeWidget->openPersistentEditor(index);
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event, this, wxEVT_LIST_BEGIN_LABEL_EDIT, index);
|
|
|
|
// close the editor again if event is vetoed
|
|
if (HandleWindowEvent(event) && !event.IsAllowed())
|
|
m_qtTreeWidget->closePersistentEditor(index);
|
|
|
|
return m_qtTreeWidget->GetEditControl();
|
|
}
|
|
|
|
bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel))
|
|
{
|
|
const int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
|
|
if ( item < 0 )
|
|
return false;
|
|
|
|
m_qtTreeWidget->closePersistentEditor(m_model->index(item, 0));
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::EnsureVisible(long item)
|
|
{
|
|
if ( item < 0 || item >= GetItemCount() )
|
|
return false;
|
|
|
|
m_qtTreeWidget->scrollTo(m_model->index(item, 0));
|
|
return true;
|
|
}
|
|
|
|
long wxListCtrl::FindItem(long start, const wxString& str, bool partial)
|
|
{
|
|
return m_model->FindItem(start, wxQtConvertString(str), partial);
|
|
}
|
|
|
|
long wxListCtrl::FindItem(long start, wxUIntPtr data)
|
|
{
|
|
return m_model->FindItem(start, data);
|
|
}
|
|
|
|
long wxListCtrl::FindItem(
|
|
long WXUNUSED(start),
|
|
const wxPoint& WXUNUSED(pt),
|
|
int WXUNUSED(direction)
|
|
)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
long wxListCtrl::HitTest(
|
|
const wxPoint& point,
|
|
int &flags,
|
|
long* ptrSubItem
|
|
) const
|
|
{
|
|
// Remove the header height as qt expects point relative to the table sub widget
|
|
QPoint qPoint = wxQtConvertPoint(point);
|
|
qPoint.setY(qPoint.y() - m_qtTreeWidget->GetHeaderHeight());
|
|
|
|
QModelIndex index = m_qtTreeWidget->indexAt(qPoint);
|
|
if ( index.isValid() )
|
|
{
|
|
flags = wxLIST_HITTEST_ONITEM;
|
|
if (ptrSubItem)
|
|
*ptrSubItem = index.column();
|
|
}
|
|
else
|
|
{
|
|
flags = wxLIST_HITTEST_NOWHERE;
|
|
if (ptrSubItem)
|
|
*ptrSubItem = 0;
|
|
}
|
|
return index.row();
|
|
}
|
|
|
|
long wxListCtrl::InsertItem(const wxListItem& info)
|
|
{
|
|
const long index = m_model->InsertItem(info);
|
|
|
|
wxListEvent event;
|
|
InitListEvent(event, this, wxEVT_LIST_INSERT_ITEM);
|
|
|
|
HandleWindowEvent(event);
|
|
|
|
return index;
|
|
}
|
|
|
|
long wxListCtrl::InsertItem(long index, const wxString& label)
|
|
{
|
|
wxListItem info;
|
|
info.m_text = label;
|
|
info.m_mask = wxLIST_MASK_TEXT;
|
|
info.m_itemId = index;
|
|
return InsertItem(info);
|
|
}
|
|
|
|
long wxListCtrl::InsertItem(long index, int imageIndex)
|
|
{
|
|
wxListItem info;
|
|
info.m_image = imageIndex;
|
|
info.m_mask = wxLIST_MASK_IMAGE;
|
|
info.m_itemId = index;
|
|
return InsertItem(info);
|
|
}
|
|
|
|
long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex)
|
|
{
|
|
wxListItem info;
|
|
info.m_text = label;
|
|
info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
|
|
info.m_image = imageIndex;
|
|
info.m_itemId = index;
|
|
return InsertItem(info);
|
|
}
|
|
|
|
long wxListCtrl::DoInsertColumn(long col, const wxListItem& info)
|
|
{
|
|
return m_model->InsertColumn(col, info);
|
|
}
|
|
|
|
|
|
void wxListCtrl::SetItemCount(long count)
|
|
{
|
|
m_model->SetVirtualItemCount(count);
|
|
}
|
|
|
|
bool wxListCtrl::ScrollList(int dx, int dy)
|
|
{
|
|
// approximate, as scrollContentsBy is protected
|
|
m_qtTreeWidget->scroll(dx, dy);
|
|
return true;
|
|
}
|
|
|
|
bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data)
|
|
{
|
|
m_model->SortItems(fn, data);
|
|
return true;
|
|
}
|
|
|
|
QWidget *wxListCtrl::GetHandle() const
|
|
{
|
|
return m_qtTreeWidget;
|
|
}
|