From b9f36558ba7f0446fc1bf0e23a433fdf9000a59b Mon Sep 17 00:00:00 2001 From: Cyano Hao Date: Sun, 29 Sep 2024 20:01:14 +0800 Subject: [PATCH] Linux Qt 6 port (#490) * initial Qt 6 port * move arch linux package to Qt 6 * minor fix --- BUILD.md | 2 +- BUILD_cn.md | 2 +- RedPandaIDE/compiler/compiler.cpp | 1 - RedPandaIDE/compiler/stdincompiler.cpp | 7 +- RedPandaIDE/cpprefacter.cpp | 1 - RedPandaIDE/debugger/debugger.cpp | 7 +- RedPandaIDE/editor.cpp | 4 +- RedPandaIDE/main.cpp | 11 +- RedPandaIDE/mainwindow.cpp | 8 +- RedPandaIDE/parser/cpppreprocessor.cpp | 1 - RedPandaIDE/parser/cpptokenizer.h | 2 +- RedPandaIDE/project.cpp | 12 +- RedPandaIDE/settings.cpp | 1 - .../settingsdialog/projectgeneralwidget.cpp | 1 - RedPandaIDE/systemconsts.cpp | 24 +- RedPandaIDE/utils.h | 8 + RedPandaIDE/widgets/coloredit.cpp | 4 + RedPandaIDE/widgets/coloredit.h | 4 + RedPandaIDE/widgets/functiontooltipwidget.cpp | 1 + RedPandaIDE/widgets/labelwithmenu.cpp | 4 + RedPandaIDE/widgets/labelwithmenu.h | 4 + RedPandaIDE/widgets/qconsole.h | 3 + RedPandaIDE/widgets/qpatchedcombobox.h | 12 + docs/detailed-build-xdg-cn.md | 2 +- docs/detailed-build-xdg.md | 2 +- libs/qsynedit/qsynedit/document.cpp | 84 ++--- libs/qsynedit/qsynedit/document.h | 4 +- libs/qsynedit/qsynedit/exporter/exporter.cpp | 24 +- libs/qsynedit/qsynedit/exporter/exporter.h | 2 +- libs/qsynedit/qsynedit/gutter.cpp | 1 + libs/qsynedit/qsynedit/keystrokes.cpp | 4 +- libs/qsynedit/qsynedit/syntaxer/asm.cpp | 4 +- libs/qsynedit/qsynedit/syntaxer/cpp.cpp | 3 +- libs/qsynedit/qsynedit/syntaxer/glsl.cpp | 2 +- libs/qsynedit/qsynedit/syntaxer/lua.cpp | 3 +- libs/qsynedit/qsynedit/syntaxer/makefile.cpp | 2 +- libs/qsynedit/qsynedit/types.h | 4 + .../qt_utils/charsetinfo.cpp | 4 +- libs/redpanda_qt_utils/qt_utils/utils.cpp | 338 +++++++++++++++--- libs/redpanda_qt_utils/qt_utils/utils.h | 116 +++++- packages/archlinux/01-in-docker.sh | 2 +- packages/archlinux/PKGBUILD.in | 6 +- 42 files changed, 558 insertions(+), 173 deletions(-) diff --git a/BUILD.md b/BUILD.md index 12dfe9a2..47cede3c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,6 +1,6 @@ # General Development Notes -Red Panda C++ need Qt 5.15 to build. +Red Panda C++ need Qt 5.15 or 6.7+ to build. Recommended development environments: 1. Visual Studio Code. diff --git a/BUILD_cn.md b/BUILD_cn.md index a8cee914..f8e7967a 100644 --- a/BUILD_cn.md +++ b/BUILD_cn.md @@ -1,6 +1,6 @@ # 通用开发说明 -小熊猫C++ 需要 Qt 5.15。 +小熊猫C++ 需要 Qt 5.15 或 6.7+。 推荐开发环境: 1. Visual Studio Code。 diff --git a/RedPandaIDE/compiler/compiler.cpp b/RedPandaIDE/compiler/compiler.cpp index fe0a5ce9..e8ef1640 100644 --- a/RedPandaIDE/compiler/compiler.cpp +++ b/RedPandaIDE/compiler/compiler.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include "../editor.h" diff --git a/RedPandaIDE/compiler/stdincompiler.cpp b/RedPandaIDE/compiler/stdincompiler.cpp index ba4ed54f..66ceec5e 100644 --- a/RedPandaIDE/compiler/stdincompiler.cpp +++ b/RedPandaIDE/compiler/stdincompiler.cpp @@ -18,7 +18,6 @@ #include "compilermanager.h" #include #include -#include StdinCompiler::StdinCompiler(const QString &filename,const QByteArray& encoding, const QString& content, bool onlyCheckSyntax): Compiler(filename, onlyCheckSyntax), @@ -98,9 +97,9 @@ QByteArray StdinCompiler::pipedText() if (mEncoding == ENCODING_ASCII) return mContent.toLatin1(); - QTextCodec* codec = QTextCodec::codecForName(mEncoding); - if (codec) { - return codec->fromUnicode(mContent); + TextEncoder encoder(mEncoding); + if (encoder.isValid()) { + return encoder.encodeUnchecked(mContent); } else { return mContent.toLocal8Bit(); } diff --git a/RedPandaIDE/cpprefacter.cpp b/RedPandaIDE/cpprefacter.cpp index 5a62af36..3716834f 100644 --- a/RedPandaIDE/cpprefacter.cpp +++ b/RedPandaIDE/cpprefacter.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "cpprefacter.h" #include "mainwindow.h" diff --git a/RedPandaIDE/debugger/debugger.cpp b/RedPandaIDE/debugger/debugger.cpp index 7a33ae22..e4954044 100644 --- a/RedPandaIDE/debugger/debugger.cpp +++ b/RedPandaIDE/debugger/debugger.cpp @@ -2542,11 +2542,8 @@ QVariant MemoryModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { if (col==line->datas.count()) { QString s; - foreach (unsigned char ch, line->datas) { - if (ch<' ' || ch>=128) - s+='.'; - else - s+=ch; + for (char ch : line->datas) { + s += isAsciiPrint(ch) ? ch : '.'; } return s; } else diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index bb02cae1..5f5c5e41 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -4230,7 +4228,7 @@ void Editor::updateFunctionTip(bool showTip) return; QChar ch=lastNonSpaceChar(currentLine,currentChar); - if (ch!="(" && ch!=",") + if (ch!='(' && ch!=',') return; QSynedit::PTokenAttribute attr; diff --git a/RedPandaIDE/main.cpp b/RedPandaIDE/main.cpp index 0789cf08..48b31627 100644 --- a/RedPandaIDE/main.cpp +++ b/RedPandaIDE/main.cpp @@ -59,7 +59,11 @@ class WindowLogoutEventFilter : public QAbstractNativeEventFilter { // QAbstractNativeEventFilter interface public: +#if QT_VERSION_MAJOR >= 6 + bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; +#else bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; +#endif }; #ifndef WM_DPICHANGED @@ -124,7 +128,12 @@ HWND getPreviousInstance() { return NULL; } -bool WindowLogoutEventFilter::nativeEventFilter(const QByteArray & /*eventType*/, void *message, long *result){ +#if QT_VERSION_MAJOR >= 6 +bool WindowLogoutEventFilter::nativeEventFilter(const QByteArray & /*eventType*/, void *message, qintptr *result) +#else +bool WindowLogoutEventFilter::nativeEventFilter(const QByteArray & /*eventType*/, void *message, long *result) +#endif +{ MSG * pMsg = static_cast(message); switch(pMsg->message) { case WM_QUERYENDSESSION: diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 6efc4738..b2bda0a0 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,7 @@ #include #include #include +#include #include "mainwindow.h" #include "ui_mainwindow.h" @@ -1733,7 +1733,7 @@ Editor* MainWindow::openFile(QString filename, bool activate, QTabWidget* page) } bool inProject = (mProject && unit); QByteArray encoding = unit ? unit->encoding() : - (pSettings->editor().autoDetectFileEncoding()? ENCODING_AUTO_DETECT : pSettings->editor().defaultEncoding()); + (pSettings->editor().autoDetectFileEncoding() ? QByteArray(ENCODING_AUTO_DETECT) : pSettings->editor().defaultEncoding()); Project * pProject = (inProject?mProject.get():nullptr); if (pProject && encoding==ENCODING_PROJECT) encoding=pProject->options().encoding; @@ -3449,7 +3449,7 @@ void MainWindow::loadLastOpens() } bool inProject = (mProject && unit); QByteArray encoding = unit ? unit->encoding() : - (pSettings->editor().autoDetectFileEncoding()? ENCODING_AUTO_DETECT : pSettings->editor().defaultEncoding()); + (pSettings->editor().autoDetectFileEncoding()? QByteArray(ENCODING_AUTO_DETECT) : pSettings->editor().defaultEncoding()); Project* pProject = (inProject?mProject.get():nullptr); if (pProject && encoding==ENCODING_PROJECT) encoding=pProject->options().encoding; @@ -6349,7 +6349,7 @@ void MainWindow::on_actionConvert_to_ANSI_triggered() return; if (QMessageBox::warning(this,tr("Confirm Convertion"), tr("The editing file will be saved using %1 encoding.
This operation can't be reverted.
Are you sure to continue?") - .arg(QString(QTextCodec::codecForLocale()->name())), + .arg(QString(TextEncoder::encoderForSystem().name())), QMessageBox::Yes, QMessageBox::No)!=QMessageBox::Yes) return; editor->convertToEncoding(ENCODING_SYSTEM_DEFAULT); diff --git a/RedPandaIDE/parser/cpppreprocessor.cpp b/RedPandaIDE/parser/cpppreprocessor.cpp index 55c67b3b..60ad6038 100644 --- a/RedPandaIDE/parser/cpppreprocessor.cpp +++ b/RedPandaIDE/parser/cpppreprocessor.cpp @@ -17,7 +17,6 @@ #include "cpppreprocessor.h" #include -#include #include #include #include "../utils.h" diff --git a/RedPandaIDE/parser/cpptokenizer.h b/RedPandaIDE/parser/cpptokenizer.h index dd1782c6..6514a587 100644 --- a/RedPandaIDE/parser/cpptokenizer.h +++ b/RedPandaIDE/parser/cpptokenizer.h @@ -113,7 +113,7 @@ private: } static bool isBlankChar(const QChar& ch) { - return (ch<=32) && (ch>0); + return (ch.unicode() <= 32) && (ch.unicode() > 0); } // static bool isOperatorChar(const QChar& ch) { diff --git a/RedPandaIDE/project.cpp b/RedPandaIDE/project.cpp index 2321a281..d8d97adb 100644 --- a/RedPandaIDE/project.cpp +++ b/RedPandaIDE/project.cpp @@ -16,6 +16,7 @@ */ #include "project.h" #include "editor.h" +#include "qt_utils/utils.h" #include "utils.h" #include "systemconsts.h" #include "editorlist.h" @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -255,7 +255,7 @@ void Project::open() if (newUnit->encoding()!=ENCODING_UTF16_BOM && newUnit->encoding()!=ENCODING_UTF8_BOM && newUnit->encoding()!=ENCODING_UTF32_BOM && - QTextCodec::codecForName(newUnit->encoding())==nullptr) { + !isEncodingAvailable(newUnit->encoding())) { newUnit->setEncoding(ENCODING_PROJECT); } newUnit->setRealEncoding(ini.GetValue(groupName, "RealEncoding",ENCODING_ASCII)); @@ -944,11 +944,9 @@ bool Project::assignTemplate(const std::shared_ptr aTemplate, b updateCompilerSetting(); mOptions.icon = aTemplate->icon(); - QTextCodec* codec=QTextCodec::codecForName(mOptions.encoding); - if (!codec) + if (!isEncodingAvailable(mOptions.encoding)) mOptions.encoding=ENCODING_SYSTEM_DEFAULT; - codec=QTextCodec::codecForName(mOptions.execEncoding); - if (!codec) + if (!isEncodingAvailable(mOptions.execEncoding)) mOptions.execEncoding=ENCODING_SYSTEM_DEFAULT; // Copy icon to project directory @@ -2611,7 +2609,7 @@ QVariant ProjectModel::data(const QModelIndex &index, int role) const if (p->isUnit) { PProjectUnit unit = p->pUnit.lock(); if (unit) - icon = mIconProvider->icon(unit->fileName()); + icon = mIconProvider->icon(QFileInfo(unit->fileName())); } else { if (p == mProject->rootNode().get()) { #ifdef ENABLE_VCS diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index b1e044a5..4febc731 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -16,7 +16,6 @@ */ #include "settings.h" #include -#include #include #include "utils.h" #include "utils/escape.h" diff --git a/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp b/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp index 59a592ca..a7cefd6a 100644 --- a/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp +++ b/RedPandaIDE/settingsdialog/projectgeneralwidget.cpp @@ -28,7 +28,6 @@ #include #include #include -#include ProjectGeneralWidget::ProjectGeneralWidget(const QString &name, const QString &group, QWidget *parent) : SettingsWidget(name,group,parent), diff --git a/RedPandaIDE/systemconsts.cpp b/RedPandaIDE/systemconsts.cpp index b6602802..1fb8cddc 100644 --- a/RedPandaIDE/systemconsts.cpp +++ b/RedPandaIDE/systemconsts.cpp @@ -20,7 +20,6 @@ #include #include #include -#include SystemConsts* pSystemConsts; @@ -44,28 +43,7 @@ SystemConsts::SystemConsts(): mDefaultFileFilters() mCodecNames.append(ENCODING_SYSTEM_DEFAULT); mCodecNames.append(ENCODING_UTF8); mCodecNames.append(ENCODING_UTF8_BOM); - QStringList codecNames; - QSet codecAlias; - codecAlias.insert("system"); - codecAlias.insert("utf-8"); - - foreach (const QByteArray& name, QTextCodec::availableCodecs()){ - QByteArray lname = name.toLower(); - if (lname.startsWith("cp")) - continue; - if (codecAlias.contains(lname)) - continue; - codecNames.append(lname); - codecAlias.insert(lname); - QTextCodec* codec = QTextCodec::codecForName(name); - if (codec) { - foreach (const QByteArray& alias, codec->aliases()) { - codecAlias.insert(alias.toLower()); - } - } - } - std::sort(codecNames.begin(),codecNames.end()); - mCodecNames.append(codecNames); + mCodecNames.append(availableEncodings()); mDefaultFileNameFilters.append("*.c"); mDefaultFileNameFilters.append("*.cpp"); diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index ef4aac82..abd458a1 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -227,4 +227,12 @@ bool osSupportsUtf8Manifest(); bool applicationIsUtf8(const QString &path); #endif +#if QT_VERSION_MAJOR >= 6 +// for xml.name() == "tag" +inline bool operator==(QStringView a, const char *b) +{ + return a.compare(b); +} +#endif + #endif // UTILS_H diff --git a/RedPandaIDE/widgets/coloredit.cpp b/RedPandaIDE/widgets/coloredit.cpp index f96b862a..eccecd49 100644 --- a/RedPandaIDE/widgets/coloredit.cpp +++ b/RedPandaIDE/widgets/coloredit.cpp @@ -115,7 +115,11 @@ void ColorEdit::mouseReleaseEvent(QMouseEvent *) } } +#if QT_VERSION_MAJOR >= 6 +void ColorEdit::enterEvent(QEnterEvent *) +#else void ColorEdit::enterEvent(QEvent *) +#endif { setCursor(Qt::PointingHandCursor); } diff --git a/RedPandaIDE/widgets/coloredit.h b/RedPandaIDE/widgets/coloredit.h index 3e8d39b2..6daa8bcb 100644 --- a/RedPandaIDE/widgets/coloredit.h +++ b/RedPandaIDE/widgets/coloredit.h @@ -37,7 +37,11 @@ public: QSize sizeHint() const override; void paintEvent(QPaintEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; +#if QT_VERSION_MAJOR >= 6 + void enterEvent(QEnterEvent *event) override; +#else void enterEvent(QEvent *event) override; +#endif void leaveEvent(QEvent *event) override; QSize minimumSizeHint() const override; }; diff --git a/RedPandaIDE/widgets/functiontooltipwidget.cpp b/RedPandaIDE/widgets/functiontooltipwidget.cpp index bca2aa22..5f82d48e 100644 --- a/RedPandaIDE/widgets/functiontooltipwidget.cpp +++ b/RedPandaIDE/widgets/functiontooltipwidget.cpp @@ -19,6 +19,7 @@ #include #include #include +#include FunctionTooltipWidget::FunctionTooltipWidget(QWidget *parent) : QFrame{parent, Qt::ToolTip | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus}, diff --git a/RedPandaIDE/widgets/labelwithmenu.cpp b/RedPandaIDE/widgets/labelwithmenu.cpp index 60ceedee..3b0e4108 100644 --- a/RedPandaIDE/widgets/labelwithmenu.cpp +++ b/RedPandaIDE/widgets/labelwithmenu.cpp @@ -33,7 +33,11 @@ void LabelWithMenu::mousePressEvent(QMouseEvent *event) event->accept(); } +#if QT_VERSION_MAJOR >= 6 +void LabelWithMenu::enterEvent(QEnterEvent *event) +#else void LabelWithMenu::enterEvent(QEvent *event) +#endif { mCursor = cursor(); setCursor(Qt::PointingHandCursor); diff --git a/RedPandaIDE/widgets/labelwithmenu.h b/RedPandaIDE/widgets/labelwithmenu.h index 6e00cfc8..ac6c1171 100644 --- a/RedPandaIDE/widgets/labelwithmenu.h +++ b/RedPandaIDE/widgets/labelwithmenu.h @@ -31,7 +31,11 @@ protected: // QWidget interface protected: +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent *event) override; +#else void enterEvent(QEvent *event) override; +#endif void leaveEvent(QEvent *event) override; private: diff --git a/RedPandaIDE/widgets/qconsole.h b/RedPandaIDE/widgets/qconsole.h index 1ffd0fc8..c68211ad 100644 --- a/RedPandaIDE/widgets/qconsole.h +++ b/RedPandaIDE/widgets/qconsole.h @@ -51,6 +51,9 @@ struct RowColumn { struct LineChar { int ch; int line; + + LineChar() = default; + constexpr LineChar(qsizetype ch_, qsizetype line_) : ch(ch_), line(line_) {} }; class QConsole; diff --git a/RedPandaIDE/widgets/qpatchedcombobox.h b/RedPandaIDE/widgets/qpatchedcombobox.h index ff1227a5..0aed0955 100644 --- a/RedPandaIDE/widgets/qpatchedcombobox.h +++ b/RedPandaIDE/widgets/qpatchedcombobox.h @@ -35,6 +35,13 @@ protected: QListView::resizeEvent(event); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void initViewItemOption(QStyleOptionViewItem *option) const override + { + QListView::initViewItemOption(option); + option->showDecorationSelected = true; + } +#else QStyleOptionViewItem viewOptions() const override { QStyleOptionViewItem option = QListView::viewOptions(); @@ -43,6 +50,7 @@ protected: // option.font = combo->font(); return option; } +#endif void paintEvent(QPaintEvent *e) override { @@ -59,7 +67,11 @@ protected: menuOpt.checkType = QStyleOptionMenuItem::NotCheckable; menuOpt.menuRect = e->rect(); menuOpt.maxIconWidth = 0; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + menuOpt.reservedShortcutWidth = 0; +#else menuOpt.tabWidth = 0; +#endif QPainter p(viewport()); combo->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this); } diff --git a/docs/detailed-build-xdg-cn.md b/docs/detailed-build-xdg-cn.md index d0322644..291c29e6 100644 --- a/docs/detailed-build-xdg-cn.md +++ b/docs/detailed-build-xdg-cn.md @@ -3,7 +3,7 @@ ## 传统 Unix 方式(`./configure`–`make`–`make install`) - 安装支持 C++17 的 GCC(≥ 7)或 Clang(≥ 6)。 -- 安装 Qt 5.15 Base、SVG、Tools 模块,包括库和开发文件。 +- 安装 Qt 5.15 或 6.7+ Base、SVG、Tools 模块,包括库和开发文件。 - 如果使用静态版本的 Qt 编译,还要安装 fcitx5-qt。 - 安装 astyle 以便在小熊猫 C++ 中对代码进行重新排版。 diff --git a/docs/detailed-build-xdg.md b/docs/detailed-build-xdg.md index 3d88c37f..84796c1d 100644 --- a/docs/detailed-build-xdg.md +++ b/docs/detailed-build-xdg.md @@ -3,7 +3,7 @@ ## Traditional Unix Way (`./configure`–`make`–`make install`) - Install recent version of GCC (≥ 7) or Clang (≥ 6) that supports C++17. -- Install Qt 5.15 Base, SVG and Tools modules, including both libraries and development files. +- Install Qt 5.15 or 6.7+ Base, SVG and Tools modules, including both libraries and development files. - Optionally install fcitx5-qt for building with static Qt library. - Install astyle for code formatting in Red Panda C++. diff --git a/libs/qsynedit/qsynedit/document.cpp b/libs/qsynedit/qsynedit/document.cpp index f8e7f5b9..a368524d 100644 --- a/libs/qsynedit/qsynedit/document.cpp +++ b/libs/qsynedit/qsynedit/document.cpp @@ -18,12 +18,12 @@ #include "qt_utils/utils.h" #include #include -#include #include #include #include #include #include +#include #include "qt_utils/charsetinfo.h" #include #include @@ -498,12 +498,11 @@ void Document::insertLines(int index, int numLines) bool Document::tryLoadFileByEncoding(QByteArray encodingName, QFile& file) { - QTextCodec* codec = QTextCodec::codecForName(encodingName); - if (!codec) + TextDecoder decoder(encodingName); + if (!decoder.isValid()) return false; file.reset(); internalClear(); - QTextCodec::ConverterState state; while (true) { if (file.atEnd()){ break; @@ -516,10 +515,9 @@ bool Document::tryLoadFileByEncoding(QByteArray encodingName, QFile& file) { } else if (line.endsWith("\n")){ line.remove(line.length()-1,1); } - QString newLine = codec->toUnicode(line.constData(),line.length(),&state); - if (state.invalidChars>0) { + auto [ok, newLine] = decoder.decode(line); + if (!ok) { return false; - break; } addItem(newLine); } @@ -528,46 +526,46 @@ bool Document::tryLoadFileByEncoding(QByteArray encodingName, QFile& file) { void Document::loadUTF16BOMFile(QFile &file) { - QTextCodec* codec=QTextCodec::codecForName(ENCODING_UTF16); - if (!codec) + TextDecoder decoder = TextDecoder::decoderForUtf16(); + if (!decoder.isValid()) return; file.reset(); internalClear(); QByteArray buf = file.readAll(); if (buf.length()<2) return; - QString text = codec->toUnicode(buf.mid(2)); + QString text = decoder.decodeUnchecked(buf.mid(2)); this->setText(text); } void Document::loadUTF32BOMFile(QFile &file) { - QTextCodec* codec=QTextCodec::codecForName(ENCODING_UTF32); - if (!codec) + TextDecoder decoder = TextDecoder::decoderForUtf32(); + if (!decoder.isValid()) return; file.reset(); internalClear(); QByteArray buf = file.readAll(); if (buf.length()<4) return; - QString text = codec->toUnicode(buf.mid(4)); + QString text = decoder.decodeUnchecked(buf.mid(4)); this->setText(text); } -void Document::saveUTF16File(QFile &file, QTextCodec* codec) +void Document::saveUTF16File(QFile &file, TextEncoder &encoder) { - if (!codec) + if (!encoder.isValid()) return; QString text=getTextStr(); - file.write(codec->fromUnicode(text)); + file.write(encoder.encodeUnchecked(text)); } -void Document::saveUTF32File(QFile &file, QTextCodec* codec) +void Document::saveUTF32File(QFile &file, TextEncoder &encoder) { - if (!codec) + if (!encoder.isValid()) return; QString text=getTextStr(); - file.write(codec->fromUnicode(text)); + file.write(encoder.encodeUnchecked(text)); } void Document::setTabSize(int newTabSize) @@ -606,15 +604,14 @@ void Document::loadFromFile(const QString& filename, const QByteArray& encoding, return; } QByteArray line = file.readLine(); - QTextCodec* codec; - QTextCodec::ConverterState state; + std::optional decoder; bool needReread = false; bool allAscii = true; //test for BOM if ((line.length()>=3) && ((unsigned char)line[0]==0xEF) && ((unsigned char)line[1]==0xBB) && ((unsigned char)line[2]==0xBF) ) { realEncoding = ENCODING_UTF8_BOM; line = line.mid(3); - codec = QTextCodec::codecForName(ENCODING_UTF8); + decoder = TextDecoder::decoderForUtf8(); } else if ((line.length()>=4) && ((unsigned char)line[0]==0xFF) && ((unsigned char)line[1]==0xFE) && ((unsigned char)line[2]==0x00) && ((unsigned char)line[3]==0x00)) { @@ -627,9 +624,9 @@ void Document::loadFromFile(const QString& filename, const QByteArray& encoding, return; } else { realEncoding = ENCODING_UTF8; - codec = QTextCodec::codecForName(ENCODING_UTF8); + decoder = TextDecoder::decoderForUtf8(); } - if (!codec) + if (!decoder.has_value()) throw FileError(tr("Can't load codec '%1'!").arg(QString(realEncoding))); if (line.endsWith("\r\n")) { mNewlineType = NewlineType::Windows; @@ -656,8 +653,8 @@ void Document::loadFromFile(const QString& filename, const QByteArray& encoding, if (allAscii) { addItem(QString::fromLatin1(line)); } else { - QString newLine = codec->toUnicode(line.constData(),line.length(),&state); - if (state.invalidChars>0) { + auto [ok, newLine] = decoder->decode(line); + if (!ok) { needReread = true; break; } @@ -703,13 +700,16 @@ void Document::loadFromFile(const QString& filename, const QByteArray& encoding, realEncoding = pCharsetInfoManager->getDefaultSystemEncoding(); } file.reset(); - QTextStream textStream(&file); + QByteArray data = file.readAll(); + QString text; + QTextStream textStream(&text); if (realEncoding == ENCODING_UTF8_BOM) { textStream.setAutoDetectUnicode(true); - textStream.setCodec(ENCODING_UTF8); + text = QString::fromUtf8(data); } else { textStream.setAutoDetectUnicode(false); - textStream.setCodec(realEncoding); + TextDecoder decoder(realEncoding); + text = decoder.decodeUnchecked(data); } QString line; internalClear(); @@ -731,28 +731,28 @@ void Document::saveToFile(QFile &file, const QByteArray& encoding, const QByteArray& defaultEncoding, QByteArray& realEncoding) { QMutexLocker locker(&mMutex); - QTextCodec* codec; + std::optional encoder; realEncoding = encoding; QString codecName = realEncoding; if (realEncoding == ENCODING_UTF16_BOM || realEncoding == ENCODING_UTF16) { - codec = QTextCodec::codecForName(ENCODING_UTF16); + encoder = TextEncoder::encoderForUtf16(); codecName = ENCODING_UTF16; } else if (realEncoding == ENCODING_UTF32_BOM || realEncoding == ENCODING_UTF32) { - codec = QTextCodec::codecForName(ENCODING_UTF32); + encoder = TextEncoder::encoderForUtf32(); codecName = ENCODING_UTF32; } else if (realEncoding == ENCODING_UTF8_BOM) { - codec = QTextCodec::codecForName(ENCODING_UTF8); + encoder = TextEncoder::encoderForUtf8(); codecName = ENCODING_UTF8; } else if (realEncoding == ENCODING_SYSTEM_DEFAULT) { - codec = QTextCodec::codecForLocale(); + encoder = TextEncoder::encoderForSystem(); codecName = realEncoding; } else if (realEncoding == ENCODING_AUTO_DETECT) { - codec = QTextCodec::codecForName(defaultEncoding); + encoder = TextEncoder(defaultEncoding); codecName = defaultEncoding; } else { - codec = QTextCodec::codecForName(realEncoding); + encoder = TextEncoder(realEncoding); } - if (!codec) + if (!encoder.has_value() || !encoder->isValid()) throw FileError(tr("Can't load codec '%1'!").arg(codecName)); if (!file.open(QFile::WriteOnly | QFile::Truncate)) @@ -760,10 +760,10 @@ void Document::saveToFile(QFile &file, const QByteArray& encoding, if (mLines.isEmpty()) return; if (realEncoding == ENCODING_UTF16) { - saveUTF16File(file,codec); + saveUTF16File(file, encoder.value()); return; } else if (realEncoding == ENCODING_UTF32) { - saveUTF32File(file,codec); + saveUTF32File(file, encoder.value()); return; } if (realEncoding == ENCODING_UTF8_BOM) { file.putChar(0xEF); @@ -774,7 +774,7 @@ void Document::saveToFile(QFile &file, const QByteArray& encoding, QByteArray data; for (PDocumentLine& line:mLines) { QString text = line->lineText()+lineBreak(); - data = codec->fromUnicode(text); + data = encoder->encodeUnchecked(text); if (allAscii) { allAscii = (data==text.toLatin1()); } @@ -784,10 +784,10 @@ void Document::saveToFile(QFile &file, const QByteArray& encoding, if (allAscii) { realEncoding = ENCODING_ASCII; } else if (realEncoding == ENCODING_SYSTEM_DEFAULT) { - if (QString(codec->name()).compare("System",Qt::CaseInsensitive)==0) { + if (encoder->name().compare("System",Qt::CaseInsensitive)==0) { realEncoding = pCharsetInfoManager->getDefaultSystemEncoding(); } else { - realEncoding = codec->name(); + realEncoding = encoder->name(); } } } diff --git a/libs/qsynedit/qsynedit/document.h b/libs/qsynedit/qsynedit/document.h index cb4ae6df..293a02d8 100644 --- a/libs/qsynedit/qsynedit/document.h +++ b/libs/qsynedit/qsynedit/document.h @@ -647,8 +647,8 @@ private: bool tryLoadFileByEncoding(QByteArray encodingName, QFile& file); void loadUTF16BOMFile(QFile& file); void loadUTF32BOMFile(QFile& file); - void saveUTF16File(QFile& file, QTextCodec* codec); - void saveUTF32File(QFile& file, QTextCodec* codec); + void saveUTF16File(QFile& file, TextEncoder &encoder); + void saveUTF32File(QFile& file, TextEncoder &encoder); private: DocumentLines mLines; diff --git a/libs/qsynedit/qsynedit/exporter/exporter.cpp b/libs/qsynedit/qsynedit/exporter/exporter.cpp index e98b9ff3..cda8de75 100644 --- a/libs/qsynedit/qsynedit/exporter/exporter.cpp +++ b/libs/qsynedit/qsynedit/exporter/exporter.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include namespace QSynedit { @@ -131,8 +131,9 @@ void Exporter::saveToFile(const QString &filename) void Exporter::writeToStream(QIODevice &stream) { - QTextCodec *codec=getCodec(); - if (stream.write(codec->fromUnicode(mText))<0) { + TextEncoder encoder = getEncoder(); + auto [ok, encoded] = encoder.encode(mText); + if (ok && stream.write(encoded) < 0) { throw FileError(QObject::tr("Failed to write data.")); } } @@ -274,12 +275,14 @@ int Exporter::getBufferSize() const return mText.size(); } -QTextCodec * Exporter::getCodec() const { - QTextCodec* codec = QTextCodec::codecForName(mCharset); - if (codec == nullptr) - codec = QTextCodec::codecForLocale(); - return codec; +TextEncoder Exporter::getEncoder() const { + TextEncoder encoder(mCharset); + if (encoder.isValid()) + return encoder; + else + return TextEncoder::encoderForSystem(); } + void Exporter::insertData(int pos, const QString &text) { if (!text.isEmpty()) { @@ -339,8 +342,9 @@ void Exporter::setTokenAttribute(PTokenAttribute attri) QByteArray Exporter::buffer() const { - QTextCodec* codec = getCodec(); - return codec->fromUnicode(mText); + TextEncoder encoder = getEncoder(); + auto [_, encoded] = encoder.encode(mText); + return encoded; } const QString &Exporter::text() const diff --git a/libs/qsynedit/qsynedit/exporter/exporter.h b/libs/qsynedit/qsynedit/exporter/exporter.h index 69281ca2..5197c8d9 100644 --- a/libs/qsynedit/qsynedit/exporter/exporter.h +++ b/libs/qsynedit/qsynedit/exporter/exporter.h @@ -242,7 +242,7 @@ protected: */ virtual void setTokenAttribute(PTokenAttribute attri); - QTextCodec *getCodec() const; + TextEncoder getEncoder() const; private: QString mText; bool mFirstAttribute; diff --git a/libs/qsynedit/qsynedit/gutter.cpp b/libs/qsynedit/qsynedit/gutter.cpp index 9cf5d055..9ffdcd51 100644 --- a/libs/qsynedit/qsynedit/gutter.cpp +++ b/libs/qsynedit/qsynedit/gutter.cpp @@ -16,6 +16,7 @@ */ #include "gutter.h" #include "algorithm" +#include namespace QSynedit { diff --git a/libs/qsynedit/qsynedit/keystrokes.cpp b/libs/qsynedit/qsynedit/keystrokes.cpp index d8de1e6a..ed3d9989 100644 --- a/libs/qsynedit/qsynedit/keystrokes.cpp +++ b/libs/qsynedit/qsynedit/keystrokes.cpp @@ -30,9 +30,9 @@ EditKeyStroke::EditKeyStroke() QKeySequence EditKeyStroke::keySequence() const { if (mKey2 == 0) { - return QKeySequence(mKey + mKeyModifiers); + return QKeySequence(mKey | mKeyModifiers); } else { - return QKeySequence(mKey + mKeyModifiers, mKey2+mKeyModifiers2); + return QKeySequence(mKey | mKeyModifiers, mKey2 | mKeyModifiers2); } } diff --git a/libs/qsynedit/qsynedit/syntaxer/asm.cpp b/libs/qsynedit/qsynedit/syntaxer/asm.cpp index deee944d..062c7b2b 100644 --- a/libs/qsynedit/qsynedit/syntaxer/asm.cpp +++ b/libs/qsynedit/qsynedit/syntaxer/asm.cpp @@ -309,7 +309,7 @@ void ASMSyntaxer::SpaceProc() mRun++; if (mLine[mRun] == 0 || mLine[mRun] == '\r' || mLine[mRun] == '\n') break; - if (mLine[mRun] > 32) + if (!isLexicalSpace(mLine[mRun])) break; } if (mRun>=mStringLen) @@ -1696,7 +1696,7 @@ void ASMSyntaxer::next() NumberProc(); } else if (isIdentStartChar(mLine[mRun])) { IdentProc(IdentPrefix::None); - } else if (mLine[mRun]<=32) { + } else if (isLexicalSpace(mLine[mRun])) { SpaceProc(); } else { UnknownProc(); diff --git a/libs/qsynedit/qsynedit/syntaxer/cpp.cpp b/libs/qsynedit/qsynedit/syntaxer/cpp.cpp index bfc12999..bcbb6a2b 100644 --- a/libs/qsynedit/qsynedit/syntaxer/cpp.cpp +++ b/libs/qsynedit/qsynedit/syntaxer/cpp.cpp @@ -16,6 +16,7 @@ */ #include "cpp.h" #include "../constants.h" +#include "qt_utils/utils.h" #include #include @@ -1069,7 +1070,7 @@ void CppSyntaxer::procSpace() { mRun += 1; mTokenId = TokenId::Space; - while (mRun=1 && mLine[mRun]<=32) + while (mRun < mLineSize && isLexicalSpace(mLine[mRun])) mRun+=1; if (mRun>=mLineSize) { mRange.hasTrailingSpaces = true; diff --git a/libs/qsynedit/qsynedit/syntaxer/glsl.cpp b/libs/qsynedit/qsynedit/syntaxer/glsl.cpp index 82f388d1..15e01a3e 100644 --- a/libs/qsynedit/qsynedit/syntaxer/glsl.cpp +++ b/libs/qsynedit/qsynedit/syntaxer/glsl.cpp @@ -846,7 +846,7 @@ void GLSLSyntaxer::spaceProc() { mRun += 1; mTokenId = TokenId::Space; - while (mLine[mRun]>=1 && mLine[mRun]<=32) + while (isLexicalSpace(mLine[mRun])) mRun+=1; mRange.state = RangeState::rsUnknown; if (mRun>=mLineSize) diff --git a/libs/qsynedit/qsynedit/syntaxer/lua.cpp b/libs/qsynedit/qsynedit/syntaxer/lua.cpp index 53fa7827..819ab98e 100644 --- a/libs/qsynedit/qsynedit/syntaxer/lua.cpp +++ b/libs/qsynedit/qsynedit/syntaxer/lua.cpp @@ -16,6 +16,7 @@ */ #include "lua.h" #include "../constants.h" +#include "qt_utils/utils.h" #include #include @@ -587,7 +588,7 @@ void LuaSyntaxer::spaceProc() { mRun += 1; mTokenId = TokenId::Space; - while (mRun=1 && mLine[mRun]<=32) + while (mRun=mLineSize) { mRange.hasTrailingSpaces = true; diff --git a/libs/qsynedit/qsynedit/syntaxer/makefile.cpp b/libs/qsynedit/qsynedit/syntaxer/makefile.cpp index 1980c7d6..81542506 100644 --- a/libs/qsynedit/qsynedit/syntaxer/makefile.cpp +++ b/libs/qsynedit/qsynedit/syntaxer/makefile.cpp @@ -200,7 +200,7 @@ MakefileSyntaxer::MakefileSyntaxer() void MakefileSyntaxer::procSpace() { mTokenID = TokenId::Space; - while (mLine[mRun]!=0 && mLine[mRun]<=32) + while (mLine[mRun]!=0 && mLine[mRun] <= ' ') mRun++; if (mRun>=mStringLen) mHasTrailingSpaces = true; diff --git a/libs/qsynedit/qsynedit/types.h b/libs/qsynedit/qsynedit/types.h index c4047407..346883ee 100644 --- a/libs/qsynedit/qsynedit/types.h +++ b/libs/qsynedit/qsynedit/types.h @@ -46,6 +46,10 @@ enum class ProgrammingLanguage { struct BufferCoord { int ch; int line; + + BufferCoord() = default; + constexpr BufferCoord(qsizetype ch_, qsizetype line_) : ch(ch_), line(line_) {} + bool operator==(const BufferCoord& coord); bool operator>=(const BufferCoord& coord); bool operator>(const BufferCoord& coord); diff --git a/libs/redpanda_qt_utils/qt_utils/charsetinfo.cpp b/libs/redpanda_qt_utils/qt_utils/charsetinfo.cpp index ce37f57d..a0e0edb2 100644 --- a/libs/redpanda_qt_utils/qt_utils/charsetinfo.cpp +++ b/libs/redpanda_qt_utils/qt_utils/charsetinfo.cpp @@ -21,8 +21,6 @@ #include #ifdef Q_OS_WIN #include -#else -#include #endif CharsetInfoManager* pCharsetInfoManager; @@ -37,7 +35,7 @@ QByteArray CharsetInfoManager::getDefaultSystemEncoding() } return "unknown"; #else - return QByteArray(nl_langinfo(CODESET)); + return "UTF-8"; #endif } diff --git a/libs/redpanda_qt_utils/qt_utils/utils.cpp b/libs/redpanda_qt_utils/qt_utils/utils.cpp index 84c63e4b..6dd464da 100644 --- a/libs/redpanda_qt_utils/qt_utils/utils.cpp +++ b/libs/redpanda_qt_utils/qt_utils/utils.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +32,8 @@ #include #include #include +#include "charsetinfo.h" + #ifdef Q_OS_WIN #include #include @@ -40,7 +41,12 @@ #include #include #endif -#include "charsetinfo.h" + +#if QT_VERSION_MAJOR >= 6 +# include +#else +# include +#endif BaseError::BaseError(const QString &reason): mReason(reason) @@ -64,7 +70,7 @@ FileError::FileError(const QString &reason): BaseError(reason) } -const QByteArray guessTextEncoding(const QByteArray& text){ +QString guessTextEncoding(const QByteArray& text){ bool allAscii; int ii; int size; @@ -253,14 +259,16 @@ QByteArray toByteArray(const QString &s) QString fromByteArray(const QByteArray &s) { - QTextCodec* codec = QTextCodec::codecForName(ENCODING_UTF8); - QTextCodec::ConverterState state; - if (!codec) - return QString(s); - QString tmp = codec->toUnicode(s,s.length(),&state); - if (state.invalidChars>0) - tmp = QString::fromLocal8Bit(s); - return tmp; +#ifdef Q_OS_WIN + TextDecoder decoder = TextDecoder::decoderForUtf8(); + auto [ok, result] = decoder.decode(s); + if (ok) + return result; + else + return QString::fromLocal8Bit(s); +#else + return QString::fromUtf8(s); +#endif } QStringList readStreamToLines(QTextStream *stream) @@ -282,37 +290,13 @@ void readStreamToLines(QTextStream *stream, } } -QStringList readFileToLines(const QString& fileName, QTextCodec* codec) -{ - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - stream.setCodec(codec); - stream.setAutoDetectUnicode(false); - return readStreamToLines(&stream); - } - return QStringList(); -} - -void readFileToLines(const QString &fileName, QTextCodec *codec, LineProcessFunc lineFunc) -{ - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - stream.setCodec(codec); - stream.setAutoDetectUnicode(false); - readStreamToLines(&stream, lineFunc); - } -} - static QStringList tryLoadFileByEncoding(QByteArray encodingName, QFile& file, bool* isOk) { QStringList result; - *isOk=false; - QTextCodec* codec = QTextCodec::codecForName(encodingName); - if (!codec) + *isOk = false; + TextDecoder decoder(encodingName); + if (!decoder.isValid()) return result; file.reset(); - QTextCodec::ConverterState state; while (true) { if (file.atEnd()){ break; @@ -325,8 +309,8 @@ static QStringList tryLoadFileByEncoding(QByteArray encodingName, QFile& file, b } else if (line.endsWith("\n")){ line.remove(line.length()-1,1); } - QString newLine = codec->toUnicode(line.constData(),line.length(),&state); - if (state.invalidChars>0) { + auto [ok, newLine] = decoder.decode(line); + if (!ok) { return QStringList(); } result.append(newLine); @@ -355,12 +339,12 @@ QStringList readFileToLines(const QString &fileName) } QList charsets = pCharsetInfoManager->findCharsetByLocale(pCharsetInfoManager->localeName()); if (!charsets.isEmpty()) { - QSet encodingSet; + QSet encodingSet; for (int i=0;iname); } encodingSet.remove(realEncoding); - foreach (const QByteArray& encodingName,encodingSet) { + foreach (const QString& encodingName,encodingSet) { if (encodingName == ENCODING_UTF8) continue; result = tryLoadFileByEncoding("UTF-8",file,&ok); @@ -765,3 +749,273 @@ const QChar *getNullTerminatedStringData(const QString &str) } return result; } + +TextEncoder::TextEncoder(const char *name) +{ +#if QT_VERSION_MAJOR >= 6 + mEncoder = QStringEncoder(name, QStringConverter::Flag::Stateless); +#else + mCodec = QTextCodec::codecForName(name); +#endif +} + +TextEncoder::TextEncoder(const QByteArray &name) : + TextEncoder(name.data()) +{ +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +TextEncoder::TextEncoder(QStringEncoder &&encoder) +{ + mEncoder = std::move(encoder); +} +#endif + +bool TextEncoder::isValid() const +{ +#if QT_VERSION_MAJOR >= 6 + return mEncoder.isValid(); +#else + return mCodec != nullptr; +#endif +} + +QByteArray TextEncoder::name() const +{ +#if QT_VERSION_MAJOR >= 6 + return mEncoder.name(); +#else + return mCodec->name(); +#endif +} + +std::pair TextEncoder::encode(const QString &text) +{ + if (!isValid()) + return {false, QByteArray()}; + QByteArray result; +#if QT_VERSION_MAJOR >= 6 + result = mEncoder(text); + if (mEncoder.hasError()) { + mEncoder.resetState(); + return {false, QByteArray()}; + } +#else + QTextCodec::ConverterState state; + result = mCodec->fromUnicode(text.constData(), text.length(), &state); + if (state.invalidChars > 0) + return {false, QByteArray()}; +#endif + return {true, result}; +} + +QByteArray TextEncoder::encodeUnchecked(const QString &text) +{ + if (!isValid()) + return QByteArray(); +#if QT_VERSION_MAJOR >= 6 + QByteArray result = mEncoder(text); + if (mEncoder.hasError()) + mEncoder.resetState(); + return result; +#else + return mCodec->fromUnicode(text); +#endif +} + +TextEncoder TextEncoder::encoderForUtf8() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringEncoder(QStringConverter::Utf8, QStringConverter::Flag::Stateless); +#else + return TextEncoder(ENCODING_UTF8); +#endif +} + +TextEncoder TextEncoder::encoderForUtf16() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringEncoder(QStringConverter::Utf16, QStringConverter::Flag::Stateless); +#else + return TextEncoder(ENCODING_UTF16); +#endif +} + +TextEncoder TextEncoder::encoderForUtf32() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringEncoder(QStringConverter::Utf32, QStringConverter::Flag::Stateless); +#else + return TextEncoder(ENCODING_UTF32); +#endif +} + +TextEncoder TextEncoder::encoderForSystem() +{ +#ifdef Q_OS_WIN +# if QT_VERSION_MAJOR >= 6 + return QStringEncoder(QStringConverter::System, QStringConverter::Flag::Stateless); +# else + return TextEncoder(ENCODING_SYSTEM_DEFAULT); +# endif +#else + return encoderForUtf8(); +#endif +} + +TextDecoder::TextDecoder(const char *name) +{ +#if QT_VERSION_MAJOR >= 6 + mDecoder = QStringDecoder(name, QStringConverter::Flag::Stateless); +#else + mCodec = QTextCodec::codecForName(name); +#endif +} + +TextDecoder::TextDecoder(const QByteArray &name) : + TextDecoder(name.data()) +{ +} + +#if QT_VERSION_MAJOR >= 6 +TextDecoder::TextDecoder(QStringDecoder &&decoder) +{ + mDecoder = std::move(decoder); +} +#endif + +bool TextDecoder::isValid() const +{ +#if QT_VERSION_MAJOR >= 6 + return mDecoder.isValid(); +#else + return mCodec != nullptr; +#endif +} + +QByteArray TextDecoder::name() const +{ +#if QT_VERSION_MAJOR >= 6 + return mDecoder.name(); +#else + return mCodec->name(); +#endif +} + +std::pair TextDecoder::decode(const QByteArray &text) +{ + if (!isValid()) + return {false, QString()}; + QString result; +#if QT_VERSION_MAJOR >= 6 + result = mDecoder(text); + if (mDecoder.hasError()) { + mDecoder.resetState(); + return {false, QString()}; + } +#else + QTextCodec::ConverterState state; + result = mCodec->toUnicode(text.constData(), text.length(), &state); + if (state.invalidChars > 0) + return {false, QString()}; +#endif + return {true, result}; +} + +QString TextDecoder::decodeUnchecked(const QByteArray &text) +{ + if (!isValid()) + return QString(); +#if QT_VERSION_MAJOR >= 6 + QString result = mDecoder(text); + if (mDecoder.hasError()) + mDecoder.resetState(); + return result; +#else + return mCodec->toUnicode(text); +#endif +} + +TextDecoder TextDecoder::decoderForUtf8() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringDecoder(QStringConverter::Utf8, QStringConverter::Flag::Stateless); +#else + return TextDecoder(ENCODING_UTF8); +#endif +} + +TextDecoder TextDecoder::decoderForUtf16() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringDecoder(QStringConverter::Utf16, QStringConverter::Flag::Stateless); +#else + return TextDecoder(ENCODING_UTF16); +#endif +} + +TextDecoder TextDecoder::decoderForUtf32() +{ +#if QT_VERSION_MAJOR >= 6 + return QStringDecoder(QStringConverter::Utf32, QStringConverter::Flag::Stateless); +#else + return TextDecoder(ENCODING_UTF32); +#endif +} + +TextDecoder TextDecoder::decoderForSystem() +{ +#ifdef Q_OS_WIN +# if QT_VERSION_MAJOR >= 6 + return QStringDecoder(QStringConverter::System, QStringConverter::Flag::Stateless); +# else + return TextDecoder(ENCODING_SYSTEM_DEFAULT); +# endif +#else + return decoderForUtf8(); +#endif +} + +const QStringList &availableEncodings() { + static bool initialized = false; + static QStringList encodings; + + if (initialized) + return encodings; + +#if QT_VERSION_MAJOR >= 6 + for (const QString &name : QStringConverter::availableCodecs()) { + QString lname = name.toLower(); + if (lname.startsWith("cp")) + continue; + if (lname == "locale" || lname == "utf-8") + continue; + encodings.append(name); + } +#else + QSet codecAlias = {"system", "utf-8"}; + for (const QByteArray &name : QTextCodec::availableCodecs()) { + QByteArray lname = name.toLower(); + if (lname.startsWith("cp")) + continue; + if (codecAlias.contains(lname)) + continue; + encodings.append(name); + codecAlias.insert(lname); + QTextCodec *codec = QTextCodec::codecForName(name); + if (codec != nullptr) { + for (const QByteArray &alias : codec->aliases()) + codecAlias.insert(alias.toLower()); + } + } +#endif + std::sort(encodings.begin(), encodings.end()); + initialized = true; + return encodings; +} + +bool isEncodingAvailable(const QByteArray &encoding) +{ + TextEncoder encoder(encoding); + return encoder.isValid(); +} diff --git a/libs/redpanda_qt_utils/qt_utils/utils.h b/libs/redpanda_qt_utils/qt_utils/utils.h index cd6dcd36..fa21f981 100644 --- a/libs/redpanda_qt_utils/qt_utils/utils.h +++ b/libs/redpanda_qt_utils/qt_utils/utils.h @@ -26,9 +26,15 @@ #include #include +#if QT_VERSION_MAJOR >= 6 +# include +# include +#else +class QTextCodec; +#endif + class QByteArray; class QTextStream; -class QTextCodec; #define ENCODING_AUTO_DETECT "AUTO" #define ENCODING_UTF8 "UTF-8" @@ -67,7 +73,7 @@ public: }; /* text processing utils */ -const QByteArray guessTextEncoding(const QByteArray& text); +QString guessTextEncoding(const QByteArray& text); const QChar *getNullTerminatedStringData(const QString& str); @@ -109,9 +115,7 @@ void readStreamToLines(QTextStream* stream, LineProcessFunc lineFunc); * @param codec * @return */ -QStringList readFileToLines(const QString& fileName, QTextCodec* codec); QStringList readFileToLines(const QString& fileName); -void readFileToLines(const QString& fileName, QTextCodec* codec, LineProcessFunc lineFunc); QByteArray readFileToByteArray(const QString& fileName); @@ -204,4 +208,108 @@ finally(F&& f) noexcept return final_action::type>::type>( std::forward(f)); } + +class TextEncoder { +public: + explicit TextEncoder(const char *name); + explicit TextEncoder(const QByteArray &name); + TextEncoder(const TextEncoder &other) = delete; + TextEncoder(TextEncoder &&other) noexcept = default; + TextEncoder &operator=(const TextEncoder &other) = delete; + TextEncoder &operator=(TextEncoder &&other) noexcept = default; + ~TextEncoder() = default; + +#if QT_VERSION_MAJOR >= 6 +private: + TextEncoder(QStringEncoder &&encoder); +#endif + +public: + bool isValid() const; + QByteArray name() const; + std::pair encode(const QString &text); + QByteArray encodeUnchecked(const QString &text); + +public: + static TextEncoder encoderForUtf8(); + static TextEncoder encoderForUtf16(); + static TextEncoder encoderForUtf32(); + static TextEncoder encoderForSystem(); + +private: +#if QT_VERSION_MAJOR >= 6 + QStringEncoder mEncoder; +#else + QTextCodec *mCodec; +#endif +}; + +class TextDecoder { +public: + explicit TextDecoder(const char *name); + explicit TextDecoder(const QByteArray &name); + TextDecoder(const TextDecoder &other) = delete; + TextDecoder(TextDecoder &&other) noexcept = default; + TextDecoder &operator=(const TextDecoder &other) = delete; + TextDecoder &operator=(TextDecoder &&other) noexcept = default; + ~TextDecoder() = default; + +#if QT_VERSION_MAJOR >= 6 +private: + TextDecoder(QStringDecoder &&decoder); +#endif + +public: + bool isValid() const; + QByteArray name() const; + std::pair decode(const QByteArray &text); + QString decodeUnchecked(const QByteArray &text); + +public: + static TextDecoder decoderForUtf8(); + static TextDecoder decoderForUtf16(); + static TextDecoder decoderForUtf32(); + static TextDecoder decoderForSystem(); + +private: +#if QT_VERSION_MAJOR >= 6 + QStringDecoder mDecoder; +#else + QTextCodec *mCodec; +#endif +}; + +const QStringList &availableEncodings(); + +bool isEncodingAvailable(const QByteArray &encoding); + +#if QT_VERSION_MAJOR >= 6 + +namespace std { + constexpr inline int max(int a, qsizetype b) { + return max(a, b); + } + constexpr inline int max(qsizetype a, int b) { + return max(a, b); + } + constexpr inline int min(int a, qsizetype b) { + return min(a, b); + } + constexpr inline int min(qsizetype a, int b) { + return min(a, b); + } +} + +#endif + +inline bool isAsciiPrint(int c) +{ + return c >= 32 && c <= 126; +} + +inline bool isLexicalSpace(QChar c) +{ + return c.unicode() >= 1 && c.unicode() <= 32; +} + #endif // UTILS_H diff --git a/packages/archlinux/01-in-docker.sh b/packages/archlinux/01-in-docker.sh index 7c8a6af4..af0d7ff5 100755 --- a/packages/archlinux/01-in-docker.sh +++ b/packages/archlinux/01-in-docker.sh @@ -11,7 +11,7 @@ pacman -Syu --noconfirm --needed base-devel git useradd -m builduser echo 'builduser ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builduser -echo "MAKEFLAGS=-j$(($(nproc)+1))" >>/etc/makepkg.conf +echo "MAKEFLAGS=-j$(($(nproc)+1))" >>/etc/makepkg.conf.d/jobs.conf su builduser -c "git config --global --add safe.directory $PWD" su builduser -c ./packages/archlinux/buildpkg.sh diff --git a/packages/archlinux/PKGBUILD.in b/packages/archlinux/PKGBUILD.in index 3763d647..8160fb01 100644 --- a/packages/archlinux/PKGBUILD.in +++ b/packages/archlinux/PKGBUILD.in @@ -6,8 +6,8 @@ pkgdesc='A fast, lightweight, open source, and cross platform C++ IDE (developme arch=('i686' 'pentium4' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64' 'riscv64') url="https://github.com/royqh1979/$_pkgname" license=('GPL3') -depends=(qt5-base qt5-svg gcc gdb astyle) -makedepends=(qt5-tools imagemagick librsvg) +depends=(qt6-base qt6-svg gcc gdb astyle) +makedepends=(qt6-tools imagemagick librsvg) optdepends=( 'clang: C/C++ compiler (alternative)' ) @@ -29,7 +29,7 @@ prepare() { build() { mkdir redpanda-build cd redpanda-build - qmake \ + qmake6 \ PREFIX='/usr' \ LIBEXECDIR='/usr/lib' \ "$srcdir/$_pkgname/Red_Panda_CPP.pro"