/*************************************************************************** * Copyright (c) 2008 Werner Mayer * * * * 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 "PreCompiled.h" #include #include #include #include "Placement.h" #include "ui_Placement.h" #include #include #include #include #include #include #include #include #include #include using namespace Gui::Dialog; namespace Gui { namespace Dialog { class find_placement { public: find_placement(const std::string& name) : propertyname(name) { } bool operator () (const std::pair& elem) const { if (elem.first == propertyname) { // flag set that property is read-only or hidden if (elem.second->testStatus(App::Property::ReadOnly) || elem.second->testStatus(App::Property::Hidden)) return false; App::PropertyContainer* parent = elem.second->getContainer(); if (parent) { // flag set that property is read-only or hidden if (parent->isReadOnly(elem.second) || parent->isHidden(elem.second)) return false; } return elem.second->isDerivedFrom (Base::Type::fromName("App::PropertyPlacement")); } return false; } std::string propertyname; }; } } /* TRANSLATOR Gui::Dialog::Placement */ Placement::Placement(QWidget* parent, Qt::WFlags fl) : Gui::LocationDialog(parent, fl) { propertyName = "Placement"; // default name ui = new Ui_PlacementComp(this); ui->applyPlacementChange->hide(); ui->xPos->setUnit(Base::Unit::Length); ui->yPos->setUnit(Base::Unit::Length); ui->zPos->setUnit(Base::Unit::Length); ui->xCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->yCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->zCnt->setValue(Base::Quantity(0, Base::Unit::Length)); ui->angle->setUnit(Base::Unit::Angle); ui->yawAngle->setUnit(Base::Unit::Angle); ui->pitchAngle->setUnit(Base::Unit::Angle); ui->rollAngle->setUnit(Base::Unit::Angle); // create a signal mapper in order to have one slot to perform the change signalMapper = new QSignalMapper(this); connect(this, SIGNAL(directionChanged()), signalMapper, SLOT(map())); signalMapper->setMapping(this, 0); int id = 1; QList sb = this->findChildren(); for (QList::iterator it = sb.begin(); it != sb.end(); ++it) { connect(*it, SIGNAL(valueChanged(double)), signalMapper, SLOT(map())); signalMapper->setMapping(*it, id++); } connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(onPlacementChanged(int))); connectAct = Application::Instance->signalActiveDocument.connect (boost::bind(&Placement::slotActiveDocument, this, _1)); App::Document* activeDoc = App::GetApplication().getActiveDocument(); if (activeDoc) documents.insert(activeDoc->getName()); } Placement::~Placement() { connectAct.disconnect(); delete ui; } void Placement::showDefaultButtons(bool ok) { ui->oKButton->setVisible(ok); ui->closeButton->setVisible(ok); ui->applyButton->setVisible(ok); } void Placement::slotActiveDocument(const Gui::Document& doc) { documents.insert(doc.getDocument()->getName()); } QWidget* Placement::getInvalidInput() const { QList sb = this->findChildren(); for (QList::iterator it = sb.begin(); it != sb.end(); ++it) { if (!(*it)->hasValidInput()) return (*it); } return 0; } void Placement::revertTransformation() { for (std::set::iterator it = documents.begin(); it != documents.end(); ++it) { Gui::Document* document = Application::Instance->getDocument(it->c_str()); if (!document) continue; std::vector obj = document->getDocument()-> getObjectsOfType(App::DocumentObject::getClassTypeId()); if (!obj.empty()) { for (std::vector::iterator it=obj.begin();it!=obj.end();++it) { std::map props; (*it)->getPropertyMap(props); // search for the placement property std::map::iterator jt; jt = std::find_if(props.begin(), props.end(), find_placement(this->propertyName)); if (jt != props.end()) { Base::Placement cur = static_cast(jt->second)->getValue(); Gui::ViewProvider* vp = document->getViewProvider(*it); if (vp) vp->setTransformation(cur.toMatrix()); } } } } } void Placement::applyPlacement(const Base::Placement& p, bool incremental) { Gui::Document* document = Application::Instance->activeDocument(); if (!document) return; std::vector sel = Gui::Selection().getObjectsOfType (App::DocumentObject::getClassTypeId(), document->getDocument()->getName()); if (!sel.empty()) { // apply transformation only on view matrix not on placement property for (std::vector::iterator it=sel.begin();it!=sel.end();++it) { std::map props; (*it)->getPropertyMap(props); // search for the placement property std::map::iterator jt; jt = std::find_if(props.begin(), props.end(), find_placement(this->propertyName)); if (jt != props.end()) { Base::Placement cur = static_cast(jt->second)->getValue(); if (incremental) cur = p * cur; else cur = p; Gui::ViewProvider* vp = document->getViewProvider(*it); if (vp) vp->setTransformation(cur.toMatrix()); } } } else { Base::Console().Warning("No object selected.\n"); } } void Placement::applyPlacement(const QString& data, bool incremental) { Gui::Document* document = Application::Instance->activeDocument(); if (!document) return; std::vector sel = Gui::Selection().getObjectsOfType (App::DocumentObject::getClassTypeId(), document->getDocument()->getName()); if (!sel.empty()) { document->openCommand("Placement"); for (std::vector::iterator it=sel.begin();it!=sel.end();++it) { std::map props; (*it)->getPropertyMap(props); // search for the placement property std::map::iterator jt; jt = std::find_if(props.begin(), props.end(), find_placement(this->propertyName)); if (jt != props.end()) { QString cmd; if (incremental) cmd = QString::fromAscii( "App.getDocument(\"%1\").%2.Placement=%3.multiply(App.getDocument(\"%1\").%2.Placement)") .arg(QLatin1String((*it)->getDocument()->getName())) .arg(QLatin1String((*it)->getNameInDocument())) .arg(data); else { cmd = QString::fromAscii( "App.getDocument(\"%1\").%2.Placement=%3") .arg(QLatin1String((*it)->getDocument()->getName())) .arg(QLatin1String((*it)->getNameInDocument())) .arg(data); } Application::Instance->runPythonCode((const char*)cmd.toLatin1()); } } document->commitCommand(); try { document->getDocument()->recompute(); } catch (...) { } } else { Base::Console().Warning("No object selected.\n"); } } void Placement::onPlacementChanged(int) { // If there are listeners to the 'placementChanged' signal we rely // on that the listener updates any placement. If no listeners // are connected the placement is applied to all selected objects // automatically. bool incr = ui->applyIncrementalPlacement->isChecked(); Base::Placement plm = this->getPlacement(); applyPlacement(plm, incr); QVariant data = QVariant::fromValue(plm); /*emit*/ placementChanged(data, incr, false); } void Placement::on_applyIncrementalPlacement_toggled(bool on) { if (on) { this->ref = getPlacementData(); on_resetButton_clicked(); } else { Base::Placement p = getPlacementData(); p = p * this->ref; setPlacementData(p); onPlacementChanged(0); } } void Placement::reject() { Base::Placement plm; applyPlacement(plm, true); QVariant data = QVariant::fromValue(plm); /*emit*/ placementChanged(data, true, false); revertTransformation(); QDialog::reject(); } void Placement::accept() { if (onApply()) { revertTransformation(); QDialog::accept(); } } void Placement::on_applyButton_clicked() { onApply(); } bool Placement::onApply() { //only process things when we have valid inputs! QWidget* input = getInvalidInput(); if (input) { input->setFocus(); QMessageBox msg(this); msg.setWindowTitle(tr("Incorrect quantity")); msg.setIcon(QMessageBox::Critical); msg.setText(tr("There are input fields with incorrect input, please ensure valid placement values!")); msg.exec(); return false; } // If there are listeners to the 'placementChanged' signal we rely // on that the listener updates any placement. If no listeners // are connected the placement is applied to all selected objects // automatically. bool incr = ui->applyIncrementalPlacement->isChecked(); Base::Placement plm = this->getPlacement(); applyPlacement(getPlacementString(), incr); QVariant data = QVariant::fromValue(plm); /*emit*/ placementChanged(data, incr, true); if (ui->applyIncrementalPlacement->isChecked()) { QList sb = this->findChildren(); for (QList::iterator it = sb.begin(); it != sb.end(); ++it) { (*it)->blockSignals(true); (*it)->setValue(0); (*it)->blockSignals(false); } } return true; } void Placement::on_resetButton_clicked() { QList sb = this->findChildren(); for (QList::iterator it = sb.begin(); it != sb.end(); ++it) { (*it)->blockSignals(true); (*it)->setValue(0); (*it)->blockSignals(false); } onPlacementChanged(0); } void Placement::directionActivated(int index) { if (ui->directionActivated(this, index)) { /*emit*/ directionChanged(); } } Base::Vector3d Placement::getDirection() const { return ui->getDirection(); } void Placement::setPlacement(const Base::Placement& p) { setPlacementData(p); } void Placement::setPlacementData(const Base::Placement& p) { signalMapper->blockSignals(true); ui->xPos->setValue(Base::Quantity(p.getPosition().x, Base::Unit::Length)); ui->yPos->setValue(Base::Quantity(p.getPosition().y, Base::Unit::Length)); ui->zPos->setValue(Base::Quantity(p.getPosition().z, Base::Unit::Length)); double Y,P,R; p.getRotation().getYawPitchRoll(Y,P,R); ui->yawAngle->setValue(Base::Quantity(Y, Base::Unit::Angle)); ui->pitchAngle->setValue(Base::Quantity(P, Base::Unit::Angle)); ui->rollAngle->setValue(Base::Quantity(R, Base::Unit::Angle)); // check if the user-defined direction is already there bool newitem = true; double angle; Base::Vector3d axis; p.getRotation().getValue(axis, angle); ui->angle->setValue(Base::Quantity(angle*180.0/D_PI, Base::Unit::Angle)); Base::Vector3d dir(axis.x,axis.y,axis.z); for (int i=0; idirection->count()-1; i++) { QVariant data = ui->direction->itemData (i); if (data.canConvert()) { const Base::Vector3d val = data.value(); if (val == dir) { ui->direction->setCurrentIndex(i); newitem = false; break; } } } if (newitem) { // add a new item before the very last item QString display = QString::fromAscii("(%1,%2,%3)") .arg(dir.x) .arg(dir.y) .arg(dir.z); ui->direction->insertItem(ui->direction->count()-1, display, QVariant::fromValue(dir)); ui->direction->setCurrentIndex(ui->direction->count()-2); } signalMapper->blockSignals(false); } Base::Placement Placement::getPlacement() const { Base::Placement p = getPlacementData(); return p; } Base::Placement Placement::getPlacementData() const { int index = ui->rotationInput->currentIndex(); Base::Rotation rot; Base::Vector3d pos; Base::Vector3d cnt; pos = Base::Vector3d(ui->xPos->value().getValue(),ui->yPos->value().getValue(),ui->zPos->value().getValue()); cnt = Base::Vector3d(ui->xCnt->value().getValue(),ui->yCnt->value().getValue(),ui->zCnt->value().getValue()); if (index == 0) { Base::Vector3d dir = getDirection(); rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),Base::toRadians(ui->angle->value().getValue())); } else if (index == 1) { // Euler angles (XY'Z'') rot.setYawPitchRoll( ui->yawAngle->value().getValue(), ui->pitchAngle->value().getValue(), ui->rollAngle->value().getValue()); } Base::Placement p(pos, rot, cnt); return p; } QString Placement::getPlacementString() const { QString cmd; int index = ui->rotationInput->currentIndex(); if (index == 0) { Base::Vector3d dir = getDirection(); cmd = QString::fromAscii( "App.Placement(App.Vector(%1,%2,%3), App.Rotation(App.Vector(%4,%5,%6),%7), App.Vector(%8,%9,%10))") .arg(ui->xPos->value().getValue()) .arg(ui->yPos->value().getValue()) .arg(ui->zPos->value().getValue()) .arg(dir.x) .arg(dir.y) .arg(dir.z) .arg(ui->angle->value().getValue()) .arg(ui->xCnt->value().getValue()) .arg(ui->yCnt->value().getValue()) .arg(ui->zCnt->value().getValue()); } else if (index == 1) { cmd = QString::fromAscii( "App.Placement(App.Vector(%1,%2,%3), App.Rotation(%4,%5,%6), App.Vector(%7,%8,%9))") .arg(ui->xPos->value().getValue()) .arg(ui->yPos->value().getValue()) .arg(ui->zPos->value().getValue()) .arg(ui->yawAngle->value().getValue()) .arg(ui->pitchAngle->value().getValue()) .arg(ui->rollAngle->value().getValue()) .arg(ui->xCnt->value().getValue()) .arg(ui->yCnt->value().getValue()) .arg(ui->zCnt->value().getValue()); } return cmd; } void Placement::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { ui->retranslate(this); } else { QDialog::changeEvent(e); } } // ---------------------------------------------- /* TRANSLATOR Gui::Dialog::DockablePlacement */ DockablePlacement::DockablePlacement(QWidget* parent, Qt::WFlags fl) : Placement(parent, fl) { Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Placement"), this, Qt::BottomDockWidgetArea); dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); dw->show(); } DockablePlacement::~DockablePlacement() { } void DockablePlacement::accept() { // closes the dock window Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); pDockMgr->removeDockWindow(this); Placement::accept(); } void DockablePlacement::reject() { // closes the dock window Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); pDockMgr->removeDockWindow(this); Placement::reject(); } // ---------------------------------------------- /* TRANSLATOR Gui::Dialog::TaskPlacement */ TaskPlacement::TaskPlacement() { this->setButtonPosition(TaskPlacement::South); widget = new Placement(); widget->showDefaultButtons(false); taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(),true, 0); taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); connect(widget, SIGNAL(placementChanged(const QVariant &, bool, bool)), this, SLOT(slotPlacementChanged(const QVariant &, bool, bool))); } TaskPlacement::~TaskPlacement() { // automatically deleted in the sub-class } void TaskPlacement::setPropertyName(const QString& name) { widget->propertyName = (const char*)name.toLatin1(); } QDialogButtonBox::StandardButtons TaskPlacement::getStandardButtons() const { return QDialogButtonBox::Ok| QDialogButtonBox::Cancel| QDialogButtonBox::Apply; } void TaskPlacement::setPlacement(const Base::Placement& p) { widget->setPlacement(p); } void TaskPlacement::slotPlacementChanged(const QVariant & p, bool incr, bool data) { /*emit*/ placementChanged(p, incr, data); } bool TaskPlacement::accept() { widget->accept(); return (widget->result() == QDialog::Accepted); } bool TaskPlacement::reject() { widget->reject(); return (widget->result() == QDialog::Rejected); } void TaskPlacement::clicked(int id) { if (id == QDialogButtonBox::Apply) { widget->on_applyButton_clicked(); } } #include "moc_Placement.cpp"