396 lines
9.7 KiB
C++
396 lines
9.7 KiB
C++
/**
|
|
* @file PaiTableWidget.cpp
|
|
* @date 2012-03-27
|
|
*/
|
|
#include <QPainter>
|
|
#include <QTableWidgetItem>
|
|
#include <QMenu>
|
|
#include <QClipboard>
|
|
#include <QApplication>
|
|
#include <QKeyEvent>
|
|
|
|
#include "PaiTableWidget.h"
|
|
#include "PaiTableItemDelegate.h"
|
|
#include "PaiHeaderView.h"
|
|
|
|
using namespace pai::gui;
|
|
|
|
PaiTableWidget::PaiTableWidget(QWidget* pParent) :
|
|
QTableWidget(pParent),
|
|
m_AutoHeight(false),
|
|
m_ShowFilterEmptyMessage(false),
|
|
m_AutoHeightMaxRowCount(10),
|
|
m_MessageWhileFilterEmpty("found nothing!")
|
|
{
|
|
setSortingEnabled(true);
|
|
QTableWidget::setShowGrid(false);
|
|
setItemDelegate(new PaiTableItemDelegate(this));
|
|
setHorizontalHeader(new PaiHeaderView(Qt::Horizontal, this));
|
|
this->horizontalHeader()->setHighlightSections(false);
|
|
this->horizontalHeader()->setMinimumHeight(27);
|
|
|
|
connect(model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(RowCountChanged()));
|
|
connect(model(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(RowCountChanged()));
|
|
connect(this, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(TableItemChanged(QTableWidgetItem*)));
|
|
|
|
setContextMenuPolicy(Qt::DefaultContextMenu);
|
|
}
|
|
|
|
PaiTableWidget::~PaiTableWidget()
|
|
{
|
|
//记录当前显示的表头
|
|
RememberCurrentSections();
|
|
}
|
|
|
|
void PaiTableWidget::RowCountChanged()
|
|
{
|
|
if(m_AutoHeight && (rowCount() <= m_AutoHeightMaxRowCount))
|
|
{
|
|
ResetTableHeight();
|
|
}
|
|
|
|
emit RowCountChanged( rowCount());
|
|
}
|
|
|
|
void PaiTableWidget::Filter(const QString & keyword, int column)
|
|
{
|
|
Filter(keyword, QList< int > () << column);
|
|
}
|
|
|
|
void PaiTableWidget::Filter(const QString & keyword, QList< int > columns, QList< int > ignoreLines)
|
|
{
|
|
m_IgnoreLines = ignoreLines;
|
|
m_IgnoreHeight = 0;
|
|
for(int i = 0; i < ignoreLines.count(); i++)
|
|
{
|
|
m_IgnoreHeight += rowHeight(ignoreLines.at(i));
|
|
}
|
|
// 清空提示信息
|
|
m_ShowFilterEmptyMessage = false;
|
|
|
|
m_FilterKeyword = keyword;
|
|
m_FilterColumns = columns;
|
|
|
|
QList< int > searchResult;
|
|
for(int rowIndex = 0; rowIndex < rowCount(); ++rowIndex)
|
|
{
|
|
// 忽略行不进行过滤
|
|
if(m_IgnoreLines.contains(rowIndex))
|
|
{
|
|
setRowHidden(rowIndex, false);
|
|
continue;
|
|
}
|
|
bool pass = Filter(rowIndex);
|
|
|
|
// 只显示过滤的结果
|
|
setRowHidden(rowIndex, !pass);
|
|
if(pass)
|
|
{
|
|
searchResult.append(rowIndex);
|
|
}
|
|
}
|
|
|
|
// 如果没搜到任何东西,则显示未找到提示
|
|
if(searchResult.empty())
|
|
{
|
|
if(!m_FilterKeyword.isEmpty()) // 停止搜索但表格原本就是空的情况下不应显示该提示
|
|
{
|
|
m_ShowFilterEmptyMessage = true;
|
|
}
|
|
}
|
|
else // 如果搜索到结果,就将搜索到的第一个节点滚动到顶端显示
|
|
{
|
|
scrollToItem(item(searchResult.first(), 0), QAbstractItemView::PositionAtTop);
|
|
}
|
|
}
|
|
|
|
bool PaiTableWidget::Filter(int rowIndex)
|
|
{
|
|
bool pass = false;
|
|
|
|
if(m_FilterKeyword.isEmpty())
|
|
{
|
|
pass = true;
|
|
}
|
|
else if(m_FilterKeyword.contains('*') || m_FilterKeyword.contains('?'))
|
|
{
|
|
QRegExp rx(m_FilterKeyword, Qt::CaseInsensitive, QRegExp::Wildcard);
|
|
|
|
foreach(int colIndex, m_FilterColumns)
|
|
{
|
|
QTableWidgetItem *pItem = item(rowIndex, colIndex);
|
|
if(pItem && rx.exactMatch(pItem->text()))
|
|
{
|
|
pass = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach(int colIndex, m_FilterColumns)
|
|
{
|
|
QTableWidgetItem *pItem = item(rowIndex, colIndex);
|
|
if(pItem && pItem->text().contains(m_FilterKeyword, Qt::CaseInsensitive))
|
|
{
|
|
pass = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
void PaiTableWidget::insertColumn(int column)
|
|
{
|
|
QTableWidget::insertColumn(column);
|
|
|
|
// 如果使用的时PaiHeaderView(表头可显CheckBox),则调整CheckBox
|
|
PaiHeaderView *pHeadView = dynamic_cast< PaiHeaderView* > (horizontalHeader());
|
|
if(pHeadView != NULL)
|
|
{
|
|
pHeadView->AdjustCheckBoxMap(true, column);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::removeColumn(int column)
|
|
{
|
|
QTableWidget::removeColumn(column);
|
|
|
|
// 如果使用的时PaiHeaderView(表头可显CheckBox),则调整CheckBox
|
|
PaiHeaderView *pHeadView = dynamic_cast< PaiHeaderView* > (horizontalHeader());
|
|
if(pHeadView != NULL)
|
|
{
|
|
pHeadView->AdjustCheckBoxMap(false, column);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::ResetTableHeight()
|
|
{
|
|
const int rcount = rowCount();
|
|
int theight = horizontalHeader()->height() + 2; // +2 is ok, I don't know why.
|
|
|
|
if(rcount)
|
|
{
|
|
theight += (rowViewportPosition(rcount - 1) + rowHeight(rcount - 1));
|
|
}
|
|
|
|
setMinimumHeight(theight);
|
|
setMaximumHeight(theight);
|
|
}
|
|
|
|
void PaiTableWidget::SetAutoHeight(const bool ok, const int maxRowCount)
|
|
{
|
|
m_AutoHeight = ok;
|
|
m_AutoHeightMaxRowCount = maxRowCount;
|
|
|
|
if(ok)
|
|
{
|
|
ResetTableHeight();
|
|
}
|
|
else
|
|
{
|
|
setMinimumHeight(0);
|
|
setMaximumHeight(16777215); // a enough large number
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::RemoveRows(const int rowIndex, const int count)
|
|
{
|
|
if(rowIndex >= rowCount())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int rCount = (rowIndex + count > rowCount()) ? (rowCount() - rowIndex) : count;
|
|
|
|
for(int i = rCount - 1; i >= rowIndex; --i)
|
|
{
|
|
removeRow(i);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::SetFilterEmptyMessage(const QString & message)
|
|
{
|
|
m_MessageWhileFilterEmpty = message;
|
|
}
|
|
|
|
void PaiTableWidget::paintEvent(QPaintEvent *pEvent)
|
|
{
|
|
QTableWidget::paintEvent(pEvent);
|
|
QPainter painter(viewport());
|
|
painter.save();
|
|
|
|
const int BASE_HEIGHT = horizontalHeader()->sectionViewportPosition(0) + 20 + m_IgnoreHeight;
|
|
|
|
if(m_ShowFilterEmptyMessage)
|
|
{
|
|
int x = (width() - painter.fontMetrics().width(m_MessageWhileFilterEmpty)) / 2;
|
|
int y = BASE_HEIGHT;
|
|
|
|
painter.drawText(x, y, m_MessageWhileFilterEmpty);
|
|
}
|
|
|
|
if(!m_PromptMessage.isEmpty())
|
|
{
|
|
int x = (width() - painter.fontMetrics().width(m_PromptMessage)) / 2;
|
|
int y = BASE_HEIGHT;
|
|
|
|
painter.drawText(x, y, m_PromptMessage);
|
|
}
|
|
painter.restore();
|
|
}
|
|
|
|
void PaiTableWidget::RemoveRows()
|
|
{
|
|
setRowCount(0);
|
|
}
|
|
|
|
void PaiTableWidget::setShowGrid(bool horizontalLine, bool verticalLine)
|
|
{
|
|
PaiTableItemDelegate *pDelegate = dynamic_cast< PaiTableItemDelegate* > (itemDelegate());
|
|
if(pDelegate)
|
|
{
|
|
pDelegate->setShowGrid(horizontalLine, verticalLine);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::ShowPromptMessage(const QString & message)
|
|
{
|
|
m_PromptMessage = message;
|
|
}
|
|
|
|
void PaiTableWidget::SetColumnVisibleSelectable(bool selectable)
|
|
{
|
|
PaiHeaderView *pHeader = qobject_cast< PaiHeaderView* > (horizontalHeader());
|
|
if(pHeader)
|
|
{
|
|
pHeader->SetSectionVisibleSelectable(selectable);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::SetUnselectableColumns(const QStringList & columnList)
|
|
{
|
|
PaiHeaderView *pHeader = qobject_cast< PaiHeaderView* > (horizontalHeader());
|
|
if(pHeader)
|
|
{
|
|
pHeader->SetUnselectableSections(columnList);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::RecallMemberedSections()
|
|
{
|
|
PaiHeaderView *paiHeader = (PaiHeaderView*) horizontalHeader();
|
|
paiHeader->RecallMemberedSections();
|
|
}
|
|
|
|
void PaiTableWidget::SetColumnCheckable(const int logicalIndex, const bool checkable, QCheckBox *pCheckBox)
|
|
{
|
|
PaiHeaderView *pHeadView = dynamic_cast< PaiHeaderView* > (horizontalHeader());
|
|
|
|
if(pHeadView)
|
|
{
|
|
pHeadView->SetColumnCheckable(logicalIndex, checkable, pCheckBox);
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::RememberCurrentSections()
|
|
{
|
|
PaiHeaderView *paiHeader = (PaiHeaderView*) horizontalHeader();
|
|
paiHeader->RememberCurrentSections();
|
|
}
|
|
|
|
void PaiTableWidget::SetClipboard()
|
|
{
|
|
QList< QTableWidgetItem * > itemList = selectedItems();
|
|
if(itemList.size() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QString dataString;
|
|
|
|
int maxColumn = 0;
|
|
int minColumn = itemList[0]->column();
|
|
int maxRow = 0;
|
|
int minRow = itemList[0]->row();
|
|
|
|
foreach (QTableWidgetItem *pItem, itemList)
|
|
{
|
|
if(pItem->row() > maxRow)
|
|
{
|
|
maxRow = pItem->row();
|
|
}
|
|
if(pItem->row() < minRow)
|
|
{
|
|
minRow = pItem->row();
|
|
}
|
|
|
|
if(pItem->column() > maxColumn)
|
|
{
|
|
maxColumn = pItem->column();
|
|
}
|
|
if(pItem->column() < minColumn)
|
|
{
|
|
minColumn = pItem->column();
|
|
}
|
|
}
|
|
|
|
for(int row = minRow; row <= maxRow; row++)
|
|
{
|
|
for(int col = minColumn; col <= maxColumn; col++)
|
|
{
|
|
QTableWidgetItem *pItem = this->item(row, col);
|
|
if(pItem && pItem->isSelected())
|
|
{
|
|
dataString += pItem->data(Qt::DisplayRole).toString();
|
|
}
|
|
|
|
if(col != maxColumn)
|
|
{
|
|
dataString += "\t";
|
|
}
|
|
}
|
|
|
|
if(row != maxRow)
|
|
{
|
|
dataString += "\n";
|
|
}
|
|
}
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(dataString);
|
|
}
|
|
|
|
void PaiTableWidget::contextMenuEvent(QContextMenuEvent *pEvent)
|
|
{
|
|
QMenu menu(NULL);
|
|
QAction action(tr("Copy"), this);
|
|
action.setShortcut(tr("Ctrl+C"));
|
|
menu.addAction(&action);
|
|
|
|
connect(&action, SIGNAL(triggered()), this, SLOT(SetClipboard()));
|
|
|
|
menu.exec(pEvent->globalPos());
|
|
}
|
|
|
|
void PaiTableWidget::keyPressEvent(QKeyEvent *pEvent)
|
|
{
|
|
QTableWidget::keyPressEvent(pEvent);
|
|
|
|
if((pEvent->key() == Qt::Key_C) && (pEvent->modifiers() & Qt::ControlModifier))
|
|
{
|
|
SetClipboard();
|
|
}
|
|
}
|
|
|
|
void PaiTableWidget::TableItemChanged(QTableWidgetItem *pItem)
|
|
{
|
|
// 当单元格数据发生变化后,如果有过滤条件则实时过滤
|
|
if(!m_FilterKeyword.isEmpty())
|
|
{
|
|
Filter(pItem->row());
|
|
}
|
|
}
|