logplus/Workflow/WFWidget/src/PaiHeaderView.cpp
2026-01-16 17:18:41 +08:00

396 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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;
}