yhkylin-backup-tools/kybackup/component/backuplistwidget.cpp

339 lines
12 KiB
C++
Executable File
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.

#include "backuplistwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMimeData>
#include <QFileInfo>
#include <QScrollBar>
#include "../messageboxutils.h"
#include "../gsettingswrapper.h"
#include "../globalbackupinfo.h"
#include "component/imageutil.h"
#define WIDTH_ITEM 36
#define MERGE_IN 8
#define WIDTH_SPACING 5
MyItemWidget::MyItemWidget(QWidget* parent, QListWidgetItem *item) :
QWidget(parent),
m_item(item)
{
if (parent && item)
item->setSizeHint(QSize(parent->width() - 5, WIDTH_ITEM));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setContentsMargins(5, 2, 2, 2);
hlayout->setSpacing(WIDTH_SPACING);
m_label = new MyLabel;
m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_label->setIsOriginal(true);
m_buttonDelete = new QPushButton;
m_buttonDelete->setFlat(true);
m_buttonDelete->setFixedSize(20, 20);
m_buttonDelete->setIcon(ImageUtil::loadTheme("window-close-symbolic", ":/symbos/window-close-symbolic", "white", 20));
m_buttonDelete->setVisible(false);
hlayout->addWidget(m_label);
hlayout->addWidget(m_buttonDelete);
// hlayout->setAlignment(Qt::AlignLeft);
connect(this, &MyItemWidget::setScrollWidth, this, [=](int width) {
if (parent && this->m_item)
item->setSizeHint(QSize(width - 5 - this->m_scrollWidth, WIDTH_ITEM));
int labelWidth = this->m_label->width();
this->m_extWidth = 0;
// 隐藏滚动条时会增加右侧边距
if (this->m_scrollWidth > 0 && width == 0)
this->m_extWidth = -1 * MERGE_IN;
// 显示滚动条时去掉右侧边距
else if (this->m_scrollWidth == 0 && width > 0)
this->m_extWidth = MERGE_IN;
this->m_label->setFixedWidth(labelWidth + (this->m_scrollWidth - width) + this->m_extWidth);
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
this->m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
this->m_scrollWidth = width;
});
connect(GlobelBackupInfo::inst().getGlobalSignals(), &GlobalSignals::fontChanged, this, [=](int fontSize) {
QFont font = this->m_label->font();
font.setPointSize(fontSize);
this->m_label->setFont(font);
// 字体家族、大小变化需重绘,并且字体大小变化也可能会造成显示不全问题
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
});
connect(this, &MyItemWidget::selected, this, [=](bool checked) {
int labelWidth = this->m_label->width();
if (checked) {
this->m_buttonDelete->setVisible(true);
this->m_label->setFixedWidth(labelWidth - WIDTH_SPACING - m_buttonDelete->width());
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
this->m_label->setFontColor(Qt::white);
this->m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
this->m_label->setStyleSheet("QLabel {color:white}"
"QToolTip {color:palette(windowText)}");
this->m_checked = true;
} else {
this->m_buttonDelete->setVisible(false);
this->m_label->setFixedWidth(labelWidth + WIDTH_SPACING + m_buttonDelete->width());
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
this->m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
this->m_label->setStyleSheet("QLabel {color:palette(windowText)}"
"QToolTip {color:palette(windowText)}");
this->m_checked = false;
}
});
connect(this, &MyItemWidget::resetItemWidth, this, [=](int width) {
if (parent && item) {
QListWidget *parentWidget = qobject_cast<QListWidget *>(parent);
QScrollBar *vertScroll = parentWidget->verticalScrollBar();
int scrollWidth = 0;
if (vertScroll->isVisible()) {
scrollWidth = vertScroll->size().width();
}
this->m_scrollWidth = scrollWidth;
item->setSizeHint(QSize(width - 5 - this->m_scrollWidth, WIDTH_ITEM));
}
if (this->m_buttonDelete->isVisible()) {
this->m_label->setFixedWidth(width - WIDTH_SPACING - m_buttonDelete->width() - 7 - this->m_scrollWidth);
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
this->m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
} else {
this->m_label->setFixedWidth(width - WIDTH_SPACING - 2 - this->m_scrollWidth);
this->m_label->setElidedText(this->m_text, Qt::ElideLeft);
this->m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
}
});
connect(m_buttonDelete, &QPushButton::clicked, this, [=]() {
this->m_item = nullptr;
emit deleteItem();
});
this->setLayout(hlayout);
}
void MyItemWidget::setText(const QString& text)
{
m_text = text;
// m_label->setFixedWidth(this->width() - 7 - this->m_scrollWidth);
m_label->setElidedText(m_text, Qt::ElideLeft);
m_label->setToolTip(m_text);
}
MyItemWidget::~MyItemWidget()
{
}
BackupListWidget::BackupListWidget(QWidget *parent /*= nullptr*/, QHBoxLayout *parentLayout /*= nullptr*/) :
QListWidget(parent),
m_parentLayout(parentLayout)
{
setSortingEnabled(false);
setAcceptDrops(true);
setAlternatingRowColors(true);
// setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// 设置为无边框默认时为StyledPanel
setFrameShape(QListWidget::NoFrame);
if (parentLayout != nullptr) {
parentLayout->setContentsMargins(MERGE_IN, MERGE_IN, MERGE_IN, MERGE_IN);
setGridSize(QSize(this->width(), 40 + MERGE_IN / 2));
} else
setGridSize(QSize(this->width(), 40));
// 列表为空时,展示一个“+”号图标和拖拽文件提示
m_plusLogo = new PixmapLabel;
m_plusLogo->setThemeIconSchema("list-add-symbolic", ":/symbos/list-add-symbolic", QSize(16, 16));
m_plusLogo->setEnabled(false);
// 文件拖放区域
m_plusText = new QLabel;
m_plusText->setText(tr("File drag and drop area"));
m_plusText->setEnabled(false);
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->addStretch();
hlayout->addWidget(m_plusLogo);
hlayout->addWidget(m_plusText);
hlayout->addStretch();
QVBoxLayout *vlayout = new QVBoxLayout;
vlayout->addStretch();
vlayout->addLayout(hlayout);
vlayout->addStretch();
setLayout(vlayout);
connect(this, &BackupListWidget::currentItemChanged, this, [=](QListWidgetItem *current, QListWidgetItem *previous) {
int row = this->currentRow();
if (current) {
MyItemWidget *widget = qobject_cast<MyItemWidget *>(this->itemWidget(current));
if (widget) {
if (-1 == row)
emit widget->selected(false);
else
emit widget->selected(true);
}
}
if (previous) {
MyItemWidget *widget = qobject_cast<MyItemWidget *>(this->itemWidget(previous));
if (widget) {
emit widget->selected(false);
}
}
});
// 大小改变
connect(GlobelBackupInfo::inst().getGlobalSignals(), &GlobalSignals::widthChanged, this, [=](){
emit this->resetItemWidth(this->width());
});
}
BackupListWidget::~BackupListWidget()
{}
bool BackupListWidget::contains(const QString& text)
{
// 1、针对使用addItem等的正规使用场景展示内容在原生item上
if (findItems(text, Qt::MatchCaseSensitive).size() > 0)
return true;
// 2、针对使用setItemWidget添加项展示内容在widget上的特殊使用场景
return m_List.contains(text);
}
bool BackupListWidget::appendItem(const QString &text)
{
if (!checkPathLimit(text))
return false;
int count = this->count();
if (count > 0)
++count;
QListWidgetItem *item = new QListWidgetItem(this, m_type);
MyItemWidget *widget = new MyItemWidget(this, item);
this->setItemWidget(item, widget);
// 必须将scrollToBottom等这种滚动条操作放到判断滚动条是否显示isVisible之前
this->scrollToBottom();
connect(this, &BackupListWidget::setScrollWidth, widget, &MyItemWidget::setScrollWidth);
// 滚动条是否展示
QScrollBar *vertScroll = verticalScrollBar();
int scrollWidth = 0;
if (vertScroll->isVisible()) {
if (m_parentLayout != nullptr)
m_parentLayout->setContentsMargins(MERGE_IN, MERGE_IN, 0, MERGE_IN);
scrollWidth = vertScroll->size().width();
emit this->setScrollWidth(scrollWidth);
}
widget->setText(text);
m_List << text;
connect(widget, &MyItemWidget::deleteItem, this, [=]() {
this->m_List.removeOne(widget->text());
this->removeItemWidget(item);
this->takeItem(this->row(item));
delete item;
this->scrollToBottom();
if (this->count() == 0) {
this->m_plusLogo->setVisible(true);
this->m_plusText->setVisible(true);
emit this->deleteEmpty();
} else {
QScrollBar *vertScrollBar = verticalScrollBar();
if (!vertScrollBar->isVisible()) {
if (m_parentLayout != nullptr)
m_parentLayout->setContentsMargins(MERGE_IN, MERGE_IN, MERGE_IN, MERGE_IN);
emit this->setScrollWidth(0);
}
}
});
connect(this, &BackupListWidget::resetItemWidth, widget, &MyItemWidget::resetItemWidth);
m_plusLogo->setVisible(false);
m_plusText->setVisible(false);
emit this->addedItem();
return true;
}
void BackupListWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasUrls()) {
for (QUrl url : event->mimeData()->urls()) {
QString file = url.toString();
if (file.startsWith("file://")) {
file.replace("file://", "");
appendItem(url.path());
}
}
}
}
bool BackupListWidget::checkPathLimit(const QString &path)
{
// 防命令注入
// 1、形如mkdir '`id&>id_bak_test.txt`'中的文件夹名称
// 2、形如$()的文件夹名称
// 3、形如${}的文件夹名称
// 4、包含[;、&、|]等可以包含并执行系统命令或用于连续执行系统命令的符号
// if ( path.contains(QRegularExpression(".*`.*`.*"))
// || path.contains(QRegularExpression(".*\\$\\(.*\\).*"))
// || path.contains(QRegularExpression(".*\\$\\{.*\\}.*"))
// || path.contains(QRegularExpression("[;&|]+"))) {
// MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"), QObject::tr("Path can not include symbols that such as : ``,$(),${},;,&,|,etc."), QObject::tr("Ok"));
// return false;
// }
// 1、列表中是否已经存在
if (contains(path)) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"),
QObject::tr("Path already exists : ") + path,
QObject::tr("Ok"));
return false;
}
// 2、路径是否存在
QFileInfo fileInfo(path);
if (!fileInfo.exists()) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"),
QObject::tr("The file or directory does not exist : ") + path,
QObject::tr("Ok"));
return false;
}
// 3、是否是限定路径及其子路径
bool blimit = false;
QString dirCanBeSelected;
for (const QString &item : m_pathLimit) {
if (path.startsWith(item)) {
blimit = true;
break;
}
if (dirCanBeSelected.isEmpty())
dirCanBeSelected = item;
else {
dirCanBeSelected += ",";
dirCanBeSelected += item;
}
}
if (m_pathLimit.size() > 0 && !blimit) {
MessageBoxUtils::QMESSAGE_BOX_WARNING(this, QObject::tr("Warning"),
QObject::tr("Only data that exists in the follow directorys can be selected: %1.\n Path:%2 is not in them.").arg(dirCanBeSelected).arg(path),
QObject::tr("Ok"));
return false;
}
return true;
}