"Professional CMake" book suggest the following: "Targets should build successfully with or without compiler support for precompiled headers. It should be considered an optimization, not a requirement. In particular, do not explicitly include a precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically generated precompile header on the compiler command line instead. This is more portable across the major compilers and is likely to be easier to maintain. It will also avoid warnings being generated from certain code checking tools like iwyu (include what you use)." Therefore, removed the "#include <PreCompiled.h>" from sources, also there is no need for the "#ifdef _PreComp_" anymore
639 lines
21 KiB
C++
639 lines
21 KiB
C++
/****************************************************************************
|
|
* Copyright (c) 2018 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
|
|
* *
|
|
* 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 <boost/algorithm/string/predicate.hpp>
|
|
#include <QColorDialog>
|
|
#include <sstream>
|
|
|
|
|
|
#include <App/ElementNamingUtils.h>
|
|
#include <App/Document.h>
|
|
|
|
#include "TaskElementColors.h"
|
|
#include "ui_TaskElementColors.h"
|
|
#include "Application.h"
|
|
#include "BitmapFactory.h"
|
|
#include "Command.h"
|
|
#include "Control.h"
|
|
#include "Document.h"
|
|
#include "FileDialog.h"
|
|
#include "Selection.h"
|
|
#include "SelectionObject.h"
|
|
#include "ViewProviderLink.h"
|
|
|
|
|
|
FC_LOG_LEVEL_INIT("Gui", true, true)
|
|
|
|
using namespace Gui;
|
|
namespace sp = std::placeholders;
|
|
|
|
class ElementColors::Private: public Gui::SelectionGate
|
|
{
|
|
public:
|
|
using Connection = boost::signals2::connection;
|
|
std::unique_ptr<Ui_TaskElementColors> ui;
|
|
ViewProviderDocumentObject* vp;
|
|
ViewProviderDocumentObject* vpParent;
|
|
Document* vpDoc;
|
|
std::map<std::string, QListWidgetItem*> elements;
|
|
std::vector<QListWidgetItem*> items;
|
|
std::string hiddenSub;
|
|
Connection connectDelDoc;
|
|
Connection connectDelObj;
|
|
QPixmap px;
|
|
bool busy;
|
|
long onTopMode;
|
|
bool touched;
|
|
|
|
std::string editDoc;
|
|
std::string editObj;
|
|
std::string editSub;
|
|
std::string editElement;
|
|
|
|
explicit Private(ViewProviderDocumentObject* vp, const char* element = "")
|
|
: ui(new Ui_TaskElementColors())
|
|
, vp(vp)
|
|
, vpParent(vp)
|
|
, vpDoc(vp->getDocument())
|
|
, editElement(element)
|
|
{
|
|
auto doc = Application::Instance->editDocument();
|
|
if (doc) {
|
|
auto editVp = doc->getInEdit(&vpParent, &editSub);
|
|
if (editVp == vp) {
|
|
auto obj = vpParent->getObject();
|
|
editDoc = obj->getDocument()->getName();
|
|
editObj = obj->getNameInDocument();
|
|
editSub = Data::noElementName(editSub.c_str());
|
|
}
|
|
}
|
|
if (editDoc.empty()) {
|
|
vpParent = vp;
|
|
editDoc = vp->getObject()->getDocument()->getName();
|
|
editObj = vp->getObject()->getNameInDocument();
|
|
editSub.clear();
|
|
}
|
|
onTopMode = vpParent->OnTopWhenSelected.getValue();
|
|
busy = false;
|
|
touched = false;
|
|
int w = QApplication::style()->standardPixmap(QStyle::SP_DirClosedIcon).width();
|
|
px = QPixmap(w, w);
|
|
}
|
|
|
|
~Private() override
|
|
{
|
|
try {
|
|
vpParent->OnTopWhenSelected.setValue(onTopMode);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
e.reportException();
|
|
}
|
|
}
|
|
|
|
bool allow(App::Document* doc, App::DocumentObject* obj, const char* subname) override
|
|
{
|
|
if (editDoc != doc->getName() || editObj != obj->getNameInDocument()
|
|
|| !boost::starts_with(subname, editSub)) {
|
|
return false;
|
|
}
|
|
if (editElement.empty()) {
|
|
return true;
|
|
}
|
|
const char* dot = strrchr(subname, '.');
|
|
if (!dot) {
|
|
dot = subname;
|
|
}
|
|
else {
|
|
++dot;
|
|
}
|
|
return *dot == 0 || boost::starts_with(dot, editElement);
|
|
}
|
|
|
|
void populate()
|
|
{
|
|
int i = 0;
|
|
for (auto& v : vp->getElementColors()) {
|
|
addItem(i++, v.first.c_str());
|
|
}
|
|
apply();
|
|
}
|
|
|
|
void addItem(int i, const char* sub, bool push = false)
|
|
{
|
|
auto itE = elements.find(sub);
|
|
if (i < 0 && itE != elements.end()) {
|
|
if (push && !ViewProvider::hasHiddenMarker(sub)) {
|
|
items.push_back(itE->second);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const char* marker = ViewProvider::hasHiddenMarker(sub);
|
|
if (marker) {
|
|
auto icon = BitmapFactory().pixmap("Invisible");
|
|
auto item =
|
|
new QListWidgetItem(icon,
|
|
QString::fromLatin1(std::string(sub, marker - sub).c_str()),
|
|
ui->elementList);
|
|
item->setData(Qt::UserRole, QColor());
|
|
item->setData(Qt::UserRole + 1, QString::fromLatin1(sub));
|
|
elements.emplace(sub, item);
|
|
return;
|
|
}
|
|
|
|
for (auto& v : vp->getElementColors(sub)) {
|
|
auto it = elements.find(v.first);
|
|
if (it != elements.end()) {
|
|
if (push) {
|
|
items.push_back(it->second);
|
|
}
|
|
continue;
|
|
}
|
|
auto color = v.second;
|
|
QColor c;
|
|
c.setRgbF(color.r, color.g, color.b, color.a);
|
|
px.fill(c);
|
|
auto item = new QListWidgetItem(
|
|
QIcon(px),
|
|
QString::fromLatin1(Data::oldElementName(v.first.c_str()).c_str()),
|
|
ui->elementList);
|
|
item->setData(Qt::UserRole, c);
|
|
item->setData(Qt::UserRole + 1, QString::fromLatin1(v.first.c_str()));
|
|
if (push) {
|
|
items.push_back(item);
|
|
}
|
|
elements.emplace(v.first, item);
|
|
}
|
|
}
|
|
|
|
void apply()
|
|
{
|
|
std::map<std::string, Base::Color> info;
|
|
int count = ui->elementList->count();
|
|
for (int i = 0; i < count; ++i) {
|
|
auto item = ui->elementList->item(i);
|
|
auto col = item->data(Qt::UserRole).value<QColor>();
|
|
std::string sub = qPrintable(item->data(Qt::UserRole + 1).value<QString>());
|
|
info.emplace(sub, Base::Color::fromValue<QColor>(col));
|
|
}
|
|
if (!App::GetApplication().getActiveTransaction()) {
|
|
App::GetApplication().setActiveTransaction("Set colors");
|
|
}
|
|
vp->setElementColors(info);
|
|
touched = true;
|
|
Selection().clearSelection();
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
touched = false;
|
|
App::GetApplication().closeActiveTransaction(true);
|
|
Selection().clearSelection();
|
|
}
|
|
|
|
void accept()
|
|
{
|
|
if (touched && ui->recompute->isChecked()) {
|
|
auto obj = vp->getObject();
|
|
obj->touch();
|
|
obj->getDocument()->recompute(obj->getInListRecursive());
|
|
touched = false;
|
|
}
|
|
App::GetApplication().closeActiveTransaction();
|
|
}
|
|
|
|
void removeAll()
|
|
{
|
|
if (!elements.empty()) {
|
|
hiddenSub.clear();
|
|
ui->elementList->clear();
|
|
elements.clear();
|
|
apply();
|
|
}
|
|
}
|
|
|
|
void removeItems()
|
|
{
|
|
const auto items = ui->elementList->selectedItems();
|
|
for (auto item : items) {
|
|
std::string sub = qPrintable(item->data(Qt::UserRole + 1).value<QString>());
|
|
if (sub == hiddenSub) {
|
|
hiddenSub.clear();
|
|
}
|
|
elements.erase(sub);
|
|
delete item;
|
|
}
|
|
apply();
|
|
}
|
|
|
|
void editItem(QWidget* parent, QListWidgetItem* item)
|
|
{
|
|
std::string sub = qPrintable(item->data(Qt::UserRole + 1).value<QString>());
|
|
if (ViewProvider::hasHiddenMarker(sub.c_str())) {
|
|
return;
|
|
}
|
|
auto color = item->data(Qt::UserRole).value<QColor>();
|
|
QColorDialog cd(color, parent);
|
|
cd.setOption(QColorDialog::ShowAlphaChannel);
|
|
if (DialogOptions::dontUseNativeColorDialog()) {
|
|
cd.setOption(QColorDialog::DontUseNativeDialog);
|
|
}
|
|
if (cd.exec() != QDialog::Accepted || color == cd.selectedColor()) {
|
|
return;
|
|
}
|
|
color = cd.selectedColor();
|
|
item->setData(Qt::UserRole, color);
|
|
px.fill(color);
|
|
item->setIcon(QIcon(px));
|
|
apply();
|
|
}
|
|
|
|
void onSelectionChanged(const SelectionChanges& msg)
|
|
{
|
|
// no object selected in the combobox or no sub-element was selected
|
|
if (busy) {
|
|
return;
|
|
}
|
|
busy = true;
|
|
switch (msg.Type) {
|
|
case SelectionChanges::ClrSelection:
|
|
ui->elementList->clearSelection();
|
|
break;
|
|
case SelectionChanges::AddSelection:
|
|
case SelectionChanges::RmvSelection:
|
|
if (msg.pDocName && msg.pObjectName && msg.pSubName && msg.pSubName[0]) {
|
|
if (editDoc == msg.pDocName && editObj == msg.pObjectName
|
|
&& boost::starts_with(msg.pSubName, editSub)) {
|
|
const auto items = ui->elementList->findItems(
|
|
QString::fromLatin1(msg.pSubName - editSub.size()),
|
|
Qt::MatchExactly);
|
|
for (auto item : items) {
|
|
item->setSelected(msg.Type == SelectionChanges::AddSelection);
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
busy = false;
|
|
}
|
|
|
|
void onSelectionChanged()
|
|
{
|
|
if (busy) {
|
|
return;
|
|
}
|
|
busy = true;
|
|
std::map<std::string, int> sels;
|
|
for (auto& sel : Selection().getSelectionEx(editDoc.c_str(),
|
|
App::DocumentObject::getClassTypeId(),
|
|
ResolveMode::NoResolve)) {
|
|
if (sel.getFeatName() != editObj) {
|
|
continue;
|
|
}
|
|
for (auto& sub : sel.getSubNames()) {
|
|
if (boost::starts_with(sub, editSub)) {
|
|
sels[sub.c_str() + editSub.size()] = 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
const auto items = ui->elementList->selectedItems();
|
|
for (auto item : items) {
|
|
std::string name(qPrintable(item->data(Qt::UserRole + 1).value<QString>()));
|
|
if (ViewProvider::hasHiddenMarker(name.c_str())) {
|
|
continue;
|
|
}
|
|
auto& v = sels[name];
|
|
if (!v) {
|
|
Selection().addSelection(editDoc.c_str(),
|
|
editObj.c_str(),
|
|
(editSub + name).c_str());
|
|
}
|
|
v = 2;
|
|
}
|
|
for (auto& v : sels) {
|
|
if (v.second != 2) {
|
|
Selection().rmvSelection(editDoc.c_str(),
|
|
editObj.c_str(),
|
|
(editSub + v.first).c_str());
|
|
}
|
|
}
|
|
busy = false;
|
|
}
|
|
};
|
|
|
|
/* TRANSLATOR Gui::TaskElementColors */
|
|
|
|
ElementColors::ElementColors(ViewProviderDocumentObject* vp, bool noHide)
|
|
: d(new Private(vp))
|
|
{
|
|
d->ui->setupUi(this);
|
|
setupConnections();
|
|
|
|
d->ui->objectLabel->setText(QString::fromUtf8(vp->getObject()->Label.getValue()));
|
|
d->ui->elementList->setMouseTracking(true); // needed for itemEntered() to work
|
|
|
|
if (noHide) {
|
|
d->ui->hideSelection->setVisible(false);
|
|
}
|
|
|
|
ParameterGrp::handle hPart =
|
|
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
|
d->ui->recompute->setChecked(hPart->GetBool("ColorRecompute", true));
|
|
d->ui->onTop->setChecked(hPart->GetBool("ColorOnTop", true));
|
|
if (d->ui->onTop->isChecked()) {
|
|
d->vpParent->OnTopWhenSelected.setValue(3);
|
|
}
|
|
|
|
Selection().addSelectionGate(d, ResolveMode::NoResolve);
|
|
|
|
// NOLINTBEGIN
|
|
// clang-format off
|
|
d->connectDelDoc = Application::Instance->signalDeleteDocument.connect(std::bind
|
|
(&ElementColors::slotDeleteDocument, this, sp::_1));
|
|
d->connectDelObj = Application::Instance->signalDeletedObject.connect(std::bind
|
|
(&ElementColors::slotDeleteObject, this, sp::_1));
|
|
// clang-format on
|
|
// NOLINTEND
|
|
|
|
d->populate();
|
|
}
|
|
|
|
ElementColors::~ElementColors()
|
|
{
|
|
d->connectDelDoc.disconnect();
|
|
d->connectDelObj.disconnect();
|
|
Selection().rmvSelectionGate();
|
|
}
|
|
|
|
void ElementColors::setupConnections()
|
|
{
|
|
// clang-format off
|
|
connect(d->ui->removeSelection, &QPushButton::clicked,
|
|
this, &ElementColors::onRemoveSelectionClicked);
|
|
connect(d->ui->addSelection, &QPushButton::clicked,
|
|
this, &ElementColors::onAddSelectionClicked);
|
|
connect(d->ui->removeAll, &QPushButton::clicked,
|
|
this, &ElementColors::onRemoveAllClicked);
|
|
connect(d->ui->elementList, &QListWidget::itemDoubleClicked,
|
|
this, &ElementColors::onElementListItemDoubleClicked);
|
|
connect(d->ui->elementList, &QListWidget::itemSelectionChanged,
|
|
this, &ElementColors::onElementListItemSelectionChanged);
|
|
connect(d->ui->elementList, &QListWidget::itemEntered,
|
|
this, &ElementColors::onElementListItemEntered);
|
|
connect(d->ui->recompute, &QCheckBox::clicked,
|
|
this, &ElementColors::onRecomputeClicked);
|
|
connect(d->ui->onTop, &QCheckBox::clicked,
|
|
this, &ElementColors::onTopClicked);
|
|
connect(d->ui->hideSelection, &QPushButton::clicked,
|
|
this, &ElementColors::onHideSelectionClicked);
|
|
connect(d->ui->boxSelect, &QPushButton::clicked,
|
|
this, &ElementColors::onBoxSelectClicked);
|
|
// clang-format on
|
|
}
|
|
|
|
void ElementColors::onRecomputeClicked(bool checked)
|
|
{
|
|
ParameterGrp::handle hPart =
|
|
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
|
hPart->SetBool("ColorRecompute", checked);
|
|
}
|
|
|
|
void ElementColors::onTopClicked(bool checked)
|
|
{
|
|
ParameterGrp::handle hPart =
|
|
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
|
hPart->SetBool("ColorOnTop", checked);
|
|
d->vpParent->OnTopWhenSelected.setValue(checked ? 3 : d->onTopMode);
|
|
}
|
|
|
|
void ElementColors::slotDeleteDocument(const Document& Doc)
|
|
{
|
|
if (d->vpDoc == &Doc || d->editDoc == Doc.getDocument()->getName()) {
|
|
Control().closeDialog();
|
|
}
|
|
}
|
|
|
|
void ElementColors::slotDeleteObject(const ViewProvider& obj)
|
|
{
|
|
if (d->vp == &obj) {
|
|
Control().closeDialog();
|
|
}
|
|
}
|
|
|
|
void ElementColors::onRemoveSelectionClicked()
|
|
{
|
|
d->removeItems();
|
|
}
|
|
|
|
void ElementColors::onBoxSelectClicked()
|
|
{
|
|
auto cmd = Application::Instance->commandManager().getCommandByName("Std_BoxElementSelection");
|
|
if (cmd) {
|
|
cmd->invoke(0);
|
|
}
|
|
}
|
|
|
|
void ElementColors::onHideSelectionClicked()
|
|
{
|
|
auto sels = Selection().getSelectionEx(d->editDoc.c_str(),
|
|
App::DocumentObject::getClassTypeId(),
|
|
ResolveMode::NoResolve);
|
|
for (auto& sel : sels) {
|
|
if (d->editObj != sel.getFeatName()) {
|
|
continue;
|
|
}
|
|
const auto& subs = sel.getSubNames();
|
|
if (!subs.empty()) {
|
|
for (auto& sub : subs) {
|
|
if (boost::starts_with(sub, d->editSub)) {
|
|
auto name = Data::noElementName(sub.c_str() + d->editSub.size());
|
|
name += ViewProvider::hiddenMarker();
|
|
d->addItem(-1, name.c_str());
|
|
}
|
|
}
|
|
d->apply();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ElementColors::onAddSelectionClicked()
|
|
{
|
|
auto sels = Selection().getSelectionEx(d->editDoc.c_str(),
|
|
App::DocumentObject::getClassTypeId(),
|
|
ResolveMode::NoResolve);
|
|
d->items.clear();
|
|
if (sels.empty()) {
|
|
d->addItem(-1, "Face", true);
|
|
}
|
|
else {
|
|
for (auto& sel : sels) {
|
|
if (d->editObj != sel.getFeatName()) {
|
|
continue;
|
|
}
|
|
const auto& subs = sel.getSubNames();
|
|
if (subs.empty()) {
|
|
d->addItem(-1, "Face", true);
|
|
break;
|
|
}
|
|
for (auto& sub : subs) {
|
|
if (boost::starts_with(sub, d->editSub)) {
|
|
d->addItem(-1, sub.c_str() + d->editSub.size(), true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!d->items.empty()) {
|
|
auto color = d->items.front()->data(Qt::UserRole).value<QColor>();
|
|
QColorDialog cd(color, this);
|
|
cd.setOption(QColorDialog::ShowAlphaChannel);
|
|
if (DialogOptions::dontUseNativeColorDialog()) {
|
|
cd.setOption(QColorDialog::DontUseNativeDialog);
|
|
}
|
|
if (cd.exec() != QDialog::Accepted) {
|
|
return;
|
|
}
|
|
color = cd.selectedColor();
|
|
for (auto item : d->items) {
|
|
item->setData(Qt::UserRole, color);
|
|
d->px.fill(color);
|
|
item->setIcon(QIcon(d->px));
|
|
}
|
|
d->apply();
|
|
}
|
|
}
|
|
|
|
void ElementColors::onRemoveAllClicked()
|
|
{
|
|
d->removeAll();
|
|
}
|
|
|
|
bool ElementColors::accept()
|
|
{
|
|
d->accept();
|
|
Application::Instance->setEditDocument(nullptr);
|
|
return true;
|
|
}
|
|
|
|
bool ElementColors::reject()
|
|
{
|
|
d->reset();
|
|
Application::Instance->setEditDocument(nullptr);
|
|
return true;
|
|
}
|
|
|
|
void ElementColors::changeEvent(QEvent* e)
|
|
{
|
|
QWidget::changeEvent(e);
|
|
if (e->type() == QEvent::LanguageChange) {
|
|
d->ui->retranslateUi(this);
|
|
}
|
|
}
|
|
|
|
void ElementColors::leaveEvent(QEvent* e)
|
|
{
|
|
QWidget::leaveEvent(e);
|
|
Selection().rmvPreselect();
|
|
|
|
if (!d->hiddenSub.empty()) {
|
|
d->vp->partialRender({d->hiddenSub}, false);
|
|
d->hiddenSub.clear();
|
|
}
|
|
}
|
|
|
|
void ElementColors::onElementListItemEntered(QListWidgetItem* item)
|
|
{
|
|
std::string name(qPrintable(item->data(Qt::UserRole + 1).value<QString>()));
|
|
if (!d->hiddenSub.empty()) {
|
|
d->vp->partialRender({d->hiddenSub}, false);
|
|
d->hiddenSub.clear();
|
|
}
|
|
|
|
if (ViewProvider::hasHiddenMarker(name.c_str())) {
|
|
d->hiddenSub = name;
|
|
d->vp->partialRender({name}, true);
|
|
name.resize(name.size() - ViewProvider::hiddenMarker().size());
|
|
}
|
|
|
|
Selection().setPreselect(d->editDoc.c_str(),
|
|
d->editObj.c_str(),
|
|
(d->editSub + name).c_str(),
|
|
0,
|
|
0,
|
|
0,
|
|
d->ui->onTop->isChecked()
|
|
? Gui::SelectionChanges::MsgSource::TreeView
|
|
: Gui::SelectionChanges::MsgSource::Internal);
|
|
}
|
|
|
|
void ElementColors::onElementListItemSelectionChanged()
|
|
{
|
|
d->onSelectionChanged();
|
|
}
|
|
|
|
void ElementColors::onSelectionChanged(const SelectionChanges& msg)
|
|
{
|
|
d->onSelectionChanged(msg);
|
|
}
|
|
|
|
void ElementColors::onElementListItemDoubleClicked(QListWidgetItem* item)
|
|
{
|
|
d->editItem(this, item);
|
|
}
|
|
|
|
/* TRANSLATOR Gui::TaskElementColors */
|
|
|
|
TaskElementColors::TaskElementColors(ViewProviderDocumentObject* vp, bool noHide)
|
|
{
|
|
widget = new ElementColors(vp, noHide);
|
|
addTaskBox(widget);
|
|
}
|
|
|
|
TaskElementColors::~TaskElementColors() = default;
|
|
|
|
void TaskElementColors::open()
|
|
{}
|
|
|
|
void TaskElementColors::clicked(int id)
|
|
{
|
|
Q_UNUSED(id)
|
|
}
|
|
|
|
bool TaskElementColors::accept()
|
|
{
|
|
return widget->accept();
|
|
}
|
|
|
|
bool TaskElementColors::reject()
|
|
{
|
|
return widget->reject();
|
|
}
|
|
|
|
#include "moc_TaskElementColors.cpp"
|
|
|