* [ Tools ]: Update SPDX License Identifiers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2122 lines
52 KiB
C++
2122 lines
52 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
/***************************************************************************
|
|
* Copyright (c) 2006 Werner Mayer <werner.wm.mayer@gmx.de> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <limits>
|
|
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QColorDialog>
|
|
#include <QCursor>
|
|
#include <QFileDialog>
|
|
#include <QHeaderView>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QRegularExpression>
|
|
#include <QRegularExpressionMatch>
|
|
#include <QStyleOptionButton>
|
|
#include <QStylePainter>
|
|
#include <QToolTip>
|
|
#include <QtGui>
|
|
|
|
#include "customwidgets.h"
|
|
|
|
using namespace Gui;
|
|
|
|
|
|
UrlLabel::UrlLabel(QWidget* parent, Qt::WindowFlags f)
|
|
: QLabel("TextLabel", parent, f)
|
|
{
|
|
_url = "http://localhost";
|
|
setToolTip(this->_url);
|
|
setCursor(Qt::PointingHandCursor);
|
|
}
|
|
|
|
UrlLabel::~UrlLabel()
|
|
{}
|
|
|
|
void UrlLabel::mouseReleaseEvent(QMouseEvent*)
|
|
{
|
|
QMessageBox::information(this,
|
|
"Browser",
|
|
QString("This starts your browser with url %1").arg(_url));
|
|
}
|
|
|
|
QString UrlLabel::url() const
|
|
{
|
|
return this->_url;
|
|
}
|
|
|
|
void UrlLabel::setUrl(const QString& u)
|
|
{
|
|
this->_url = u;
|
|
setToolTip(this->_url);
|
|
}
|
|
|
|
LocationWidget::LocationWidget(QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
box = new QGridLayout();
|
|
|
|
xValue = new QDoubleSpinBox(this);
|
|
xValue->setMinimum(-2.14748e+09);
|
|
xValue->setMaximum(2.14748e+09);
|
|
xLabel = new QLabel(this);
|
|
box->addWidget(xLabel, 0, 0, 1, 1);
|
|
box->addWidget(xValue, 0, 1, 1, 1);
|
|
|
|
yValue = new QDoubleSpinBox(this);
|
|
yValue->setMinimum(-2.14748e+09);
|
|
yValue->setMaximum(2.14748e+09);
|
|
yLabel = new QLabel(this);
|
|
box->addWidget(yLabel, 1, 0, 1, 1);
|
|
box->addWidget(yValue, 1, 1, 1, 1);
|
|
|
|
zValue = new QDoubleSpinBox(this);
|
|
zValue->setMinimum(-2.14748e+09);
|
|
zValue->setMaximum(2.14748e+09);
|
|
zLabel = new QLabel(this);
|
|
box->addWidget(zLabel, 2, 0, 1, 1);
|
|
box->addWidget(zValue, 2, 1, 1, 1);
|
|
|
|
dLabel = new QLabel(this);
|
|
dValue = new QComboBox(this);
|
|
dValue->setCurrentIndex(-1);
|
|
box->addWidget(dLabel, 3, 0, 1, 1);
|
|
box->addWidget(dValue, 3, 1, 1, 1);
|
|
|
|
QGridLayout* gridLayout = new QGridLayout(this);
|
|
gridLayout->addLayout(box, 0, 0, 1, 2);
|
|
|
|
retranslateUi();
|
|
}
|
|
|
|
LocationWidget::~LocationWidget()
|
|
{}
|
|
|
|
QSize LocationWidget::sizeHint() const
|
|
{
|
|
return QSize(150, 100);
|
|
}
|
|
|
|
void LocationWidget::changeEvent(QEvent* e)
|
|
{
|
|
if (e->type() == QEvent::LanguageChange) {
|
|
this->retranslateUi();
|
|
}
|
|
QWidget::changeEvent(e);
|
|
}
|
|
|
|
void LocationWidget::retranslateUi()
|
|
{
|
|
xLabel->setText(QApplication::translate("Gui::LocationWidget", "X:"));
|
|
yLabel->setText(QApplication::translate("Gui::LocationWidget", "Y:"));
|
|
zLabel->setText(QApplication::translate("Gui::LocationWidget", "Z:"));
|
|
dLabel->setText(QApplication::translate("Gui::LocationWidget", "Direction:"));
|
|
}
|
|
|
|
FileChooser::FileChooser(QWidget* parent)
|
|
: QWidget(parent)
|
|
, md(File)
|
|
, _filter(QString())
|
|
{
|
|
QHBoxLayout* layout = new QHBoxLayout(this);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->setSpacing(6);
|
|
|
|
lineEdit = new QLineEdit(this);
|
|
layout->addWidget(lineEdit);
|
|
|
|
connect(lineEdit, &QLineEdit::textChanged, this, &FileChooser::fileNameChanged);
|
|
|
|
button = new QPushButton("...", this);
|
|
button->setFixedWidth(2 * button->fontMetrics().horizontalAdvance(" ... "));
|
|
|
|
layout->addWidget(button);
|
|
|
|
connect(button, &QPushButton::clicked, this, &FileChooser::chooseFile);
|
|
|
|
setFocusProxy(lineEdit);
|
|
}
|
|
|
|
FileChooser::~FileChooser()
|
|
{}
|
|
|
|
QString FileChooser::fileName() const
|
|
{
|
|
return lineEdit->text();
|
|
}
|
|
|
|
void FileChooser::setFileName(const QString& fn)
|
|
{
|
|
lineEdit->setText(fn);
|
|
}
|
|
|
|
void FileChooser::chooseFile()
|
|
{
|
|
QFileDialog::Options dlgOpt = QFileDialog::DontUseNativeDialog;
|
|
QString fn;
|
|
if (mode() == File) {
|
|
fn = QFileDialog::getOpenFileName(this,
|
|
tr("Select a file"),
|
|
lineEdit->text(),
|
|
_filter,
|
|
0,
|
|
dlgOpt);
|
|
}
|
|
else {
|
|
QFileDialog::Options option = QFileDialog::ShowDirsOnly | dlgOpt;
|
|
fn = QFileDialog::getExistingDirectory(this,
|
|
tr("Select a directory"),
|
|
lineEdit->text(),
|
|
option);
|
|
}
|
|
|
|
if (!fn.isEmpty()) {
|
|
lineEdit->setText(fn);
|
|
Q_EMIT fileNameSelected(fn);
|
|
}
|
|
}
|
|
|
|
FileChooser::Mode FileChooser::mode() const
|
|
{
|
|
return md;
|
|
}
|
|
|
|
void FileChooser::setMode(Mode m)
|
|
{
|
|
md = m;
|
|
}
|
|
|
|
QString FileChooser::filter() const
|
|
{
|
|
return _filter;
|
|
}
|
|
|
|
void FileChooser::setFilter(const QString& filter)
|
|
{
|
|
_filter = filter;
|
|
}
|
|
|
|
void FileChooser::setButtonText(const QString& txt)
|
|
{
|
|
button->setText(txt);
|
|
|
|
int w1 = 2 * button->fontMetrics().horizontalAdvance(txt);
|
|
int w2 = 2 * button->fontMetrics().horizontalAdvance(" ... ");
|
|
|
|
button->setFixedWidth((w1 > w2 ? w1 : w2));
|
|
}
|
|
|
|
QString FileChooser::buttonText() const
|
|
{
|
|
return button->text();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
PrefFileChooser::PrefFileChooser(QWidget* parent)
|
|
: FileChooser(parent)
|
|
{}
|
|
|
|
PrefFileChooser::~PrefFileChooser()
|
|
{}
|
|
|
|
QByteArray PrefFileChooser::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefFileChooser::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefFileChooser::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefFileChooser::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
AccelLineEdit::AccelLineEdit(QWidget* parent)
|
|
: QLineEdit(parent)
|
|
{
|
|
setPlaceholderText(tr("Press a keyboard shortcut"));
|
|
setClearButtonEnabled(true);
|
|
keyPressedCount = 0;
|
|
}
|
|
|
|
bool AccelLineEdit::isNone() const
|
|
{
|
|
return text().isEmpty();
|
|
}
|
|
|
|
void AccelLineEdit::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
if (isReadOnly()) {
|
|
QLineEdit::keyPressEvent(e);
|
|
return;
|
|
}
|
|
|
|
QString txtLine = text();
|
|
|
|
int key = e->key();
|
|
Qt::KeyboardModifiers state = e->modifiers();
|
|
|
|
// Backspace clears the shortcut if text is present, else sets Backspace as shortcut.
|
|
// If a modifier is pressed without any other key, return.
|
|
// AltGr is not a modifier but doesn't have a QString representation.
|
|
switch (key) {
|
|
case Qt::Key_Backspace:
|
|
case Qt::Key_Delete:
|
|
if (state == Qt::NoModifier) {
|
|
keyPressedCount = 0;
|
|
if (isNone()) {
|
|
QKeySequence ks(key);
|
|
setText(ks.toString(QKeySequence::NativeText));
|
|
}
|
|
else {
|
|
clear();
|
|
}
|
|
}
|
|
case Qt::Key_Control:
|
|
case Qt::Key_Shift:
|
|
case Qt::Key_Alt:
|
|
case Qt::Key_Meta:
|
|
case Qt::Key_AltGr:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (txtLine.isEmpty()) {
|
|
// Text maybe cleared by QLineEdit's built in clear button
|
|
keyPressedCount = 0;
|
|
}
|
|
else {
|
|
// 4 keys are allowed for QShortcut
|
|
switch (keyPressedCount) {
|
|
case 4:
|
|
keyPressedCount = 0;
|
|
txtLine.clear();
|
|
break;
|
|
case 0:
|
|
txtLine.clear();
|
|
break;
|
|
default:
|
|
txtLine += QString::fromLatin1(",");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handles modifiers applying a mask.
|
|
if ((state & Qt::ControlModifier) == Qt::ControlModifier) {
|
|
QKeySequence ks(Qt::CTRL);
|
|
txtLine += ks.toString(QKeySequence::NativeText);
|
|
}
|
|
if ((state & Qt::AltModifier) == Qt::AltModifier) {
|
|
QKeySequence ks(Qt::ALT);
|
|
txtLine += ks.toString(QKeySequence::NativeText);
|
|
}
|
|
if ((state & Qt::ShiftModifier) == Qt::ShiftModifier) {
|
|
QKeySequence ks(Qt::SHIFT);
|
|
txtLine += ks.toString(QKeySequence::NativeText);
|
|
}
|
|
if ((state & Qt::MetaModifier) == Qt::MetaModifier) {
|
|
QKeySequence ks(Qt::META);
|
|
txtLine += ks.toString(QKeySequence::NativeText);
|
|
}
|
|
|
|
// Handles normal keys
|
|
QKeySequence ks(key);
|
|
txtLine += ks.toString(QKeySequence::NativeText);
|
|
|
|
setText(txtLine);
|
|
keyPressedCount++;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
ActionSelector::ActionSelector(QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
addButton = new QPushButton(this);
|
|
addButton->setMinimumSize(QSize(30, 30));
|
|
QIcon icon;
|
|
icon.addFile(QString::fromUtf8(":/icons/button_right.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
addButton->setIcon(icon);
|
|
gridLayout = new QGridLayout(this);
|
|
gridLayout->addWidget(addButton, 1, 1, 1, 1);
|
|
|
|
spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
gridLayout->addItem(spacerItem, 5, 1, 1, 1);
|
|
spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
gridLayout->addItem(spacerItem1, 0, 1, 1, 1);
|
|
|
|
removeButton = new QPushButton(this);
|
|
removeButton->setMinimumSize(QSize(30, 30));
|
|
QIcon icon1;
|
|
icon1.addFile(QString::fromUtf8(":/icons/button_left.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
removeButton->setIcon(icon1);
|
|
removeButton->setAutoDefault(true);
|
|
removeButton->setDefault(false);
|
|
|
|
gridLayout->addWidget(removeButton, 2, 1, 1, 1);
|
|
|
|
upButton = new QPushButton(this);
|
|
upButton->setMinimumSize(QSize(30, 30));
|
|
QIcon icon3;
|
|
icon3.addFile(QString::fromUtf8(":/icons/button_up.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
upButton->setIcon(icon3);
|
|
|
|
gridLayout->addWidget(upButton, 3, 1, 1, 1);
|
|
|
|
downButton = new QPushButton(this);
|
|
downButton->setMinimumSize(QSize(30, 30));
|
|
QIcon icon2;
|
|
icon2.addFile(QString::fromUtf8(":/icons/button_down.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
downButton->setIcon(icon2);
|
|
downButton->setAutoDefault(true);
|
|
|
|
gridLayout->addWidget(downButton, 4, 1, 1, 1);
|
|
|
|
vboxLayout = new QVBoxLayout();
|
|
vboxLayout->setContentsMargins(0, 0, 0, 0);
|
|
labelAvailable = new QLabel(this);
|
|
vboxLayout->addWidget(labelAvailable);
|
|
|
|
availableWidget = new QTreeWidget(this);
|
|
availableWidget->setRootIsDecorated(false);
|
|
availableWidget->setHeaderLabels(QStringList() << QString());
|
|
availableWidget->header()->hide();
|
|
vboxLayout->addWidget(availableWidget);
|
|
|
|
gridLayout->addLayout(vboxLayout, 0, 0, 6, 1);
|
|
|
|
vboxLayout1 = new QVBoxLayout();
|
|
vboxLayout1->setContentsMargins(0, 0, 0, 0);
|
|
labelSelected = new QLabel(this);
|
|
vboxLayout1->addWidget(labelSelected);
|
|
|
|
selectedWidget = new QTreeWidget(this);
|
|
selectedWidget->setRootIsDecorated(false);
|
|
selectedWidget->setHeaderLabels(QStringList() << QString());
|
|
selectedWidget->header()->hide();
|
|
vboxLayout1->addWidget(selectedWidget);
|
|
|
|
gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1);
|
|
|
|
addButton->setText(QString());
|
|
removeButton->setText(QString());
|
|
upButton->setText(QString());
|
|
downButton->setText(QString());
|
|
|
|
labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:"));
|
|
labelSelected->setText(QApplication::translate("Gui::ActionSelector", "Selected:"));
|
|
addButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Add"));
|
|
removeButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Remove"));
|
|
upButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move up"));
|
|
downButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move down"));
|
|
}
|
|
|
|
ActionSelector::~ActionSelector()
|
|
{}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
InputField::InputField(QWidget* parent)
|
|
: QLineEdit(parent)
|
|
, Value(0)
|
|
, Maximum(std::numeric_limits<int>::max())
|
|
, Minimum(-std::numeric_limits<int>::max())
|
|
, StepSize(1.0)
|
|
, HistorySize(5)
|
|
{}
|
|
|
|
InputField::~InputField()
|
|
{}
|
|
|
|
/** Sets the preference path to \a path. */
|
|
void InputField::setParamGrpPath(const QByteArray& path)
|
|
{
|
|
m_sPrefGrp = path;
|
|
}
|
|
|
|
/** Returns the widget's preferences path. */
|
|
QByteArray InputField::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
/// sets the field with a quantity
|
|
void InputField::setValue(double quant)
|
|
{
|
|
Value = quant;
|
|
setText(QString("%1 %2").arg(Value).arg(UnitStr));
|
|
}
|
|
|
|
/// sets the field with a quantity
|
|
double InputField::getQuantity() const
|
|
{
|
|
return Value;
|
|
}
|
|
|
|
/// get the value of the singleStep property
|
|
double InputField::singleStep(void) const
|
|
{
|
|
return StepSize;
|
|
}
|
|
|
|
/// set the value of the singleStep property
|
|
void InputField::setSingleStep(double s)
|
|
{
|
|
StepSize = s;
|
|
}
|
|
|
|
/// get the value of the maximum property
|
|
double InputField::maximum(void) const
|
|
{
|
|
return Maximum;
|
|
}
|
|
|
|
/// set the value of the maximum property
|
|
void InputField::setMaximum(double m)
|
|
{
|
|
Maximum = m;
|
|
}
|
|
|
|
/// get the value of the minimum property
|
|
double InputField::minimum(void) const
|
|
{
|
|
return Minimum;
|
|
}
|
|
|
|
/// set the value of the minimum property
|
|
void InputField::setMinimum(double m)
|
|
{
|
|
Minimum = m;
|
|
}
|
|
|
|
void InputField::setUnitText(QString str)
|
|
{
|
|
UnitStr = str;
|
|
setText(QString("%1 %2").arg(Value).arg(UnitStr));
|
|
}
|
|
|
|
QString InputField::getUnitText(void)
|
|
{
|
|
return UnitStr;
|
|
}
|
|
|
|
// get the value of the minimum property
|
|
int InputField::historySize(void) const
|
|
{
|
|
return HistorySize;
|
|
}
|
|
|
|
// set the value of the minimum property
|
|
void InputField::setHistorySize(int i)
|
|
{
|
|
HistorySize = i;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
ExpressionLineEdit::ExpressionLineEdit(QWidget* parent)
|
|
: QLineEdit(parent)
|
|
, exactMatch {false}
|
|
{
|
|
completer = new QCompleter(this);
|
|
connect(this, &QLineEdit::textEdited, this, &ExpressionLineEdit::slotTextChanged);
|
|
}
|
|
|
|
void ExpressionLineEdit::setExactMatch(bool enabled)
|
|
{
|
|
exactMatch = enabled;
|
|
if (completer) {
|
|
completer->setFilterMode(exactMatch ? Qt::MatchStartsWith : Qt::MatchContains);
|
|
}
|
|
}
|
|
|
|
void ExpressionLineEdit::slotTextChanged(const QString& text)
|
|
{
|
|
Q_EMIT textChanged2(text, cursorPosition());
|
|
}
|
|
|
|
void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool isActivated)
|
|
{
|
|
Q_UNUSED(completionPrefix)
|
|
Q_UNUSED(isActivated)
|
|
}
|
|
|
|
void ExpressionLineEdit::slotCompleteTextHighlighted(const QString& completionPrefix)
|
|
{
|
|
slotCompleteText(completionPrefix, false);
|
|
}
|
|
|
|
void ExpressionLineEdit::slotCompleteTextSelected(const QString& completionPrefix)
|
|
{
|
|
slotCompleteText(completionPrefix, true);
|
|
}
|
|
|
|
void ExpressionLineEdit::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
QLineEdit::keyPressEvent(e);
|
|
}
|
|
|
|
void ExpressionLineEdit::contextMenuEvent(QContextMenuEvent* event)
|
|
{
|
|
QMenu* menu = createStandardContextMenu();
|
|
|
|
if (completer) {
|
|
menu->addSeparator();
|
|
QAction* match = menu->addAction(tr("Exact match"));
|
|
match->setCheckable(true);
|
|
match->setChecked(completer->filterMode() == Qt::MatchStartsWith);
|
|
QObject::connect(match, &QAction::toggled, this, &Gui::ExpressionLineEdit::setExactMatch);
|
|
}
|
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
menu->popup(event->globalPos());
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
namespace Base
|
|
{
|
|
|
|
Unit::Unit()
|
|
{}
|
|
|
|
Unit::Unit(const QString& u)
|
|
: unit(u)
|
|
{}
|
|
|
|
bool Unit::isEmpty() const
|
|
{
|
|
return unit.isEmpty();
|
|
}
|
|
|
|
bool Unit::operator==(const Unit& that)
|
|
{
|
|
return this->unit == that.unit;
|
|
}
|
|
|
|
bool Unit::operator!=(const Unit& that)
|
|
{
|
|
return this->unit != that.unit;
|
|
}
|
|
|
|
const QString& Unit::getString() const
|
|
{
|
|
return unit;
|
|
}
|
|
|
|
int QuantityFormat::defaultDenominator = 8; // for 1/8"
|
|
|
|
|
|
QuantityFormat::QuantityFormat()
|
|
: option(OmitGroupSeparator | RejectGroupSeparator)
|
|
, format(Fixed)
|
|
, precision(4)
|
|
, denominator(defaultDenominator)
|
|
{}
|
|
|
|
Quantity::Quantity()
|
|
: value(0)
|
|
, unit()
|
|
{}
|
|
|
|
Quantity::Quantity(double v, const Unit& u)
|
|
: value(v)
|
|
, unit(u)
|
|
{}
|
|
|
|
Quantity Quantity::parse(const QString& str)
|
|
{
|
|
bool ok;
|
|
QString txt = str;
|
|
QString unit;
|
|
while (!txt.isEmpty() && txt[txt.length() - 1].isLetter()) {
|
|
unit.prepend(txt[txt.length() - 1]);
|
|
txt.chop(1);
|
|
}
|
|
|
|
double v = QLocale::system().toDouble(txt, &ok);
|
|
// if (!ok && !txt.isEmpty())
|
|
// throw Base::Exception();
|
|
return Quantity(v, Unit(unit));
|
|
}
|
|
|
|
void Quantity::setValue(double v)
|
|
{
|
|
value = v;
|
|
}
|
|
|
|
double Quantity::getValue() const
|
|
{
|
|
return value;
|
|
}
|
|
|
|
void Quantity::setUnit(const Unit& u)
|
|
{
|
|
unit = u;
|
|
}
|
|
|
|
Unit Quantity::getUnit() const
|
|
{
|
|
return unit;
|
|
}
|
|
|
|
QString Quantity::getUserString() const
|
|
{
|
|
QLocale Lc;
|
|
const QuantityFormat& format = getFormat();
|
|
if (format.option != QuantityFormat::None) {
|
|
uint opt = static_cast<uint>(format.option);
|
|
Lc.setNumberOptions(static_cast<QLocale::NumberOptions>(opt));
|
|
}
|
|
|
|
QString Ln = Lc.toString(value, format.toFormat(), format.precision);
|
|
return QString::fromUtf8("%1 %2").arg(Ln, unit.getString());
|
|
}
|
|
|
|
QString Quantity::getUserString(double& factor, QString& unitString) const
|
|
{
|
|
factor = 1;
|
|
unitString = unit.getString();
|
|
|
|
QLocale Lc;
|
|
const QuantityFormat& format = getFormat();
|
|
if (format.option != QuantityFormat::None) {
|
|
uint opt = static_cast<uint>(format.option);
|
|
Lc.setNumberOptions(static_cast<QLocale::NumberOptions>(opt));
|
|
}
|
|
|
|
QString Ln = Lc.toString(value, format.toFormat(), format.precision);
|
|
return QString::fromUtf8("%1 %2").arg(Ln, unit.getString());
|
|
}
|
|
|
|
} // namespace Base
|
|
|
|
namespace Gui
|
|
{
|
|
|
|
class QuantitySpinBoxPrivate
|
|
{
|
|
public:
|
|
QuantitySpinBoxPrivate()
|
|
: validInput(true)
|
|
, pendingEmit(false)
|
|
, unitValue(0)
|
|
, maximum(std::numeric_limits<int>::max())
|
|
, minimum(-std::numeric_limits<int>::max())
|
|
, singleStep(1.0)
|
|
{}
|
|
~QuantitySpinBoxPrivate()
|
|
{}
|
|
|
|
QString stripped(const QString& t, int* pos) const
|
|
{
|
|
QString text = t;
|
|
const int s = text.size();
|
|
text = text.trimmed();
|
|
if (pos) {
|
|
(*pos) -= (s - text.size());
|
|
}
|
|
return text;
|
|
}
|
|
|
|
bool validate(QString& input, Base::Quantity& result) const
|
|
{
|
|
bool success = false;
|
|
QString tmp = input;
|
|
int pos = 0;
|
|
QValidator::State state;
|
|
Base::Quantity res = validateAndInterpret(tmp, pos, state);
|
|
res.setFormat(quantity.getFormat());
|
|
if (state == QValidator::Acceptable) {
|
|
success = true;
|
|
result = res;
|
|
input = tmp;
|
|
}
|
|
else if (state == QValidator::Intermediate) {
|
|
tmp = tmp.trimmed();
|
|
tmp += QLatin1Char(' ');
|
|
tmp += unitStr;
|
|
Base::Quantity res2 = validateAndInterpret(tmp, pos, state);
|
|
res2.setFormat(quantity.getFormat());
|
|
if (state == QValidator::Acceptable) {
|
|
success = true;
|
|
result = res2;
|
|
input = tmp;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
Base::Quantity validateAndInterpret(QString& input, int& pos, QValidator::State& state) const
|
|
{
|
|
Base::Quantity res;
|
|
const double max = this->maximum;
|
|
const double min = this->minimum;
|
|
|
|
QString copy = input;
|
|
|
|
int len = copy.size();
|
|
|
|
const bool plus = max >= 0;
|
|
const bool minus = min <= 0;
|
|
|
|
switch (len) {
|
|
case 0:
|
|
state = max != min ? QValidator::Intermediate : QValidator::Invalid;
|
|
goto end;
|
|
case 1:
|
|
if (copy.at(0) == locale.decimalPoint()) {
|
|
state = QValidator::Intermediate;
|
|
copy.prepend(QLatin1Char('0'));
|
|
pos++;
|
|
len++;
|
|
goto end;
|
|
}
|
|
else if (copy.at(0) == QLatin1Char('+')) {
|
|
// the quantity parser doesn't allow numbers of the form '+1.0'
|
|
state = QValidator::Invalid;
|
|
goto end;
|
|
}
|
|
else if (copy.at(0) == QLatin1Char('-')) {
|
|
if (minus) {
|
|
state = QValidator::Intermediate;
|
|
}
|
|
else {
|
|
state = QValidator::Invalid;
|
|
}
|
|
goto end;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (copy.at(1) == locale.decimalPoint()
|
|
&& (plus && copy.at(0) == QLatin1Char('+'))) {
|
|
state = QValidator::Intermediate;
|
|
goto end;
|
|
}
|
|
if (copy.at(1) == locale.decimalPoint()
|
|
&& (minus && copy.at(0) == QLatin1Char('-'))) {
|
|
state = QValidator::Intermediate;
|
|
copy.insert(1, QLatin1Char('0'));
|
|
pos++;
|
|
len++;
|
|
goto end;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
{
|
|
if (copy.at(0) == locale.groupSeparator()) {
|
|
state = QValidator::Invalid;
|
|
goto end;
|
|
}
|
|
else if (len > 1) {
|
|
bool decOccurred = false;
|
|
for (int i = 0; i < copy.size(); i++) {
|
|
if (copy.at(i) == locale.decimalPoint()) {
|
|
// Disallow multiple decimal points within the same numeric substring
|
|
if (decOccurred) {
|
|
state = QValidator::Invalid;
|
|
goto end;
|
|
}
|
|
decOccurred = true;
|
|
}
|
|
// Reset decOcurred if non-numeric character found
|
|
else if (!(copy.at(i) == locale.groupSeparator() || copy.at(i).isDigit())) {
|
|
decOccurred = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ok = false;
|
|
double value = min;
|
|
|
|
QChar plus = QLatin1Char('+'), minus = QLatin1Char('-');
|
|
|
|
if (locale.negativeSign() != minus) {
|
|
copy.replace(locale.negativeSign(), minus);
|
|
}
|
|
if (locale.positiveSign() != plus) {
|
|
copy.replace(locale.positiveSign(), plus);
|
|
}
|
|
|
|
try {
|
|
QString copy2 = copy;
|
|
copy2.remove(locale.groupSeparator());
|
|
|
|
res = Base::Quantity::parse(copy2);
|
|
value = res.getValue();
|
|
ok = true;
|
|
}
|
|
catch (Base::Exception&) {
|
|
}
|
|
|
|
if (!ok) {
|
|
// input may not be finished
|
|
state = QValidator::Intermediate;
|
|
}
|
|
else if (value >= min && value <= max) {
|
|
if (copy.endsWith(locale.decimalPoint())) {
|
|
// input shouldn't end with a decimal point
|
|
state = QValidator::Intermediate;
|
|
}
|
|
else if (res.getUnit().isEmpty() && !this->unit.isEmpty()) {
|
|
// if not dimensionless the input should have a dimension
|
|
state = QValidator::Intermediate;
|
|
}
|
|
else if (res.getUnit() != this->unit) {
|
|
state = QValidator::Invalid;
|
|
}
|
|
else {
|
|
state = QValidator::Acceptable;
|
|
}
|
|
}
|
|
else if (max == min) { // when max and min is the same the only non-Invalid input is
|
|
// max (or min)
|
|
state = QValidator::Invalid;
|
|
}
|
|
else {
|
|
if ((value >= 0 && value > max) || (value < 0 && value < min)) {
|
|
state = QValidator::Invalid;
|
|
}
|
|
else {
|
|
state = QValidator::Intermediate;
|
|
}
|
|
}
|
|
}
|
|
end:
|
|
if (state != QValidator::Acceptable) {
|
|
res.setValue(max > 0 ? min : max);
|
|
}
|
|
|
|
input = copy;
|
|
return res;
|
|
}
|
|
|
|
QLocale locale;
|
|
bool validInput;
|
|
bool pendingEmit;
|
|
QString validStr;
|
|
Base::Quantity quantity;
|
|
Base::Quantity cached;
|
|
Base::Unit unit;
|
|
double unitValue;
|
|
QString unitStr;
|
|
double maximum;
|
|
double minimum;
|
|
double singleStep;
|
|
};
|
|
} // namespace Gui
|
|
|
|
QuantitySpinBox::QuantitySpinBox(QWidget* parent)
|
|
: QAbstractSpinBox(parent)
|
|
, d_ptr(new QuantitySpinBoxPrivate())
|
|
{
|
|
d_ptr->locale = locale();
|
|
this->setContextMenuPolicy(Qt::DefaultContextMenu);
|
|
connect(lineEdit(), &QLineEdit::textChanged, this, &QuantitySpinBox::userInput);
|
|
connect(this, &QuantitySpinBox::editingFinished, this, &QuantitySpinBox::handlePendingEmit);
|
|
}
|
|
|
|
QuantitySpinBox::~QuantitySpinBox()
|
|
{}
|
|
|
|
void QuantitySpinBox::resizeEvent(QResizeEvent* event)
|
|
{
|
|
QAbstractSpinBox::resizeEvent(event);
|
|
}
|
|
|
|
void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
QAbstractSpinBox::keyPressEvent(event);
|
|
}
|
|
|
|
|
|
void QuantitySpinBox::updateText(const Base::Quantity& quant)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
double dFactor;
|
|
QString txt = getUserString(quant, dFactor, d->unitStr);
|
|
d->unitValue = quant.getValue() / dFactor;
|
|
lineEdit()->setText(txt);
|
|
handlePendingEmit();
|
|
}
|
|
|
|
Base::Quantity QuantitySpinBox::value() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->quantity;
|
|
}
|
|
|
|
double QuantitySpinBox::rawValue() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->quantity.getValue();
|
|
}
|
|
|
|
void QuantitySpinBox::setValue(const Base::Quantity& value)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
d->quantity = value;
|
|
// check limits
|
|
if (d->quantity.getValue() > d->maximum) {
|
|
d->quantity.setValue(d->maximum);
|
|
}
|
|
if (d->quantity.getValue() < d->minimum) {
|
|
d->quantity.setValue(d->minimum);
|
|
}
|
|
|
|
d->unit = value.getUnit();
|
|
|
|
updateText(value);
|
|
}
|
|
|
|
void QuantitySpinBox::setValue(double value)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
setValue(Base::Quantity(value, d->unit));
|
|
}
|
|
|
|
bool QuantitySpinBox::hasValidInput() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->validInput;
|
|
}
|
|
|
|
// Gets called after call of 'validateAndInterpret'
|
|
void QuantitySpinBox::userInput(const QString& text)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
d->pendingEmit = true;
|
|
|
|
QString tmp = text;
|
|
Base::Quantity res;
|
|
if (d->validate(tmp, res)) {
|
|
d->validStr = tmp;
|
|
d->validInput = true;
|
|
}
|
|
else {
|
|
d->validInput = false;
|
|
return;
|
|
}
|
|
|
|
if (keyboardTracking()) {
|
|
d->cached = res;
|
|
handlePendingEmit();
|
|
}
|
|
else {
|
|
d->cached = res;
|
|
}
|
|
}
|
|
|
|
void QuantitySpinBox::handlePendingEmit()
|
|
{
|
|
updateFromCache(true);
|
|
}
|
|
|
|
void QuantitySpinBox::updateFromCache(bool notify)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
if (d->pendingEmit) {
|
|
double factor;
|
|
const Base::Quantity& res = d->cached;
|
|
QString text = getUserString(res, factor, d->unitStr);
|
|
d->unitValue = res.getValue() / factor;
|
|
d->quantity = res;
|
|
|
|
// signaling
|
|
if (notify) {
|
|
d->pendingEmit = false;
|
|
valueChanged(res);
|
|
valueChanged(res.getValue());
|
|
textChanged(text);
|
|
}
|
|
}
|
|
}
|
|
|
|
Base::Unit QuantitySpinBox::unit() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->unit;
|
|
}
|
|
|
|
void QuantitySpinBox::setUnit(const Base::Unit& unit)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
d->unit = unit;
|
|
d->quantity.setUnit(unit);
|
|
updateText(d->quantity);
|
|
}
|
|
|
|
void QuantitySpinBox::setUnitText(const QString& str)
|
|
{
|
|
try {
|
|
Base::Quantity quant = Base::Quantity::parse(str);
|
|
setUnit(quant.getUnit());
|
|
}
|
|
catch (const Base::Exception&) {
|
|
}
|
|
}
|
|
|
|
QString QuantitySpinBox::unitText(void)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
return d->unitStr;
|
|
}
|
|
|
|
double QuantitySpinBox::singleStep() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->singleStep;
|
|
}
|
|
|
|
void QuantitySpinBox::setSingleStep(double value)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
if (value >= 0) {
|
|
d->singleStep = value;
|
|
}
|
|
}
|
|
|
|
double QuantitySpinBox::minimum() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->minimum;
|
|
}
|
|
|
|
void QuantitySpinBox::setMinimum(double minimum)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
d->minimum = minimum;
|
|
}
|
|
|
|
double QuantitySpinBox::maximum() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->maximum;
|
|
}
|
|
|
|
void QuantitySpinBox::setMaximum(double maximum)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
d->maximum = maximum;
|
|
}
|
|
|
|
void QuantitySpinBox::setRange(double minimum, double maximum)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
d->minimum = minimum;
|
|
d->maximum = maximum;
|
|
}
|
|
|
|
int QuantitySpinBox::decimals() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
return d->quantity.getFormat().precision;
|
|
}
|
|
|
|
void QuantitySpinBox::setDecimals(int v)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
Base::QuantityFormat f = d->quantity.getFormat();
|
|
f.precision = v;
|
|
d->quantity.setFormat(f);
|
|
updateText(d->quantity);
|
|
}
|
|
|
|
void QuantitySpinBox::clearSchema()
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
updateText(d->quantity);
|
|
}
|
|
|
|
QString
|
|
QuantitySpinBox::getUserString(const Base::Quantity& val, double& factor, QString& unitString) const
|
|
{
|
|
return val.getUserString(factor, unitString);
|
|
}
|
|
|
|
QString QuantitySpinBox::getUserString(const Base::Quantity& val) const
|
|
{
|
|
return val.getUserString();
|
|
}
|
|
|
|
QAbstractSpinBox::StepEnabled QuantitySpinBox::stepEnabled() const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
if (isReadOnly() /* || !d->validInput*/) {
|
|
return StepNone;
|
|
}
|
|
if (wrapping()) {
|
|
return StepEnabled(StepUpEnabled | StepDownEnabled);
|
|
}
|
|
StepEnabled ret = StepNone;
|
|
if (d->quantity.getValue() < d->maximum) {
|
|
ret |= StepUpEnabled;
|
|
}
|
|
if (d->quantity.getValue() > d->minimum) {
|
|
ret |= StepDownEnabled;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void QuantitySpinBox::stepBy(int steps)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
updateFromCache(false);
|
|
|
|
double step = d->singleStep * steps;
|
|
double val = d->unitValue + step;
|
|
if (val > d->maximum) {
|
|
val = d->maximum;
|
|
}
|
|
else if (val < d->minimum) {
|
|
val = d->minimum;
|
|
}
|
|
|
|
lineEdit()->setText(QString::fromUtf8("%L1 %2").arg(val).arg(d->unitStr));
|
|
updateFromCache(true);
|
|
update();
|
|
selectNumber();
|
|
}
|
|
|
|
QSize QuantitySpinBox::sizeHint() const
|
|
{
|
|
ensurePolished();
|
|
|
|
const QFontMetrics fm(fontMetrics());
|
|
int frameWidth = lineEdit()->style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
|
|
int iconHeight = fm.height() - frameWidth;
|
|
int h = lineEdit()->sizeHint().height();
|
|
int w = 0;
|
|
|
|
QString s = QLatin1String("000000000000000000");
|
|
QString fixedContent = QLatin1String(" ");
|
|
s += fixedContent;
|
|
|
|
w = fm.horizontalAdvance(s);
|
|
w += 2; // cursor blinking space
|
|
w += iconHeight;
|
|
|
|
QStyleOptionSpinBox opt;
|
|
initStyleOption(&opt);
|
|
QSize hint(w, h);
|
|
QSize size = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
|
|
return size;
|
|
}
|
|
|
|
QSize QuantitySpinBox::minimumSizeHint() const
|
|
{
|
|
ensurePolished();
|
|
|
|
const QFontMetrics fm(fontMetrics());
|
|
int frameWidth = lineEdit()->style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
|
|
int iconHeight = fm.height() - frameWidth;
|
|
int h = lineEdit()->minimumSizeHint().height();
|
|
int w = 0;
|
|
|
|
QString s = QLatin1String("000000000000000000");
|
|
QString fixedContent = QLatin1String(" ");
|
|
s += fixedContent;
|
|
|
|
w = fm.horizontalAdvance(s);
|
|
w += 2; // cursor blinking space
|
|
w += iconHeight;
|
|
|
|
QStyleOptionSpinBox opt;
|
|
initStyleOption(&opt);
|
|
QSize hint(w, h);
|
|
QSize size = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
|
|
return size;
|
|
}
|
|
|
|
void QuantitySpinBox::showEvent(QShowEvent* event)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
QAbstractSpinBox::showEvent(event);
|
|
|
|
bool selected = lineEdit()->hasSelectedText();
|
|
updateText(d->quantity);
|
|
if (selected) {
|
|
selectNumber();
|
|
}
|
|
}
|
|
|
|
void QuantitySpinBox::hideEvent(QHideEvent* event)
|
|
{
|
|
handlePendingEmit();
|
|
QAbstractSpinBox::hideEvent(event);
|
|
}
|
|
|
|
void QuantitySpinBox::closeEvent(QCloseEvent* event)
|
|
{
|
|
handlePendingEmit();
|
|
QAbstractSpinBox::closeEvent(event);
|
|
}
|
|
|
|
|
|
void QuantitySpinBox::focusInEvent(QFocusEvent* event)
|
|
{
|
|
bool hasSel = lineEdit()->hasSelectedText();
|
|
QAbstractSpinBox::focusInEvent(event);
|
|
|
|
if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason
|
|
|| event->reason() == Qt::ShortcutFocusReason) {
|
|
|
|
if (!hasSel) {
|
|
selectNumber();
|
|
}
|
|
}
|
|
}
|
|
|
|
void QuantitySpinBox::focusOutEvent(QFocusEvent* event)
|
|
{
|
|
Q_D(QuantitySpinBox);
|
|
|
|
int pos = 0;
|
|
QString text = lineEdit()->text();
|
|
QValidator::State state;
|
|
d->validateAndInterpret(text, pos, state);
|
|
if (state != QValidator::Acceptable) {
|
|
lineEdit()->setText(d->validStr);
|
|
}
|
|
|
|
handlePendingEmit();
|
|
|
|
QToolTip::hideText();
|
|
QAbstractSpinBox::focusOutEvent(event);
|
|
}
|
|
|
|
void QuantitySpinBox::clear()
|
|
{
|
|
QAbstractSpinBox::clear();
|
|
}
|
|
|
|
void QuantitySpinBox::selectNumber()
|
|
{
|
|
QString expr = QString::fromLatin1("^([%1%2]?[0-9\\%3]*)\\%4?([0-9]+(%5[%1%2]?[0-9]+)?)")
|
|
.arg(locale().negativeSign())
|
|
.arg(locale().positiveSign())
|
|
.arg(locale().groupSeparator())
|
|
.arg(locale().decimalPoint())
|
|
.arg(locale().exponential());
|
|
auto rmatch = QRegularExpression(expr).match(lineEdit()->text());
|
|
if (rmatch.hasMatch()) {
|
|
lineEdit()->setSelection(0, rmatch.capturedLength());
|
|
}
|
|
}
|
|
|
|
QString QuantitySpinBox::textFromValue(const Base::Quantity& value) const
|
|
{
|
|
double factor;
|
|
QString unitStr;
|
|
QString str = getUserString(value, factor, unitStr);
|
|
if (qAbs(value.getValue()) >= 1000.0) {
|
|
str.remove(locale().groupSeparator());
|
|
}
|
|
return str;
|
|
}
|
|
|
|
Base::Quantity QuantitySpinBox::valueFromText(const QString& text) const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
|
|
QString copy = text;
|
|
int pos = lineEdit()->cursorPosition();
|
|
QValidator::State state = QValidator::Acceptable;
|
|
Base::Quantity quant = d->validateAndInterpret(copy, pos, state);
|
|
if (state != QValidator::Acceptable) {
|
|
fixup(copy);
|
|
quant = d->validateAndInterpret(copy, pos, state);
|
|
}
|
|
|
|
return quant;
|
|
}
|
|
|
|
QValidator::State QuantitySpinBox::validate(QString& text, int& pos) const
|
|
{
|
|
Q_D(const QuantitySpinBox);
|
|
|
|
QValidator::State state;
|
|
d->validateAndInterpret(text, pos, state);
|
|
return state;
|
|
}
|
|
|
|
void QuantitySpinBox::fixup(QString& input) const
|
|
{
|
|
input.remove(locale().groupSeparator());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
PrefUnitSpinBox::PrefUnitSpinBox(QWidget* parent)
|
|
: QuantitySpinBox(parent)
|
|
{}
|
|
|
|
PrefUnitSpinBox::~PrefUnitSpinBox()
|
|
{}
|
|
|
|
QByteArray PrefUnitSpinBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefUnitSpinBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefUnitSpinBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefUnitSpinBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefQuantitySpinBox::PrefQuantitySpinBox(QWidget* parent)
|
|
: QuantitySpinBox(parent)
|
|
{}
|
|
|
|
PrefQuantitySpinBox::~PrefQuantitySpinBox()
|
|
{}
|
|
|
|
QByteArray PrefQuantitySpinBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefQuantitySpinBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefQuantitySpinBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefQuantitySpinBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
CommandIconView::CommandIconView(QWidget* parent)
|
|
: QListWidget(parent)
|
|
{
|
|
connect(this, &QListWidget::currentItemChanged, this, &CommandIconView::onSelectionChanged);
|
|
}
|
|
|
|
CommandIconView::~CommandIconView()
|
|
{}
|
|
|
|
void CommandIconView::startDrag(Qt::DropActions /*supportedActions*/)
|
|
{
|
|
QList<QListWidgetItem*> items = selectedItems();
|
|
QByteArray itemData;
|
|
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
|
|
|
|
QPixmap pixmap;
|
|
dataStream << items.count();
|
|
for (QList<QListWidgetItem*>::ConstIterator it = items.begin(); it != items.end(); ++it) {
|
|
if (it == items.begin()) {
|
|
pixmap = ((*it)->data(Qt::UserRole)).value<QPixmap>();
|
|
}
|
|
dataStream << (*it)->text();
|
|
}
|
|
|
|
QMimeData* mimeData = new QMimeData;
|
|
mimeData->setData("text/x-action-items", itemData);
|
|
|
|
QDrag* drag = new QDrag(this);
|
|
drag->setMimeData(mimeData);
|
|
drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
|
|
drag->setPixmap(pixmap);
|
|
drag->exec(Qt::MoveAction);
|
|
}
|
|
|
|
void CommandIconView::onSelectionChanged(QListWidgetItem* item, QListWidgetItem*)
|
|
{
|
|
if (item) {
|
|
emitSelectionChanged(item->toolTip());
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
namespace Gui
|
|
{
|
|
|
|
class UnsignedValidator: public QValidator
|
|
{
|
|
public:
|
|
UnsignedValidator(QObject* parent);
|
|
UnsignedValidator(uint minimum, uint maximum, QObject* parent);
|
|
~UnsignedValidator();
|
|
|
|
QValidator::State validate(QString&, int&) const;
|
|
|
|
void setBottom(uint);
|
|
void setTop(uint);
|
|
virtual void setRange(uint bottom, uint top);
|
|
|
|
uint bottom() const
|
|
{
|
|
return b;
|
|
}
|
|
uint top() const
|
|
{
|
|
return t;
|
|
}
|
|
|
|
private:
|
|
uint b, t;
|
|
};
|
|
|
|
UnsignedValidator::UnsignedValidator(QObject* parent)
|
|
: QValidator(parent)
|
|
{
|
|
b = 0;
|
|
t = std::numeric_limits<unsigned>::max();
|
|
}
|
|
|
|
UnsignedValidator::UnsignedValidator(uint minimum, uint maximum, QObject* parent)
|
|
: QValidator(parent)
|
|
{
|
|
b = minimum;
|
|
t = maximum;
|
|
}
|
|
|
|
UnsignedValidator::~UnsignedValidator()
|
|
{}
|
|
|
|
QValidator::State UnsignedValidator::validate(QString& input, int&) const
|
|
{
|
|
QString stripped; // = input.stripWhiteSpace();
|
|
if (stripped.isEmpty()) {
|
|
return Intermediate;
|
|
}
|
|
bool ok;
|
|
uint entered = input.toUInt(&ok);
|
|
if (!ok) {
|
|
return Invalid;
|
|
}
|
|
else if (entered < b) {
|
|
return Intermediate;
|
|
}
|
|
else if (entered > t) {
|
|
return Invalid;
|
|
}
|
|
// else if ( entered < b || entered > t )
|
|
// return Invalid;
|
|
else {
|
|
return Acceptable;
|
|
}
|
|
}
|
|
|
|
void UnsignedValidator::setRange(uint minimum, uint maximum)
|
|
{
|
|
b = minimum;
|
|
t = maximum;
|
|
}
|
|
|
|
void UnsignedValidator::setBottom(uint bottom)
|
|
{
|
|
setRange(bottom, top());
|
|
}
|
|
|
|
void UnsignedValidator::setTop(uint top)
|
|
{
|
|
setRange(bottom(), top);
|
|
}
|
|
|
|
class UIntSpinBoxPrivate
|
|
{
|
|
public:
|
|
UnsignedValidator* mValidator;
|
|
|
|
UIntSpinBoxPrivate()
|
|
: mValidator(0)
|
|
{}
|
|
unsigned mapToUInt(int v) const
|
|
{
|
|
using int_limits = std::numeric_limits<int>;
|
|
using uint_limits = std::numeric_limits<unsigned>;
|
|
|
|
unsigned ui;
|
|
if (v == int_limits::min()) {
|
|
ui = 0;
|
|
}
|
|
else if (v == int_limits::max()) {
|
|
ui = uint_limits::max();
|
|
}
|
|
else if (v < 0) {
|
|
v -= int_limits::min();
|
|
ui = static_cast<unsigned>(v);
|
|
}
|
|
else {
|
|
ui = static_cast<unsigned>(v);
|
|
ui -= int_limits::min();
|
|
}
|
|
return ui;
|
|
}
|
|
int mapToInt(unsigned v) const
|
|
{
|
|
using int_limits = std::numeric_limits<int>;
|
|
using uint_limits = std::numeric_limits<unsigned>;
|
|
|
|
int in;
|
|
if (v == uint_limits::max()) {
|
|
in = int_limits::max();
|
|
}
|
|
else if (v == 0) {
|
|
in = int_limits::min();
|
|
}
|
|
else if (v > int_limits::max()) {
|
|
v += int_limits::min();
|
|
in = static_cast<int>(v);
|
|
}
|
|
else {
|
|
in = v;
|
|
in += int_limits::min();
|
|
}
|
|
return in;
|
|
}
|
|
};
|
|
|
|
} // namespace Gui
|
|
|
|
// -------------------------------------------------------------
|
|
|
|
UIntSpinBox::UIntSpinBox(QWidget* parent)
|
|
: QSpinBox(parent)
|
|
{
|
|
d = new UIntSpinBoxPrivate;
|
|
d->mValidator = new UnsignedValidator(this->minimum(), this->maximum(), this);
|
|
connect(this, qOverload<int>(&QSpinBox::valueChanged), this, &UIntSpinBox::valueChange);
|
|
setRange(0, 99);
|
|
setValue(0);
|
|
updateValidator();
|
|
}
|
|
|
|
UIntSpinBox::~UIntSpinBox()
|
|
{
|
|
delete d->mValidator;
|
|
delete d;
|
|
d = 0;
|
|
}
|
|
|
|
void UIntSpinBox::setRange(uint minVal, uint maxVal)
|
|
{
|
|
int iminVal = d->mapToInt(minVal);
|
|
int imaxVal = d->mapToInt(maxVal);
|
|
QSpinBox::setRange(iminVal, imaxVal);
|
|
updateValidator();
|
|
}
|
|
|
|
QValidator::State UIntSpinBox::validate(QString& input, int& pos) const
|
|
{
|
|
return d->mValidator->validate(input, pos);
|
|
}
|
|
|
|
uint UIntSpinBox::value() const
|
|
{
|
|
return d->mapToUInt(QSpinBox::value());
|
|
}
|
|
|
|
void UIntSpinBox::setValue(uint value)
|
|
{
|
|
QSpinBox::setValue(d->mapToInt(value));
|
|
}
|
|
|
|
void UIntSpinBox::valueChange(int value)
|
|
{
|
|
unsignedChanged(d->mapToUInt(value));
|
|
}
|
|
|
|
uint UIntSpinBox::minimum() const
|
|
{
|
|
return d->mapToUInt(QSpinBox::minimum());
|
|
}
|
|
|
|
void UIntSpinBox::setMinimum(uint minVal)
|
|
{
|
|
uint maxVal = maximum();
|
|
if (maxVal < minVal) {
|
|
maxVal = minVal;
|
|
}
|
|
setRange(minVal, maxVal);
|
|
}
|
|
|
|
uint UIntSpinBox::maximum() const
|
|
{
|
|
return d->mapToUInt(QSpinBox::maximum());
|
|
}
|
|
|
|
void UIntSpinBox::setMaximum(uint maxVal)
|
|
{
|
|
uint minVal = minimum();
|
|
if (minVal > maxVal) {
|
|
minVal = maxVal;
|
|
}
|
|
setRange(minVal, maxVal);
|
|
}
|
|
|
|
QString UIntSpinBox::textFromValue(int v) const
|
|
{
|
|
uint val = d->mapToUInt(v);
|
|
QString s;
|
|
s.setNum(val);
|
|
return s;
|
|
}
|
|
|
|
int UIntSpinBox::valueFromText(const QString& text) const
|
|
{
|
|
bool ok;
|
|
QString s = text;
|
|
uint newVal = s.toUInt(&ok);
|
|
if (!ok && !(prefix().isEmpty() && suffix().isEmpty())) {
|
|
s = cleanText();
|
|
newVal = s.toUInt(&ok);
|
|
}
|
|
|
|
return d->mapToInt(newVal);
|
|
}
|
|
|
|
void UIntSpinBox::updateValidator()
|
|
{
|
|
d->mValidator->setRange(this->minimum(), this->maximum());
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
IntSpinBox::IntSpinBox(QWidget* parent)
|
|
: QSpinBox(parent)
|
|
{}
|
|
|
|
IntSpinBox::~IntSpinBox()
|
|
{}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefSpinBox::PrefSpinBox(QWidget* parent)
|
|
: QSpinBox(parent)
|
|
{}
|
|
|
|
PrefSpinBox::~PrefSpinBox()
|
|
{}
|
|
|
|
QByteArray PrefSpinBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefSpinBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefSpinBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefSpinBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
DoubleSpinBox::DoubleSpinBox(QWidget* parent)
|
|
: QDoubleSpinBox(parent)
|
|
{}
|
|
|
|
DoubleSpinBox::~DoubleSpinBox()
|
|
{}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefDoubleSpinBox::PrefDoubleSpinBox(QWidget* parent)
|
|
: QDoubleSpinBox(parent)
|
|
{}
|
|
|
|
PrefDoubleSpinBox::~PrefDoubleSpinBox()
|
|
{}
|
|
|
|
QByteArray PrefDoubleSpinBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefDoubleSpinBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefDoubleSpinBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefDoubleSpinBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
|
|
ColorButton::ColorButton(QWidget* parent)
|
|
: QPushButton(parent)
|
|
, _allowChange(true)
|
|
, _allowTransparency(false)
|
|
, _drawFrame(true)
|
|
{
|
|
_col = palette().color(QPalette::Active, QPalette::Midlight);
|
|
connect(this, &ColorButton::clicked, this, &ColorButton::onChooseColor);
|
|
}
|
|
|
|
ColorButton::~ColorButton()
|
|
{}
|
|
|
|
void ColorButton::setColor(const QColor& c)
|
|
{
|
|
_col = c;
|
|
update();
|
|
}
|
|
|
|
QColor ColorButton::color() const
|
|
{
|
|
return _col;
|
|
}
|
|
|
|
void ColorButton::setAllowChangeColor(bool ok)
|
|
{
|
|
_allowChange = ok;
|
|
}
|
|
|
|
bool ColorButton::allowChangeColor() const
|
|
{
|
|
return _allowChange;
|
|
}
|
|
|
|
void ColorButton::setAllowTransparency(bool ok)
|
|
{
|
|
_allowTransparency = ok;
|
|
}
|
|
|
|
bool ColorButton::allowTransparency() const
|
|
{
|
|
return _allowTransparency;
|
|
}
|
|
|
|
void ColorButton::setDrawFrame(bool ok)
|
|
{
|
|
_drawFrame = ok;
|
|
}
|
|
|
|
bool ColorButton::drawFrame() const
|
|
{
|
|
return _drawFrame;
|
|
}
|
|
|
|
void ColorButton::paintEvent(QPaintEvent* e)
|
|
{
|
|
// first paint the complete button
|
|
QPushButton::paintEvent(e);
|
|
|
|
// repaint the rectangle area
|
|
QPalette::ColorGroup group =
|
|
isEnabled() ? hasFocus() ? QPalette::Active : QPalette::Inactive : QPalette::Disabled;
|
|
QColor pen = palette().color(group, QPalette::ButtonText);
|
|
{
|
|
QPainter paint(this);
|
|
paint.setPen(pen);
|
|
|
|
if (_drawFrame) {
|
|
paint.setBrush(QBrush(_col));
|
|
paint.drawRect(5, 5, width() - 10, height() - 10);
|
|
}
|
|
else {
|
|
paint.fillRect(5, 5, width() - 10, height() - 10, QBrush(_col));
|
|
}
|
|
}
|
|
|
|
// overpaint the rectangle to paint icon and text
|
|
QStyleOptionButton opt;
|
|
opt.initFrom(this);
|
|
opt.text = text();
|
|
opt.icon = icon();
|
|
opt.iconSize = iconSize();
|
|
|
|
QStylePainter p(this);
|
|
p.drawControl(QStyle::CE_PushButtonLabel, opt);
|
|
}
|
|
|
|
void ColorButton::onChooseColor()
|
|
{
|
|
if (!_allowChange) {
|
|
return;
|
|
}
|
|
QColor c = QColorDialog::getColor(_col, this);
|
|
if (c.isValid()) {
|
|
setColor(c);
|
|
Q_EMIT changed();
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
|
|
PrefColorButton::PrefColorButton(QWidget* parent)
|
|
: ColorButton(parent)
|
|
{}
|
|
|
|
PrefColorButton::~PrefColorButton()
|
|
{}
|
|
|
|
QByteArray PrefColorButton::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefColorButton::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefColorButton::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefColorButton::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefLineEdit::PrefLineEdit(QWidget* parent)
|
|
: QLineEdit(parent)
|
|
{}
|
|
|
|
PrefLineEdit::~PrefLineEdit()
|
|
{}
|
|
|
|
QByteArray PrefLineEdit::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefLineEdit::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefLineEdit::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefLineEdit::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefComboBox::PrefComboBox(QWidget* parent)
|
|
: QComboBox(parent)
|
|
{
|
|
setEditable(false);
|
|
}
|
|
|
|
PrefComboBox::~PrefComboBox()
|
|
{}
|
|
|
|
QByteArray PrefComboBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefComboBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefComboBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefComboBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefCheckBox::PrefCheckBox(QWidget* parent)
|
|
: QCheckBox(parent)
|
|
{
|
|
setText("CheckBox");
|
|
}
|
|
|
|
PrefCheckBox::~PrefCheckBox()
|
|
{}
|
|
|
|
QByteArray PrefCheckBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefCheckBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefCheckBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefCheckBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefRadioButton::PrefRadioButton(QWidget* parent)
|
|
: QRadioButton(parent)
|
|
{
|
|
setText("RadioButton");
|
|
}
|
|
|
|
PrefRadioButton::~PrefRadioButton()
|
|
{}
|
|
|
|
QByteArray PrefRadioButton::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefRadioButton::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefRadioButton::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefRadioButton::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefSlider::PrefSlider(QWidget* parent)
|
|
: QSlider(parent)
|
|
{}
|
|
|
|
PrefSlider::~PrefSlider()
|
|
{}
|
|
|
|
QByteArray PrefSlider::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefSlider::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefSlider::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefSlider::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
PrefFontBox::PrefFontBox(QWidget* parent)
|
|
: QFontComboBox(parent)
|
|
{}
|
|
|
|
PrefFontBox::~PrefFontBox()
|
|
{}
|
|
|
|
QByteArray PrefFontBox::entryName() const
|
|
{
|
|
return m_sPrefName;
|
|
}
|
|
|
|
QByteArray PrefFontBox::paramGrpPath() const
|
|
{
|
|
return m_sPrefGrp;
|
|
}
|
|
|
|
void PrefFontBox::setEntryName(const QByteArray& name)
|
|
{
|
|
m_sPrefName = name;
|
|
}
|
|
|
|
void PrefFontBox::setParamGrpPath(const QByteArray& name)
|
|
{
|
|
m_sPrefGrp = name;
|
|
}
|