Compare commits

..

1 Commits
master ... qt6

Author SHA1 Message Date
Gres
6eb5a6d9f1 Port to qt6 2023-01-28 12:29:59 +03:00
20 changed files with 85 additions and 79 deletions

View File

@ -78,7 +78,7 @@ jobs:
- name: Download release url - name: Download release url
if: contains(github.ref, '/tags/') if: contains(github.ref, '/tags/')
uses: actions/download-artifact@v4.1.7 uses: actions/download-artifact@v1
with: with:
name: release_upload_url name: release_upload_url
path: ./ path: ./

View File

@ -1,7 +1,5 @@
# Screen Translator # Screen Translator
**The project is almost abandoned. I don't have time for it and I can only fix minor issues**
## Introduction ## Introduction
This software allows you to translate any text on screen. This software allows you to translate any text on screen.
@ -29,7 +27,7 @@ file and place it into the `translations` folder next to `screen-translator.exe`
The app doesn't have a main window. The app doesn't have a main window.
After start it shows only the tray icon. After start it shows only the tray icon.
If the app detects invalid settings, it will show the error message via system tray. If the app detects invalid settings, it will show the error message via system tray.
It will also highlight the section name in red on the left panel of the settings window. It will also highlight the section name in red on the left panel of the settings window.
Clicking on that section name will show a more detailed error message in the right panel (also in red). Clicking on that section name will show a more detailed error message in the right panel (also in red).

View File

@ -14,7 +14,7 @@ win32{
LIBS += -lUser32 LIBS += -lUser32
} }
linux{ linux{
QT += x11extras # QT += x11extras
LIBS += -lX11 LIBS += -lX11
} }

View File

@ -7,7 +7,7 @@
#include <QDir> #include <QDir>
#include <QRegularExpression> #include <QRegularExpression>
#include <QTextCodec> #include <QStringConverter>
static int levenshteinDistance(const QString &source, const QString &target) static int levenshteinDistance(const QString &source, const QString &target)
{ {
@ -106,19 +106,21 @@ QString HunspellCorrector::correct(const QString &original)
{ {
SOFT_ASSERT(engine_, return original); SOFT_ASSERT(engine_, return original);
const auto codec = const auto encoding =
QTextCodec::codecForName(engine_->get_dict_encoding().c_str()); QStringConverter::encodingForName(engine_->get_dict_encoding().c_str());
SOFT_ASSERT(codec, return original); SOFT_ASSERT(encoding, return original);
auto codec = QStringEncoder(*encoding);
QString result; QString result;
QString word; QString word;
QString separator; QString separator;
for (auto i = 0, end = original.size(); i < end; ++i) {
for (auto i = 0ll, end = original.size(); i < end; ++i) {
const auto ch = original[i]; const auto ch = original[i];
if (ch.isPunct() || ch.isSpace()) { if (ch.isPunct() || ch.isSpace()) {
if (!word.isEmpty()) { if (!word.isEmpty()) {
correctWord(word, *codec); correctWord(word, codec);
result += word; result += word;
word.clear(); word.clear();
} }
@ -139,7 +141,7 @@ QString HunspellCorrector::correct(const QString &original)
} }
if (!word.isEmpty()) { if (!word.isEmpty()) {
correctWord(word, *codec); correctWord(word, codec);
result += word; result += word;
} }
result += separator; result += separator;
@ -147,12 +149,12 @@ QString HunspellCorrector::correct(const QString &original)
return result; return result;
} }
void HunspellCorrector::correctWord(QString &word, QTextCodec &codec) const void HunspellCorrector::correctWord(QString &word, QStringEncoder &codec) const
{ {
if (word.isEmpty()) if (word.isEmpty())
return; return;
const auto stdWord = codec.fromUnicode(word).toStdString(); const auto stdWord = codec(word).data.toStdString();
if (engine_->spell(stdWord)) if (engine_->spell(stdWord))
return; return;

View File

@ -5,6 +5,7 @@
#include <QString> #include <QString>
class Hunspell; class Hunspell;
class QStringEncoder;
class HunspellCorrector class HunspellCorrector
{ {
@ -19,7 +20,7 @@ public:
private: private:
void init(const QString& path); void init(const QString& path);
void correctWord(QString& word, QTextCodec& codec) const; void correctWord(QString& word, QStringEncoder& codec) const;
std::unique_ptr<Hunspell> engine_; std::unique_ptr<Hunspell> engine_;
QString error_; QString error_;

View File

@ -16,6 +16,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QStandardPaths>
#include <QThread> #include <QThread>
namespace namespace
@ -187,7 +188,8 @@ bool Manager::setupTrace(bool isOn)
const auto traceFile = const auto traceFile =
QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
QLatin1String("/screen-translator-") + QLatin1String("/screen-translator-") +
QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"); QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss") +
QLatin1String(".txt");
if (!debug::setTraceFileName(traceFile)) { if (!debug::setTraceFileName(traceFile)) {
QMessageBox::warning( QMessageBox::warning(

View File

@ -4,6 +4,8 @@
#include <QObject> #include <QObject>
Q_MOC_INCLUDE("tesseract.h")
class Tesseract; class Tesseract;
class RecognizeWorker : public QObject class RecognizeWorker : public QObject

View File

@ -7,7 +7,6 @@
#include <QApplication> #include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
#include <QDesktopWidget>
#include <QLabel> #include <QLabel>
#include <QMenu> #include <QMenu>
#include <QMouseEvent> #include <QMouseEvent>
@ -70,7 +69,7 @@ ResultWidget::ResultWidget(Manager &manager, Representer &representer,
layout->addWidget(separator_); layout->addWidget(separator_);
layout->addWidget(translated_); layout->addWidget(translated_);
layout->setMargin(0); layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0); layout->setSpacing(0);
updateSettings(); updateSettings();

View File

@ -77,7 +77,7 @@ void GlobalAction::triggerHotKey(quint32 nativeKey, quint32 nativeMods)
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include <QX11Info> #include <QWindow>
namespace service namespace service
{ {
@ -101,18 +101,22 @@ static int customHandler(Display *display, XErrorEvent *event)
bool GlobalAction::registerHotKey(quint32 nativeKey, quint32 nativeMods) bool GlobalAction::registerHotKey(quint32 nativeKey, quint32 nativeMods)
{ {
Display *display = QX11Info::display(); auto nativeInterface =
Window window = QX11Info::appRootWindow(); qApp->nativeInterface<QNativeInterface::QX11Application>();
SOFT_ASSERT(nativeInterface, return false);
Display *display = nativeInterface->display();
SOFT_ASSERT(display, return false);
Bool owner = True; Bool owner = True;
int pointer = GrabModeAsync; int pointer = GrabModeAsync;
int keyboard = GrabModeAsync; int keyboard = GrabModeAsync;
error = false; error = false;
int (*handler)(Display * display, XErrorEvent * event) = int (*handler)(Display * display, XErrorEvent * event) =
XSetErrorHandler(customHandler); XSetErrorHandler(customHandler);
XGrabKey(display, nativeKey, nativeMods, window, owner, pointer, keyboard); XGrabKey(display, nativeKey, nativeMods, DefaultRootWindow(display), owner,
pointer, keyboard);
// allow numlock // allow numlock
XGrabKey(display, nativeKey, nativeMods | Mod2Mask, window, owner, pointer, XGrabKey(display, nativeKey, nativeMods | Mod2Mask,
keyboard); DefaultRootWindow(display), owner, pointer, keyboard);
XSync(display, False); XSync(display, False);
XSetErrorHandler(handler); XSetErrorHandler(handler);
return !error; return !error;
@ -120,21 +124,25 @@ bool GlobalAction::registerHotKey(quint32 nativeKey, quint32 nativeMods)
bool GlobalAction::unregisterHotKey(quint32 nativeKey, quint32 nativeMods) bool GlobalAction::unregisterHotKey(quint32 nativeKey, quint32 nativeMods)
{ {
Display *display = QX11Info::display(); auto nativeInterface =
Window window = QX11Info::appRootWindow(); qApp->nativeInterface<QNativeInterface::QX11Application>();
SOFT_ASSERT(nativeInterface, return false);
Display *display = nativeInterface->display();
SOFT_ASSERT(display, return false);
error = false; error = false;
int (*handler)(Display * display, XErrorEvent * event) = int (*handler)(Display * display, XErrorEvent * event) =
XSetErrorHandler(customHandler); XSetErrorHandler(customHandler);
XUngrabKey(display, nativeKey, nativeMods, window); XUngrabKey(display, nativeKey, nativeMods, DefaultRootWindow(display));
// allow numlock // allow numlock
XUngrabKey(display, nativeKey, nativeMods | Mod2Mask, window); XUngrabKey(display, nativeKey, nativeMods | Mod2Mask,
DefaultRootWindow(display));
XSync(display, False); XSync(display, False);
XSetErrorHandler(handler); XSetErrorHandler(handler);
return !error; return !error;
} }
bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message, bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message,
long *result) qintptr *result)
{ {
Q_UNUSED(eventType); Q_UNUSED(eventType);
Q_UNUSED(result); Q_UNUSED(result);
@ -151,7 +159,9 @@ bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message,
quint32 GlobalAction::nativeKeycode(Qt::Key key) quint32 GlobalAction::nativeKeycode(Qt::Key key)
{ {
Display *display = QX11Info::display(); auto nativeInterface =
qApp->nativeInterface<QNativeInterface::QX11Application>();
Display *display = nativeInterface->display();
KeySym keySym = XStringToKeysym(qPrintable(QKeySequence(key).toString())); KeySym keySym = XStringToKeysym(qPrintable(QKeySequence(key).toString()));
if (XKeysymToString(keySym) == nullptr) { if (XKeysymToString(keySym) == nullptr) {
keySym = QChar(key).unicode(); keySym = QChar(key).unicode();
@ -191,7 +201,7 @@ bool GlobalAction::unregisterHotKey(quint32 nativeKey, quint32 nativeMods)
} }
bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message, bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message,
long *result) qintptr *result)
{ {
Q_UNUSED(eventType); Q_UNUSED(eventType);
Q_UNUSED(result); Q_UNUSED(result);
@ -407,8 +417,8 @@ bool GlobalAction::unregisterHotKey(quint32 nativeKey, quint32 nativeMods)
} }
} }
bool GlobalAction::nativeEventFilter(const QByteArray & /*eventType*/, bool GlobalAction::nativeEventFilter(const QByteArray &eventType, void *message,
void * /*message*/, long * /*result*/) qintptr *result)
{ {
return false; return false;
} }

View File

@ -11,7 +11,7 @@ class GlobalAction : public QAbstractNativeEventFilter
{ {
public: public:
bool nativeEventFilter(const QByteArray &eventType, void *message, bool nativeEventFilter(const QByteArray &eventType, void *message,
long *result); qintptr *result) override;
static void init(); static void init();
static bool makeGlobal(QAction *action); static bool makeGlobal(QAction *action);

View File

@ -63,7 +63,7 @@ void KeySequenceEdit::keyPressEvent(QKeyEvent *event)
return; return;
} }
QKeySequence seq = event->modifiers() + event->key(); QKeySequence seq(QKeyCombination(event->modifiers(), Qt::Key(event->key())));
setKeySequence(seq, false); setKeySequence(seq, false);
event->accept(); event->accept();
} }

View File

@ -85,7 +85,7 @@ Substitutions unpackSubstitutions(const QStringList& raw)
return {}; return {};
Substitutions result; Substitutions result;
for (auto i = 0, end = raw.size(); i < end; i += 3) { for (auto i = 0ll, end = raw.size(); i < end; i += 3) {
result.emplace(raw[i], Substitution{raw[i + 1], raw[i + 2]}); result.emplace(raw[i], Substitution{raw[i + 1], raw[i + 2]});
} }
return result; return result;

View File

@ -9,6 +9,7 @@
#include "widgetstate.h" #include "widgetstate.h"
#include <QColorDialog> #include <QColorDialog>
#include <QRegularExpression>
#include <QStandardItemModel> #include <QStandardItemModel>
namespace namespace
@ -112,10 +113,10 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Updater &updater)
proxyTypes.insert(ProxyType::Http, tr("HTTP")); proxyTypes.insert(ProxyType::Http, tr("HTTP"));
ui->proxyTypeCombo->addItems(proxyTypes.values()); ui->proxyTypeCombo->addItems(proxyTypes.values());
QRegExp urlRegexp( QRegularExpression urlRegexp(
R"(^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$)"); R"(^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$)");
ui->proxyHostEdit->setValidator( ui->proxyHostEdit->setValidator(
new QRegExpValidator(urlRegexp, ui->proxyHostEdit)); new QRegularExpressionValidator(urlRegexp, ui->proxyHostEdit));
ui->proxyPassEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit); ui->proxyPassEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit);
} }

View File

@ -3,7 +3,9 @@
#include <memory> #include <memory>
class QString; class QString;
class QStringList; template <class T>
class QList;
using QStringList = QList<QString>;
class Manager; class Manager;
class Settings; class Settings;

View File

@ -55,7 +55,7 @@ public:
{"\\\\", "\\"}, {"\\\\", "\\"},
{"\\n", "\n"}, {"\\n", "\n"},
}; };
for (auto i = 0, end = text.size() - 1; i < end; ++i) { for (auto i = 0ll, end = text.size() - 1; i < end; ++i) {
const auto pair = text.mid(i, 2); const auto pair = text.mid(i, 2);
const auto replaced = replacements.value(pair); const auto replaced = replacements.value(pair);
if (replaced.isEmpty()) if (replaced.isEmpty())

View File

@ -14,6 +14,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QSplitter> #include <QSplitter>
#include <QTabWidget> #include <QTabWidget>
#include <QTcpSocket>
#include <QTextEdit> #include <QTextEdit>
#include <QToolBar> #include <QToolBar>

View File

@ -33,6 +33,9 @@ WebPage::WebPage(Translator &translator, const QString &script,
channel->registerObject("proxy", proxy_.get()); channel->registerObject("proxy", proxy_.get());
setWebChannel(channel, QWebEngineScript::ScriptWorldId::UserWorld); setWebChannel(channel, QWebEngineScript::ScriptWorldId::UserWorld);
connect(this, &QWebEnginePage::certificateError, //
this, &WebPage::handleCertificateError);
// to load scripts // to load scripts
setUrl(QUrl::fromUserInput("about:blank")); setUrl(QUrl::fromUserInput("about:blank"));
} }
@ -186,11 +189,15 @@ void WebPage::javaScriptConsoleMessage(
emit log(QString("%1: %2 %3").arg(sourceID).arg(lineNumber).arg(message)); emit log(QString("%1: %2 %3").arg(sourceID).arg(lineNumber).arg(message));
} }
bool WebPage::certificateError(const QWebEngineCertificateError &error) void WebPage::handleCertificateError(const QWebEngineCertificateError &error)
{ {
qDebug() << "certificateError" << error.url() << error.error() qDebug() << "certificateError" << error.url() << error.type()
<< error.errorDescription(); << error.description();
return ignoreSslErrors_; if (ignoreSslErrors_) {
const_cast<QWebEngineCertificateError &>(error).acceptCertificate();
return;
}
const_cast<QWebEngineCertificateError &>(error).rejectCertificate();
} }
void WebPage::authenticateProxy(const QUrl & /*requestUrl*/, void WebPage::authenticateProxy(const QUrl & /*requestUrl*/,

View File

@ -34,7 +34,7 @@ protected:
void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level,
const QString &message, int lineNumber, const QString &message, int lineNumber,
const QString &sourceID) override; const QString &sourceID) override;
bool certificateError(const QWebEngineCertificateError &error) override; void handleCertificateError(const QWebEngineCertificateError &error);
private: private:
void authenticateProxy(const QUrl &requestUrl, QAuthenticator *authenticator, void authenticateProxy(const QUrl &requestUrl, QAuthenticator *authenticator,

View File

@ -4,16 +4,8 @@ var active = window.location.href !== "about:blank";
function checkFinished() { function checkFinished() {
if (!active) return; if (!active) return;
let area = document.querySelector('div#target-dummydiv'); let area = document.querySelector('textarea[dl-test=translator-target-input]');
let text = area ? area.innerHTML.trim() : ''; let text = area ? area.value : '';
if (area == null) {
area = document.querySelector('d-textarea.lmt__target_textarea p');
text = area ? area.innerText.trim() : '';
}
if (area == null) {
area = document.querySelector('d-textarea[data-testid=translator-target-input] p');
text = area ? area.innerText.trim() : '';
}
if (text === lastText || text === '') if (text === lastText || text === '')
return; return;
@ -28,16 +20,16 @@ function translate(text, from, to) {
console.log('start translate', text, from, to) console.log('start translate', text, from, to)
if (text.trim().length == 0) { if (text.trim().length == 0) {
proxy.setTranslated(''); proxy.setTranslated('');
return; return;
} }
from = from == 'zh-CN' ? 'zh' : from; from = from == 'zh-CN' ? 'zh' : from;
to = to == 'zh-CN' ? 'zh' : to; to = to == 'zh-CN' ? 'zh' : to;
let supported = ['ru', 'en', 'de', 'fr', 'es', 'pt', 'it', 'nl', 'pl', 'ja', 'zh', let supported = ['ru', 'en', 'de', 'fr', 'es', 'pt', 'it', 'nl', 'pl', 'ja', 'zh',
'uk', 'bg', 'hu', 'el', 'da', 'id', 'lt', 'pt', 'ro', 'sk', 'sk', 'tr', 'fi', 'cs', 'uk', 'bg', 'hu', 'el', 'da', 'id', 'lt', 'pt', 'ro', 'sk', 'sk', 'tr', 'fi', 'cs',
'sv', 'et'] 'sv', 'et']
if (supported.indexOf(from) == -1) { if (supported.indexOf(from) == -1) {
proxy.setFailed('Source language not supported'); proxy.setFailed('Source language not supported');
return; return;
@ -49,32 +41,21 @@ function translate(text, from, to) {
active = true; active = true;
var singleLineText = text.replace(/(?:\r\n|\r|\n)/g, ' ');
let langs = from + '/' + to + '/'; let langs = from + '/' + to + '/';
if (window.location.href.indexOf('www.deepl.com/translator') !== -1 if (window.location.href.indexOf('www.deepl.com/translator') !== -1
&& window.location.href.indexOf(langs) !== -1) { && window.location.href.indexOf(langs) !== -1) {
var input = document.querySelector('textarea[dl-test=translator-source-input]');
var input = document.querySelector('d-textarea[dl-test=translator-source-input] p'); if (input.value == text) {
if (input == null) console.log('using cached result');
input = document.querySelector('d-textarea.lmt__source_textarea p'); lastText = '';
if (input == null) return;
input = document.querySelector('d-textarea[data-testid=translator-source-input] p');
if (input.innerText == singleLineText) {
console.log('using cached result');
lastText = '';
return;
} }
input.innerText = singleLineText; input.value = text;
if (areaCopy = document.querySelector('div#source-dummydiv')) input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
areaCopy.innerHTML = singleLineText;
setTimeout(function () {
input.dispatchEvent(new Event("input", { bubbles: true, cancelable: true }));
}, 300);
return; return;
} }
let url = 'https://www.deepl.com/translator#' + langs + encodeURIComponent(singleLineText); let url = 'https://www.deepl.com/translator#' + langs + encodeURIComponent(text);
console.log("setting url", url); console.log("setting url", url);
window.location = url; window.location = url;
} }

View File

@ -594,7 +594,7 @@
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"a982e9aa6cac598f4c9bf4a56386d13e", "size":1481} {"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/bing.js", "path":"$translators$/bing.js", "md5":"a982e9aa6cac598f4c9bf4a56386d13e", "size":1481}
]} ]}
,"deepl": {"files":[ ,"deepl": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"76856af9b80c3d0e852ca73f8f1ebbdb", "size":2611} {"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/deepl.js", "path":"$translators$/deepl.js", "md5":"6f1c5cd1ccd18cd663f65e6a9bf8462a", "size":1854}
]} ]}
,"google": {"files":[ ,"google": {"files":[
{"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"793d6628ac9e26a1f3cc00fa9c863495", "size":1508} {"url":"https://raw.githubusercontent.com/OneMoreGres/ScreenTranslator/master/translators/google.js", "path":"$translators$/google.js", "md5":"793d6628ac9e26a1f3cc00fa9c863495", "size":1508}