Add ability to edit results
This commit is contained in:
parent
c9b2677bec
commit
1ff3fb1f1b
@ -42,6 +42,7 @@ HEADERS += \
|
|||||||
src/ocr/recognizerworker.h \
|
src/ocr/recognizerworker.h \
|
||||||
src/ocr/tesseract.h \
|
src/ocr/tesseract.h \
|
||||||
src/represent/representer.h \
|
src/represent/representer.h \
|
||||||
|
src/represent/resulteditor.h \
|
||||||
src/represent/resultwidget.h \
|
src/represent/resultwidget.h \
|
||||||
src/service/apptranslator.h \
|
src/service/apptranslator.h \
|
||||||
src/service/debug.h \
|
src/service/debug.h \
|
||||||
@ -75,6 +76,7 @@ SOURCES += \
|
|||||||
src/ocr/recognizerworker.cpp \
|
src/ocr/recognizerworker.cpp \
|
||||||
src/ocr/tesseract.cpp \
|
src/ocr/tesseract.cpp \
|
||||||
src/represent/representer.cpp \
|
src/represent/representer.cpp \
|
||||||
|
src/represent/resulteditor.cpp \
|
||||||
src/represent/resultwidget.cpp \
|
src/represent/resultwidget.cpp \
|
||||||
src/service/apptranslator.cpp \
|
src/service/apptranslator.cpp \
|
||||||
src/service/debug.cpp \
|
src/service/debug.cpp \
|
||||||
|
@ -38,8 +38,8 @@ Manager::Manager()
|
|||||||
recognizer_ = std::make_unique<Recognizer>(*this, *settings_);
|
recognizer_ = std::make_unique<Recognizer>(*this, *settings_);
|
||||||
translator_ = std::make_unique<Translator>(*this, *settings_);
|
translator_ = std::make_unique<Translator>(*this, *settings_);
|
||||||
corrector_ = std::make_unique<Corrector>(*this, *settings_);
|
corrector_ = std::make_unique<Corrector>(*this, *settings_);
|
||||||
representer_ = std::make_unique<Representer>(*this, *tray_, *settings_);
|
representer_ =
|
||||||
|
std::make_unique<Representer>(*this, *tray_, *settings_, *models_);
|
||||||
qRegisterMetaType<TaskPtr>();
|
qRegisterMetaType<TaskPtr>();
|
||||||
|
|
||||||
settings_->load();
|
settings_->load();
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "representer.h"
|
#include "representer.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "geometryutils.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "resulteditor.h"
|
||||||
#include "resultwidget.h"
|
#include "resultwidget.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
@ -8,15 +10,19 @@
|
|||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
Representer::Representer(Manager &manager, TrayIcon &tray,
|
Representer::Representer(Manager &manager, TrayIcon &tray,
|
||||||
const Settings &settings)
|
const Settings &settings, const CommonModels &models)
|
||||||
: manager_(manager)
|
: manager_(manager)
|
||||||
, tray_(tray)
|
, tray_(tray)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
|
, models_(models)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Representer::~Representer() = default;
|
||||||
|
|
||||||
void Representer::showLast()
|
void Representer::showLast()
|
||||||
{
|
{
|
||||||
SOFT_ASSERT(widget_, return );
|
SOFT_ASSERT(widget_, return );
|
||||||
@ -27,18 +33,11 @@ void Representer::clipboardLast()
|
|||||||
{
|
{
|
||||||
SOFT_ASSERT(widget_, return );
|
SOFT_ASSERT(widget_, return );
|
||||||
SOFT_ASSERT(widget_->task(), return );
|
SOFT_ASSERT(widget_->task(), return );
|
||||||
const auto task = widget_->task();
|
clipboardText(widget_->task());
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
auto text = task->recognized;
|
|
||||||
if (!task->translated.isEmpty())
|
|
||||||
text += QLatin1String(" - ") + task->translated;
|
|
||||||
clipboard->setText(text);
|
|
||||||
tray_.showInformation(
|
tray_.showInformation(
|
||||||
QObject::tr("The last result was copied to the clipboard."));
|
QObject::tr("The last result was copied to the clipboard."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Representer::~Representer() = default;
|
|
||||||
|
|
||||||
void Representer::represent(const TaskPtr &task)
|
void Representer::represent(const TaskPtr &task)
|
||||||
{
|
{
|
||||||
if (settings_.resultShowType == ResultMode::Tooltip)
|
if (settings_.resultShowType == ResultMode::Tooltip)
|
||||||
@ -53,6 +52,41 @@ void Representer::updateSettings()
|
|||||||
widget_->updateSettings();
|
widget_->updateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Representer::clipboardText(const TaskPtr &task)
|
||||||
|
{
|
||||||
|
if (!task)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
auto text = task->recognized;
|
||||||
|
if (!task->translated.isEmpty())
|
||||||
|
text += QLatin1String(" - ") + task->translated;
|
||||||
|
clipboard->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Representer::clipboardImage(const TaskPtr &task)
|
||||||
|
{
|
||||||
|
if (!task)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
clipboard->setPixmap(task->captured);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Representer::edit(const TaskPtr &task)
|
||||||
|
{
|
||||||
|
if (!editor_)
|
||||||
|
editor_ = std::make_unique<ResultEditor>(manager_, models_, settings_);
|
||||||
|
|
||||||
|
editor_->show(task);
|
||||||
|
|
||||||
|
const auto cursor = QCursor::pos();
|
||||||
|
const auto screen = QApplication::screenAt(cursor);
|
||||||
|
SOFT_ASSERT(screen, return );
|
||||||
|
editor_->move(service::geometry::cornerAtPoint(cursor, editor_->size(),
|
||||||
|
screen->geometry()));
|
||||||
|
}
|
||||||
|
|
||||||
void Representer::showTooltip(const TaskPtr &task)
|
void Representer::showTooltip(const TaskPtr &task)
|
||||||
{
|
{
|
||||||
auto message = task->recognized + " - " + task->translated;
|
auto message = task->recognized + " - " + task->translated;
|
||||||
@ -62,7 +96,7 @@ void Representer::showTooltip(const TaskPtr &task)
|
|||||||
void Representer::showWidget(const TaskPtr &task)
|
void Representer::showWidget(const TaskPtr &task)
|
||||||
{
|
{
|
||||||
if (!widget_)
|
if (!widget_)
|
||||||
widget_ = std::make_unique<ResultWidget>(settings_);
|
widget_ = std::make_unique<ResultWidget>(*this, settings_);
|
||||||
|
|
||||||
widget_->show(task);
|
widget_->show(task);
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
enum class ResultMode;
|
enum class ResultMode;
|
||||||
class ResultWidget;
|
class ResultWidget;
|
||||||
|
class ResultEditor;
|
||||||
|
|
||||||
class Representer
|
class Representer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Representer(Manager &manager, TrayIcon &tray, const Settings &settings);
|
Representer(Manager &manager, TrayIcon &tray, const Settings &settings,
|
||||||
|
const CommonModels &models);
|
||||||
~Representer();
|
~Representer();
|
||||||
|
|
||||||
void showLast();
|
void showLast();
|
||||||
@ -16,6 +18,10 @@ public:
|
|||||||
void represent(const TaskPtr &task);
|
void represent(const TaskPtr &task);
|
||||||
void updateSettings();
|
void updateSettings();
|
||||||
|
|
||||||
|
void clipboardText(const TaskPtr &task);
|
||||||
|
void clipboardImage(const TaskPtr &task);
|
||||||
|
void edit(const TaskPtr &task);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void showTooltip(const TaskPtr &task);
|
void showTooltip(const TaskPtr &task);
|
||||||
void showWidget(const TaskPtr &task);
|
void showWidget(const TaskPtr &task);
|
||||||
@ -23,5 +29,7 @@ private:
|
|||||||
Manager &manager_;
|
Manager &manager_;
|
||||||
TrayIcon &tray_;
|
TrayIcon &tray_;
|
||||||
const Settings &settings_;
|
const Settings &settings_;
|
||||||
|
const CommonModels &models_;
|
||||||
std::unique_ptr<ResultWidget> widget_;
|
std::unique_ptr<ResultWidget> widget_;
|
||||||
|
std::unique_ptr<ResultEditor> editor_;
|
||||||
};
|
};
|
||||||
|
109
src/represent/resulteditor.cpp
Normal file
109
src/represent/resulteditor.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "resulteditor.h"
|
||||||
|
#include "commonmodels.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "languagecodes.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
ResultEditor::ResultEditor(Manager &manager, const CommonModels &models,
|
||||||
|
const Settings &settings, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, manager_(manager)
|
||||||
|
, settings_(settings)
|
||||||
|
, image_(new QLabel(this))
|
||||||
|
, recognizedEdit_(new QTextEdit(this))
|
||||||
|
, sourceLanguage_(new QComboBox(this))
|
||||||
|
, targetLanguage_(new QComboBox(this))
|
||||||
|
, recognizeOnly_(new QPushButton(tr("Recognize"), this))
|
||||||
|
, recognizeAndTranslate_(new QPushButton(tr("Recognize and translate"), this))
|
||||||
|
, translateOnly_(new QPushButton(tr("Translate"), this))
|
||||||
|
{
|
||||||
|
image_->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
sourceLanguage_->setModel(models.sourceLanguageModel());
|
||||||
|
targetLanguage_->setModel(models.targetLanguageModel());
|
||||||
|
|
||||||
|
connect(recognizeOnly_, &QPushButton::clicked, //
|
||||||
|
this, &ResultEditor::recognize);
|
||||||
|
connect(recognizeAndTranslate_, &QPushButton::clicked, //
|
||||||
|
this, &ResultEditor::recognizeAndTranslate);
|
||||||
|
connect(translateOnly_, &QPushButton::clicked, //
|
||||||
|
this, &ResultEditor::translate);
|
||||||
|
|
||||||
|
auto layout = new QGridLayout(this);
|
||||||
|
auto row = 0;
|
||||||
|
layout->addWidget(image_, row, 0, 1, 2);
|
||||||
|
|
||||||
|
++row;
|
||||||
|
layout->addWidget(new QLabel(tr("Recognize:")), row, 0);
|
||||||
|
layout->addWidget(sourceLanguage_, row, 1);
|
||||||
|
|
||||||
|
++row;
|
||||||
|
layout->addWidget(new QLabel(tr("Translate:")), row, 0);
|
||||||
|
layout->addWidget(targetLanguage_, row, 1);
|
||||||
|
|
||||||
|
++row;
|
||||||
|
layout->addWidget(recognizedEdit_, row, 0, 1, 2);
|
||||||
|
|
||||||
|
++row;
|
||||||
|
auto box = new QHBoxLayout;
|
||||||
|
layout->addLayout(box, row, 0, 1, 2);
|
||||||
|
box->addWidget(recognizeOnly_);
|
||||||
|
box->addWidget(recognizeAndTranslate_);
|
||||||
|
box->addWidget(translateOnly_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultEditor::show(const TaskPtr &task)
|
||||||
|
{
|
||||||
|
SOFT_ASSERT(task, return );
|
||||||
|
task_ = task;
|
||||||
|
|
||||||
|
image_->setPixmap(task->captured);
|
||||||
|
recognizedEdit_->setText(task->recognized);
|
||||||
|
|
||||||
|
const auto target = task->targetLanguage.isEmpty() ? settings_.targetLanguage
|
||||||
|
: task->targetLanguage;
|
||||||
|
targetLanguage_->setCurrentText(LanguageCodes::name(target));
|
||||||
|
sourceLanguage_->setCurrentText(LanguageCodes::name(task->sourceLanguage));
|
||||||
|
|
||||||
|
QWidget::show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultEditor::recognize()
|
||||||
|
{
|
||||||
|
task_->sourceLanguage =
|
||||||
|
LanguageCodes::idForName(sourceLanguage_->currentText());
|
||||||
|
task_->targetLanguage.clear();
|
||||||
|
manager_.captured(task_);
|
||||||
|
close();
|
||||||
|
task_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultEditor::recognizeAndTranslate()
|
||||||
|
{
|
||||||
|
task_->sourceLanguage =
|
||||||
|
LanguageCodes::idForName(sourceLanguage_->currentText());
|
||||||
|
task_->targetLanguage =
|
||||||
|
LanguageCodes::idForName(targetLanguage_->currentText());
|
||||||
|
task_->translators = settings_.translators;
|
||||||
|
manager_.captured(task_);
|
||||||
|
close();
|
||||||
|
task_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultEditor::translate()
|
||||||
|
{
|
||||||
|
task_->targetLanguage =
|
||||||
|
LanguageCodes::idForName(targetLanguage_->currentText());
|
||||||
|
task_->translators = settings_.translators;
|
||||||
|
manager_.corrected(task_);
|
||||||
|
close();
|
||||||
|
task_.reset();
|
||||||
|
}
|
38
src/represent/resulteditor.h
Normal file
38
src/represent/resulteditor.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stfwd.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QMenu;
|
||||||
|
class QTextEdit;
|
||||||
|
class QComboBox;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class ResultEditor : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ResultEditor(Manager& manager, const CommonModels& models,
|
||||||
|
const Settings& settings, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void show(const TaskPtr& task);
|
||||||
|
using QWidget::show;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void recognize();
|
||||||
|
void recognizeAndTranslate();
|
||||||
|
void translate();
|
||||||
|
|
||||||
|
Manager& manager_;
|
||||||
|
const Settings& settings_;
|
||||||
|
TaskPtr task_;
|
||||||
|
QLabel* image_;
|
||||||
|
QTextEdit* recognizedEdit_;
|
||||||
|
QComboBox* sourceLanguage_;
|
||||||
|
QComboBox* targetLanguage_;
|
||||||
|
QPushButton* recognizeOnly_;
|
||||||
|
QPushButton* recognizeAndTranslate_;
|
||||||
|
QPushButton* translateOnly_;
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
#include "resultwidget.h"
|
#include "resultwidget.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "representer.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
@ -7,14 +8,18 @@
|
|||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
||||||
ResultWidget::ResultWidget(const Settings &settings, QWidget *parent)
|
ResultWidget::ResultWidget(Representer &representer, const Settings &settings,
|
||||||
|
QWidget *parent)
|
||||||
: QFrame(parent)
|
: QFrame(parent)
|
||||||
|
, representer_(representer)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
, image_(new QLabel(this))
|
, image_(new QLabel(this))
|
||||||
, recognized_(new QLabel(this))
|
, recognized_(new QLabel(this))
|
||||||
, translated_(new QLabel(this))
|
, translated_(new QLabel(this))
|
||||||
|
, contextMenu_(new QMenu(this))
|
||||||
{
|
{
|
||||||
setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
|
setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
|
||||||
Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
||||||
@ -33,6 +38,18 @@ ResultWidget::ResultWidget(const Settings &settings, QWidget *parent)
|
|||||||
translated_->setAlignment(Qt::AlignCenter);
|
translated_->setAlignment(Qt::AlignCenter);
|
||||||
translated_->setWordWrap(true);
|
translated_->setWordWrap(true);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto clipboardText = contextMenu_->addAction(tr("Copy text"));
|
||||||
|
connect(clipboardText, &QAction::triggered, //
|
||||||
|
this, &ResultWidget::copyText);
|
||||||
|
auto clipboardImage = contextMenu_->addAction(tr("Copy image"));
|
||||||
|
connect(clipboardImage, &QAction::triggered, //
|
||||||
|
this, &ResultWidget::copyImage);
|
||||||
|
auto edit = contextMenu_->addAction(tr("Edit..."));
|
||||||
|
connect(edit, &QAction::triggered, //
|
||||||
|
this, &ResultWidget::edit);
|
||||||
|
}
|
||||||
|
|
||||||
const auto styleSheet =
|
const auto styleSheet =
|
||||||
"#recognizeLabel, #translateLabel {"
|
"#recognizeLabel, #translateLabel {"
|
||||||
"color: black;"
|
"color: black;"
|
||||||
@ -120,36 +137,28 @@ bool ResultWidget::eventFilter(QObject *watched, QEvent *event)
|
|||||||
{
|
{
|
||||||
if (event->type() == QEvent::MouseButtonPress) {
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
const auto button = static_cast<QMouseEvent *>(event)->button();
|
const auto button = static_cast<QMouseEvent *>(event)->button();
|
||||||
if (button == Qt::LeftButton) {
|
if (button == Qt::RightButton) {
|
||||||
|
contextMenu_->exec(QCursor::pos());
|
||||||
|
} else {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
// else if (button == Qt::RightButton) {
|
|
||||||
// QAction *action = contextMenu_->exec(QCursor::pos());
|
|
||||||
// if (recognizeSubMenu_->findChildren<QAction *>().contains(action)) {
|
|
||||||
// ProcessingItem item = item_;
|
|
||||||
// task->translated = task->recognized = QString();
|
|
||||||
// task->ocrLanguage = dictionary_.ocrUiToCode(action->text());
|
|
||||||
// emit requestRecognize(item);
|
|
||||||
// } else if (translateSubMenu_->findChildren<QAction *>().contains(
|
|
||||||
// action)) {
|
|
||||||
// ProcessingItem item = item_;
|
|
||||||
// task->translated.clear();
|
|
||||||
// task->translateLanguage =
|
|
||||||
// dictionary_.translateUiToCode(action->text()); emit
|
|
||||||
// requestTranslate(item);
|
|
||||||
// } else if (action == clipboardAction_) {
|
|
||||||
// emit requestClipboard();
|
|
||||||
// } else if (action == imageClipboardAction_) {
|
|
||||||
// emit requestImageClipboard();
|
|
||||||
// } else if (action == correctAction_) {
|
|
||||||
// emit requestEdition(item_);
|
|
||||||
// // Return because Manager calls showResult() before hide()
|
|
||||||
// otherwise.return QWidget::eventFilter(watched, event);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// hide();
|
|
||||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
} else if (event->type() == QEvent::WindowDeactivate) {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
return QWidget::eventFilter(watched, event);
|
return QWidget::eventFilter(watched, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResultWidget::edit()
|
||||||
|
{
|
||||||
|
representer_.edit(task_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultWidget::copyText()
|
||||||
|
{
|
||||||
|
representer_.clipboardText(task_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultWidget::copyImage()
|
||||||
|
{
|
||||||
|
representer_.clipboardImage(task_);
|
||||||
|
}
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
class ResultWidget : public QFrame
|
class ResultWidget : public QFrame
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ResultWidget(const Settings& settings, QWidget* parent = nullptr);
|
ResultWidget(Representer& representer, const Settings& settings,
|
||||||
|
QWidget* parent = nullptr);
|
||||||
|
|
||||||
const TaskPtr& task() const;
|
const TaskPtr& task() const;
|
||||||
void show(const TaskPtr& task);
|
void show(const TaskPtr& task);
|
||||||
@ -20,9 +22,15 @@ public:
|
|||||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void edit();
|
||||||
|
void copyImage();
|
||||||
|
void copyText();
|
||||||
|
|
||||||
|
Representer& representer_;
|
||||||
const Settings& settings_;
|
const Settings& settings_;
|
||||||
TaskPtr task_;
|
TaskPtr task_;
|
||||||
QLabel* image_;
|
QLabel* image_;
|
||||||
QLabel* recognized_;
|
QLabel* recognized_;
|
||||||
QLabel* translated_;
|
QLabel* translated_;
|
||||||
|
QMenu* contextMenu_;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user