2015-09-06 20:20:00 +02:00
|
|
|
#include "./tagedit.h"
|
|
|
|
#include "./tagfieldedit.h"
|
2015-04-22 19:33:53 +02:00
|
|
|
|
2015-09-06 20:20:00 +02:00
|
|
|
#include "../application/knownfieldmodel.h"
|
2018-03-07 01:18:01 +01:00
|
|
|
#include "../application/settings.h"
|
2015-04-22 19:33:53 +02:00
|
|
|
|
|
|
|
#include <tagparser/tag.h>
|
|
|
|
|
2021-03-20 21:59:49 +01:00
|
|
|
#include <qtutilities/misc/conversion.h>
|
|
|
|
|
2015-04-22 19:33:53 +02:00
|
|
|
#include <c++utilities/conversion/stringconversion.h>
|
|
|
|
|
|
|
|
#include <QFormLayout>
|
|
|
|
#include <QLabel>
|
2018-03-07 01:18:01 +01:00
|
|
|
#include <QSplitter>
|
|
|
|
#include <QVBoxLayout>
|
2015-04-22 19:33:53 +02:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace std::placeholders;
|
2019-06-10 22:49:46 +02:00
|
|
|
using namespace CppUtilities;
|
2018-03-06 23:10:13 +01:00
|
|
|
using namespace TagParser;
|
2015-04-22 19:33:53 +02:00
|
|
|
|
|
|
|
namespace QtGui {
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \class QtGui::TagEdit
|
2018-06-03 20:39:00 +02:00
|
|
|
* \brief The TagEdit widget allows the user to edit TagParser::Tag objects.
|
2015-04-22 19:33:53 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new TagEdit object.
|
|
|
|
*/
|
2018-03-07 01:18:01 +01:00
|
|
|
TagEdit::TagEdit(QWidget *parent)
|
|
|
|
: QWidget(parent)
|
2015-04-22 19:33:53 +02:00
|
|
|
{
|
2019-06-25 14:00:09 +02:00
|
|
|
auto *const mainLayout = new QVBoxLayout(this);
|
|
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
2018-05-13 20:42:16 +02:00
|
|
|
QSplitter *const splitter = new QSplitter(this);
|
2015-04-22 19:33:53 +02:00
|
|
|
splitter->setOrientation(Qt::Horizontal);
|
2019-06-25 14:00:09 +02:00
|
|
|
auto *widget = new QWidget(this);
|
2015-04-22 19:33:53 +02:00
|
|
|
m_layoutLeft = new QFormLayout(widget);
|
|
|
|
m_layoutLeft->setContentsMargins(QMargins());
|
2016-01-09 03:21:14 +01:00
|
|
|
m_layoutLeft->setSpacing(2);
|
2015-04-22 19:33:53 +02:00
|
|
|
widget->setLayout(m_layoutLeft);
|
|
|
|
splitter->addWidget(widget);
|
|
|
|
widget = new QWidget(this);
|
|
|
|
m_layoutRight = new QVBoxLayout(widget);
|
|
|
|
m_layoutRight->setContentsMargins(QMargins());
|
|
|
|
widget->setLayout(m_layoutRight);
|
|
|
|
splitter->addWidget(widget);
|
|
|
|
splitter->setSizes(QList<int>() << 500);
|
|
|
|
mainLayout->addWidget(splitter);
|
|
|
|
setLayout(mainLayout);
|
|
|
|
}
|
|
|
|
|
2016-03-03 22:21:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief Returns the current value for the specified \a field.
|
|
|
|
* \remarks Doesn't work for fields of the type picture.
|
|
|
|
*/
|
2016-12-01 22:23:01 +01:00
|
|
|
TagValue TagEdit::value(KnownField field, TagTextEncoding encoding) const
|
2016-03-03 22:21:15 +01:00
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
if (const TagFieldEdit *const edit = m_widgets.value(field, nullptr)) {
|
2016-12-01 22:23:01 +01:00
|
|
|
return edit->value(encoding, false);
|
2016-03-03 22:21:15 +01:00
|
|
|
} else {
|
|
|
|
return TagValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 14:45:26 +02:00
|
|
|
std::int32_t TagEdit::trackNumber() const
|
|
|
|
{
|
|
|
|
std::int32_t trackNumber = 0;
|
|
|
|
try {
|
|
|
|
trackNumber = value(KnownField::TrackPosition).toPositionInSet().position();
|
|
|
|
} catch (const ConversionException &) {
|
|
|
|
}
|
|
|
|
if (trackNumber) {
|
|
|
|
return trackNumber;
|
|
|
|
}
|
|
|
|
for (const auto *const tag : tags()) {
|
|
|
|
if (tag->supportsTarget() && tag->targetLevel() != TagTargetLevel::Track) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
trackNumber = value(KnownField::PartNumber).toInteger();
|
|
|
|
} catch (const ConversionException &) {
|
|
|
|
}
|
|
|
|
if (trackNumber) {
|
|
|
|
return trackNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return trackNumber;
|
|
|
|
}
|
|
|
|
|
2019-06-01 12:50:09 +02:00
|
|
|
int32_t TagEdit::diskNumber() const
|
|
|
|
{
|
|
|
|
std::int32_t diskNumber = 0;
|
|
|
|
try {
|
|
|
|
diskNumber = value(KnownField::DiskPosition).toPositionInSet().position();
|
|
|
|
} catch (const ConversionException &) {
|
|
|
|
}
|
|
|
|
if (diskNumber) {
|
|
|
|
return diskNumber;
|
|
|
|
}
|
|
|
|
for (const auto *const tag : tags()) {
|
|
|
|
if (!tag->supportsTarget() || tag->targetLevel() != TagTargetLevel::Part) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
diskNumber = value(KnownField::PartNumber).toInteger();
|
|
|
|
} catch (const ConversionException &) {
|
|
|
|
}
|
|
|
|
if (diskNumber) {
|
|
|
|
return diskNumber;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return diskNumber;
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:33:53 +02:00
|
|
|
/*!
|
|
|
|
* \brief Assigns the specified \a tag to the edit.
|
|
|
|
* \param updateUi Specifies whether the UI of should be updated.
|
|
|
|
* \remarks The TagEdit object does not take ownership.
|
|
|
|
*/
|
|
|
|
void TagEdit::setTag(Tag *tag, bool updateUi)
|
|
|
|
{
|
|
|
|
m_tags.clear();
|
2018-03-07 01:18:01 +01:00
|
|
|
if (tag) {
|
2015-04-22 19:33:53 +02:00
|
|
|
m_tags << tag;
|
|
|
|
}
|
2018-03-07 01:18:01 +01:00
|
|
|
if (updateUi) {
|
2015-04-22 19:33:53 +02:00
|
|
|
setupUi();
|
|
|
|
} else {
|
|
|
|
assignTags();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Assigns the specified \a tags to the edit.
|
|
|
|
* \param updateUi Specifies whether the UI of should be updated.
|
|
|
|
* \remarks The TagEdit object does not take ownership.
|
|
|
|
*/
|
|
|
|
void TagEdit::setTags(const QList<Tag *> &tags, bool updateUi)
|
|
|
|
{
|
|
|
|
m_tags = tags;
|
2018-03-07 01:18:01 +01:00
|
|
|
if (updateUi) {
|
2015-04-22 19:33:53 +02:00
|
|
|
setupUi();
|
|
|
|
} else {
|
|
|
|
assignTags();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Sets the \a value for the specified \a field manually applying the specified \a previousValueHandling.
|
2018-05-13 20:42:16 +02:00
|
|
|
*
|
|
|
|
* Used for editing tags programmatically, eg. in TagEditorWidget::insertTitleFromFilename() and DbQueryWidget::applyResults().
|
2015-04-22 19:33:53 +02:00
|
|
|
*/
|
2018-03-06 23:10:13 +01:00
|
|
|
bool TagEdit::setValue(KnownField field, const TagParser::TagValue &value, PreviousValueHandling previousValueHandling)
|
2015-04-22 19:33:53 +02:00
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
if (TagFieldEdit *const edit = m_widgets.value(field, nullptr)) {
|
2015-04-22 19:33:53 +02:00
|
|
|
return edit->setValue(value, previousValueHandling);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns whether at least on of the assigned tags supports the specified \a field.
|
|
|
|
*/
|
|
|
|
bool TagEdit::hasField(KnownField field) const
|
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
for (Tag *const tag : m_tags) {
|
2018-03-07 01:18:01 +01:00
|
|
|
if (tag->supportsField(field)) {
|
2015-04-22 19:33:53 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-01 12:53:44 +02:00
|
|
|
bool TagEdit::hasAutoCorrectionBeenApplied() const
|
|
|
|
{
|
|
|
|
for (auto i = m_widgets.constBegin(), end = m_widgets.constEnd(); i != end; ++i) {
|
|
|
|
if (i.value()->hasAutoCorrectionBeenApplied()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:33:53 +02:00
|
|
|
/*!
|
|
|
|
* \brief Generates a label for the currently assigned tags.
|
|
|
|
*
|
|
|
|
* (Used as combo box item for the tag edit.)
|
|
|
|
*/
|
|
|
|
QString TagEdit::generateLabel() const
|
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
if (m_tags.isEmpty()) {
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
const TagTarget &target = m_tags.at(0)->target();
|
|
|
|
bool differentTargets = false, haveMatroskaTags = false;
|
|
|
|
QStringList tagNames;
|
|
|
|
tagNames.reserve(m_tags.size());
|
|
|
|
for (const Tag *const tag : m_tags) {
|
2021-03-20 21:59:49 +01:00
|
|
|
tagNames << QtUtilities::qstringFromStdStringView(tag->typeName());
|
2018-05-13 20:42:16 +02:00
|
|
|
if (!differentTargets && !(target == tag->target())) {
|
|
|
|
differentTargets = true;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
2018-05-13 20:42:16 +02:00
|
|
|
if (tag->type() == TagType::MatroskaTag) {
|
|
|
|
haveMatroskaTags = true;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-13 20:42:16 +02:00
|
|
|
QString res = tagNames.join(QStringLiteral(", "));
|
|
|
|
if (differentTargets) {
|
|
|
|
res.append(tr(" with different targets"));
|
|
|
|
} else if (haveMatroskaTags || !target.isEmpty()) {
|
2021-03-20 21:59:49 +01:00
|
|
|
res.append(tr(" targeting %1").arg(QString::fromStdString(m_tags.front()->targetString())));
|
2018-05-13 20:42:16 +02:00
|
|
|
}
|
|
|
|
return res;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Clears all TagFieldEdit widgets.
|
|
|
|
*/
|
|
|
|
void TagEdit::clear()
|
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
for (TagFieldEdit *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
edit->clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Restores all TagFieldEdit widgets.
|
|
|
|
*/
|
|
|
|
void TagEdit::restore()
|
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
for (TagFieldEdit *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
edit->restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Applies all entered values to the assigned tags.
|
|
|
|
*/
|
|
|
|
void TagEdit::apply()
|
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
switch (Settings::values().tagPocessing.unsupportedFieldHandling) {
|
2015-04-22 19:33:53 +02:00
|
|
|
case Settings::UnsupportedFieldHandling::Discard:
|
|
|
|
// remove all old fields of all tags to discard
|
|
|
|
// all unsupported values
|
2018-05-13 20:42:16 +02:00
|
|
|
for (Tag *const tag : m_tags) {
|
2015-04-22 19:33:53 +02:00
|
|
|
tag->removeAllFields();
|
|
|
|
}
|
|
|
|
break;
|
2018-03-07 01:18:01 +01:00
|
|
|
default:;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
2018-05-13 20:42:16 +02:00
|
|
|
for (TagFieldEdit *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
edit->apply();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Invalidates the UI.
|
|
|
|
*
|
|
|
|
* All widgets are removed and recreated. Hence all fields are restored
|
|
|
|
* to the initial values. Assigned tags will remain.
|
|
|
|
*/
|
|
|
|
void TagEdit::invalidate()
|
|
|
|
{
|
|
|
|
// remove current widgets
|
2018-05-13 20:42:16 +02:00
|
|
|
for (QWidget *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
removeEdit(edit);
|
|
|
|
}
|
|
|
|
m_widgets.clear();
|
|
|
|
// recreate widgets
|
|
|
|
setupUi();
|
|
|
|
}
|
|
|
|
|
2016-05-16 21:01:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Sets whether cover buttons are hidden.
|
|
|
|
*/
|
|
|
|
void TagEdit::setCoverButtonsHidden(bool hideCoverButtons)
|
|
|
|
{
|
2018-03-07 01:18:01 +01:00
|
|
|
for (auto i = m_widgets.begin(), end = m_widgets.end(); i != end; ++i) {
|
2016-05-16 21:01:01 +02:00
|
|
|
i.value()->setCoverButtonsHidden(hideCoverButtons);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:33:53 +02:00
|
|
|
/*!
|
|
|
|
* \brief Internally called to setup the UI.
|
|
|
|
*/
|
|
|
|
void TagEdit::setupUi()
|
|
|
|
{
|
|
|
|
setUpdatesEnabled(false);
|
2018-05-13 20:42:16 +02:00
|
|
|
if (m_tags.empty()) {
|
2015-04-22 19:33:53 +02:00
|
|
|
// there are no tags assigned -> remove all editing controls
|
2020-03-09 19:05:24 +01:00
|
|
|
for (QWidget *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
removeEdit(edit);
|
|
|
|
}
|
|
|
|
m_widgets.clear();
|
2018-05-13 20:42:16 +02:00
|
|
|
setUpdatesEnabled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// there are tags assigned
|
|
|
|
// setup editing controls
|
|
|
|
TagFieldEdit *edit = nullptr;
|
2023-02-20 19:55:37 +01:00
|
|
|
auto rowLeft = 0, rowRight = 0;
|
2018-05-13 20:42:16 +02:00
|
|
|
for (const auto &item : Settings::values().editor.fields.items()) {
|
2021-07-25 20:36:33 +02:00
|
|
|
const auto field = static_cast<KnownField>(item.id().toInt());
|
2018-05-13 20:42:16 +02:00
|
|
|
if (!item.isChecked() || !hasField(field)) {
|
|
|
|
// the field is either disabled or it is not supported by at least one of the assigned tags
|
|
|
|
if ((edit = m_widgets.value(field))) {
|
|
|
|
m_widgets.remove(field);
|
|
|
|
removeEdit(edit);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the field is not disabled and the field is supported by at least one of the assigned tags
|
|
|
|
edit = m_widgets.value(field, nullptr);
|
|
|
|
if (edit) {
|
|
|
|
// we have already an edit for the field -> try to recycle it
|
|
|
|
// the order might have changed
|
|
|
|
int prevIndex; // stores the previous index (NOT row)
|
|
|
|
switch (field) {
|
|
|
|
case KnownField::Cover:
|
|
|
|
case KnownField::Lyrics:
|
|
|
|
// these fields are shown at the right side
|
|
|
|
prevIndex = m_layoutRight->indexOf(edit);
|
|
|
|
if (prevIndex > 0 && (rowRight * 2 + 1) != prevIndex) {
|
|
|
|
QLayoutItem *item1 = m_layoutRight->itemAt(prevIndex - 1);
|
|
|
|
QLayoutItem *item2 = m_layoutRight->itemAt(prevIndex);
|
|
|
|
m_layoutRight->removeItem(item1);
|
|
|
|
m_layoutRight->removeItem(item2);
|
|
|
|
m_layoutRight->insertItem(rowRight * 2, item1);
|
|
|
|
m_layoutRight->insertItem(rowRight * 2 + 1, item2);
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
2018-05-13 20:42:16 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// the other fields are shown at the left side
|
|
|
|
prevIndex = m_layoutLeft->indexOf(edit);
|
2021-07-25 20:36:33 +02:00
|
|
|
if (prevIndex >= 0 && (rowLeft * 2 + 1) != prevIndex) {
|
2018-05-13 20:42:16 +02:00
|
|
|
QLayoutItem *item1 = m_layoutLeft->itemAt(prevIndex - 1);
|
|
|
|
QLayoutItem *item2 = m_layoutLeft->itemAt(prevIndex);
|
|
|
|
QWidget *label = item1->widget();
|
|
|
|
m_layoutLeft->removeItem(item1);
|
|
|
|
m_layoutLeft->removeItem(item2);
|
|
|
|
delete item1;
|
|
|
|
delete item2;
|
|
|
|
m_layoutLeft->insertRow(rowLeft, label, edit);
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-13 20:42:16 +02:00
|
|
|
// update the tag field
|
|
|
|
edit->setTagField(m_tags, field, m_previousValueHandling);
|
|
|
|
} else {
|
|
|
|
// we need to create a new edit for the field
|
|
|
|
edit = new TagFieldEdit(m_tags, field, this);
|
|
|
|
connect(edit, &TagFieldEdit::returnPressed, this, &TagEdit::returnPressed);
|
|
|
|
switch (field) {
|
|
|
|
case KnownField::Cover:
|
|
|
|
case KnownField::Lyrics:
|
|
|
|
// editing widgets for these fields will be show at the right side (m_layoutRight)
|
|
|
|
m_layoutRight->insertWidget(rowRight * 2, new QLabel(item.label(), this));
|
|
|
|
edit->setContentsMargins(10, 0, 0, 0);
|
|
|
|
m_layoutRight->insertWidget(rowRight * 2 + 1, edit);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// editing widgets for the other fields will be show at the left side (m_layoutLeft)
|
|
|
|
m_layoutLeft->insertRow(rowLeft, item.label(), edit);
|
|
|
|
}
|
|
|
|
m_widgets.insert(field, edit);
|
|
|
|
}
|
|
|
|
// update the current position
|
2020-03-09 19:05:24 +01:00
|
|
|
// note: The position is required to keep the order of Settings::fieldModel().fields().
|
2018-05-13 20:42:16 +02:00
|
|
|
switch (field) {
|
|
|
|
case KnownField::Cover:
|
|
|
|
case KnownField::Lyrics:
|
|
|
|
++rowRight;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
++rowLeft;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
setUpdatesEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2021-07-25 20:36:33 +02:00
|
|
|
* \brief Internally called to delete an edit removing it (and its label) from its current layout.
|
2015-04-22 19:33:53 +02:00
|
|
|
*/
|
|
|
|
void TagEdit::removeEdit(QWidget *edit)
|
|
|
|
{
|
2021-07-25 20:36:33 +02:00
|
|
|
edit->deleteLater();
|
|
|
|
// the left layout might contain the edit
|
|
|
|
const auto leftIndex = m_layoutLeft->indexOf(edit);
|
|
|
|
if (leftIndex > 0) {
|
|
|
|
// delete label as well
|
|
|
|
if (QWidget *const label = m_layoutLeft->labelForField(edit)) {
|
|
|
|
label->deleteLater();
|
|
|
|
}
|
|
|
|
m_layoutLeft->removeWidget(edit);
|
|
|
|
return;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
2021-07-25 20:36:33 +02:00
|
|
|
// or the right layout might contain the edit
|
|
|
|
const auto rightIndex = m_layoutRight->indexOf(edit);
|
|
|
|
if (rightIndex < 0) {
|
2018-05-13 20:42:16 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-07-25 20:36:33 +02:00
|
|
|
// delete label as well
|
|
|
|
QLayoutItem *const labelItem = m_layoutRight->itemAt(rightIndex - 1);
|
|
|
|
m_layoutRight->removeWidget(edit);
|
|
|
|
if (!labelItem || !labelItem->widget()) {
|
2018-05-13 20:42:16 +02:00
|
|
|
return;
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
2021-07-25 20:36:33 +02:00
|
|
|
labelItem->widget()->deleteLater();
|
|
|
|
m_layoutRight->removeWidget(labelItem->widget());
|
2015-04-22 19:33:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Internlly called to assign tags without updating the UI.
|
|
|
|
*/
|
|
|
|
void TagEdit::assignTags()
|
|
|
|
{
|
2018-05-13 20:42:16 +02:00
|
|
|
for (TagFieldEdit *const edit : m_widgets) {
|
2015-04-22 19:33:53 +02:00
|
|
|
edit->setTagField(m_tags, edit->field(), m_previousValueHandling, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:18:01 +01:00
|
|
|
} // namespace QtGui
|