396 lines
12 KiB
C++
396 lines
12 KiB
C++
/**
|
||
* @file PaiHeaderView.cpp
|
||
* @date 2012-11-09
|
||
*/
|
||
#include <QPainter>
|
||
#include <QApplication>
|
||
#include <QMouseEvent>
|
||
#include <QToolButton>
|
||
#include <QMenu>
|
||
#include <QCheckBox>
|
||
|
||
#include "PaiHeaderView.h"
|
||
#include "PaiSettings.h"
|
||
|
||
const int SORT_ARRAW_LEN = 30; // 可以排序时右侧的盘需模式指示标记宽度
|
||
const int LAST_SECTION_ADD_SPACE_LEN = 15; //最后一列加点空隙不让pSectionSelectTBtn遮挡
|
||
|
||
#define LOGICAL_INDEX "logic index" //QCheckBox用于记录自身所属逻辑列数
|
||
|
||
using namespace pai::gui;
|
||
|
||
PaiHeaderView::PaiHeaderView(Qt::Orientation orientation, QWidget *pParent)
|
||
: QHeaderView(orientation, pParent),
|
||
m_SectionSelectableVisible(false)
|
||
{
|
||
m_pSectionSelectToolButton = new QToolButton(this);
|
||
// 该ToolButton底色需与HeaderView底色保持一致
|
||
m_pSectionSelectToolButton->setStyleSheet(QString::fromUtf8("background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, \n"
|
||
"stop:0 #FEFEFF, stop:1 #E9F2F8);\n"
|
||
"border-left: 1px solid #C9D5DC;\n"
|
||
"border-right: 0px;\n"
|
||
"border-top: 1px solid #C9D5DC;\n"
|
||
"border-bottom: 1px solid #C9D5DC;"));
|
||
m_pSectionSelectToolButton->setIcon(QIcon(":/add_01.png"));
|
||
m_pSectionSelectToolButton->setVisible(m_SectionSelectableVisible);
|
||
|
||
// setClickable(true);
|
||
setStretchLastSection(true); // PAI风格默认
|
||
|
||
connect(this, SIGNAL(sectionResized(int, int, int)),
|
||
this, SLOT(AdjustSectionSuitable(int, int, int)));
|
||
connect(m_pSectionSelectToolButton, SIGNAL(clicked()),
|
||
this, SLOT(ShowSectionVisibleMenu()));
|
||
}
|
||
|
||
PaiHeaderView::~PaiHeaderView()
|
||
{
|
||
}
|
||
|
||
void PaiHeaderView::SetSectionVisibleSelectable(bool selectable)
|
||
{
|
||
m_SectionSelectableVisible = selectable;
|
||
m_pSectionSelectToolButton->setVisible(m_SectionSelectableVisible);
|
||
}
|
||
|
||
void PaiHeaderView::paintSection(QPainter *pPainter, const QRect & rect, int logicalIndex) const
|
||
{
|
||
// 当显示列选择功能时,需单独绘制最后一列,以使标题信息不被选择按钮挡住
|
||
if(m_SectionSelectableVisible && (logicalIndex == LastVisibleSection()))
|
||
{
|
||
int adjustSize = m_pSectionSelectToolButton->width()-1; // -1px来保证做测边界只有一个边被显示
|
||
|
||
// 分两部分绘制,此处为前半部分
|
||
QHeaderView::paintSection(pPainter, rect.adjusted(0, 0, -adjustSize, 0), logicalIndex);
|
||
|
||
// 后半部分,其实就是不绘制任何图像
|
||
QStyleOptionHeader opt;
|
||
initStyleOption(&opt);
|
||
opt.sortIndicator = QStyleOptionHeader::None;
|
||
opt.rect = rect.adjusted(rect.width() - adjustSize, 0, 0, 0);
|
||
opt.section = logicalIndex;
|
||
opt.position = QStyleOptionHeader::End;
|
||
QApplication::style()->drawControl(QStyle::CE_Header, &opt, pPainter, this);
|
||
}
|
||
else // 其它列绘制
|
||
{
|
||
QHeaderView::paintSection(pPainter, rect, logicalIndex);
|
||
}
|
||
|
||
// 调整CheckBox位置
|
||
if(m_ColumnCheckBoxMap.contains(logicalIndex))
|
||
{
|
||
QCheckBox *pCheckBox = m_ColumnCheckBoxMap.value(logicalIndex);
|
||
|
||
int x = rect.x(); //靠左
|
||
int y = (rect.height() - pCheckBox->height()) / 2; //垂直居中
|
||
|
||
pCheckBox->move(x, y);
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::resizeEvent(QResizeEvent *pEvent)
|
||
{
|
||
QHeaderView::resizeEvent(pEvent);
|
||
|
||
// 使列选择按钮始终在表格右上角
|
||
if(m_SectionSelectableVisible)
|
||
{
|
||
m_pSectionSelectToolButton->resize(height(), height());
|
||
m_pSectionSelectToolButton->move(width()-height(), 0);
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::mousePressEvent(QMouseEvent *pEvent)
|
||
{
|
||
QHeaderView::mousePressEvent(pEvent);
|
||
|
||
if(Qt::RightButton == pEvent->button())
|
||
{
|
||
emit TitleRightClicked(logicalIndexAt(pEvent->pos()), pEvent->globalPos());
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::ShowSectionVisibleMenu()
|
||
{
|
||
QMenu contextMenu;
|
||
|
||
for(int i = 0; i < count(); ++i)
|
||
{
|
||
QString colText = model()->headerData(i, Qt::Horizontal).toString();
|
||
if(!m_UnselectableSections.contains(colText))
|
||
{
|
||
QAction *pVisibleAct = contextMenu.addAction(colText, this, SLOT(ReverseSectionVisible()));
|
||
pVisibleAct->setCheckable(true);
|
||
pVisibleAct->setChecked(!isSectionHidden(i));
|
||
pVisibleAct->setData(i); // 以该值来标记action所代表的列
|
||
}
|
||
}
|
||
|
||
contextMenu.exec(mapToGlobal(m_pSectionSelectToolButton->geometry().bottomLeft()));
|
||
}
|
||
|
||
void PaiHeaderView::CheckBoxStateChanged()
|
||
{
|
||
QCheckBox *pCheckBox = dynamic_cast<QCheckBox *>(sender());
|
||
|
||
if(pCheckBox != NULL)
|
||
{
|
||
emit CheckStateChanged(pCheckBox->property(LOGICAL_INDEX).toInt(), pCheckBox->checkState());
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::ReverseSectionVisible()
|
||
{
|
||
QAction *pAct = qobject_cast<QAction*>(sender());
|
||
|
||
if(pAct)
|
||
{
|
||
int index = LastVisibleSection(); // 显示列索引变为逻辑索引
|
||
if((index >= 0) && (index < count()))
|
||
{
|
||
int size = SectionSuitableWidth(index);
|
||
if(sectionSize(index) > size)
|
||
{
|
||
resizeSection(index, size);
|
||
}
|
||
}
|
||
|
||
int sectionIndex = pAct->data().toInt();
|
||
if((sectionIndex >= 0) && (sectionIndex < count())) // 判断逻辑索引是否超出范围
|
||
{
|
||
SetSectionHidden(sectionIndex, !isSectionHidden(sectionIndex)); // 交换显示状态
|
||
// 隐藏一列后将最后一列重新resize下,不让pSectionSelectTBtn遮挡住
|
||
resizeSection (LastVisibleSection(), SectionSuitableWidth(LastVisibleSection()));
|
||
}
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::RememberCurrentSections()
|
||
{
|
||
if(!m_SectionSelectableVisible)
|
||
{
|
||
return;
|
||
}
|
||
if(parent() == NULL)
|
||
{
|
||
return;
|
||
}
|
||
// 生成对应的KEY
|
||
QString objName = parent()->objectName();
|
||
if(objName.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
QString key = objName.append("_VISIBLE_SECTIONS");
|
||
|
||
// 将当前显示的列索引记录到sections中
|
||
QStringList sections;
|
||
int cn = count();
|
||
for(int i = 0; i < cn; i++)
|
||
{
|
||
if(!isSectionHidden(i))
|
||
{
|
||
sections.append(QString::number(i));
|
||
}
|
||
}
|
||
|
||
PaiSettings settings;
|
||
settings.setValue(key, sections.join(","));
|
||
}
|
||
|
||
void PaiHeaderView::SetColumnCheckable(const int logicalIndex, const bool checkable, QCheckBox *pCheckBox)
|
||
{
|
||
if(checkable)
|
||
{
|
||
pCheckBox->setParent(this);
|
||
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(logicalIndex));
|
||
pCheckBox->setFixedSize(20, 20);
|
||
connect(pCheckBox, SIGNAL(stateChanged(int)), this, SLOT(CheckBoxStateChanged()));
|
||
|
||
m_ColumnCheckBoxMap.insert(logicalIndex, pCheckBox);
|
||
}
|
||
else
|
||
{
|
||
//从Map移除,并返回NULL
|
||
if(m_ColumnCheckBoxMap.contains(logicalIndex))
|
||
{
|
||
delete m_ColumnCheckBoxMap.value(logicalIndex);
|
||
m_ColumnCheckBoxMap.remove(logicalIndex);
|
||
}
|
||
}
|
||
|
||
update();
|
||
}
|
||
|
||
void PaiHeaderView::AdjustCheckBoxMap(bool insert, int column)
|
||
{
|
||
QMap<int, QCheckBox*> newMap;
|
||
|
||
if(insert)
|
||
{
|
||
//如果为插入操作,则将插入所在序列后的列序号+1
|
||
foreach(int index , m_ColumnCheckBoxMap.keys())
|
||
{
|
||
if(index < column)
|
||
{
|
||
newMap.insert(index, m_ColumnCheckBoxMap.value(index));
|
||
}
|
||
else
|
||
{
|
||
newMap.insert(index + 1, m_ColumnCheckBoxMap.value(index));
|
||
|
||
// 调整CheckBox对应的逻辑列
|
||
QCheckBox *pCheckBox = newMap.value(index + 1);
|
||
if(pCheckBox != NULL)
|
||
{
|
||
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(index + 1));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果为删除操作,则将删除所在序列后的列序号-1
|
||
foreach(int index , m_ColumnCheckBoxMap.keys())
|
||
{
|
||
if(index < column)
|
||
{
|
||
newMap.insert(index, m_ColumnCheckBoxMap.value(index));
|
||
}
|
||
else if(index == column)
|
||
{
|
||
// 如果此行为删除行,则将CheckBox解析掉
|
||
QCheckBox *pCheckBox = m_ColumnCheckBoxMap.value(index);
|
||
if(pCheckBox != NULL)
|
||
{
|
||
delete pCheckBox;
|
||
pCheckBox = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
newMap.insert(index - 1, m_ColumnCheckBoxMap.value(index));
|
||
|
||
// 调整CheckBox对应的逻辑列
|
||
QCheckBox *pCheckBox = newMap.value(index - 1);
|
||
if(pCheckBox != NULL)
|
||
{
|
||
pCheckBox->setProperty(LOGICAL_INDEX, QString::number(index - 1));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 替换为调整后的Map
|
||
m_ColumnCheckBoxMap = newMap;
|
||
update();
|
||
}
|
||
|
||
void PaiHeaderView::RecallMemberedSections()
|
||
{
|
||
if(!m_SectionSelectableVisible)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if(parent() == NULL)
|
||
{
|
||
return;
|
||
}
|
||
|
||
QString objName = parent()->objectName();
|
||
if(objName.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
// 生成当前表格显示列记录的KEY
|
||
QString key = objName.append("_VISIBLE_SECTIONS");
|
||
PaiSettings setting;
|
||
if(setting.contains(key))
|
||
{
|
||
int count = this->count();
|
||
// 余下可隐藏的默认显示列也设置其为隐藏
|
||
for(int i = 0; i < count; i++)
|
||
{
|
||
QString colText = model()->headerData(i, Qt::Horizontal).toString();
|
||
if(!m_UnselectableSections.contains(colText))
|
||
{
|
||
SetSectionHidden(i, true);
|
||
}
|
||
}
|
||
|
||
QString value = setting.value(key).toString();
|
||
if(value.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
QStringList indexs = value.split(",");
|
||
|
||
int size = indexs.size();
|
||
bool ok;
|
||
for(int i = 0; i < size; i++)
|
||
{
|
||
int index = indexs.at(i).toInt(&ok, 10);
|
||
if(ok && (index < count) && (index >= 0))
|
||
{
|
||
SetSectionHidden(index, false);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void PaiHeaderView::SetSectionHidden(int logicalIndex, bool hide)
|
||
{
|
||
setSectionHidden(logicalIndex, hide);
|
||
emit SectionVisibleChanged(logicalIndex, !hide);
|
||
}
|
||
|
||
void PaiHeaderView::AdjustSectionSuitable(int logicalIndex, int /*oldSize*/, int newSize)
|
||
{
|
||
// 为0的时候,认为其将要被隐藏,这时就不能再限制它的长度,注意这里使用isSectionHidden无效
|
||
if(newSize != 0)
|
||
{
|
||
int sSize = SectionSuitableWidth(logicalIndex); // 获得该列的最适大小
|
||
if(newSize < sSize) // 当最新的宽度小于最适大小时,重新调整其大小
|
||
{
|
||
resizeSection(logicalIndex, sSize);
|
||
}
|
||
}
|
||
}
|
||
|
||
int PaiHeaderView::SectionSuitableWidth(int logicalIndex)
|
||
{
|
||
// 文本长度
|
||
int sw = fontMetrics().width(model()->headerData(logicalIndex, Qt::Horizontal).toString());
|
||
if(isSortIndicatorShown()) // 当有排序箭头时,加上该箭头的宽度
|
||
{
|
||
sw += SORT_ARRAW_LEN;
|
||
}
|
||
if(m_SectionSelectableVisible && (logicalIndex == LastVisibleSection())) // 当时最后显示列的时候,加上列选择按钮宽度
|
||
{
|
||
sw += m_pSectionSelectToolButton->width();
|
||
sw += LAST_SECTION_ADD_SPACE_LEN;//加长点要不最后一列的后两个字母被pSectionSelectTBtn遮挡
|
||
}
|
||
return sw;
|
||
}
|
||
|
||
int PaiHeaderView::LastVisibleSection() const
|
||
{
|
||
for(int i = count() - 1; i >= 0; --i)
|
||
{
|
||
int lgIndex = logicalIndex(i); // 显示列索引变为逻辑索引
|
||
if(!isSectionHidden(lgIndex))
|
||
{
|
||
return lgIndex;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
void PaiHeaderView::SetUnselectableSections(const QStringList & sectionList)
|
||
{
|
||
m_UnselectableSections = sectionList;
|
||
}
|