diff --git a/CMakeLists.txt b/CMakeLists.txt index 792ca3e..10dd071 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,10 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) +#target_include_directories(RenpyTranslationHelper PRIVATE /usr/include/qt6) + +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets Network) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network) set(TS_FILES RenpyTranslationHelper_en_GB.ts) @@ -27,19 +29,11 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) MANUAL_FINALIZATION ${PROJECT_SOURCES} ) -# Define target properties for Android with Qt 6 as: -# set_property(TARGET RenpyTranslationHelper APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR -# ${CMAKE_CURRENT_SOURCE_DIR}/android) -# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation - -# qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) else() if(ANDROID) add_library(RenpyTranslationHelper SHARED ${PROJECT_SOURCES} ) -# Define properties for Android with Qt 5 after find_package() calls as: -# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() add_executable(RenpyTranslationHelper ${PROJECT_SOURCES} @@ -51,12 +45,9 @@ endif() target_link_libraries(RenpyTranslationHelper PRIVATE Qt${QT_VERSION_MAJOR}::Widgets - Qt6Network + Qt${QT_VERSION_MAJOR}::Network ) -# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. -# If you are developing for iOS or macOS you should consider setting an -# explicit, fixed bundle identifier manually though. if(${QT_VERSION} VERSION_LESS 6.1.0) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.RenpyTranslationHelper) endif() diff --git a/mainwindow.cpp b/mainwindow.cpp index 4f4efb6..ae2ed2f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -117,29 +119,33 @@ void MainWindow::on_languageCombo_currentTextChanged(const QString &selectedLang } void MainWindow::populateTreeWidgetFromMap() { - ui->treeWidget->clear(); - foreach (const auto &pair, fileContentsMap) { - QTreeWidgetItem *fileItem = new QTreeWidgetItem(ui->treeWidget); - QString fileName = QFileInfo(pair.first).fileName(); - fileItem->setText(0, fileName); - fileItem->setData(0, Qt::UserRole, false); - auto parsedBlocks = parseTextBlock(pair.second); - for (const auto& block : std::as_const(parsedBlocks)) { - QTreeWidgetItem *blockItem = new QTreeWidgetItem(fileItem); - blockItem->setText(0, QString::number(block.line)); - blockItem->setText(1, block.oldText); - blockItem->setText(2, block.newText); - blockItem->setText(3, block.character); + try { + ui->treeWidget->clear(); + foreach (const auto &pair, fileContentsMap) { + QTreeWidgetItem *fileItem = new QTreeWidgetItem(ui->treeWidget); + QString fileName = QFileInfo(pair.first).fileName(); + fileItem->setText(0, fileName); + fileItem->setData(0, Qt::UserRole, false); + auto parsedBlocks = parseTextBlock(pair.second); + for (const auto &block : std::as_const(parsedBlocks)) { + QTreeWidgetItem *blockItem = new QTreeWidgetItem(fileItem); + blockItem->setText(0, QString::number(block.line)); + blockItem->setText(1, QString("%1").arg(block.character)); + blockItem->setText(2, block.oldText); + blockItem->setText(3, block.newText); + } } + ui->treeWidget->resizeColumnToContents(0); + int firstColumnWidth = ui->treeWidget->columnWidth(0); + int secondColumnWidth = ui->treeWidget->columnWidth(1); + int remainingWidth = ui->treeWidget->viewport()->width() - firstColumnWidth - secondColumnWidth; + int columnWidth = remainingWidth / 2; + ui->treeWidget->setColumnWidth(2, columnWidth); + ui->treeWidget->setColumnWidth(3, columnWidth); + countAndShowUntranslated(); + } catch (const std::exception &e) { + qDebug() << e.what(); } - - ui->treeWidget->resizeColumnToContents(0); - int firstColumnWidth = ui->treeWidget->columnWidth(0); - int remainingWidth = ui->treeWidget->viewport()->width() - firstColumnWidth; - int columnWidth = remainingWidth / 2; - ui->treeWidget->setColumnWidth(1, columnWidth); - ui->treeWidget->setColumnWidth(2, columnWidth); - countAndShowUntranslated(); } QVector MainWindow::parseTextBlock(const QString& input) { @@ -196,17 +202,18 @@ void MainWindow::on_reloadFilesButton_clicked() populateTreeWidgetFromMap(); } -void MainWindow::on_nextUntranslatedButton_clicked() -{ +void MainWindow::on_nextUntranslatedButton_clicked() { for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i); for (int j = 0; j < topLevelItem->childCount(); ++j) { QTreeWidgetItem *childItem = topLevelItem->child(j); - if (childItem->text(2).isEmpty()) { - ui->originalTextEdit->setText(childItem->text(1)); + // Prüfe, ob Übersetzung (Spalte 3) leer ist + if (childItem->text(3).isEmpty()) { + ui->originalTextEdit->setText(childItem->text(2)); // Originaltext aus Spalte 2 ui->translationEdit->clear(); ui->treeWidget->scrollToItem(childItem); ui->treeWidget->setCurrentItem(childItem); + on_copyButton_clicked(); return; } } @@ -214,34 +221,39 @@ void MainWindow::on_nextUntranslatedButton_clicked() QMessageBox::information(this, tr("Information"), tr("No untranslated item found.")); } -void MainWindow::on_treeWidget_itemSelectionChanged() -{ +void MainWindow::on_treeWidget_itemSelectionChanged() { QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem(); if (selectedItem) { if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) { - ui->originalTextEdit->setText(selectedItem->text(1)); - ui->translationEdit->setText(selectedItem->text(2)); + ui->originalTextEdit->setText(selectedItem->text(2)); // Originaltext + ui->translationEdit->setText(selectedItem->text(3)); // Übersetzung } } } - -void MainWindow::on_setTranslationButton_clicked() -{ +void MainWindow::on_setTranslationButton_clicked() { QTreeWidgetItem *selectedItem = ui->treeWidget->currentItem(); if (selectedItem) { if (selectedItem->treeWidget()->indexOfTopLevelItem(selectedItem) < 0) { - selectedItem->setText(2, ui->translationEdit->text()); + // Setze Übersetzung in Spalte 3 + selectedItem->setText(3, ui->translationEdit->text()); QTreeWidgetItem *parentItem = selectedItem->parent(); if (parentItem) { parentItem->setData(0, Qt::UserRole, true); + for (int index = 0; index < parentItem->childCount(); ++index) { + auto item = parentItem->child(index); + // Vergleiche den Originaltext (Spalte 2) + if (item->text(2) == ui->originalTextEdit->text()) { + item->setText(3, ui->translationEdit->text()); + } + } } } } + ui->originalTextEdit->setFocus(); countAndShowUntranslated(); } - void MainWindow::on_setTranslationAndJumpNextButton_clicked() { on_setTranslationButton_clicked(); @@ -352,7 +364,7 @@ bool MainWindow::parseAndEditFile(QFile &backupFile, QTreeWidgetItem *fileItem, auto oldText = match.captured(4); auto newText = match.captured(6); auto replacedText = findNewText(fileItem, lineNumber, oldText, speaker); - QString toReplace = QString("\"%1\"").arg(newText); + QString toReplace = QString("\"%1\"").arg(newText).trimmed(); QString replacement = QString("\"%1\"").arg(replacedText.isEmpty() ? newText : replacedText); auto replacedBlock = block.replace(toReplace, replacement); content.replace(match.capturedStart(0), match.capturedLength(0), replacedBlock); @@ -381,16 +393,37 @@ bool MainWindow::parseAndEditFile(QFile &backupFile, QTreeWidgetItem *fileItem, QString MainWindow::findNewText(QTreeWidgetItem *fileItem, const QString &lineNumber, const QString &originalText, const QString &speaker) { for (int i = 0; i < fileItem->childCount(); ++i) { QTreeWidgetItem *childItem = fileItem->child(i); - if (childItem->text(0) == lineNumber && childItem->text(1) == originalText && childItem->text(3) == speaker) { - return childItem->text(2); + if (childItem->text(0) == lineNumber && + childItem->text(1) == speaker && + childItem->text(2) == originalText) { + return childItem->text(3); } } return QString(); } void MainWindow::loadDeeplTranslationPossibilities() { + static int attempt = 1; QNetworkAccessManager *manager = new QNetworkAccessManager(this); - connect(manager, &QNetworkAccessManager::finished, this, &MainWindow::onDeeplTranslationPossibilitiesLoaded); + connect(manager, &QNetworkAccessManager::finished, this, [this, manager](QNetworkReply *reply) { + if (reply->error() != QNetworkReply::NoError) { + qDebug() << "Network error:" << reply->errorString(); + qDebug() << "Attempt:" << attempt; + if (attempt < 5) { + attempt++; + QTimer::singleShot(250, this, &MainWindow::loadDeeplTranslationPossibilities); + } else { + qDebug() << "Max attempts reached, giving up."; + attempt = 1; + } + } else { + attempt = 1; + onDeeplTranslationPossibilitiesLoaded(reply); + } + reply->deleteLater(); + manager->deleteLater(); + }); + QNetworkRequest request(QUrl("https://api-free.deepl.com/v2/glossary-language-pairs")); request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8()); manager->get(request); @@ -483,6 +516,8 @@ void MainWindow::on_deeplTranslateFrom_currentTextChanged(const QString &sourceL } } +#include + void MainWindow::on_autoTranslateButton_clicked() { if (ui->deeplApiKey->text().isEmpty()) { QMessageBox::information(this, "Deepl error", "For auto translation, you need to add a deepl API key."); @@ -495,12 +530,32 @@ void MainWindow::on_autoTranslateButton_clicked() { jsonData["text"] = QJsonArray::fromStringList({originalText}); jsonData["source_lang"] = sourceLang; jsonData["target_lang"] = targetLang; - QNetworkAccessManager *manager = new QNetworkAccessManager(this); - connect(manager, &QNetworkAccessManager::finished, this, &MainWindow::translationRequestFinished); QUrl url("https://api-free.deepl.com/v2/translate"); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Authorization", "DeepL-Auth-Key " + ui->deeplApiKey->text().toUtf8()); + static int attempt = 1; + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + connect(manager, &QNetworkAccessManager::finished, this, + [this, manager, jsonData, request](QNetworkReply *reply) mutable { + if (reply->error() != QNetworkReply::NoError) { + qDebug() << "Translation request failed:" << reply->errorString(); + qDebug() << "Attempt:" << attempt; + if (attempt < 5) { + attempt++; + QTimer::singleShot(250, this, &MainWindow::on_autoTranslateButton_clicked); + } else { + qDebug() << "Max attempts reached for auto translation."; + attempt = 1; + } + } else { + attempt = 1; + translationRequestFinished(reply); + } + reply->deleteLater(); + manager->deleteLater(); + }); + manager->post(request, QJsonDocument(jsonData).toJson()); } @@ -524,21 +579,24 @@ void MainWindow::translationRequestFinished(QNetworkReply *reply) { reply->deleteLater(); } -void MainWindow::countAndShowUntranslated() -{ +void MainWindow::countAndShowUntranslated() { int untranslatedCount = 0; - std::function countUntranslated = [&](QTreeWidgetItem *parentItem) { + std::function countUntranslated = [&](QTreeWidgetItem *parentItem) { + int untranslatedInSectionCount = 0; for (int i = 0; i < parentItem->childCount(); ++i) { QTreeWidgetItem *childItem = parentItem->child(i); - if (!childItem->text(1).isEmpty() && childItem->text(2).isEmpty()) { + if (!childItem->text(2).isEmpty() && childItem->text(3).isEmpty()) { untranslatedCount++; + untranslatedInSectionCount++; } countUntranslated(childItem); } + return untranslatedInSectionCount; }; for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem *topLevelItem = ui->treeWidget->topLevelItem(i); - countUntranslated(topLevelItem); + int sectionCount = countUntranslated(topLevelItem); + topLevelItem->setText(1, QString("%1").arg(sectionCount)); } ui->untranslatedLabel->setText(QString("%1").arg(untranslatedCount)); } @@ -567,8 +625,8 @@ void MainWindow::searchNext() { auto fileItem {ui->treeWidget->invisibleRootItem()->child(fileItemPos)}; for (int linePos = 0; linePos < fileItem->childCount(); ++linePos) { auto line {fileItem->child(linePos)}; - if (currentItem == NULL && (line->text(1).contains(searchQuery, Qt::CaseInsensitive) - || line->text(2).contains(searchQuery, Qt::CaseInsensitive))) { + if (currentItem == NULL && (line->text(2).contains(searchQuery, Qt::CaseInsensitive) + || line->text(3).contains(searchQuery, Qt::CaseInsensitive))) { ui->treeWidget->setCurrentItem(line); ui->treeWidget->scrollToItem(line); return; @@ -602,3 +660,7 @@ void MainWindow::on_deeplApiKey_editingFinished() loadDeeplTranslationPossibilities(); } +void MainWindow::on_copyButton_clicked() { + QGuiApplication::clipboard()->setText(ui->originalTextEdit->text()); +} + diff --git a/mainwindow.h b/mainwindow.h index 4bd72ba..40a210c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -3,6 +3,8 @@ #include #include +#include +#include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -11,6 +13,7 @@ QT_END_NAMESPACE class QTreeWidgetItem; class QFile; class QNetworkReply; +class QAuthenticator; class MainWindow : public QMainWindow { @@ -40,8 +43,8 @@ private slots: void on_autoTranslateButton_clicked(); void on_searchButton_clicked(); void on_searchNextButton_clicked(); - void on_deeplApiKey_editingFinished(); + void on_copyButton_clicked(); private: struct TranslationItem { @@ -55,6 +58,7 @@ private: QString oldText; QString newText; }; + std::unique_ptr networkManager; Ui::MainWindow *ui; QJsonObject configuration; bool noConfigChange{false}; @@ -72,6 +76,7 @@ private: void loadDeeplTranslationPossibilities(); void renderDeeplSources(); void translationRequestFinished(QNetworkReply *reply); + void onDeeplTranslationAuthenticationError(QNetworkReply *reply, QAuthenticator *authenticator); void countAndShowUntranslated(); void searchNext(); QVector parseTextBlock(const QString &block); diff --git a/mainwindow.ui b/mainwindow.ui index bd00149..0bded77 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -102,6 +102,11 @@ File + + + Speaker + + Original @@ -112,11 +117,6 @@ Translation - - - Speaker - - All items @@ -230,7 +230,14 @@ - false + true + + + + + + + Copy