notepad--/encodeconvert.cpp

736 lines
18 KiB
C++
Raw Normal View History

#include "encodeconvert.h"
#include "rcglobal.h"
#include "CmpareMode.h"
#include "doctypelistview.h"
#include <QFileDialog>
#include <QTreeWidgetItem>
#include <QDateTime>
#include <QFutureWatcher>
#include <QString>
#include <QtConcurrent>
#include <QInputDialog>
#include <QDragEnterEvent>
const int ITEM_CODE = Qt::UserRole + 1;
static QString fileSuffix(const QString& filePath)
{
QFileInfo fi(filePath);
return fi.suffix();
}
static QString getFileSizeFormat(qint64 size)
{
#if 0
if (size <= 1000)
{
return QString("%1").arg(size);
}
QString fileSize = QString("%1").arg(size);
return QString("%1,%2").arg(fileSize.left(fileSize.count() - 3)).arg(fileSize.right(3));
#endif
return QString::number(size);
}
EncodeConvert::EncodeConvert(QWidget *parent): QWidget(parent), m_commitCmpFileNums(0), m_finishCmpFileNums(0), m_menu(nullptr)
{
ui.setupUi(this);
m_extComBoxNum = 0;
connect(ui.treeWidget, &QTreeWidget::itemPressed, this, &EncodeConvert::slot_itemClicked);
setAcceptDrops(true);
}
EncodeConvert::~EncodeConvert()
{
for (auto var : m_supportFileExt)
{
delete var;
}
m_supportFileExt.clear();
}
bool EncodeConvert::isSupportExt(int index, QString ext)
{
bool ret = false;
if (0 == index)
{
ret = DocTypeListView::isSupportExt(ext);
}
else if (index >= 1)
{
int i = index - 1;
if (i < m_supportFileExt.count())
{
ret = m_supportFileExt[i]->contains(ext);
}
}
return ret;
}
//右键菜单
void EncodeConvert::slot_itemClicked(QTreeWidgetItem* item, int /*column*/)
{
if ((item != nullptr) && (Qt::RightButton == QGuiApplication::mouseButtons()))
{
if (m_menu == nullptr)
{
m_menu = new QMenu(this);
m_menu->addAction(tr("&Show File in Explorer..."), this, [&]() {
QString path, cmd;
QTreeWidgetItem* it = ui.treeWidget->currentItem();
if (it == nullptr)
{
return;
}
path = QString("%1").arg(it->data(0, Qt::ToolTipRole).toString());
#ifdef _WIN32
path = path.replace("/", "\\");
cmd = QString("explorer.exe /select,%1").arg(path);
#else
path = path.replace("\\", "/");
cmd = QString("open -R %1").arg(path);
#endif
QProcess process;
process.startDetached(cmd);
});
}
m_menu->move(QCursor::pos());
m_menu->show();
}
}
//用户自定义类型
void EncodeConvert::slot_userDefineExt()
{
bool ok = false;
QString text = QInputDialog::getText(this, tr("input file ext()"),tr("ext (Split With :)"), QLineEdit::Normal, QString(".h:.cpp"), &ok);
if (ok && !text.isEmpty())
{
text = text.trimmed();
ui.extComboBox->addItem(text);
QStringList extList = text.split(":");
QMap<QString, bool>* p = new QMap<QString, bool>;
for (QString var : extList)
{
if (var.startsWith("."))
{
p->insert(var.mid(1), true);
}
}
m_supportFileExt.append(p);
++m_extComBoxNum;
ui.extComboBox->setCurrentIndex(m_extComBoxNum);
}
}
//打开文件目录
void EncodeConvert::slot_selectFile()
{
//加载左边的文件树
QString rootpath = QFileDialog::getExistingDirectory(this, tr("Open Directory"), QString(), QFileDialog::DontResolveSymlinks);
if (!rootpath.isEmpty())
{
ui.treeWidget->clear();
m_fileAttris.clear();
loadDir(rootpath);
setItemIntervalBackground();
scanFileCode();
}
}
int EncodeConvert::allfile(QTreeWidgetItem* root_, QString path_)
{
QList<WalkFileInfo> dirsList;
WalkFileInfo oneDir(0, root_, path_);
dirsList.append(oneDir);
int fileNums = 0;
m_fileDirPath = path_;
while (!dirsList.isEmpty())
{
WalkFileInfo curDir = dirsList.first();
dirsList.pop_front();
QTreeWidgetItem* root = curDir.root;
QString path = curDir.path;
int direction = curDir.direction;
/*添加path路径文件*/
QDir dir(path); //遍历各级子目录
//先获取文件到列表
//再获取文件夹到列表
QFileInfoList folder_list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); //获取当前所有目录
for (int i = 0; i != folder_list.size(); ++i) //自动递归添加各目录到上一级目录
{
QString namepath = folder_list.at(i).absoluteFilePath(); //获取路径
QFileInfo folderinfo = folder_list.at(i);
QString name = folderinfo.fileName(); //获取目录名
QTreeWidgetItem* childroot = new QTreeWidgetItem(QStringList() << name);
childroot->setIcon(0, QIcon(":/Resources/img/dir.png"));
root->addChild(childroot); //将当前目录添加成path的子项
fileAttriNode node;
node.type = RC_DIR;//是目录
node.selfItem = childroot;
node.parent = root;
node.relativePath = folderinfo.absoluteFilePath();
//把路径名称保存到tips中后续需要这个来排序下同
childroot->setData(0, Qt::ToolTipRole, node.relativePath);
m_fileAttris.append(node);
WalkFileInfo oneDir(direction, childroot, namepath);
dirsList.push_front(oneDir);
}
QDir dir_file(path);
dir_file.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);//获取当前所有文件
QFileInfoList list_file = dir_file.entryInfoList();
for (int i = 0; i < list_file.size(); ++i)
{ //将当前目录中所有文件添加到treewidget中
QFileInfo fileInfo = list_file.at(i);
QString name2 = fileInfo.fileName();
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList() << name2);
child->setIcon(0, QIcon(":/Resources/img/point.png"));
child->setText(1, getFileSizeFormat(fileInfo.size()));
/*QString lastModifyTime = fileInfo.lastModified().toString("yy/MM/dd hh:mm:ss");
child->setText(2, lastModifyTime);*/
root->addChild(child);
fileAttriNode node;
node.type = RC_FILE;//是文件
node.selfItem = child;
node.parent = root;
node.relativePath = fileInfo.absoluteFilePath();
//把路径名称保存到tips中后续需要这个来排序下同
child->setData(0, Qt::ToolTipRole, node.relativePath);
m_fileAttris.append(node);
}
fileNums += list_file.size();
}
return fileNums;
}
int EncodeConvert::loadDir(QString rootDirPath)
{
QString rootpath = rootDirPath;
QTreeWidgetItem* root = nullptr;
int fileNums = 0;
ui.treeWidget->setColumnWidth(0, 400);
ui.treeWidget->clear();
root = new QTreeWidgetItem(ui.treeWidget);
root->setText(0, rootpath);
root->setExpanded(true);
//第一个节点是目录根节点
fileAttriNode node;
node.type = RC_DIR;//是目录
node.selfItem = root;
node.parent = nullptr;
node.relativePath = ".";
m_fileAttris.append(node);
fileNums = allfile(root, rootpath);
return fileNums;
}
QFuture<EncodeThreadParameter*> EncodeConvert::commitTask(std::function<EncodeThreadParameter* (EncodeThreadParameter*)> fun, EncodeThreadParameter* parameter)
{
/* 这里最开始准备使用信号提交多线程但是发现std:;function无法使用槽函数机制需要自己是实现元对象
* 使QtConcurrent::run机制
*/
return QtConcurrent::run(fun, parameter);
}
//对比左右文件的大小sha1值来判断文件是否相等
QFuture<EncodeThreadParameter_*> EncodeConvert::checkFileCode(QString filePath, QTreeWidgetItem* item)
{
EncodeThreadParameter_* p = new EncodeThreadParameter_(filePath);
p->item = item;
//int 0相等 1 不等
return commitTask([](EncodeThreadParameter_* parameter)->EncodeThreadParameter_*
{
parameter->code = CmpareMode::scanFileRealCode(parameter->filepath);
return parameter;
}
, p);
}
CODE_ID EncodeConvert::convertFileToCode(QString& filePath, CODE_ID srcCode, CODE_ID dstCode)
{
if (srcCode == CODE_ID::UNKOWN)
{
return CODE_ID::UNKOWN;
}
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::ExistingOnly))
{
return CODE_ID::UNKOWN;
}
QByteArray content = file.readAll();
file.close();
int skip = 0;
switch (srcCode)
{
case UNKOWN:
break;
case ANSI:
break;
case UNICODE_LE:
skip = 2;
break;
case UNICODE_BE:
skip = 2;
break;
case UTF8_NOBOM:
break;
case UTF8_BOM:
skip = 3;
break;
case GBK:
break;
default:
break;
}
if (!file.open(QIODevice::WriteOnly | QIODevice::ExistingOnly | QIODevice::Truncate))
{
return CODE_ID::UNKOWN;
}
QByteArray text2Save;
if (skip == 2 && content.size() >= 2)
{
text2Save = QByteArray(content.mid(2));
}
else if (skip == 3 && content.size() >= 3)
{
text2Save = QByteArray(content.mid(3));
}
else
{
text2Save = QByteArray(content);
}
QString textOut;
Encode::tranStrToUNICODE(srcCode, text2Save.data(), text2Save.size(), textOut);
if (dstCode != UNKOWN)
{
//QByteArray codeFlag = Encode::getEncodeStartFlagByte(dstCode);
//20210822 发现大坑,转换到一定格式后,字符串前面自动带了标识,不需要再来检查一次
//if (!codeFlag.isEmpty())
//{
// //先写入标识头
// file.write(codeFlag);
//}
//如果编码是已知如下类型,则后续保存其它行时,不修改编码格式,继续按照原编码进行保存
//前面已经设置过编码了,这里不需要再设置
if (dstCode == CODE_ID::UTF8_BOM)
{
//自动转换不会带UTF-8 BOM所以自己要在前面写个BOM头。这是一个例外。需要手动写入头
//其他必然BL LE则不需要。
QByteArray codeFlag = Encode::getEncodeStartFlagByte(dstCode);
if (!codeFlag.isEmpty())
{
//先写入标识头
file.write(codeFlag);
}
}
if (textOut.length() > 0)
{
//保存时注意编码问题。这个tolocal已经带了字符BOM头了。只要UTF8_BOM不会带
QByteArray t = textOut.toLocal8Bit();
file.write(textOut.toLocal8Bit());
}
}
file.close();
return dstCode;
}
CODE_ID EncodeConvert::getComboBoxCode(int index){
CODE_ID ret = CODE_ID::UNKOWN;
if (index < CODE_END)
{
ret = (CODE_ID)index;
}
return ret;
};
QFuture<EncodeThreadParameter_*> EncodeConvert::convertFileCode(QString filePath, QTreeWidgetItem* item)
{
EncodeThreadParameter_* p = new EncodeThreadParameter_(filePath);
p->item = item;
CODE_ID srcCode = static_cast<CODE_ID>(item->data(0, ITEM_CODE).toInt());
CODE_ID dstCode = getComboBoxCode(ui.codeToComboBox->currentIndex());
//int 0相等 1 不等
return commitTask([=](EncodeThreadParameter_* parameter)->EncodeThreadParameter_*
{
if (dstCode != CODE_ID::UNKOWN)
{
parameter->code = convertFileToCode(parameter->filepath, srcCode, dstCode);
}
else
{
parameter->code = UNKOWN;
}
return parameter;
}
, p);
}
//20220114 仅仅使用第一行失败编码还是不行因为utf8和gbk其实有相同的编码范围。
//如果识别第一行为gbk的则直接使用gbk。但是如果识别为utf8的则需要识别更多的文本内容这样会更慢
void EncodeConvert::scanFileCode()
{
m_finishCmpFileNums = 0;
m_commitCmpFileNums = 0;
ui.selectFileBt->setEnabled(false);
ui.startBt->setEnabled(false);
ui.closeBt->setEnabled(false);
ui.logTextBrowser->clear();
ui.logTextBrowser->append(tr("start scan file text code, please wait..."));
for (QList<fileAttriNode>::iterator iter = m_fileAttris.begin(); iter != m_fileAttris.end(); ++iter)
{
if (iter->type == RC_DIR)
{
iter->selfItem->setText(2, QString("--"));
}
else if ((iter->type == RC_FILE) && DocTypeListView::isSupportExt(fileSuffix(iter->relativePath)))
{
QFutureWatcher<EncodeThreadParameter_*>* futureWatcher = new QFutureWatcher<EncodeThreadParameter_*>();
QObject::connect(futureWatcher, &QFutureWatcher<EncodeThreadParameter_>::finished, this, &EncodeConvert::slot_scanFileCode);
futureWatcher->setFuture(this->checkFileCode(iter->relativePath,iter->selfItem));
++m_commitCmpFileNums;
}
else
{
iter->selfItem->setText(2, tr("ignore"));
}
}
int finishProcessRatio = 0;
while (m_finishCmpFileNums < m_commitCmpFileNums)
{
int curProcessRatio = m_finishCmpFileNums * 100 / m_commitCmpFileNums;
//没%5更新一下
if (curProcessRatio - finishProcessRatio >= 5)
{
finishProcessRatio = curProcessRatio;
ui.logTextBrowser->append(tr("please wait, total file %1,cur scan index %2, scan finish %3%").arg(m_commitCmpFileNums).arg(m_finishCmpFileNums).arg(curProcessRatio));
}
QCoreApplication::processEvents();
}
ui.logTextBrowser->append(tr("scan finished, total file %1").arg(m_commitCmpFileNums));
ui.selectFileBt->setEnabled(true);
ui.startBt->setEnabled(true);
ui.closeBt->setEnabled(true);
}
//文件对比完毕,显示出文件是否意义,不一样则红色字符标识
void EncodeConvert::slot_scanFileCode()
{
QFutureWatcher<EncodeThreadParameter_*>* s = dynamic_cast<QFutureWatcher<EncodeThreadParameter_*> *>(sender());
EncodeThreadParameter_* result = s->result();
//这里释放的内容其实是在mode里面new出来的
if (result != nullptr)
{
result->item->setText(2, Encode::getCodeNameById(result->code));
result->item->setData(0, ITEM_CODE, result->code);
delete result;
result = nullptr;
}
delete s;
s = nullptr;
++m_finishCmpFileNums;
}
void EncodeConvert::slot_startConvert()
{
int extComboBoxIndex = ui.extComboBox->currentIndex();
CODE_ID dstCode = getComboBoxCode(ui.codeToComboBox->currentIndex());
m_finishCmpFileNums = 0;
m_commitCmpFileNums = 0;
ui.logTextBrowser->clear();
//如果编码是已知如下类型,则后续保存其它行时,不修改编码格式,继续按照原编码进行保存
QString destCodeName = Encode::getQtCodecNameById(dstCode);
if (destCodeName.isEmpty() || destCodeName == "unknown")
{
//这里永远不会走。因为界面上不会有未知选项
assert(false);
return;
}
else
{
QTextCodec::setCodecForLocale(QTextCodec::codecForName(destCodeName.toStdString().c_str()));
}
ui.selectFileBt->setEnabled(false);
ui.codeToComboBox->setEditable(false);
ui.closeBt->setEnabled(false);
for (QList<fileAttriNode>::iterator iter = m_fileAttris.begin(); iter != m_fileAttris.end(); ++iter)
{
if ((iter->type == RC_FILE) && isSupportExt(extComboBoxIndex, fileSuffix(iter->relativePath)))
{
qDebug() << iter->relativePath;
CODE_ID srcCode = static_cast<CODE_ID>(iter->selfItem->data(0, ITEM_CODE).toInt());
CODE_ID dstCode = getComboBoxCode(ui.codeToComboBox->currentIndex());
if (srcCode != dstCode)
{
QFutureWatcher<EncodeThreadParameter_*>* futureWatcher = new QFutureWatcher<EncodeThreadParameter_*>();
QObject::connect(futureWatcher, &QFutureWatcher<EncodeThreadParameter_>::finished, this, &EncodeConvert::slot_convertFileFinish);
futureWatcher->setFuture(this->convertFileCode(iter->relativePath, iter->selfItem));
++m_commitCmpFileNums;
}
else
{
iter->selfItem->setText(4, tr("already %1 ignore").arg(Encode::getCodeNameById(srcCode)));
}
}
else
{
iter->selfItem->setText(4, tr("ignore"));
}
}
int finishProcessRatio = 0;
while (m_finishCmpFileNums < m_commitCmpFileNums)
{
int curProcessRatio = m_finishCmpFileNums * 100 / m_commitCmpFileNums;
//没%5更新一下
if (curProcessRatio - finishProcessRatio >= 5)
{
finishProcessRatio = curProcessRatio;
ui.logTextBrowser->append(tr("total file %1,cur deal index %2,finish %3%").arg(m_commitCmpFileNums).arg(m_finishCmpFileNums).arg(curProcessRatio));
}
QCoreApplication::processEvents();
}
ui.logTextBrowser->append(tr("total file %1,cur deal index %2,finish 100%").arg(m_commitCmpFileNums).arg(m_finishCmpFileNums));
ui.logTextBrowser->append(tr("convert finished !"));
ui.selectFileBt->setEnabled(true);
ui.codeToComboBox->setEditable(true);
ui.closeBt->setEnabled(true);
}
//转换完成,设置当前表格上的显示状态
void EncodeConvert::slot_convertFileFinish()
{
QFutureWatcher<EncodeThreadParameter_*>* s = dynamic_cast<QFutureWatcher<EncodeThreadParameter_*> *>(sender());
EncodeThreadParameter_* result = s->result();
//这里释放的内容其实是在mode里面new出来的
if (result != nullptr)
{
if (result->code != UNKOWN)
{
result->item->setText(3, Encode::getCodeNameById(result->code));
result->item->setText(4, tr("convert finish"));
}
else
{
result->item->setText(4, tr("convert fail"));
ui.logTextBrowser->append(tr("file %1 convert failed,pleas check...").arg(result->item->data(0, Qt::ToolTipRole).toString()));
}
result->item->setData(0, ITEM_CODE, result->code);
delete result;
result = nullptr;
}
delete s;
s = nullptr;
++m_finishCmpFileNums;
}
//对item进行间隔着色
void EncodeConvert::setItemIntervalBackground()
{
int curItemIndex = 0;
QTreeWidgetItemIterator it(ui.treeWidget);
while (*it) {
if (curItemIndex % 2 == 1)
{
setItemBackground(*it, QColor(0xf8faf9));
}
++it;
++curItemIndex;
}
}
void EncodeConvert::setItemBackground(QTreeWidgetItem* item, const QColor& color)
{
QBrush b(color);
item->setBackground(0, b);
item->setBackground(1, b);
item->setBackground(2, b);
item->setBackground(3, b);
item->setBackground(4, b);
}
void EncodeConvert::dragEnterEvent(QDragEnterEvent* event)
{
if (event->mimeData()->hasFormat("text/uri-list")) //只能打开文本文件
{
event->accept(); //可以在这个窗口部件上拖放对象
}
else
{
event->ignore();
}
}
void EncodeConvert::dropEvent(QDropEvent* e)
{
QList<QUrl> urls = e->mimeData()->urls();
if (urls.isEmpty())
return;
QString dirName = urls.first().toLocalFile();
if (dirName.isEmpty())
{
return;
}
QDir dir(dirName);
if (!dir.exists())
{
ui.logTextBrowser->append(tr("please drop a file dir ..."));
return;
}
ui.treeWidget->clear();
m_fileAttris.clear();
loadDir(dirName);
setItemIntervalBackground();
scanFileCode();
e->accept();
}