Merge branch 'master' into path_custom_source
This commit is contained in:
@@ -261,6 +261,7 @@ SET(FreeCADApp_CPP_SRCS
|
||||
ComplexGeoData.cpp
|
||||
ComplexGeoDataPyImp.cpp
|
||||
Enumeration.cpp
|
||||
IndexedName.cpp
|
||||
Material.cpp
|
||||
MaterialPyImp.cpp
|
||||
Metadata.cpp
|
||||
@@ -277,6 +278,7 @@ SET(FreeCADApp_HPP_SRCS
|
||||
ColorModel.h
|
||||
ComplexGeoData.h
|
||||
Enumeration.h
|
||||
IndexedName.h
|
||||
Material.h
|
||||
Metadata.h
|
||||
)
|
||||
|
||||
122
src/App/IndexedName.cpp
Normal file
122
src/App/IndexedName.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2022 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* Copyright (c) 2023 FreeCAD Project Association *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <cstdlib>
|
||||
# include <unordered_set>
|
||||
#endif
|
||||
|
||||
#include "IndexedName.h"
|
||||
|
||||
using namespace Data;
|
||||
|
||||
/// Check whether the input character is an underscore or an ASCII letter a-Z or A-Z
|
||||
inline bool isInvalidChar(char test)
|
||||
{
|
||||
return test != '_' && (test < 'a' || test > 'z' ) && (test < 'A' || test > 'Z');
|
||||
}
|
||||
|
||||
/// Get the integer suffix of name. Returns a tuple of (suffix, suffixPosition). Calling code
|
||||
/// should check to ensure that suffixPosition is not equal to nameLength (in which case there was no
|
||||
/// suffix).
|
||||
///
|
||||
/// \param name The name to check
|
||||
/// \param nameLength The length of the string in name
|
||||
/// \returns An integer pair of the suffix itself and the position of that suffix in name
|
||||
std::pair<int,int> getIntegerSuffix(const char *name, int nameLength)
|
||||
{
|
||||
int suffixPosition {nameLength - 1};
|
||||
|
||||
for (; suffixPosition >= 0; --suffixPosition) {
|
||||
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
|
||||
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
if (!isdigit(name[suffixPosition])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++suffixPosition;
|
||||
int suffix {0};
|
||||
if (suffixPosition < nameLength) {
|
||||
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
|
||||
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
suffix = std::atoi(name + suffixPosition);
|
||||
}
|
||||
return std::make_pair(suffix, suffixPosition);
|
||||
}
|
||||
|
||||
void IndexedName::set(
|
||||
const char* name,
|
||||
int length,
|
||||
const std::vector<const char*>& allowedNames,
|
||||
bool allowOthers)
|
||||
{
|
||||
// Storage for names that we weren't given external storage for
|
||||
static std::unordered_set<ByteArray, ByteArrayHasher> NameSet;
|
||||
|
||||
if (length < 0) {
|
||||
length = static_cast<int>(std::strlen(name));
|
||||
}
|
||||
// Name typically ends with an integer: find that integer
|
||||
auto [suffix, suffixPosition] = getIntegerSuffix(name, length);
|
||||
if (suffixPosition < length) {
|
||||
this->index = suffix;
|
||||
}
|
||||
|
||||
// Make sure that every character is either an ASCII letter (upper or lowercase), or an
|
||||
// underscore. If any other character appears, reject the entire string.
|
||||
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
|
||||
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
if (std::any_of(name, name+suffixPosition, isInvalidChar)) {
|
||||
this->type = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// If a list of allowedNames was provided, see if our set name matches one of those allowedNames: if it
|
||||
// does, reference that memory location and return.
|
||||
for (const auto *typeName : allowedNames) {
|
||||
if (std::strncmp(name, typeName, suffixPosition) == 0) {
|
||||
this->type = typeName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the type was NOT in the list of allowedNames, but the caller has set the allowOthers flag to
|
||||
// true, then add the new type to the static NameSet (if it is not already there).
|
||||
if (allowOthers) {
|
||||
auto res = NameSet.insert(ByteArray(QByteArray::fromRawData(name, suffixPosition)));
|
||||
if (res.second /*The insert succeeded (the type was new)*/) {
|
||||
// Make sure that the data in the set is a unique (unshared) copy of the text
|
||||
res.first->ensureUnshared();
|
||||
}
|
||||
this->type = res.first->bytes.constData();
|
||||
}
|
||||
else {
|
||||
// The passed-in type is not in the allowed list, and allowOthers was not true, so don't
|
||||
// store the type
|
||||
this->type = "";
|
||||
}
|
||||
}
|
||||
338
src/App/IndexedName.h
Normal file
338
src/App/IndexedName.h
Normal file
@@ -0,0 +1,338 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2022 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* Copyright (c) 2023 FreeCAD Project Association *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD 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 *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef APP_INDEXEDNAME_H
|
||||
#define APP_INDEXEDNAME_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
|
||||
#include "FCGlobal.h"
|
||||
|
||||
|
||||
namespace Data
|
||||
{
|
||||
|
||||
/// The IndexedName class provides a very memory-efficient data structure to hold a name and an index
|
||||
/// value, and to perform various comparisons and validations of those values. The name must only
|
||||
/// consist of upper- and lower-case ASCII characters and the underscore ('_') character. The index
|
||||
/// must be a positive integer. The string representation of this IndexedName is the name followed by
|
||||
/// the index, with no spaces between: an IndexedName may be constructed from this string. For
|
||||
/// example "EDGE1" or "FACE345" might be the names of elements that use an IndexedName. If there is
|
||||
/// then an "EDGE2", only a pointer to the original stored name "EDGE" is retained.
|
||||
///
|
||||
/// The memory efficiency of the class comes from re-using the same character storage for names that
|
||||
/// match, while retaining their differing indices. This is achieved by either using user-provided
|
||||
/// const char * names (provided as a list of typeNames and presumed to never be deallocated), or by
|
||||
/// maintaining an internal list of names that have been used before, and can be re-used later.
|
||||
class AppExport IndexedName {
|
||||
public:
|
||||
|
||||
/// Construct from a name and an optional index. If the name contains an index it is read, but
|
||||
/// is used as the index *only* if _index parameter is unset. If the _index parameter is given
|
||||
/// it overrides any trailing integer in the name. Index must be positive, and name must contain
|
||||
/// only ASCII letters and the underscore character. If these conditions are not met, name is
|
||||
/// set to the empty string, and isNull() will return true.
|
||||
///
|
||||
/// \param name The new name - ASCII letters and underscores only, with optional integer suffix.
|
||||
/// This memory will be copied into a new internal storage location and need not be persistent.
|
||||
/// \param _index The new index - if provided, it overrides any suffix provided by name
|
||||
explicit IndexedName(const char *name = nullptr, int _index = 0)
|
||||
: index(0)
|
||||
{
|
||||
assert(_index >= 0);
|
||||
if (!name) {
|
||||
this->type = "";
|
||||
}
|
||||
else {
|
||||
set(name);
|
||||
if (_index > 0) {
|
||||
this->index = _index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an indexed name that is restricted to a list of preset type names. If it appears in
|
||||
/// that list, only a pointer to the character storage in the list is retained: the memory
|
||||
/// locations pointed at by the list must never be destroyed once they have been used to create
|
||||
/// names. If allowOthers is true (the default) then a requested name that is not in the list
|
||||
/// will be added to a static internal storage table, and its memory then re-used for later
|
||||
/// objects with the same name. If allowOthers is false, then the name request is rejected, and
|
||||
/// the name is treated as null.
|
||||
///
|
||||
/// \param name The new name - ASCII letters and underscores only, with optional integer suffix
|
||||
/// \param allowedTypeNames A vector of allowed names. Storage locations must persist for the
|
||||
/// entire run of the program.
|
||||
/// \param allowOthers Whether a name not in allowedTypeNames is permitted. If true (the
|
||||
/// default) then a name not in allowedTypeNames is added to a static internal storage vector
|
||||
/// so that it can be re-used later without additional memory allocation.
|
||||
IndexedName(const char *name,
|
||||
const std::vector<const char*> & allowedTypeNames,
|
||||
bool allowOthers=true) : type(""), index(0)
|
||||
{
|
||||
set(name, -1, allowedTypeNames, allowOthers);
|
||||
}
|
||||
|
||||
/// Construct from a QByteArray, but explicitly making a copy of the name on its first
|
||||
/// occurrence. If this is a name that has already been stored internally, no additional copy
|
||||
/// is made.
|
||||
///
|
||||
/// \param data The QByteArray to copy the data from
|
||||
explicit IndexedName(const QByteArray & data) : type(""), index(0)
|
||||
{
|
||||
set(data.constData(), data.size());
|
||||
}
|
||||
|
||||
/// Given constant name and an index, re-use the existing memory for the name, not making a copy
|
||||
/// of it, or scanning any existing storage for it. The name must never become invalid for the
|
||||
/// lifetime of the object it names. This memory will never be re-used by another object.
|
||||
///
|
||||
/// \param name The name of the object. This memory is NOT copied and must be persistent.
|
||||
/// \param index A positive, non-zero integer
|
||||
/// \return An IndexedName with the given name and index, re-using the existing memory for name
|
||||
static IndexedName fromConst(const char *name, int index) {
|
||||
assert (index >= 0);
|
||||
IndexedName res;
|
||||
res.type = name;
|
||||
res.index = index;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Given an existing std::string, *append* this name to it. If index is not zero, this will
|
||||
/// include the index.
|
||||
///
|
||||
/// \param buffer A (possibly non-empty) string buffer to append the name to.
|
||||
void appendToStringBuffer(std::string & buffer) const
|
||||
{
|
||||
buffer += this->type;
|
||||
if (this->index > 0) {
|
||||
buffer += std::to_string(this->index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and return a new std::string with this name in it.
|
||||
///
|
||||
/// \return A newly-created string with the IndexedName in it (e.g. "EDGE42")
|
||||
std::string toString() const
|
||||
{
|
||||
std::string result;
|
||||
this->appendToStringBuffer(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// An indexedName is represented as the simple concatenation of the name and its index, e.g.
|
||||
/// "EDGE1" or "FACE42".
|
||||
friend std::ostream & operator<<(std::ostream & stream, const IndexedName & indexedName)
|
||||
{
|
||||
stream << indexedName.type;
|
||||
if (indexedName.index > 0) {
|
||||
stream << indexedName.index;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// True only if both the name and index compare exactly equal.
|
||||
bool operator==(const IndexedName & other) const
|
||||
{
|
||||
return this->index == other.index
|
||||
&& (this->type == other.type
|
||||
|| std::strcmp(this->type, other.type)==0);
|
||||
}
|
||||
|
||||
/// Increments the index by the given offset. Does not affect the text part of the name.
|
||||
IndexedName & operator+=(int offset)
|
||||
{
|
||||
this->index += offset;
|
||||
assert(this->index >= 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Pre-increment operator: increases the index of this element by one.
|
||||
IndexedName & operator++()
|
||||
{
|
||||
++this->index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Pre-decrement operator: decreases the index of this element by one. Must not make the index
|
||||
/// negative (only checked when compiled in debug mode).
|
||||
IndexedName & operator--()
|
||||
{
|
||||
--this->index;
|
||||
assert(this->index >= 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// True if either the name or the index compare not equal.
|
||||
bool operator!=(const IndexedName & other) const
|
||||
{
|
||||
return !(this->operator==(other));
|
||||
}
|
||||
|
||||
/// Equivalent to C++20's operator <=>
|
||||
int compare(const IndexedName & other) const
|
||||
{
|
||||
int res = std::strcmp(this->type, other.type);
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
if (this->index < other.index) {
|
||||
return -1;
|
||||
}
|
||||
if (this->index > other.index) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Provided to enable sorting operations: the comparison is first lexicographical for the text
|
||||
/// element of the names, then numerical for the indices.
|
||||
bool operator<(const IndexedName & other) const
|
||||
{
|
||||
return compare(other) < 0;
|
||||
}
|
||||
|
||||
/// Allow direct memory access to the individual characters of the text portion of the name.
|
||||
/// NOTE: input is not range-checked when compiled in release mode.
|
||||
char operator[](int input) const
|
||||
{
|
||||
assert(input >= 0);
|
||||
assert(input < static_cast<int>(std::strlen(this->type)));
|
||||
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
|
||||
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
return this->type[input];
|
||||
}
|
||||
|
||||
/// Get a pointer to text part of the name - does NOT make a copy, returns direct memory access
|
||||
const char * getType() const { return this->type; }
|
||||
|
||||
/// Get the numerical part of the name
|
||||
int getIndex() const { return this->index; }
|
||||
|
||||
/// Set the numerical part of the name (note that there is no equivalent function to allow
|
||||
/// changing the text part of the name, which is immutable once created).
|
||||
///
|
||||
/// \param input The new index. Must be a positive non-zero integer
|
||||
void setIndex(int input) { assert(input>=0); this->index = input; }
|
||||
|
||||
/// A name is considered "null" if its text component is an empty string.
|
||||
// When we support C++20 we can use std::span<> to eliminate the clang-tidy warning
|
||||
// NOLINTNEXTLINE cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
bool isNull() const { return this->type[0] == '\0'; }
|
||||
|
||||
/// Boolean conversion provides the opposite of isNull(), yielding true when the text part of
|
||||
/// the name is NOT the empty string.
|
||||
explicit operator bool() const { return !isNull(); }
|
||||
|
||||
protected:
|
||||
/// Apply the IndexedName rules and either store the characters of a new type or a reference to
|
||||
/// the characters in a type named in types, or stored statically within this function. If len
|
||||
/// is not set, or set to -1 (the default), then the provided string in name is scanned for its
|
||||
/// length using strlen (e.g. it must be null-terminated).
|
||||
///
|
||||
/// \param name The new name. If necessary a copy is made, this char * need not be persistent
|
||||
/// \param length The length of name
|
||||
/// \param allowedNames A vector of storage locations of allowed names. These storage locations
|
||||
/// must be persistent for the duration of the program run.
|
||||
/// \param allowOthers If true (the default), then if name is not in allowedNames it is allowed,
|
||||
/// and it is added to internal storage (making a copy of the name if this is its first
|
||||
/// occurrence).
|
||||
void set(const char *name,
|
||||
int length = -1,
|
||||
const std::vector<const char *> & allowedNames = {},
|
||||
bool allowOthers = true);
|
||||
|
||||
private:
|
||||
const char * type;
|
||||
int index;
|
||||
};
|
||||
|
||||
|
||||
/// A thin wrapper around a QByteArray providing the ability to force a copy of the data at any
|
||||
/// time, even if it isn't being written to. The standard assignment operator for this class *does*
|
||||
/// make a copy of the data, unlike the standard assignment operator for QByteArray.
|
||||
struct ByteArray
|
||||
{
|
||||
explicit ByteArray(QByteArray other)
|
||||
:bytes(std::move(other))
|
||||
{}
|
||||
|
||||
ByteArray(const ByteArray& other) = default;
|
||||
|
||||
ByteArray(ByteArray&& other) noexcept
|
||||
:bytes(std::move(other.bytes))
|
||||
{}
|
||||
|
||||
~ByteArray() = default;
|
||||
|
||||
/// Guarantee that the stored QByteArray does not share its memory with another instance.
|
||||
void ensureUnshared() const
|
||||
{
|
||||
QByteArray copy;
|
||||
copy.append(bytes.constData(), bytes.size());
|
||||
bytes = copy;
|
||||
}
|
||||
|
||||
bool operator==(const ByteArray& other) const {
|
||||
return bytes == other.bytes;
|
||||
}
|
||||
|
||||
ByteArray &operator=(const ByteArray & other) {
|
||||
bytes.clear();
|
||||
bytes.append(other.bytes.constData(), other.bytes.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteArray &operator= (ByteArray&& other) noexcept
|
||||
{
|
||||
bytes = std::move(other.bytes);
|
||||
return *this;
|
||||
}
|
||||
|
||||
mutable QByteArray bytes;
|
||||
};
|
||||
|
||||
|
||||
struct ByteArrayHasher
|
||||
{
|
||||
std::size_t operator()(const ByteArray& bytes) const
|
||||
{
|
||||
return qHash(bytes.bytes);
|
||||
}
|
||||
|
||||
std::size_t operator()(const QByteArray& bytes) const
|
||||
{
|
||||
return qHash(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // APP_INDEXEDNAME_H
|
||||
@@ -49,8 +49,8 @@ public:
|
||||
|
||||
|
||||
/** Base class of all factories
|
||||
* This class has the purpose to produce a runtime instance
|
||||
* of classes unknown at compile time. It holds a map of so called
|
||||
* This class has the purpose to produce instances of classes at runtime
|
||||
* that are unknown at compile time. It holds a map of so called
|
||||
* producers which are able to produce an instance of a special class.
|
||||
* Producer can be registered at runtime through e.g. application modules
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,21 @@ void ProgressIndicatorPy::init_type()
|
||||
add_varargs_method("stop",&ProgressIndicatorPy::stop,"stop()");
|
||||
}
|
||||
|
||||
Py::PythonType& ProgressIndicatorPy::behaviors()
|
||||
{
|
||||
return Py::PythonExtension<ProgressIndicatorPy>::behaviors();
|
||||
}
|
||||
|
||||
PyTypeObject* ProgressIndicatorPy::type_object()
|
||||
{
|
||||
return Py::PythonExtension<ProgressIndicatorPy>::type_object();
|
||||
}
|
||||
|
||||
bool ProgressIndicatorPy::check(PyObject* p)
|
||||
{
|
||||
return Py::PythonExtension<ProgressIndicatorPy>::check(p);
|
||||
}
|
||||
|
||||
PyObject *ProgressIndicatorPy::PyMake(struct _typeobject *, PyObject *, PyObject *)
|
||||
{
|
||||
return new ProgressIndicatorPy();
|
||||
|
||||
@@ -35,6 +35,9 @@ class BaseExport ProgressIndicatorPy : public Py::PythonExtension<ProgressIndica
|
||||
{
|
||||
public:
|
||||
static void init_type(); // announce properties and methods
|
||||
static Py::PythonType& behaviors();
|
||||
static PyTypeObject* type_object();
|
||||
static bool check(PyObject* p);
|
||||
|
||||
ProgressIndicatorPy();
|
||||
~ProgressIndicatorPy() override;
|
||||
|
||||
@@ -1648,6 +1648,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #65A2E5, stop:1 #65A2E5);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #D2D8E1;
|
||||
background: #232932;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1615,6 +1615,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #3874f2, stop:1 #5e90fa);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #e0e0e0;
|
||||
background-color: #5a5a5a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1615,6 +1615,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #1b3774, stop:1 #2053c0);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #fefefe;
|
||||
background-color: #111111;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
QComboBox inside Task Panel content
|
||||
|
||||
@@ -1614,6 +1614,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #819c0c, stop:1 #94b30f);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #e0e0e0;
|
||||
background-color: #5a5a5a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1615,6 +1615,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #d0970c, stop:1 #daa116);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #e0e0e0;
|
||||
background-color: #5a5a5a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1615,6 +1615,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #1b3774, stop:1 #2053c0);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #f5f5f5;
|
||||
background: #2a2a2a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
QComboBox inside Task Panel content
|
||||
|
||||
@@ -1615,6 +1615,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #566214, stop:1 #74831d);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #f5f5f5;
|
||||
background: #2a2a2a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
QComboBox inside Task Panel content
|
||||
|
||||
@@ -1609,6 +1609,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #624b14, stop:1 #b28416);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #f5f5f5;
|
||||
background-color: #2a2a2a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
QComboBox inside Task Panel content
|
||||
|
||||
@@ -1612,6 +1612,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #3874f2, stop:1 #5e90fa);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: black;
|
||||
background: #f5f5f5;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1612,6 +1612,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #819c0c, stop:1 #94b30f);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: black;
|
||||
background: #f5f5f5;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1612,6 +1612,33 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #d0970c, stop:1 #daa116);
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: black;
|
||||
background: #f5f5f5;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
/*==================================================================================================
|
||||
Radio button
|
||||
|
||||
@@ -1624,20 +1624,6 @@ QWidget#Form QPushButton:pressed {
|
||||
background-color: #557BB6;
|
||||
}
|
||||
|
||||
/* Sketcher Manual Update Button */
|
||||
|
||||
QPushButton#manualUpdate {
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
border: 1px solid #494949;
|
||||
}
|
||||
|
||||
QPushButton:pressed#manualUpdate {
|
||||
color: #ffffff;
|
||||
border: 1px solid #3c3c3c;
|
||||
background-color: #48699a;
|
||||
}
|
||||
|
||||
/* Addon Manager */
|
||||
|
||||
QDialog#Dialog QPushButton {
|
||||
@@ -1815,6 +1801,32 @@ QSint--ActionGroup QFrame[class="content"] QToolButton:pressed {
|
||||
background-color: #557BB6;
|
||||
}
|
||||
|
||||
/* QToolButtons with a menu found in Sketcher task panel*/
|
||||
QSint--ActionGroup QToolButton::menu-button {
|
||||
border: none;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 2px;
|
||||
width: 16px; /* 16px width + 4px for border = 20px allocated above */
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
QSint--ActionGroup QToolButton#filterButton QListWidget {
|
||||
color: #f5f5f5;
|
||||
background: #2a2a2a;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/*==================================================================================================
|
||||
QComboBox inside Task Panel content
|
||||
@@ -1824,7 +1836,7 @@ QComboBox inside Task Panel content
|
||||
/* TODO: external border not working, in the rest of GUI works setting up Qmenu background color but inside Task Panel it doesn't... */
|
||||
QSint--ActionGroup QFrame[class="content"] QMenu,
|
||||
QSint--ActionGroup QFrame[class="content"] QMenu::item {
|
||||
background-color: #696969;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
QSint--ActionGroup QFrame[class="content"] QComboBox QAbstractItemView {
|
||||
|
||||
@@ -42,16 +42,17 @@ translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class UpdaterFactory:
|
||||
"""A factory class for generating updaters. Mainly exists to allow eaily mocking those
|
||||
updaters during testing. A replacement class need only provide a "get_updater" function
|
||||
that returns mock updater objects. Those objects must be QObjects with a run() function
|
||||
and a finished signal."""
|
||||
"""A factory class for generating updaters. Mainly exists to allow easily mocking
|
||||
those updaters during testing. A replacement class need only provide a
|
||||
"get_updater" function that returns mock updater objects. Those objects must be
|
||||
QObjects with a run() function and a finished signal."""
|
||||
|
||||
def __init__(self, addons):
|
||||
self.addons = addons
|
||||
|
||||
def get_updater(self, addon):
|
||||
"""Get an updater for this addon (either a MacroInstaller or an AddonInstaller)"""
|
||||
"""Get an updater for this addon (either a MacroInstaller or an
|
||||
AddonInstaller)"""
|
||||
if addon.macro is not None:
|
||||
return MacroInstaller(addon)
|
||||
return AddonInstaller(addon, self.addons)
|
||||
@@ -101,7 +102,8 @@ class UpdateAllGUI(QtCore.QObject):
|
||||
self.cancelled = False
|
||||
|
||||
def run(self):
|
||||
"""Run the Update All process. Blocks until updates are complete or cancelled."""
|
||||
"""Run the Update All process. Blocks until updates are complete or
|
||||
cancelled."""
|
||||
self.running = True
|
||||
self._setup_dialog()
|
||||
self.dialog.show()
|
||||
@@ -194,14 +196,16 @@ class UpdateAllGUI(QtCore.QObject):
|
||||
text = translate("Addons installer", "Finished updating the following addons")
|
||||
self._set_dialog_to_final_state(text)
|
||||
self.running = False
|
||||
self.finished.emit()
|
||||
|
||||
def _setup_cancelled_state(self):
|
||||
text1 = translate("AddonsInstaller", "Update was cancelled")
|
||||
text2 = translate("AddonsInstaller", "some addons may have been updated")
|
||||
self._set_dialog_to_final_state(text1 + ": " + text2)
|
||||
self.running = False
|
||||
self.finished.emit()
|
||||
|
||||
def _set_dialog_to_final_state(self,new_content):
|
||||
def _set_dialog_to_final_state(self, new_content):
|
||||
self.dialog.buttonBox.clear()
|
||||
self.dialog.buttonBox.addButton(QtWidgets.QDialogButtonBox.Close)
|
||||
self.dialog.label.setText(new_content)
|
||||
|
||||
@@ -49,8 +49,6 @@ PyMOD_INIT_FUNC(Import)
|
||||
// add mesh elements
|
||||
Base::Interpreter().addType(&Import::StepShapePy ::Type, importModule, "StepShape");
|
||||
|
||||
// init Type system
|
||||
//Import::StepShape ::init();
|
||||
|
||||
Base::Console().Log("Loading Import module... done\n");
|
||||
PyMOD_Return(importModule);
|
||||
|
||||
@@ -104,9 +104,6 @@ public:
|
||||
add_keyword_method("insert",&Module::importer,
|
||||
"insert(string,string) -- Insert the file into the given document."
|
||||
);
|
||||
// add_varargs_method("openAssembly",&Module::importAssembly,
|
||||
// "openAssembly(string) -- Open the assembly file and create a new document."
|
||||
// );
|
||||
add_keyword_method("export",&Module::exporter,
|
||||
"export(list,string) -- Export a list of objects into a single file."
|
||||
);
|
||||
@@ -144,7 +141,6 @@ private:
|
||||
std::string name8bit = Part::encodeFilename(Utf8Name);
|
||||
|
||||
try {
|
||||
//Base::Console().Log("Insert in Part with %s",Name);
|
||||
Base::FileInfo file(Utf8Name.c_str());
|
||||
|
||||
App::Document *pcDoc = nullptr;
|
||||
@@ -231,7 +227,6 @@ private:
|
||||
throw Py::Exception(PyExc_IOError, "no supported file format");
|
||||
}
|
||||
|
||||
#if 1
|
||||
ImportOCAFExt ocaf(hDoc, pcDoc, file.fileNamePure());
|
||||
ocaf.setImportOptions(ImportOCAFExt::customImportOptions());
|
||||
if (merge != Py_None)
|
||||
@@ -243,14 +238,7 @@ private:
|
||||
if (mode >= 0)
|
||||
ocaf.setMode(mode);
|
||||
ocaf.loadShapes();
|
||||
#elif 1
|
||||
Import::ImportOCAFCmd ocaf(hDoc, pcDoc, file.fileNamePure());
|
||||
ocaf.loadShapes();
|
||||
#else
|
||||
Import::ImportXCAF xcaf(hDoc, pcDoc, file.fileNamePure());
|
||||
xcaf.loadShapes();
|
||||
pcDoc->recompute();
|
||||
#endif
|
||||
|
||||
hApp->Close(hDoc);
|
||||
|
||||
if (!ocaf.partColors.empty()) {
|
||||
@@ -326,7 +314,6 @@ private:
|
||||
ocaf.exportObjects(objs);
|
||||
}
|
||||
else {
|
||||
//bool keepExplicitPlacement = objs.size() > 1;
|
||||
bool keepExplicitPlacement = Standard_True;
|
||||
ExportOCAF ocaf(hDoc, keepExplicitPlacement);
|
||||
// That stuff is exporting a list of selected objects into FreeCAD Tree
|
||||
@@ -340,26 +327,22 @@ private:
|
||||
std::vector <TDF_Label> FreeLabels;
|
||||
std::vector <int> part_id;
|
||||
ocaf.getFreeLabels(hierarchical_label,FreeLabels, part_id);
|
||||
#if OCC_VERSION_HEX >= 0x070200
|
||||
// Update is not performed automatically anymore: https://tracker.dev.opencascade.org/view.php?id=28055
|
||||
XCAFDoc_DocumentTool::ShapeTool(hDoc->Main())->UpdateAssemblies();
|
||||
#endif
|
||||
}
|
||||
|
||||
Base::FileInfo file(Utf8Name.c_str());
|
||||
if (file.hasExtension("stp") || file.hasExtension("step")) {
|
||||
//Interface_Static::SetCVal("write.step.schema", "AP214IS");
|
||||
STEPCAFControl_Writer writer;
|
||||
Part::Interface::writeStepAssembly(Part::Interface::Assembly::On);
|
||||
// writer.SetColorMode(Standard_False);
|
||||
writer.Transfer(hDoc, STEPControl_AsIs);
|
||||
|
||||
APIHeaderSection_MakeHeader makeHeader(writer.ChangeWriter().Model());
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
|
||||
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part")->GetGroup("STEP");
|
||||
|
||||
// Don't set name because STEP doesn't support UTF-8
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=8&t=52967
|
||||
//makeHeader.SetName(new TCollection_HAsciiString((Standard_CString)Utf8Name.c_str()));
|
||||
makeHeader.SetAuthorValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Author", "Author").c_str()));
|
||||
makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Company").c_str()));
|
||||
makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::Application::getExecutableName().c_str()));
|
||||
@@ -690,111 +673,7 @@ private:
|
||||
throw Py::TypeError("expected ([DocObject],path");
|
||||
}
|
||||
};
|
||||
/*
|
||||
static PyObject * importAssembly(PyObject *self, PyObject *args)
|
||||
{
|
||||
char* Name;
|
||||
PyObject* TargetObjectPy=0;
|
||||
if (!PyArg_ParseTuple(args, "et|O!","utf-8",&Name,&(App::DocumentObjectPy::Type),&TargetObjectPy))
|
||||
return 0;
|
||||
std::string Utf8Name = std::string(Name);
|
||||
PyMem_Free(Name);
|
||||
std::string name8bit = Part::encodeFilename(Utf8Name);
|
||||
|
||||
PY_TRY {
|
||||
//Base::Console().Log("Insert in Part with %s",Name);
|
||||
Base::FileInfo file(name8bit);
|
||||
|
||||
App::DocumentObject* target = nullptr;
|
||||
|
||||
if(TargetObjectPy)
|
||||
target = static_cast<App::DocumentObjectPy*>(TargetObjectPy)->getDocumentObjectPtr();
|
||||
|
||||
|
||||
App::Document *pcDoc = 0;
|
||||
|
||||
pcDoc = App::GetApplication().getActiveDocument();
|
||||
|
||||
if (!pcDoc)
|
||||
pcDoc = App::GetApplication().newDocument("ImportedAssembly");
|
||||
|
||||
|
||||
Handle(XCAFApp_Application) hApp = XCAFApp_Application::GetApplication();
|
||||
Handle(TDocStd_Document) hDoc;
|
||||
hApp->NewDocument(TCollection_ExtendedString("MDTV-CAF"), hDoc);
|
||||
|
||||
if (file.hasExtension("stp") || file.hasExtension("step")) {
|
||||
try {
|
||||
STEPCAFControl_Reader aReader;
|
||||
aReader.SetColorMode(true);
|
||||
aReader.SetNameMode(true);
|
||||
aReader.SetLayerMode(true);
|
||||
if (aReader.ReadFile((Standard_CString)(name8bit.c_str())) != IFSelect_RetDone) {
|
||||
PyErr_SetString(PyExc_IOError, "cannot read STEP file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle(Message_ProgressIndicator) pi = new Part::ProgressIndicator(100);
|
||||
aReader.Reader().WS()->MapReader()->SetProgress(pi);
|
||||
pi->NewScope(100, "Reading STEP file...");
|
||||
pi->Show();
|
||||
aReader.Transfer(hDoc);
|
||||
pi->EndScope();
|
||||
}
|
||||
catch (OSD_Exception& e) {
|
||||
Base::Console().Error("%s\n", e.GetMessageString());
|
||||
Base::Console().Message("Try to load STEP file without colors...\n");
|
||||
|
||||
Part::ImportStepParts(pcDoc,Name);
|
||||
pcDoc->recompute();
|
||||
}
|
||||
}
|
||||
else if (file.hasExtension("igs") || file.hasExtension("iges")) {
|
||||
try {
|
||||
IGESControl_Controller::Init();
|
||||
Interface_Static::SetIVal("read.surfacecurve.mode",3);
|
||||
IGESCAFControl_Reader aReader;
|
||||
aReader.SetColorMode(true);
|
||||
aReader.SetNameMode(true);
|
||||
aReader.SetLayerMode(true);
|
||||
if (aReader.ReadFile((Standard_CString)(name8bit.c_str())) != IFSelect_RetDone) {
|
||||
PyErr_SetString(PyExc_IOError, "cannot read IGES file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle(Message_ProgressIndicator) pi = new Part::ProgressIndicator(100);
|
||||
aReader.WS()->MapReader()->SetProgress(pi);
|
||||
pi->NewScope(100, "Reading IGES file...");
|
||||
pi->Show();
|
||||
aReader.Transfer(hDoc);
|
||||
pi->EndScope();
|
||||
}
|
||||
catch (OSD_Exception& e) {
|
||||
Base::Console().Error("%s\n", e.GetMessageString());
|
||||
Base::Console().Message("Try to load IGES file without colors...\n");
|
||||
|
||||
Part::ImportIgesParts(pcDoc,Name);
|
||||
pcDoc->recompute();
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_RuntimeError, "no supported file format");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Import::ImportOCAFAssembly ocaf(hDoc, pcDoc, file.fileNamePure(),target);
|
||||
ocaf.loadAssembly();
|
||||
pcDoc->recompute();
|
||||
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.GetMessageString());
|
||||
return 0;
|
||||
}
|
||||
PY_CATCH
|
||||
|
||||
Py_Return;
|
||||
}*/
|
||||
|
||||
PyObject* initModule()
|
||||
{
|
||||
|
||||
@@ -41,24 +41,11 @@ void FeatureImportIges::InitLabel(const TDF_Label &rcLabel)
|
||||
addProperty("String","FileName");
|
||||
}
|
||||
|
||||
/*
|
||||
bool FeaturePartImportStep::MustExecute(void)
|
||||
{
|
||||
Base::Console().Log("PartBoxFeature::MustExecute()\n");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
Standard_Integer FeatureImportIges::Execute(void)
|
||||
{
|
||||
Base::Console().Log("FeaturePartImportIges::Execute()\n");
|
||||
|
||||
/* cout << GetFloatProperty("x") << endl;
|
||||
cout << GetFloatProperty("y") << endl;
|
||||
cout << GetFloatProperty("z") << endl;
|
||||
cout << GetFloatProperty("l") << endl;
|
||||
cout << GetFloatProperty("h") << endl;
|
||||
cout << GetFloatProperty("w") << endl;*/
|
||||
|
||||
try{
|
||||
|
||||
IGESControl_Reader aReader;
|
||||
@@ -83,10 +70,6 @@ Standard_Integer FeatureImportIges::Execute(void)
|
||||
if (aReader.ReadFile((const Standard_CString)FileName.c_str()) != IFSelect_RetDone)
|
||||
throw Base::FileException("IGES read failed (load file)");
|
||||
|
||||
// check iges-file (memory)
|
||||
//if (!aReader.Check(Standard_True))
|
||||
// Base::Console().Warning( "IGES model contains errors! try loading anyway....\n" );
|
||||
|
||||
// make brep
|
||||
aReader.TransferRoots();
|
||||
// one shape, who contain's all subshapes
|
||||
@@ -104,17 +87,3 @@ Standard_Integer FeatureImportIges::Execute(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void FeatureImportIges::Validate(void)
|
||||
{
|
||||
Base::Console().Log("FeaturePartImportStep::Validate()\n");
|
||||
|
||||
// We validate the object label ( Label() ), all the arguments and the results of the object:
|
||||
log.SetValid(Label(), Standard_True);
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,21 +36,12 @@ public:
|
||||
|
||||
virtual void InitLabel(const TDF_Label &rcLabel);
|
||||
|
||||
// virtual bool MustExecute(void);
|
||||
|
||||
virtual Standard_Integer Execute(void);
|
||||
|
||||
// virtual void Validate(void);
|
||||
|
||||
/// Returns the Name/Type of the feature
|
||||
virtual const char *Type(void){return "PartImportIges";}
|
||||
/// Returns the Name/Type of the feature
|
||||
virtual const char *Type(void){return "PartImportIges";}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // __FeaturePartImportIges_H__
|
||||
|
||||
@@ -42,24 +42,11 @@ void FeatureImportStep::InitLabel(const TDF_Label &rcLabel)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
bool FeaturePartImportStep::MustExecute(void)
|
||||
{
|
||||
Base::Console().Log("PartBoxFeature::MustExecute()\n");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
Standard_Integer FeatureImportStep::Execute(void)
|
||||
{
|
||||
Base::Console().Log("FeaturePartImportStep::Execute()\n");
|
||||
|
||||
/* cout << GetFloatProperty("x") << endl;
|
||||
cout << GetFloatProperty("y") << endl;
|
||||
cout << GetFloatProperty("z") << endl;
|
||||
cout << GetFloatProperty("l") << endl;
|
||||
cout << GetFloatProperty("h") << endl;
|
||||
cout << GetFloatProperty("w") << endl;*/
|
||||
|
||||
try{
|
||||
|
||||
STEPControl_Reader aReader;
|
||||
@@ -92,7 +79,7 @@ Standard_Integer FeatureImportStep::Execute(void)
|
||||
|
||||
// Root transfers
|
||||
Standard_Integer nbr = aReader.NbRootsForTransfer();
|
||||
//aReader.PrintCheckTransfer (failsonly, IFSelect_ItemsByEntity);
|
||||
|
||||
for ( Standard_Integer n = 1; n<= nbr; n++)
|
||||
{
|
||||
printf("STEP: Transferring Root %d\n",n);
|
||||
@@ -124,16 +111,3 @@ Standard_Integer FeatureImportStep::Execute(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void FeatureImportStep::Validate(void)
|
||||
{
|
||||
Base::Console().Log("FeaturePartImportStep::Validate()\n");
|
||||
|
||||
// We validate the object label ( Label() ), all the arguments and the results of the object:
|
||||
log.SetValid(Label(), Standard_True);
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -37,17 +37,12 @@ public:
|
||||
|
||||
virtual Standard_Integer Execute(void);
|
||||
|
||||
// virtual void Validate(void);
|
||||
|
||||
/// Returns the Name/Type of the feature
|
||||
virtual const char *Type(void){return "PartImportStep";}
|
||||
/// Returns the Name/Type of the feature
|
||||
virtual const char *Type(void){return "PartImportStep";}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // __FeatureImportStep_H__
|
||||
|
||||
@@ -560,9 +560,7 @@ bool ImportOCAF2::createGroup(App::Document *doc, Info &info, const TopoDS_Shape
|
||||
link->Placement.setValue(pla->getValue());
|
||||
child = link;
|
||||
}
|
||||
// child->Visibility.setValue(false);
|
||||
}
|
||||
// group->Visibility.setValue(false);
|
||||
group->ElementList.setValues(children);
|
||||
group->VisibilityList.setValue(visibilities);
|
||||
info.obj = group;
|
||||
@@ -627,7 +625,6 @@ App::DocumentObject* ImportOCAF2::loadShapes()
|
||||
ret = info.obj;
|
||||
}
|
||||
if(ret) {
|
||||
// ret->Visibility.setValue(true);
|
||||
ret->recomputeFeature(true);
|
||||
}
|
||||
if(options.merge && ret && !ret->isDerivedFrom(Part::Feature::getClassTypeId())) {
|
||||
@@ -766,7 +763,6 @@ App::DocumentObject *ImportOCAF2::loadShape(App::Document *doc,
|
||||
}
|
||||
|
||||
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
||||
// link->Visibility.setValue(false);
|
||||
link->setLink(-1,info.obj);
|
||||
setPlacement(&link->Placement,shape);
|
||||
info.obj = link;
|
||||
@@ -861,7 +857,6 @@ bool ImportOCAF2::createAssembly(App::Document *_doc,
|
||||
|
||||
// Okay, we are creating a link array
|
||||
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
||||
// link->Visibility.setValue(false);
|
||||
link->setLink(-1,child);
|
||||
link->ShowElement.setValue(false);
|
||||
link->ElementCount.setValue(childInfo.plas.size());
|
||||
@@ -1180,13 +1175,12 @@ void ExportOCAF2::exportObjects(std::vector<App::DocumentObject*> &objs, const c
|
||||
setName(label,nullptr,name);
|
||||
}
|
||||
|
||||
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
||||
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
dumpLabels(pDoc->Main(),aShapeTool,aColorTool);
|
||||
}
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x070200
|
||||
// Update is not performed automatically anymore: https://tracker.dev.opencascade.org/view.php?id=28055
|
||||
aShapeTool->UpdateAssemblies();
|
||||
#endif
|
||||
}
|
||||
|
||||
TDF_Label ExportOCAF2::exportObject(App::DocumentObject* parentObj,
|
||||
|
||||
@@ -91,21 +91,6 @@ std::string ImportOCAFAssembly::getName(const TDF_Label& label)
|
||||
part_name = str;
|
||||
delete [] str;
|
||||
return part_name;
|
||||
|
||||
//if (part_name.empty()) {
|
||||
// return "";
|
||||
//}
|
||||
//else {
|
||||
// bool ws=true;
|
||||
// for (std::string::iterator it = part_name.begin(); it != part_name.end(); ++it) {
|
||||
// if (*it != ' ') {
|
||||
// ws = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (ws)
|
||||
// part_name = defaultname;
|
||||
//}
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -255,15 +240,6 @@ void ImportOCAFAssembly::createShape(const TopoDS_Shape& aShape, const TopLoc_Lo
|
||||
std::vector<App::Color> colors;
|
||||
colors.push_back(color);
|
||||
applyColors(part, colors);
|
||||
#if 0//TODO
|
||||
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(part);
|
||||
if (vp && vp->isDerivedFrom(PartGui::ViewProviderPart::getClassTypeId())) {
|
||||
color.r = aColor.Red();
|
||||
color.g = aColor.Green();
|
||||
color.b = aColor.Blue();
|
||||
static_cast<PartGui::ViewProviderPart*>(vp)->ShapeColor.setValue(color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TopTools_IndexedMapOfShape faces;
|
||||
|
||||
@@ -65,25 +65,13 @@ int StepShape::read(const char* fileName)
|
||||
throw Base::FileException("Cannot open STEP file");
|
||||
}
|
||||
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.precision.mode");
|
||||
//Standard_Real rp = Interface_Static::RVal("read.maxprecision.val");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.maxprecision.mode");
|
||||
//Standard_Integer mv = Interface_Static::IVal("read.stdsameparameter.mode");
|
||||
//Standard_Integer rp = Interface_Static::IVal("read.surfacecurve.mode");
|
||||
//Standard_Real era = Interface_Static::RVal("read.encoderegularity.angle");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.product.mode");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.product.context");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.shape.repr");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.assembly.level");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.shape.relationship");
|
||||
//Standard_Integer ic = Interface_Static::IVal("read.step.shape.aspect");
|
||||
|
||||
Handle(TColStd_HSequenceOfTransient) list = aReader.GiveList();
|
||||
|
||||
//Use method StepData_StepModel::NextNumberForLabel to find its rank with the following:
|
||||
//Standard_CString label = "#...";
|
||||
Handle(StepData_StepModel) model = aReader.StepModel();
|
||||
//rank = model->NextNumberForLabe(label, 0, Standard_False);
|
||||
|
||||
|
||||
std::cout << "dump of step header:" << std::endl;
|
||||
#if OCC_VERSION_HEX < 0x070401
|
||||
|
||||
@@ -180,15 +180,12 @@ void OCAFBrowser::load(const TDF_Label& label, QTreeWidgetItem* item, const QStr
|
||||
item->setText(0, text);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TDF_IDList localList = myList;
|
||||
#else
|
||||
|
||||
TDF_IDList localList;
|
||||
TDF_AttributeIterator itr (label);
|
||||
for ( ; itr.More(); itr.Next()) {
|
||||
localList.Append(itr.Value()->ID());
|
||||
}
|
||||
#endif
|
||||
|
||||
for (TDF_ListIteratorOfIDList it(localList); it.More(); it.Next()) {
|
||||
Handle(TDF_Attribute) attr;
|
||||
@@ -260,15 +257,6 @@ void OCAFBrowser::load(const TDF_Label& label, QTreeWidgetItem* item, const QStr
|
||||
}
|
||||
}
|
||||
|
||||
//TDF_ChildIDIterator nodeIterator(label, XCAFDoc::ShapeRefGUID());
|
||||
//for (; nodeIterator.More(); nodeIterator.Next()) {
|
||||
// Handle(TDataStd_TreeNode) node = Handle(TDataStd_TreeNode)::DownCast(nodeIterator.Value());
|
||||
// //if (node->HasFather())
|
||||
// // ;
|
||||
// QTreeWidgetItem* child = new QTreeWidgetItem();
|
||||
// child->setText(0, QString::fromLatin1("TDataStd_TreeNode"));
|
||||
// item->addChild(child);
|
||||
//}
|
||||
|
||||
int i=1;
|
||||
for (TDF_ChildIterator it(label); it.More(); it.Next(),i++) {
|
||||
@@ -295,12 +283,9 @@ private:
|
||||
if (!vp)
|
||||
return;
|
||||
if(colors.empty()) {
|
||||
// vp->MapFaceColor.setValue(true);
|
||||
// vp->MapLineColor.setValue(true);
|
||||
// vp->updateColors(0,true);
|
||||
return;
|
||||
}
|
||||
// vp->MapFaceColor.setValue(false);
|
||||
|
||||
if(colors.size() == 1) {
|
||||
vp->ShapeColor.setValue(colors.front());
|
||||
vp->Transparency.setValue(100 * colors.front().a);
|
||||
@@ -313,7 +298,6 @@ private:
|
||||
auto vp = dynamic_cast<PartGui::ViewProviderPartExt*>(Gui::Application::Instance->getViewProvider(part));
|
||||
if (!vp)
|
||||
return;
|
||||
// vp->MapLineColor.setValue(false);
|
||||
if(colors.size() == 1)
|
||||
vp->LineColor.setValue(colors.front());
|
||||
else
|
||||
@@ -344,7 +328,6 @@ private:
|
||||
if(!vp)
|
||||
return;
|
||||
(void)colors;
|
||||
// vp->setElementColors(colors);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -411,7 +394,6 @@ private:
|
||||
std::string name8bit = Part::encodeFilename(Utf8Name);
|
||||
|
||||
try {
|
||||
//Base::Console().Log("Insert in Part with %s",Name);
|
||||
Base::FileInfo file(Utf8Name.c_str());
|
||||
|
||||
App::Document *pcDoc = nullptr;
|
||||
@@ -666,10 +648,7 @@ private:
|
||||
ocaf.getPartColors(hierarchical_part,FreeLabels,part_id,Colors);
|
||||
ocaf.reallocateFreeShape(hierarchical_part,FreeLabels,part_id,Colors);
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x070200
|
||||
// Update is not performed automatically anymore: https://tracker.dev.opencascade.org/view.php?id=28055
|
||||
XCAFDoc_DocumentTool::ShapeTool(hDoc->Main())->UpdateAssemblies();
|
||||
#endif
|
||||
}
|
||||
|
||||
Base::FileInfo file(Utf8Name.c_str());
|
||||
@@ -682,7 +661,6 @@ private:
|
||||
|
||||
STEPCAFControl_Writer writer;
|
||||
Part::Interface::writeStepAssembly(Part::Interface::Assembly::On);
|
||||
// writer.SetColorMode(Standard_False);
|
||||
writer.Transfer(hDoc, STEPControl_AsIs);
|
||||
|
||||
// edit STEP header
|
||||
@@ -691,8 +669,8 @@ private:
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
|
||||
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part")->GetGroup("STEP");
|
||||
|
||||
// Don't set name because STEP doesn't support UTF-8
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=8&t=52967
|
||||
//makeHeader.SetName(new TCollection_HAsciiString((Standard_CString)Utf8Name.c_str()));
|
||||
makeHeader.SetAuthorValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Author", "Author").c_str()));
|
||||
makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString(hGrp->GetASCII("Company").c_str()));
|
||||
makeHeader.SetOriginatingSystem(new TCollection_HAsciiString(App::Application::getExecutableName().c_str()));
|
||||
@@ -758,7 +736,6 @@ private:
|
||||
throw Py::Exception();
|
||||
|
||||
try {
|
||||
//Base::Console().Log("Insert in Part with %s",Name);
|
||||
Base::FileInfo file(Name);
|
||||
|
||||
Handle(XCAFApp_Application) hApp = XCAFApp_Application::GetApplication();
|
||||
|
||||
@@ -952,8 +952,8 @@ void TopoShape::exportStep(const char *filename) const
|
||||
throw Base::FileException("Error in transferring STEP");
|
||||
|
||||
APIHeaderSection_MakeHeader makeHeader(aWriter.Model());
|
||||
// Don't set name because STEP doesn't support UTF-8
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=8&t=52967
|
||||
//makeHeader.SetName(new TCollection_HAsciiString((Standard_CString)(encodeFilename(filename).c_str())));
|
||||
makeHeader.SetAuthorValue (1, new TCollection_HAsciiString("FreeCAD"));
|
||||
makeHeader.SetOrganizationValue (1, new TCollection_HAsciiString("FreeCAD"));
|
||||
makeHeader.SetOriginatingSystem(new TCollection_HAsciiString("FreeCAD"));
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QAction>
|
||||
#endif
|
||||
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
@@ -52,14 +55,6 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) :
|
||||
|
||||
ui->labelConstrainStatus->setOpenExternalLinks(false);
|
||||
|
||||
ui->autoUpdate->onRestore();
|
||||
ui->autoRemoveRedundants->onRestore();
|
||||
|
||||
if(ui->autoUpdate->isChecked())
|
||||
sketchView->getSketchObject()->noRecomputes=false;
|
||||
else
|
||||
sketchView->getSketchObject()->noRecomputes=true;
|
||||
|
||||
// Set up the possible state values for the status label
|
||||
ui->labelConstrainStatus->setParameterGroup("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
|
||||
ui->labelConstrainStatus->registerState(QString::fromUtf8("empty_sketch"), QColor("black"), std::string("EmptySketchMessageColor"));
|
||||
@@ -77,14 +72,27 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) :
|
||||
connect(ui->labelConstrainStatusLink, &Gui::UrlLabel::linkClicked,
|
||||
this, &TaskSketcherMessages::on_labelConstrainStatusLink_linkClicked);
|
||||
|
||||
//Set Auto Update in the 'Manual Update' button menu.
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
bool state = hGrp->GetBool("AutoRecompute", false);
|
||||
|
||||
sketchView->getSketchObject()->noRecomputes = !state;
|
||||
|
||||
QAction* action = new QAction(tr("Auto update"), this);
|
||||
action->setToolTip(tr("Executes a recomputation of active document after every sketch action"));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(state);
|
||||
ui->manualUpdate->addAction(action);
|
||||
|
||||
QObject::connect(
|
||||
qAsConst(ui->manualUpdate)->actions()[0], &QAction::changed,
|
||||
this, &TaskSketcherMessages::onAutoUpdateStateChanged
|
||||
);
|
||||
|
||||
/*QObject::connect(
|
||||
ui->labelConstrainStatus, SIGNAL(linkActivated(const QString &)),
|
||||
this , SLOT (on_labelConstrainStatus_linkActivated(const QString &))
|
||||
);
|
||||
QObject::connect(
|
||||
ui->autoUpdate, SIGNAL(stateChanged(int)),
|
||||
this , SLOT (on_autoUpdate_stateChanged(int))
|
||||
);
|
||||
QObject::connect(
|
||||
ui->manualUpdate, SIGNAL(clicked(bool)),
|
||||
this , SLOT (on_manualUpdate_clicked(bool))
|
||||
@@ -123,22 +131,13 @@ void TaskSketcherMessages::on_labelConstrainStatusLink_linkClicked(const QString
|
||||
|
||||
}
|
||||
|
||||
void TaskSketcherMessages::on_autoUpdate_stateChanged(int state)
|
||||
void TaskSketcherMessages::onAutoUpdateStateChanged()
|
||||
{
|
||||
if(state==Qt::Checked) {
|
||||
sketchView->getSketchObject()->noRecomputes=false;
|
||||
ui->autoUpdate->onSave();
|
||||
}
|
||||
else if (state==Qt::Unchecked) {
|
||||
sketchView->getSketchObject()->noRecomputes=true;
|
||||
ui->autoUpdate->onSave();
|
||||
}
|
||||
}
|
||||
bool state = qAsConst(ui->manualUpdate)->actions()[0]->isChecked();
|
||||
|
||||
void TaskSketcherMessages::on_autoRemoveRedundants_stateChanged(int state)
|
||||
{
|
||||
Q_UNUSED(state);
|
||||
ui->autoRemoveRedundants->onSave();
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
||||
hGrp->SetBool("AutoRecompute", state);
|
||||
sketchView->getSketchObject()->noRecomputes = !state;
|
||||
}
|
||||
|
||||
void TaskSketcherMessages::on_manualUpdate_clicked(bool checked)
|
||||
|
||||
@@ -47,12 +47,11 @@ public:
|
||||
explicit TaskSketcherMessages(ViewProviderSketch *sketchView);
|
||||
~TaskSketcherMessages() override;
|
||||
|
||||
void slotSetUp(const QString &state, const QString &msg, const QString& link, const QString& linkText);
|
||||
void slotSetUp(const QString& state, const QString& msg, const QString& link, const QString& linkText);
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_labelConstrainStatusLink_linkClicked(const QString &);
|
||||
void on_autoUpdate_stateChanged(int state);
|
||||
void on_autoRemoveRedundants_stateChanged(int state);
|
||||
void onAutoUpdateStateChanged();
|
||||
void on_manualUpdate_clicked(bool checked);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -7,104 +7,53 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>253</width>
|
||||
<height>108</height>
|
||||
<height>48</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="Gui::StatefulLabel" name="labelConstrainStatus">
|
||||
<property name="text">
|
||||
<string>DOF</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::UrlLabel" name="labelConstrainStatusLink">
|
||||
<property name="text">
|
||||
<string>Link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="autoRemoveRedundants">
|
||||
<property name="toolTip">
|
||||
<string>New constraints that would be redundant will automatically be removed</string>
|
||||
</property>
|
||||
<widget class="Gui::StatefulLabel" name="labelConstrainStatus">
|
||||
<property name="text">
|
||||
<string>Auto remove redundants</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AutoRemoveRedundants</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Sketcher</cstring>
|
||||
<string>DOF</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="autoUpdate">
|
||||
<property name="toolTip">
|
||||
<string>Executes a recomputation of active document after every sketch action</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto update</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AutoRecompute</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Sketcher</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="manualUpdate">
|
||||
<property name="toolTip">
|
||||
<string>Forces recomputation of active document</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="Gui::UrlLabel" name="labelConstrainStatusLink">
|
||||
<property name="text">
|
||||
<string>Link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="manualUpdate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Forces recomputation of active document</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/icons/view-refresh.svg</normaloff>:/icons/view-refresh.svg</iconset>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::MenuButtonPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::PrefCheckBox</class>
|
||||
<extends>QCheckBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::StatefulLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
|
||||
@@ -3,6 +3,7 @@ target_sources(
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Branding.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Expression.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/IndexedName.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/License.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Metadata.cpp
|
||||
)
|
||||
|
||||
595
tests/src/App/IndexedName.cpp
Normal file
595
tests/src/App/IndexedName.cpp
Normal file
@@ -0,0 +1,595 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "App/IndexedName.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
// NOLINTBEGIN(readability-magic-numbers)
|
||||
|
||||
class IndexedNameTest : public ::testing::Test {
|
||||
protected:
|
||||
// void SetUp() override {}
|
||||
|
||||
// void TearDown() override {}
|
||||
|
||||
// Create and return a list of invalid IndexedNames
|
||||
static std::vector<Data::IndexedName> givenInvalidIndexedNames() {
|
||||
return std::vector<Data::IndexedName> {
|
||||
Data::IndexedName(),
|
||||
Data::IndexedName("",1),
|
||||
Data::IndexedName("INVALID42NAME",1),
|
||||
Data::IndexedName(".EDGE",1)
|
||||
};
|
||||
}
|
||||
|
||||
// Create and return a list of valid IndexedNames
|
||||
static std::vector<Data::IndexedName> givenValidIndexedNames() {
|
||||
return std::vector<Data::IndexedName> {
|
||||
Data::IndexedName("NAME"),
|
||||
Data::IndexedName("NAME1"),
|
||||
Data::IndexedName("NAME",1),
|
||||
Data::IndexedName("NAME_WITH_UNDERSCORES12345")
|
||||
};
|
||||
}
|
||||
|
||||
// An arbitrary list of C strings used for testing some types of construction
|
||||
// NOLINTNEXTLINE cppcoreguidelines-non-private-member-variables-in-classes
|
||||
std::vector<const char *> allowedTypes {
|
||||
"VERTEX",
|
||||
"EDGE",
|
||||
"FACE",
|
||||
"WIRE"
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(IndexedNameTest, defaultConstruction)
|
||||
{
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName();
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "");
|
||||
EXPECT_EQ(indexedName.getIndex(), 0);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, nameOnlyConstruction)
|
||||
{
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName("TestName");
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "TestName");
|
||||
EXPECT_EQ(indexedName.getIndex(), 0);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, nameAndIndexConstruction)
|
||||
{
|
||||
// Arrange
|
||||
const int testIndex {42};
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName("TestName", testIndex);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "TestName");
|
||||
EXPECT_EQ(indexedName.getIndex(), testIndex);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, nameAndIndexConstructionWithOverride)
|
||||
{
|
||||
// Arrange
|
||||
const int testIndex {42};
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName("TestName17", testIndex);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "TestName");
|
||||
EXPECT_EQ(indexedName.getIndex(), testIndex);
|
||||
}
|
||||
|
||||
// Names must only contain ASCII letters and underscores (but may end with a number)
|
||||
TEST_F(IndexedNameTest, constructionInvalidCharInName)
|
||||
{
|
||||
// Arrange
|
||||
constexpr int lastASCIICode{127};
|
||||
std::vector<char> illegalCharacters = {};
|
||||
for (int code = 1; code <= lastASCIICode; ++code) {
|
||||
if ((std::isalnum(code) == 0) && code != '_') {
|
||||
illegalCharacters.push_back(char(code));
|
||||
}
|
||||
}
|
||||
for (auto illegalChar : illegalCharacters) {
|
||||
std::string testName {"TestName"};
|
||||
testName += illegalChar;
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName(testName.c_str(), 1);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "") << "Expected empty name when given " << testName;
|
||||
}
|
||||
}
|
||||
|
||||
// Names must not contain numbers in the middle:
|
||||
TEST_F(IndexedNameTest, constructionNumberInName)
|
||||
{
|
||||
// Arrange
|
||||
const int testIndex {42};
|
||||
std::string testName;
|
||||
testName += "Test" + std::to_string(testIndex) + "Name";
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName(testName.c_str(), testIndex);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, nameAndTypeListConstructionWithoutAllowOthers)
|
||||
{
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName("EDGE19", allowedTypes, false);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "EDGE");
|
||||
EXPECT_EQ(indexedName.getIndex(), 19);
|
||||
|
||||
// Act
|
||||
indexedName = Data::IndexedName("EDGES_ARE_REALLY_GREAT19", allowedTypes, false);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "");
|
||||
EXPECT_EQ(indexedName.getIndex(), 19);
|
||||
|
||||
// Act
|
||||
indexedName = Data::IndexedName("NOT_IN_THE_LIST42", allowedTypes, false);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, nameAndTypeListConstructionWithAllowOthers)
|
||||
{
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName("NOT_IN_THE_LIST42", allowedTypes, true);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "NOT_IN_THE_LIST");
|
||||
EXPECT_EQ(indexedName.getIndex(), 42);
|
||||
}
|
||||
|
||||
// Check that the same memory location is used for two names that are not in the allowedTypes list
|
||||
TEST_F(IndexedNameTest, nameAndTypeListConstructionReusedMemoryCheck)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName1 = Data::IndexedName("NOT_IN_THE_LIST42", allowedTypes, true);
|
||||
auto indexedName2 = Data::IndexedName("NOT_IN_THE_LIST43", allowedTypes, true);
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName1.getType(), indexedName2.getType());
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, byteArrayConstruction)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray qba{"EDGE42"};
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName(qba);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "EDGE");
|
||||
EXPECT_EQ(indexedName.getIndex(), 42);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, copyConstruction)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act
|
||||
auto indexedNameCopy {indexedName};
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName, indexedNameCopy);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, streamInsertionOperator)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
std::stringstream ss;
|
||||
|
||||
// Act
|
||||
ss << indexedName;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(ss.str(), std::string{"EDGE42"});
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, compoundAssignmentOperator)
|
||||
{
|
||||
// NOTE: Only += is defined for this class
|
||||
|
||||
// Arrange
|
||||
constexpr int base{42};
|
||||
constexpr int offset{10};
|
||||
auto indexedName = Data::IndexedName("EDGE",base);
|
||||
|
||||
// Act
|
||||
indexedName += offset;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName.getIndex(), 52);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, preincrementOperator)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act
|
||||
++indexedName;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName.getIndex(), 43);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, predecrementOperator)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act
|
||||
--indexedName;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName.getIndex(), 41);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, comparisonOperators)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName1 = Data::IndexedName("EDGE42");
|
||||
auto indexedName2 = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName1.compare(indexedName2), 0);
|
||||
EXPECT_TRUE(indexedName1 == indexedName2);
|
||||
EXPECT_FALSE(indexedName1 != indexedName2);
|
||||
EXPECT_FALSE(indexedName1 < indexedName2);
|
||||
|
||||
// Arrange
|
||||
auto indexedName3 = Data::IndexedName("EDGE42");
|
||||
auto indexedName4 = Data::IndexedName("FACE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName3.compare(indexedName4), -1);
|
||||
EXPECT_FALSE(indexedName3 == indexedName4);
|
||||
EXPECT_TRUE(indexedName3 != indexedName4);
|
||||
EXPECT_TRUE(indexedName3 < indexedName4);
|
||||
|
||||
// Arrange
|
||||
auto indexedName5 = Data::IndexedName("FACE42");
|
||||
auto indexedName6 = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName5.compare(indexedName6), 1);
|
||||
EXPECT_FALSE(indexedName5 == indexedName6);
|
||||
EXPECT_TRUE(indexedName5 != indexedName6);
|
||||
EXPECT_FALSE(indexedName5 < indexedName6);
|
||||
|
||||
// Arrange
|
||||
auto indexedName7 = Data::IndexedName("EDGE41");
|
||||
auto indexedName8 = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName7.compare(indexedName8), -1);
|
||||
EXPECT_FALSE(indexedName7 == indexedName8);
|
||||
EXPECT_TRUE(indexedName7 != indexedName8);
|
||||
EXPECT_TRUE(indexedName7 < indexedName8);
|
||||
|
||||
// Arrange
|
||||
auto indexedName9 = Data::IndexedName("EDGE43");
|
||||
auto indexedName10 = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName9.compare(indexedName10), 1);
|
||||
EXPECT_FALSE(indexedName9 == indexedName10);
|
||||
EXPECT_TRUE(indexedName9 != indexedName10);
|
||||
EXPECT_FALSE(indexedName9 < indexedName10);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, subscriptOperator)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(indexedName[0], 'E');
|
||||
EXPECT_EQ(indexedName[1], 'D');
|
||||
EXPECT_EQ(indexedName[2], 'G');
|
||||
EXPECT_EQ(indexedName[3], 'E');
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, getType)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "EDGE");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, setIndex)
|
||||
{
|
||||
// Arrange
|
||||
auto indexedName = Data::IndexedName("EDGE42");
|
||||
EXPECT_EQ(indexedName.getIndex(), 42);
|
||||
|
||||
// Act
|
||||
indexedName.setIndex(1);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName.getIndex(), 1);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, isNullTrue)
|
||||
{
|
||||
// Arrange
|
||||
auto invalidNames = givenInvalidIndexedNames();
|
||||
for (const auto &name : invalidNames) {
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_TRUE(name.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, isNullFalse)
|
||||
{
|
||||
// Arrange
|
||||
auto validNames = givenValidIndexedNames();
|
||||
for (const auto &name : validNames) {
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_FALSE(name.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, booleanConversionFalse)
|
||||
{
|
||||
// Arrange
|
||||
auto invalidNames = givenInvalidIndexedNames();
|
||||
for (const auto &name : invalidNames) {
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_FALSE(static_cast<bool>(name));
|
||||
}
|
||||
|
||||
// Usage example:
|
||||
auto indexedName = Data::IndexedName(".EDGE",1); // Invalid name
|
||||
if (indexedName) {
|
||||
FAIL() << "indexedName as a boolean should have been false for an invalid name";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, booleanConversionTrue)
|
||||
{
|
||||
// Arrange
|
||||
auto validNames = givenValidIndexedNames();
|
||||
for (const auto& name : validNames) {
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_TRUE(static_cast<bool>(name));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, fromConst)
|
||||
{
|
||||
// Arrange
|
||||
const int testIndex {42};
|
||||
|
||||
// Act
|
||||
auto indexedName = Data::IndexedName::fromConst("TestName", testIndex);
|
||||
|
||||
// Assert
|
||||
EXPECT_STREQ(indexedName.getType(), "TestName");
|
||||
EXPECT_EQ(indexedName.getIndex(), testIndex);
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, appendToStringBufferEmptyBuffer)
|
||||
{
|
||||
// Arrange
|
||||
std::string bufferStartedEmpty;
|
||||
Data::IndexedName testName("TEST_NAME", 1);
|
||||
|
||||
// Act
|
||||
testName.appendToStringBuffer(bufferStartedEmpty);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(bufferStartedEmpty, "TEST_NAME1");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, appendToStringBufferNonEmptyBuffer)
|
||||
{
|
||||
// Arrange
|
||||
std::string bufferWithData {"DATA"};
|
||||
Data::IndexedName testName("TEST_NAME", 1);
|
||||
|
||||
// Act
|
||||
testName.appendToStringBuffer(bufferWithData);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(bufferWithData, "DATATEST_NAME1");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, appendToStringBufferZeroIndex)
|
||||
{
|
||||
// Arrange
|
||||
std::string bufferStartedEmpty;
|
||||
Data::IndexedName testName("TEST_NAME", 0);
|
||||
|
||||
// Act
|
||||
testName.appendToStringBuffer(bufferStartedEmpty);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(bufferStartedEmpty, "TEST_NAME");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, toString)
|
||||
{
|
||||
// Arrange
|
||||
Data::IndexedName testName("TEST_NAME", 1);
|
||||
|
||||
// Act
|
||||
auto result = testName.toString();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(result, "TEST_NAME1");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, toStringNoIndex)
|
||||
{
|
||||
// Arrange
|
||||
Data::IndexedName testName("TEST_NAME", 0);
|
||||
|
||||
// Act
|
||||
auto result = testName.toString();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(result, "TEST_NAME");
|
||||
}
|
||||
|
||||
TEST_F(IndexedNameTest, assignmentOperator)
|
||||
{
|
||||
// Arrange
|
||||
const int testIndex1 {42};
|
||||
const int testIndex2 {24};
|
||||
auto indexedName1 = Data::IndexedName::fromConst("TestName", testIndex1);
|
||||
auto indexedName2 = Data::IndexedName::fromConst("TestName2", testIndex2);
|
||||
EXPECT_NE(indexedName1, indexedName2); // Ensure the test is set up correctly
|
||||
|
||||
// Act
|
||||
indexedName1 = indexedName2;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(indexedName1, indexedName2);
|
||||
}
|
||||
|
||||
|
||||
class ByteArrayTest : public ::testing::Test {
|
||||
protected:
|
||||
// void SetUp() override {}
|
||||
|
||||
// void TearDown() override {}
|
||||
};
|
||||
|
||||
TEST_F(ByteArrayTest, QByteArrayConstruction)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA("Data in a QByteArray");
|
||||
|
||||
// Act
|
||||
Data::ByteArray testByteArray (testQBA);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(testQBA, testByteArray.bytes);
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, CopyConstruction)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA("Data in a QByteArray");
|
||||
Data::ByteArray originalByteArray (testQBA);
|
||||
|
||||
// Act
|
||||
// NOLINTNEXTLINE performance-unnecessary-copy-initialization
|
||||
Data::ByteArray copiedByteArray (originalByteArray);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(originalByteArray, copiedByteArray);
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, MoveConstruction)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA("Data in a QByteArray");
|
||||
Data::ByteArray originalByteArray (testQBA);
|
||||
const auto *originalDataLocation = originalByteArray.bytes.constData();
|
||||
|
||||
// Act
|
||||
Data::ByteArray copiedByteArray (std::move(originalByteArray));
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(testQBA, copiedByteArray.bytes);
|
||||
EXPECT_EQ(originalDataLocation, copiedByteArray.bytes.constData());
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, ensureUnshared)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA("Data in a QByteArray");
|
||||
Data::ByteArray originalByteArray (testQBA);
|
||||
const auto *originalDataLocation = originalByteArray.bytes.constData();
|
||||
Data::ByteArray copiedByteArray (originalByteArray);
|
||||
|
||||
// Act
|
||||
copiedByteArray.ensureUnshared();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(testQBA, copiedByteArray.bytes);
|
||||
EXPECT_NE(originalDataLocation, copiedByteArray.bytes.constData());
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, equalityOperator)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA1("Data in a QByteArray");
|
||||
QByteArray testQBA2("Data in a QByteArray");
|
||||
QByteArray testQBA3("Not the same data in a QByteArray");
|
||||
Data::ByteArray byteArray1 (testQBA1);
|
||||
Data::ByteArray byteArray2 (testQBA2);
|
||||
Data::ByteArray byteArray3 (testQBA3);
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_TRUE(byteArray1 == byteArray2);
|
||||
EXPECT_FALSE(byteArray1 == byteArray3);
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, assignmentOperator)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA1("Data in a QByteArray");
|
||||
QByteArray testQBA2("Different data in a QByteArray");
|
||||
Data::ByteArray originalByteArray (testQBA1);
|
||||
Data::ByteArray newByteArray (testQBA2);
|
||||
ASSERT_FALSE(originalByteArray == newByteArray);
|
||||
|
||||
// Act
|
||||
newByteArray = originalByteArray;
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(originalByteArray == newByteArray);
|
||||
}
|
||||
|
||||
TEST_F(ByteArrayTest, moveAssignmentOperator)
|
||||
{
|
||||
// Arrange
|
||||
QByteArray testQBA1("Data in a QByteArray");
|
||||
QByteArray testQBA2("Different data in a QByteArray");
|
||||
Data::ByteArray originalByteArray (testQBA1);
|
||||
const auto *originalByteArrayLocation = originalByteArray.bytes.constData();
|
||||
Data::ByteArray newByteArray (testQBA2);
|
||||
ASSERT_FALSE(originalByteArray == newByteArray);
|
||||
|
||||
// Act
|
||||
newByteArray = std::move(originalByteArray);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(originalByteArrayLocation, newByteArray.bytes.constData());
|
||||
}
|
||||
|
||||
// NOLINTEND(readability-magic-numbers)
|
||||
@@ -1,7 +1,5 @@
|
||||
target_sources(
|
||||
Tests_run
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test1.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test2.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fmt.cpp
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user