diff --git a/CMakeLists.txt b/CMakeLists.txt index dda4867a28..6461a3e103 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,10 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_definitions(-Wno-write-strings) add_definitions(-Wno-deprecated) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + # get linker errors as soon as possible and not at runtime e.g. for modules + if(UNIX) + # SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") + endif(UNIX) endif(CMAKE_COMPILER_IS_GNUCXX) @@ -98,7 +102,14 @@ OPTION(FREECAD_BUILD_FEM "Build the FreeCAD FEM module, be aware, unfinished cod OPTION(FREECAD_BUILD_SANDBOX "Build the FreeCAD Sandbox module which is only for testing purposes" OFF) OPTION(FREECAD_BUILD_TEMPLATE "Build the FreeCAD template module which is only for testing purposes" OFF) OPTION(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF) -OPTION(USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) +OPTION(FREECAD_USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) +OPTION(FREECAD_USE_EXTERNAL_PIVY "Use system installed python-pivy instead of the bundled." OFF) + +# if this is set override some options +if (FREECAD_BUILD_DEBIAN) + set(FREECAD_USE_EXTERNAL_ZIPIOS ON) + set(FREECAD_USE_EXTERNAL_PIVY ON) +endif (FREECAD_BUILD_DEBIAN) # ============================================================================== diff --git a/src/3rdParty/CMakeLists.txt b/src/3rdParty/CMakeLists.txt index 5309c7a1fa..456c1f3253 100644 --- a/src/3rdParty/CMakeLists.txt +++ b/src/3rdParty/CMakeLists.txt @@ -20,7 +20,7 @@ elseif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE7X) #endif(MINGW) # applies for Unix, MinGW and Windows with custom LibPack elseif(FREECAD_BUILD_GUI) - if (NOT FREECAD_BUILD_DEBIAN) + if (NOT FREECAD_USE_EXTERNAL_PIVY) find_path(COIN_VERSION3 Inventor/scxml/ScXML.h ${COIN3D_INCLUDE_DIR}) if (COIN_VERSION3) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy-0.5) @@ -31,7 +31,7 @@ elseif(FREECAD_BUILD_GUI) add_subdirectory(Pivy) endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy) endif(COIN_VERSION3) - endif (NOT FREECAD_BUILD_DEBIAN) + endif (NOT FREECAD_USE_EXTERNAL_PIVY) endif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE6X) # For Windows we have all stuff in the LibPack diff --git a/src/3rdParty/Pivy-0.5/CMakeLists.txt b/src/3rdParty/Pivy-0.5/CMakeLists.txt index 3b71e24a17..fc810fb790 100644 --- a/src/3rdParty/Pivy-0.5/CMakeLists.txt +++ b/src/3rdParty/Pivy-0.5/CMakeLists.txt @@ -88,7 +88,7 @@ if(MSVC) debug ${PYTHON_DEBUG_LIBRARY} optimized ${PYTHON_LIBRARY}) else(MSVC) - set(CoinPy_LIBS + set(SoQtPy_LIBS ${SOQT_LIBRARIES} ${COIN3D_LIBRARY} ${PYTHON_LIBRARY}) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 30033380a1..17b99601fb 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1475,6 +1475,28 @@ void Application::ParseOptions(int ac, char ** av) //x11.add_options() // ("display", boost::program_options::value< string >(), "set the X-Server") // ; + //0000723: improper handling of qt specific comand line arguments + std::vector args; + bool merge=false; + for (int i=1; i args; copy(tok.begin(), tok.end(), back_inserter(args)); // Parse the file and store the options - store( boost::program_options::command_line_parser(ac, av). + store( boost::program_options::command_line_parser(args). options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm); } diff --git a/src/App/PropertyFile.cpp b/src/App/PropertyFile.cpp index 13599c88a6..52278ea991 100644 --- a/src/App/PropertyFile.cpp +++ b/src/App/PropertyFile.cpp @@ -257,13 +257,18 @@ void PropertyFileIncluded::setPyObject(PyObject *value) void PropertyFileIncluded::Save (Base::Writer &writer) const { if (writer.isForceXML()) { - writer.Stream() << writer.ind() << "" << endl; - - // write the file in the XML stream - if (!_cValue.empty()) + if (!_cValue.empty()) { + Base::FileInfo file(_cValue.c_str()); + writer.Stream() << writer.ind() << "" << std::endl; + // write the file in the XML stream + writer.incInd(); writer.insertBinFile(_cValue.c_str()); - - writer.Stream() << writer.ind() <<"" << endl ; + writer.decInd(); + writer.Stream() << writer.ind() <<"" << endl; + } + else + writer.Stream() << writer.ind() << "" << std::endl; } else { // instead initiate an extra file @@ -280,17 +285,30 @@ void PropertyFileIncluded::Save (Base::Writer &writer) const void PropertyFileIncluded::Restore(Base::XMLReader &reader) { reader.readElement("FileIncluded"); - string file (reader.getAttribute("file") ); - - if (!file.empty()) { - // initate a file read - reader.addFile(file.c_str(),this); - - // is in the document transient path - aboutToSetValue(); - _cValue = getDocTransientPath() + "/" + file; - _BaseFileName = file; - hasSetValue(); + if (reader.hasAttribute("file")) { + string file (reader.getAttribute("file") ); + if (!file.empty()) { + // initate a file read + reader.addFile(file.c_str(),this); + // is in the document transient path + aboutToSetValue(); + _cValue = getDocTransientPath() + "/" + file; + _BaseFileName = file; + hasSetValue(); + } + } + // section is XML stream + else if (reader.hasAttribute("data")) { + string file (reader.getAttribute("data") ); + if (!file.empty()) { + // is in the document transient path + aboutToSetValue(); + _cValue = getDocTransientPath() + "/" + file; + reader.readBinFile(_cValue.c_str()); + reader.readEndElement("FileIncluded"); + _BaseFileName = file; + hasSetValue(); + } } } diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index 6cc1058083..9ffb1d0336 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -130,12 +130,18 @@ void PropertyLink::Restore(Base::XMLReader &reader) assert(getContainer()->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId()) ); if (name != "") { - DocumentObject *pcObject = static_cast(getContainer())-> - getDocument()->getObject(name.c_str()); - if (!pcObject) + DocumentObject* parent = static_cast(getContainer()); + DocumentObject* object = parent->getDocument()->getObject(name.c_str()); + if (!object) { Base::Console().Warning("Lost link to '%s' while loading, maybe " "an object was not loaded correctly\n",name.c_str()); - setValue(pcObject); + } + else if (parent == object) { + Base::Console().Warning("Object '%s' links to itself, nullify it\n",name.c_str()); + object = 0; + } + + setValue(object); } else { setValue(0); diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index fdfb8a2b10..3cab433544 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -68,7 +68,7 @@ if(SWIG_FOUND) add_definitions(-DHAVE_SWIG=1) endif(SWIG_FOUND) -if (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) +if (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_USE_EXTERNAL_ZIPIOS) SET(zipios_SRCS ../zipios++/backbuffer.h ../zipios++/basicentry.cpp @@ -122,12 +122,7 @@ SET(zipios_SRCS ../zipios++/zipoutputstream.h ) SOURCE_GROUP("zipios" FILES ${zipios_SRCS}) -else (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) - set(FreeCADBase_LIBS - ${FreeCADBase_LIBS} - -lzipios - ) -endif (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++ AND NOT FREECAD_BUILD_DEBIAN) +endif () SET(pycxx_SRCS ../CXX/Config.hxx @@ -293,7 +288,7 @@ SET(FreeCADBase_SRCS ) # Use external zipios++ if specified. -if(USE_EXTERNAL_ZIPIOS) +if(FREECAD_USE_EXTERNAL_ZIPIOS) find_library(ZIPIOS_LIBRARY zipios) find_path(ZIPIOS_INCLUDES zipios++/zipios-config.h) if(ZIPIOS_LIBRARY) @@ -308,9 +303,9 @@ if(USE_EXTERNAL_ZIPIOS) else() message(FATAL_ERROR "Using external zipios++ was specified but was not found.") endif() -else(USE_EXTERNAL_ZIPIOS) +else(FREECAD_USE_EXTERNAL_ZIPIOS) list(APPEND FreeCADBase_SRCS ${zipios_SRCS}) -endif(USE_EXTERNAL_ZIPIOS) +endif(FREECAD_USE_EXTERNAL_ZIPIOS) if(MSVC) diff --git a/src/Base/Reader.cpp b/src/Base/Reader.cpp index db355f723f..6791b99f62 100644 --- a/src/Base/Reader.cpp +++ b/src/Base/Reader.cpp @@ -34,6 +34,7 @@ /// Here the FreeCAD includes sorted by Base,App,Gui...... #include "Reader.h" +#include "Base64.h" #include "Exception.h" #include "Persistence.h" #include "InputSource.h" @@ -79,6 +80,7 @@ Base::XMLReader::XMLReader(const char* FileName, std::istream& str) //parser->setFeature(XMLUni::fgXercesDynamic, true); parser->setContentHandler(this); + parser->setLexicalHandler(this); parser->setErrorHandler(this); try { @@ -127,7 +129,7 @@ long Base::XMLReader::getAttributeAsInteger(const char* AttrName) const if (pos != AttrMap.end()) return atol(pos->second.c_str()); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0; @@ -140,7 +142,7 @@ unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName) cons if (pos != AttrMap.end()) return strtoul(pos->second.c_str(),0,10); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0; @@ -153,7 +155,7 @@ double Base::XMLReader::getAttributeAsFloat (const char* AttrName) const if (pos != AttrMap.end()) return atof(pos->second.c_str()); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return 0.0; @@ -166,7 +168,7 @@ const char* Base::XMLReader::getAttribute (const char* AttrName) const if (pos != AttrMap.end()) return pos->second.c_str(); else - // wrong name, use hasAttribute if not shure! + // wrong name, use hasAttribute if not sure! assert(0); return ""; @@ -255,6 +257,22 @@ void Base::XMLReader::readCharacters(void) { } +void Base::XMLReader::readBinFile(const char* filename) +{ + Base::FileInfo fi(filename); + Base::ofstream to(fi, std::ios::out | std::ios::binary); + if (!to) + throw Base::Exception("XMLReader::readBinFile() Could not open file!"); + + bool ok; + do { + ok = read(); if (!ok) break; + } while (ReadType != EndCDATA); + + to << Base::base64_decode(Characters); + to.close(); +} + void Base::XMLReader::readFiles(zipios::ZipInputStream &zipstream) const { // It's possible that not all objects inside the document could be created, e.g. if a module @@ -370,15 +388,32 @@ void Base::XMLReader::endElement (const XMLCh* const /*uri*/, const XMLCh *cons ReadType = EndElement; } +void Base::XMLReader::startCDATA () +{ + ReadType = StartCDATA; +} +void Base::XMLReader::endCDATA () +{ + ReadType = EndCDATA; +} + +#if (XERCES_VERSION_MAJOR == 2) void Base::XMLReader::characters(const XMLCh* const chars, const unsigned int length) +#else +void Base::XMLReader::characters(const XMLCh* const chars, const XMLSize_t length) +#endif { Characters = StrX(chars).c_str(); ReadType = Chars; CharacterCount += length; } +#if (XERCES_VERSION_MAJOR == 2) void Base::XMLReader::ignorableWhitespace( const XMLCh* const /*chars*/, const unsigned int /*length*/) +#else +void Base::XMLReader::ignorableWhitespace( const XMLCh* const /*chars*/, const XMLSize_t /*length*/) +#endif { //fSpaceCount += length; } diff --git a/src/Base/Reader.h b/src/Base/Reader.h index 3ced7241b5..7e55575d42 100644 --- a/src/Base/Reader.h +++ b/src/Base/Reader.h @@ -131,6 +131,8 @@ public: void readEndElement(const char* ElementName=0); /// read until characters are found void readCharacters(void); + /// read binary file + void readBinFile(const char*); //@} /** @name Attribute handling */ @@ -171,8 +173,15 @@ protected: // ----------------------------------------------------------------------- virtual void startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const XERCES_CPP_NAMESPACE_QUALIFIER Attributes& attrs); virtual void endElement (const XMLCh* const uri, const XMLCh *const localname, const XMLCh *const qname); - virtual void characters (const XMLCh* const chars, const unsigned int length); + virtual void startCDATA (); + virtual void endCDATA (); +#if (XERCES_VERSION_MAJOR == 2) + virtual void characters (const XMLCh* const chars, const unsigned int length); virtual void ignorableWhitespace(const XMLCh* const chars, const unsigned int length); +#else + virtual void characters (const XMLCh* const chars, const XMLSize_t length); + virtual void ignorableWhitespace(const XMLCh* const chars, const XMLSize_t length); +#endif virtual void resetDocument(); @@ -198,7 +207,9 @@ protected: Chars, StartElement, StartEndElement, - EndElement + EndElement, + StartCDATA, + EndCDATA } ReadType; diff --git a/src/Base/Writer.cpp b/src/Base/Writer.cpp index 01eecef6b5..621893a8a3 100644 --- a/src/Base/Writer.cpp +++ b/src/Base/Writer.cpp @@ -65,32 +65,26 @@ void Writer::insertAsciiFile(const char* FileName) if (!from) throw Base::Exception("Writer::insertAsciiFile() Could not open file!"); - Stream() << "" << endl; + Stream() << "]]>" << endl; } void Writer::insertBinFile(const char* FileName) { Base::FileInfo fi(FileName); - Base::ifstream from(fi, std::ios::in | std::ios::binary); + Base::ifstream from(fi, std::ios::in | std::ios::binary | std::ios::ate); if (!from) throw Base::Exception("Writer::insertAsciiFile() Could not open file!"); - Stream() << " bytes(fileSize); + from.read((char*)&bytes[0], fileSize); + Stream() << Base::base64_encode(&bytes[0], fileSize); Stream() << "]]>" << endl; } diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index d62c36b4ef..a4c3542b39 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -341,6 +341,10 @@ Application::Application(bool GUIenabled) "workbenches."); Py::Module(module).setAttr(std::string("ActiveDocument"),Py::None()); + UiLoaderPy::init_type(); + Base::Interpreter().addType(UiLoaderPy::type_object(), + module,"UiLoader"); + //insert Selection module PyObject* pSelectionModule = Py_InitModule3("Selection", SelectionSingleton::Methods, "Selection module"); diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 74b3dbb64b..44a73d0c67 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -231,6 +231,8 @@ public: PYFUNCDEF_S(sActiveDocument); PYFUNCDEF_S(sGetDocument); + PYFUNCDEF_S(sDoCommand); + static PyMethodDef Methods[]; private: diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 9ca9ee3afe..6f0bfd5ba2 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -128,6 +128,9 @@ PyMethodDef Application::Methods[] = { {"getDocument", (PyCFunction) Application::sGetDocument, 1, "getDocument(string) -> object\n\n" "Get a document by its name"}, + {"doCommand", (PyCFunction) Application::sDoCommand, 1, + "doCommand(string) -> None\n\n" + "Prints the given string in the python console and runs it"}, {NULL, NULL} /* Sentinel */ }; @@ -359,10 +362,18 @@ PyObject* Application::sExport(PyObject * /*self*/, PyObject *args,PyObject * /* if (ext == QLatin1String("iv") || ext == QLatin1String("wrl") || ext == QLatin1String("vrml") || ext == QLatin1String("wrz") || ext == QLatin1String("svg") || ext == QLatin1String("idtf")) { - QString cmd = QString::fromLatin1( - "Gui.getDocument(\"%1\").ActiveView.dump(\"%2\")" - ).arg(QLatin1String(doc->getName())).arg(fi.absoluteFilePath()); - Base::Interpreter().runString(cmd.toUtf8()); + Gui::Document* gui_doc = Application::Instance->getDocument(doc); + std::list view3d = gui_doc->getMDIViewsOfType(View3DInventor::getClassTypeId()); + if (view3d.empty()) { + PyErr_SetString(PyExc_Exception, "Cannot export to SVG because document doesn't have a 3d view"); + return 0; + } + else { + QString cmd = QString::fromLatin1( + "Gui.getDocument(\"%1\").mdiViewsOfType('Gui::View3DInventor')[0].dump(\"%2\")" + ).arg(QLatin1String(doc->getName())).arg(fi.absoluteFilePath()); + Base::Interpreter().runString(cmd.toUtf8()); + } } else if (ext == QLatin1String("pdf")) { Gui::Document* gui_doc = Application::Instance->getDocument(doc); @@ -765,3 +776,12 @@ PyObject* Application::sRunCommand(PyObject * /*self*/, PyObject *args,PyObject return 0; } } + +PyObject* Application::sDoCommand(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +{ + char *pstr=0; + if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C + return NULL; // NULL triggers exception + Command::doCommand(Command::Doc,pstr); + return Py_None; +} diff --git a/src/Gui/CombiView.cpp b/src/Gui/CombiView.cpp index 1632c4a749..b7cdb1f7f9 100644 --- a/src/Gui/CombiView.cpp +++ b/src/Gui/CombiView.cpp @@ -64,6 +64,8 @@ CombiView::CombiView(Gui::Document* pcDocument, QWidget *parent) // tree widget tree = new TreeWidget(this); //tree->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + tree->setIndentation(hGrp->GetInt("Indentation", tree->indentation())); splitter->addWidget(tree); // property view diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 4a0880ffa8..0192d3e86e 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -748,7 +748,7 @@ void Document::exportObjects(const std::vector& obj, Base: << views.size() <<"\">" << std::endl; bool xml = writer.isForceXML(); - writer.setForceXML(true); + //writer.setForceXML(true); writer.incInd(); // indention for 'ViewProvider name' std::map::const_iterator jt; for (jt = views.begin(); jt != views.end(); ++jt) { diff --git a/src/Gui/MergeDocuments.cpp b/src/Gui/MergeDocuments.cpp index a1fc7172a7..52713fa474 100644 --- a/src/Gui/MergeDocuments.cpp +++ b/src/Gui/MergeDocuments.cpp @@ -122,16 +122,13 @@ MergeDocuments::importObjects(std::istream& input) reader.readElement("Object"); std::string type = reader.getAttribute("type"); std::string name = reader.getAttribute("name"); - std::string docn = name; - - // remove number from end to avoid lengthy names - size_t lastpos = docn.length()-1; - while (docn[lastpos] >= 48 && docn[lastpos] <= 57) - lastpos--; - docn = docn.substr(0, lastpos+1); try { - App::DocumentObject* o = appdoc->addObject(type.c_str(),docn.c_str()); + // Use name from XML as is and do NOT remove trailing digits because + // otherwise we may cause a dependency to itself + // Example: Object 'Cut001' references object 'Cut' and removing the + // digits we make an object 'Cut' referencing itself. + App::DocumentObject* o = appdoc->addObject(type.c_str(),name.c_str()); objs.push_back(o); // use this name for the later access because an object with // the given name may already exist @@ -235,4 +232,8 @@ void MergeDocuments::RestoreDocFile(Base::Reader & reader) } xmlReader.readEndElement("Document"); + + // In the file GuiDocument.xml new data files might be added + if (!xmlReader.getFilenames().empty()) + xmlReader.readFiles(static_cast(reader)); } diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index ba3672d8cd..48d27f3256 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -726,6 +726,8 @@ TreeDockWidget::TreeDockWidget(Gui::Document* pcDocument,QWidget *parent) setWindowTitle(tr("Tree view")); this->treeWidget = new TreeWidget(this); this->treeWidget->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + this->treeWidget->setIndentation(hGrp->GetInt("Indentation", this->treeWidget->indentation())); QGridLayout* pLayout = new QGridLayout(this); pLayout->setSpacing(0); @@ -830,9 +832,14 @@ void DocumentItem::slotChangeObject(const Gui::ViewProviderDocumentObject& view) children.insert(kt->second); QTreeWidgetItem* parent = kt->second->parent(); if (parent && parent != it->second) { - int index = parent->indexOfChild(kt->second); - parent->takeChild(index); - it->second->addChild(kt->second); + if (it->second != kt->second) { + int index = parent->indexOfChild(kt->second); + parent->takeChild(index); + it->second->addChild(kt->second); + } + else { + Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Object references to itself.\n"); + } } } else { diff --git a/src/Gui/WidgetFactory.cpp b/src/Gui/WidgetFactory.cpp index c074fcd7e3..fd734154f2 100644 --- a/src/Gui/WidgetFactory.cpp +++ b/src/Gui/WidgetFactory.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" +#include #include #include #include @@ -194,6 +195,79 @@ QWidget* UiLoader::createWidget(const QString & className, QWidget * parent, // ---------------------------------------------------- +PyObject *UiLoaderPy::PyMake(struct _typeobject *type, PyObject * args, PyObject * kwds) +{ + if (!PyArg_ParseTuple(args, "")) + return 0; + return new UiLoaderPy(); +} + +void UiLoaderPy::init_type() +{ + behaviors().name("UiLoader"); + behaviors().doc("UiLoader to create widgets"); + behaviors().type_object()->tp_new = &PyMake; + // you must have overwritten the virtual functions + behaviors().supportRepr(); + behaviors().supportGetattr(); + behaviors().supportSetattr(); + add_varargs_method("createWidget",&UiLoaderPy::createWidget,"createWidget()"); +} + +UiLoaderPy::UiLoaderPy() +{ +} + +UiLoaderPy::~UiLoaderPy() +{ +} + +Py::Object UiLoaderPy::repr() +{ + std::string s; + std::ostringstream s_out; + s_out << "Ui loader"; + return Py::String(s_out.str()); +} + +Py::Object UiLoaderPy::createWidget(const Py::Tuple& args) +{ + Py::Module sipmod(PyImport_AddModule((char*)"sip")); + Py::Module qtmod(PyImport_ImportModule((char*)"PyQt4.Qt")); + + // 1st argument + std::string className = (std::string)Py::String(args[0]); + + // 2nd argument + QWidget* parent = 0; + if (args.size() > 1) { + Py::Callable func = sipmod.getDict().getItem("unwrapinstance"); + Py::Tuple arguments(1); + arguments[0] = args[1]; //PyQt pointer + Py::Object result = func.apply(arguments); + void* ptr = PyLong_AsVoidPtr(result.ptr()); + QObject* object = reinterpret_cast(ptr); + if (object) + parent = qobject_cast(object); + } + + // 3rd argument + std::string objectName; + if (args.size() > 2) { + objectName = (std::string)Py::String(args[2]); + } + + QWidget* widget = loader.createWidget(QString::fromAscii(className.c_str()), parent, + QString::fromAscii(objectName.c_str())); + Py::Callable func = sipmod.getDict().getItem("wrapinstance"); + Py::Tuple arguments(2); + arguments[0] = Py::asObject(PyLong_FromVoidPtr(widget)); + arguments[1] = qtmod.getDict().getItem("QWidget"); + return func.apply(arguments); +} + +// ---------------------------------------------------- + WidgetFactorySupplier* WidgetFactorySupplier::_pcSingleton = 0L; WidgetFactorySupplier & WidgetFactorySupplier::instance() diff --git a/src/Gui/WidgetFactory.h b/src/Gui/WidgetFactory.h index 8f20c88643..705e4c36b1 100644 --- a/src/Gui/WidgetFactory.h +++ b/src/Gui/WidgetFactory.h @@ -32,6 +32,7 @@ #include "DlgPreferencesImp.h" #include "DlgCustomizeImp.h" #include "PropertyPage.h" +#include namespace Gui { namespace Dialog{ @@ -85,13 +86,33 @@ public: * Fore more details see the documentation to QWidgetFactory. */ QWidget* createWidget(const QString & className, QWidget * parent=0, - const QString& name =QString()); + const QString& name = QString()); private: QStringList cw; }; // -------------------------------------------------------------------- +class UiLoaderPy : public Py::PythonExtension +{ +public: + static void init_type(void); // announce properties and methods + + UiLoaderPy(); + ~UiLoaderPy(); + + Py::Object repr(); + Py::Object createWidget(const Py::Tuple&); + +private: + static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *); + +private: + UiLoader loader; +}; + +// -------------------------------------------------------------------- + /** * The WidgetProducer class is a value-based template class that provides * the ability to create widgets dynamically. diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 1db07b7cd9..d4e66e3bea 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -23,7 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include +# include # include # include # include @@ -104,6 +104,260 @@ void CommandIconView::onSelectionChanged(QListWidgetItem * item, QListWidgetItem // ------------------------------------------------------------------------------ +/* TRANSLATOR Gui::ActionSelector */ + +ActionSelector::ActionSelector(QWidget* parent) + : QWidget(parent) +{ + addButton = new QPushButton(this); + addButton->setObjectName(QLatin1String("addButton")); + addButton->setMinimumSize(QSize(30, 30)); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); + addButton->setIcon(icon); + gridLayout = new QGridLayout(this); + gridLayout->addWidget(addButton, 1, 1, 1, 1); + + spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem, 5, 1, 1, 1); + spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem1, 0, 1, 1, 1); + + removeButton = new QPushButton(this); + removeButton->setObjectName(QLatin1String("removeButton")); + removeButton->setMinimumSize(QSize(30, 30)); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); + removeButton->setIcon(icon1); + removeButton->setAutoDefault(true); + removeButton->setDefault(false); + + gridLayout->addWidget(removeButton, 2, 1, 1, 1); + + upButton = new QPushButton(this); + upButton->setObjectName(QLatin1String("upButton")); + upButton->setMinimumSize(QSize(30, 30)); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); + upButton->setIcon(icon3); + + gridLayout->addWidget(upButton, 3, 1, 1, 1); + + downButton = new QPushButton(this); + downButton->setObjectName(QLatin1String("downButton")); + downButton->setMinimumSize(QSize(30, 30)); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); + downButton->setIcon(icon2); + downButton->setAutoDefault(true); + + gridLayout->addWidget(downButton, 4, 1, 1, 1); + + vboxLayout = new QVBoxLayout(); + vboxLayout->setContentsMargins(0, 0, 0, 0); + labelAvailable = new QLabel(this); + vboxLayout->addWidget(labelAvailable); + + availableWidget = new QTreeWidget(this); + availableWidget->setObjectName(QLatin1String("availableTreeWidget")); + availableWidget->setRootIsDecorated(false); + availableWidget->setHeaderLabels(QStringList() << QString()); + availableWidget->header()->hide(); + vboxLayout->addWidget(availableWidget); + + gridLayout->addLayout(vboxLayout, 0, 0, 6, 1); + + vboxLayout1 = new QVBoxLayout(); + vboxLayout1->setContentsMargins(0, 0, 0, 0); + labelSelected = new QLabel(this); + vboxLayout1->addWidget(labelSelected); + + selectedWidget = new QTreeWidget(this); + selectedWidget->setObjectName(QLatin1String("selectedTreeWidget")); + selectedWidget->setRootIsDecorated(false); + selectedWidget->setHeaderLabels(QStringList() << QString()); + selectedWidget->header()->hide(); + vboxLayout1->addWidget(selectedWidget); + + gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1); + + addButton->setText(QString()); + removeButton->setText(QString()); + upButton->setText(QString()); + downButton->setText(QString()); + + connect(addButton, SIGNAL(clicked()), + this, SLOT(on_addButton_clicked()) ); + connect(removeButton, SIGNAL(clicked()), + this, SLOT(on_removeButton_clicked()) ); + connect(upButton, SIGNAL(clicked()), + this, SLOT(on_upButton_clicked()) ); + connect(downButton, SIGNAL(clicked()), + this, SLOT(on_downButton_clicked()) ); + connect(availableWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); + connect(selectedWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)) ); + connect(availableWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem *)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)) ); + connect(selectedWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem *)), + this, SLOT(onCurrentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)) ); + retranslateUi(); + setButtonsEnabled(); +} + +ActionSelector::~ActionSelector() +{ +} + +void ActionSelector::setSelectedLabel(const QString& label) +{ + labelSelected->setText(label); +} + +QString ActionSelector::selectedLabel() const +{ + return labelSelected->text(); +} + +void ActionSelector::setAvailableLabel(const QString& label) +{ + labelAvailable->setText(label); +} + +QString ActionSelector::availableLabel() const +{ + return labelAvailable->text(); +} + + +void ActionSelector::retranslateUi() +{ + labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); + labelSelected->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); + addButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Add", 0, QApplication::UnicodeUTF8)); + removeButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Remove", 0, QApplication::UnicodeUTF8)); + upButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move up", 0, QApplication::UnicodeUTF8)); + downButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move down", 0, QApplication::UnicodeUTF8)); +} + +void ActionSelector::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) { + retranslateUi(); + } + QWidget::changeEvent(event); +} + +void ActionSelector::keyPressEvent(QKeyEvent* event) +{ + if ((event->modifiers() & Qt::ControlModifier)) { + switch (event->key()) + { + case Qt::Key_Right: + on_addButton_clicked(); + break; + case Qt::Key_Left: + on_removeButton_clicked(); + break; + case Qt::Key_Up: + on_upButton_clicked(); + break; + case Qt::Key_Down: + on_downButton_clicked(); + break; + default: + event->ignore(); + return; + } + } +} + +void ActionSelector::setButtonsEnabled() +{ + addButton->setEnabled(availableWidget->indexOfTopLevelItem(availableWidget->currentItem()) > -1); + removeButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > -1); + upButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > 0); + downButton->setEnabled(selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) > -1 && + selectedWidget->indexOfTopLevelItem(selectedWidget->currentItem()) < selectedWidget->topLevelItemCount() - 1); +} + +void ActionSelector::onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*) +{ + setButtonsEnabled(); +} + +void ActionSelector::onItemDoubleClicked(QTreeWidgetItem * item, int column) +{ + QTreeWidget* treeWidget = item->treeWidget(); + if (treeWidget == availableWidget) { + int index = availableWidget->indexOfTopLevelItem(item); + item = availableWidget->takeTopLevelItem(index); + availableWidget->setCurrentItem(0); + selectedWidget->addTopLevelItem(item); + selectedWidget->setCurrentItem(item); + } + else if (treeWidget == selectedWidget) { + int index = selectedWidget->indexOfTopLevelItem(item); + item = selectedWidget->takeTopLevelItem(index); + selectedWidget->setCurrentItem(0); + availableWidget->addTopLevelItem(item); + availableWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_addButton_clicked() +{ + QTreeWidgetItem* item = availableWidget->currentItem(); + if (item) { + int index = availableWidget->indexOfTopLevelItem(item); + item = availableWidget->takeTopLevelItem(index); + availableWidget->setCurrentItem(0); + selectedWidget->addTopLevelItem(item); + selectedWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_removeButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item) { + int index = selectedWidget->indexOfTopLevelItem(item); + item = selectedWidget->takeTopLevelItem(index); + selectedWidget->setCurrentItem(0); + availableWidget->addTopLevelItem(item); + availableWidget->setCurrentItem(item); + } +} + +void ActionSelector::on_upButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item && selectedWidget->isItemSelected(item)) { + int index = selectedWidget->indexOfTopLevelItem(item); + if (index > 0) { + selectedWidget->takeTopLevelItem(index); + selectedWidget->insertTopLevelItem(index-1, item); + selectedWidget->setCurrentItem(item); + } + } +} + +void ActionSelector::on_downButton_clicked() +{ + QTreeWidgetItem* item = selectedWidget->currentItem(); + if (item && selectedWidget->isItemSelected(item)) { + int index = selectedWidget->indexOfTopLevelItem(item); + if (index < selectedWidget->topLevelItemCount()-1) { + selectedWidget->takeTopLevelItem(index); + selectedWidget->insertTopLevelItem(index+1, item); + selectedWidget->setCurrentItem(item); + } + } +} + +// ------------------------------------------------------------------------------ + /* TRANSLATOR Gui::AccelLineEdit */ /** @@ -628,10 +882,10 @@ StatusWidget::StatusWidget(QWidget* parent) label = new QLabel(this); label->setAlignment(Qt::AlignCenter); - QGridLayout* gridLayout = new QGridLayout(this); - gridLayout->setSpacing(6); - gridLayout->setMargin(9); - gridLayout->addWidget(label, 0, 0, 1, 1); + QGridLayout* gridLayout = new QGridLayout(this); + gridLayout->setSpacing(6); + gridLayout->setMargin(9); + gridLayout->addWidget(label, 0, 0, 1, 1); } StatusWidget::~StatusWidget() @@ -775,20 +1029,20 @@ void LabelEditor::setText(const QString& s) void LabelEditor::changeText() { QDialog dlg(this); - QVBoxLayout* hboxLayout = new QVBoxLayout(&dlg); - QDialogButtonBox* buttonBox = new QDialogButtonBox(&dlg); - buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close); + QVBoxLayout* hboxLayout = new QVBoxLayout(&dlg); + QDialogButtonBox* buttonBox = new QDialogButtonBox(&dlg); + buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Close); QPlainTextEdit *edit = new QPlainTextEdit(&dlg); edit->setPlainText(this->lineEdit->text()); - - hboxLayout->addWidget(edit); - hboxLayout->addWidget(buttonBox); - connect(buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject())); - if (dlg.exec() == QDialog::Accepted) { - this->lineEdit->setText(edit->toPlainText()); - } + + hboxLayout->addWidget(edit); + hboxLayout->addWidget(buttonBox); + connect(buttonBox, SIGNAL(accepted()), &dlg, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), &dlg, SLOT(reject())); + if (dlg.exec() == QDialog::Accepted) { + this->lineEdit->setText(edit->toPlainText()); + } } /** diff --git a/src/Gui/Widgets.h b/src/Gui/Widgets.h index 16baf74b7a..70366a67f6 100644 --- a/src/Gui/Widgets.h +++ b/src/Gui/Widgets.h @@ -63,6 +63,55 @@ Q_SIGNALS: // ------------------------------------------------------------------------------ +class GuiExport ActionSelector : public QWidget +{ + Q_OBJECT + +public: + ActionSelector(QWidget* parent=0); + ~ActionSelector(); + + QTreeWidget* availableTreeWidget() const + { return availableWidget; } + QTreeWidget* selectedTreeWidget() const + { return selectedWidget; } + void setSelectedLabel(const QString&); + QString selectedLabel() const; + void setAvailableLabel(const QString&); + QString availableLabel() const; + +private: + void keyPressEvent(QKeyEvent *); + void changeEvent(QEvent*); + void retranslateUi(); + void setButtonsEnabled(); + +private Q_SLOTS: + void on_addButton_clicked(); + void on_removeButton_clicked(); + void on_upButton_clicked(); + void on_downButton_clicked(); + void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); + void onItemDoubleClicked(QTreeWidgetItem * item, int column); + +private: + QGridLayout *gridLayout; + QVBoxLayout *vboxLayout; + QVBoxLayout *vboxLayout1; + QPushButton *addButton; + QPushButton *removeButton; + QPushButton *upButton; + QPushButton *downButton; + QLabel *labelAvailable; + QLabel *labelSelected; + QTreeWidget *availableWidget; + QTreeWidget *selectedWidget; + QSpacerItem *spacerItem; + QSpacerItem *spacerItem1; +}; + +// ------------------------------------------------------------------------------ + /** * The AccelLineEdit class provides a lineedit to specfify shortcuts. * \author Werner Mayer diff --git a/src/Gui/resource.cpp b/src/Gui/resource.cpp index 3143f1502a..b7f3ce8fbd 100644 --- a/src/Gui/resource.cpp +++ b/src/Gui/resource.cpp @@ -91,6 +91,7 @@ WidgetFactorySupplier::WidgetFactorySupplier() new WidgetProducer; new WidgetProducer; new WidgetProducer; + new WidgetProducer; new WidgetProducer; new WidgetProducer; new WidgetProducer; diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index 37cdf9f545..d165e69eaf 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -25,12 +25,13 @@ import FreeCAD,FreeCADGui,Draft,math,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore, QtGui from pivy import coin +from DraftTools import translate __title__="FreeCAD Axis System" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeAxis(num=0,size=0,name="Axes"): +def makeAxis(num=5,size=1,name=str(translate("Arch","Axes"))): '''makeAxis(num,size): makes an Axis System based on the given number of axes and interval distances''' obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) @@ -56,16 +57,17 @@ class _CommandAxis: 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Axis","Creates an axis system.")} def Activated(self): - FreeCAD.ActiveDocument.openTransaction("Axis") - makeAxis(5,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Axis"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeAxis()") FreeCAD.ActiveDocument.commitTransaction() class _Axis: "The Axis object" def __init__(self,obj): - obj.addProperty("App::PropertyFloatList","Distances","Base", "The intervals between axes") - obj.addProperty("App::PropertyFloatList","Angles","Base", "The angles of each axis") - obj.addProperty("App::PropertyFloat","Length","Base", "The length of the axes") + obj.addProperty("App::PropertyFloatList","Distances","Base", str(translate("Arch","The intervals between axes"))) + obj.addProperty("App::PropertyFloatList","Angles","Base", str(translate("Arch","The angles of each axis"))) + obj.addProperty("App::PropertyFloat","Length","Base", str(translate("Arch","The length of the axes"))) self.Type = "Axis" obj.Length=1.0 obj.Proxy = self @@ -98,8 +100,8 @@ class _ViewProviderAxis: "A View Provider for the Axis object" def __init__(self,vobj): - vobj.addProperty("App::PropertyLength","BubbleSize","Base", "The size of the axis bubbles") - vobj.addProperty("App::PropertyEnumeration","NumerationStyle","Base", "The numeration style") + vobj.addProperty("App::PropertyLength","BubbleSize","Base", str(translate("Arch","The size of the axis bubbles"))) + vobj.addProperty("App::PropertyEnumeration","NumerationStyle","Base", str(translate("Arch","The numeration style"))) vobj.NumerationStyle = ["1,2,3","01,02,03","001,002,003","A,B,C","a,b,c","I,II,III","L0,L1,L2"] vobj.Proxy = self vobj.BubbleSize = .1 diff --git a/src/Mod/Arch/ArchBuilding.py b/src/Mod/Arch/ArchBuilding.py index f8ed4e8ca3..0d6d5cc403 100644 --- a/src/Mod/Arch/ArchBuilding.py +++ b/src/Mod/Arch/ArchBuilding.py @@ -21,14 +21,15 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Draft,ArchCommands +import FreeCAD,FreeCADGui,Draft,ArchCommands,ArchFloor from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Building" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeBuilding(objectslist=None,join=False,name="Building"): +def makeBuilding(objectslist=None,join=False,name=str(translate("Arch","Building"))): '''makeBuilding(objectslist,[joinmode]): creates a building including the objects from the given list. If joinmode is True, components will be joined.''' obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name) @@ -51,59 +52,40 @@ class _CommandBuilding: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Site","Floor"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") - nobj = makeBuilding() - ArchCommands.copyProperties(sel[0],nobj) - FreeCAD.ActiveDocument.removeObject(sel[0].Name) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeBuilding()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Building") - makeBuilding(sel) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch"," Create Building"))) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Floor"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeBuilding("+ss+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() -class _Building: +class _Building(ArchFloor._Floor): "The Building object" def __init__(self,obj): + ArchFloor._Floor.__init__(self,obj) self.Type = "Building" - obj.Proxy = self - self.Object = obj - - def execute(self,obj): - self.Object = obj - - def onChanged(self,obj,prop): - pass - - def addObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if not child in g: - g.append(child) - self.Object.Group = g - - def removeObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if child in g: - g.remove(child) - self.Object.Group = g - -class _ViewProviderBuilding: + obj.setEditorMode('Height',2) + +class _ViewProviderBuilding(ArchFloor._ViewProviderFloor): "A View Provider for the Building object" def __init__(self,vobj): - vobj.Proxy = self + ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): return ":/icons/Arch_Building_Tree.svg" - def attach(self,vobj): - self.Object = vobj.Object - return - - def claimChildren(self): - return self.Object.Group - - FreeCADGui.addCommand('Arch_Building',_CommandBuilding()) diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 2ebfad0d0b..2025a6f308 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -24,6 +24,7 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Arch Commands" __author__ = "Yorik van Havre" @@ -341,11 +342,20 @@ class _CommandAdd: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Grouping") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Grouping"))) if not mergeCells(sel): host = sel.pop() - addComponents(sel,host) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.addComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + class _CommandRemove: "the Arch Add command definition" @@ -362,13 +372,22 @@ class _CommandRemove: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Ungrouping") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Ungrouping"))) if Draft.getType(sel[-1]) in ["Wall","Structure"]: host = sel.pop() - removeComponents(sel,host) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.removeComponents("+ss+",FreeCAD.ActiveDocument."+host.Name+")") else: - removeComponents(sel) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.removeComponents("+ss+")") FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _CommandSplitMesh: @@ -387,7 +406,7 @@ class _CommandSplitMesh: def Activated(self): if FreeCADGui.Selection.getSelection(): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Split Mesh") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Split Mesh"))) for obj in sel: n = obj.Name nobjs = splitMesh(obj) @@ -396,6 +415,7 @@ class _CommandSplitMesh: for o in nobjs: g.addObject(o) FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _CommandMeshToShape: @@ -424,7 +444,7 @@ class _CommandMeshToShape: if f.InList: if f.InList[0].isDerivedFrom("App::DocumentObjectGroup"): g = f.InList[0] - FreeCAD.ActiveDocument.openTransaction("Mesh to Shape") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Mesh to Shape"))) for obj in FreeCADGui.Selection.getSelection(): newobj = meshToShape(obj) if g and newobj: diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index c9fa5a347e..5e0206fe93 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -257,9 +257,9 @@ class Component: obj.addProperty("App::PropertyLink","Base","Base", "The base object this component is built upon") obj.addProperty("App::PropertyLinkList","Additions","Base", - "Other shapes that are appended to this wall") + "Other shapes that are appended to this object") obj.addProperty("App::PropertyLinkList","Subtractions","Base", - "Other shapes that are subtracted from this wall") + "Other shapes that are subtracted from this object") obj.Proxy = self self.Type = "Component" self.Subvolume = None @@ -308,16 +308,23 @@ class ViewProviderComponent: return False class ArchSelectionObserver: - def __init__(self,origin,watched): + def __init__(self,origin,watched,hide=True,nextCommand=None): self.origin = origin self.watched = watched + self.hide = hide + self.nextCommand = nextCommand def addSelection(self,document, object, element, position): if object == self.watched.Name: if not element: print "closing Sketch edit" - self.origin.ViewObject.Transparency = 0 - self.origin.ViewObject.Selectable = True - self.watched.ViewObject.hide() + if self.hide: + self.origin.ViewObject.Transparency = 0 + self.origin.ViewObject.Selectable = True + self.watched.ViewObject.hide() FreeCADGui.activateWorkbench("ArchWorkbench") FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver) + if self.nextCommand: + FreeCADGui.Selection.clearSelection() + FreeCADGui.Selection.addSelection(self.watched) + FreeCADGui.runCommand(self.nextCommand) del FreeCAD.ArchObserver diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index 606395961d..f7ccab17ae 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -23,12 +23,13 @@ import FreeCAD,FreeCADGui,Draft,ArchCommands from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Arch Floor" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeFloor(objectslist=None,join=True,name="Floor"): +def makeFloor(objectslist=None,join=True,name=str(translate("Arch","Floor"))): '''makeFloor(objectslist,[joinmode]): creates a floor including the objects from the given list. If joinmode is False, components will not be joined.''' @@ -52,32 +53,46 @@ class _CommandFloor: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Site","Building"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") - nobj = makeFloor() - ArchCommands.copyProperties(sel[0],nobj) - FreeCAD.ActiveDocument.removeObject(sel[0].Name) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeFloor()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Floor") - makeFloor(sel) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Floor"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeFloor("+ss+")") FreeCAD.ActiveDocument.commitTransaction() - FreeCAD.ActiveDocument.recompute() + FreeCAD.ActiveDocument.recompute() class _Floor: - "The Cell object" + "The Floor object" def __init__(self,obj): obj.addProperty("App::PropertyLength","Height","Base", - "The height of this floor") + str(translate("Arch","The height of this floor"))) self.Type = "Floor" obj.Proxy = self self.Object = obj + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + def execute(self,obj): - self.Object = obj + pass def onChanged(self,obj,prop): - pass + self.Object = obj def addObject(self,child): if hasattr(self,"Object"): @@ -94,7 +109,7 @@ class _Floor: self.Object.Group = g class _ViewProviderFloor: - "A View Provider for the Cell object" + "A View Provider for the Floor object" def __init__(self,vobj): vobj.Proxy = self @@ -108,4 +123,10 @@ class _ViewProviderFloor: def claimChildren(self): return self.Object.Group + def __getstate__(self): + return None + + def __setstate__(self,state): + return None + FreeCADGui.addCommand('Arch_Floor',_CommandFloor()) diff --git a/src/Mod/Arch/ArchRoof.py b/src/Mod/Arch/ArchRoof.py index 31515afbc8..4c8d6964c1 100644 --- a/src/Mod/Arch/ArchRoof.py +++ b/src/Mod/Arch/ArchRoof.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent, DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Roof" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeRoof(baseobj,facenr=1,angle=45,name="Roof"): +def makeRoof(baseobj,facenr=1,angle=45,name=str(translate("Arch","Roof"))): '''makeRoof(baseobj,[facenr],[angle],[name]) : Makes a roof based on a face from an existing object. You can provide the number of the face to build the roof on (default = 1), the angle (default=45) and a name (default @@ -64,35 +65,38 @@ class _CommandRoof: if sel.HasSubObjects: if "Face" in sel.SubElementNames[0]: idx = int(sel.SubElementNames[0][4:]) - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,idx) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+","+str(idx)+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() elif obj.isDerivedFrom("Part::Feature"): if len(obj.Shape.Faces) == 1: - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+",1)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() elif obj.isDerivedFrom("Part::Feature"): if len(obj.Shape.Faces) == 1: - FreeCAD.ActiveDocument.openTransaction("Create Roof") - makeRoof(obj,1) + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Roof"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeRoof(FreeCAD.ActiveDocument."+obj.Name+",1)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() else: - FreeCAD.Console.PrintMessage("Unable to create a roof") + FreeCAD.Console.PrintMessage(str(translate("Arch","Unable to create a roof"))) else: - FreeCAD.Console.PrintMessage("No object selected") + FreeCAD.Console.PrintMessage(str(translate("Arch","No object selected"))) class _Roof(ArchComponent.Component): "The Roof object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyAngle","Angle","Base", - "The angle of this roof") + str(translate("Arch","The angle of this roof"))) obj.addProperty("App::PropertyInteger","Face","Base", - "The face number of the base object used to build this roof") + str(translate("Arch","The face number of the base object used to build this roof"))) self.Type = "Structure" def execute(self,obj): diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index e1230e59f7..aa1d8bb990 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -25,7 +25,45 @@ import FreeCAD,FreeCADGui,ArchComponent,WorkingPlane,math,Draft,ArchCommands, Dr from FreeCAD import Vector from PyQt4 import QtCore from pivy import coin +from DraftTools import translate +def makeSectionPlane(objectslist=None): + """makeSectionPlane([objectslist]) : Creates a Section plane objects including the + given objects. If no object is given, the whole document will be considered.""" + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Section") + _SectionPlane(obj) + _ViewProviderSectionPlane(obj.ViewObject) + if objectslist: + g = [] + for o in objectslist: + if o.isDerivedFrom("Part::Feature"): + g.append(o) + elif o.isDerivedFrom("App::DocumentObjectGroup"): + g.append(o) + obj.Objects = g + return obj + +def makeSectionView(section): + """makeSectionView(section) : Creates a Drawing view of the given Section Plane + in the active Page object (a new page will be created if none exists""" + page = None + for o in FreeCAD.ActiveDocument.Objects: + if o.isDerivedFrom("Drawing::FeaturePage"): + page = o + break + if not page: + page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage",str(translate("Arch","Page"))) + template = Draft.getParam("template") + if not template: + template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg' + page.Template = template + + view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View") + page.addObject(view) + _ArchDrawingView(view) + view.Source = section + view.Label = str(translate("Arch","View of"))+" "+section.Name + return view class _CommandSectionPlane: "the Arch SectionPlane command definition" @@ -37,30 +75,17 @@ class _CommandSectionPlane: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Section Plane") - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Section") - _SectionPlane(obj) - _ViewProviderSectionPlane(obj.ViewObject) - FreeCAD.ActiveDocument.commitTransaction() - g = [] + ss = "[" for o in sel: - if o.isDerivedFrom("Part::Feature"): - g.append(o) - elif o.isDerivedFrom("App::DocumentObjectGroup"): - g.append(o) - obj.Objects = g - page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage","Page") - template = Draft.getParam("template") - if not template: - template = FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg' - page.ViewObject.HintOffsetX = 200 - page.ViewObject.HintOffsetY = 100 - page.ViewObject.HintScale = 20 - page.Template = template - view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython","View") - page.addObject(view) - _ArchDrawingView(view) - view.Source = obj + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Section Plane"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("section = Arch.makeSectionPlane("+ss+")") + FreeCADGui.doCommand("Arch.makeSectionView(section)") + FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class _SectionPlane: @@ -68,7 +93,7 @@ class _SectionPlane: def __init__(self,obj): obj.Proxy = self obj.addProperty("App::PropertyLinkList","Objects","Base", - "The objects that must be considered by this section plane. Empty means all document") + str(translate("Arch","The objects that must be considered by this section plane. Empty means all document"))) self.Type = "SectionPlane" def execute(self,obj): @@ -89,7 +114,7 @@ class _ViewProviderSectionPlane(ArchComponent.ViewProviderComponent): "A View Provider for Section Planes" def __init__(self,vobj): vobj.addProperty("App::PropertyLength","DisplaySize","Base", - "The display size of the section plane") + str(translate("Arch","The display size of this section plane"))) vobj.DisplaySize = 1 vobj.Transparency = 85 vobj.LineWidth = 1 @@ -209,6 +234,7 @@ class _ArchDrawingView: svgf = Drawing.projectToSVG(base,DraftVecUtils.neg(direction)) if svgf: svgf = svgf.replace('stroke-width="0.35"','stroke-width="' + str(linewidth) + 'px"') + svgf = svgf.replace('stroke-width:0.01','stroke-width:' + str(linewidth) + 'px') svg += svgf result = '' diff --git a/src/Mod/Arch/ArchSite.py b/src/Mod/Arch/ArchSite.py index 300fd09eff..69cfda8bab 100644 --- a/src/Mod/Arch/ArchSite.py +++ b/src/Mod/Arch/ArchSite.py @@ -21,14 +21,15 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,Draft,ArchCommands +import FreeCAD,FreeCADGui,Draft,ArchCommands,ArchFloor from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Site" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeSite(objectslist=None,name="Site"): +def makeSite(objectslist=None,name=str(translate("Arch","Site"))): '''makeBuilding(objectslist): creates a site including the objects from the given list.''' obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name) @@ -51,58 +52,44 @@ class _CommandSite: ok = False if (len(sel) == 1): if Draft.getType(sel[0]) in ["Cell","Building","Floor"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Type conversion"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("obj = Arch.makeSite()") + FreeCADGui.doCommand("Arch.copyProperties(FreeCAD.ActiveDocument."+sel[0].Name+",obj)") + FreeCADGui.doCommand('FreeCAD.ActiveDocument.removeObject("'+sel[0].Name+'")') + nobj = makeSite() ArchCommands.copyProperties(sel[0],nobj) FreeCAD.ActiveDocument.removeObject(sel[0].Name) FreeCAD.ActiveDocument.commitTransaction() ok = True if not ok: - FreeCAD.ActiveDocument.openTransaction("Site") - makeSite(sel) + ss = "[" + for o in sel: + if len(ss) > 1: + ss += "," + ss += "FreeCAD.ActiveDocument."+o.Name + ss += "]" + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Site"))) + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.makeSite("+ss+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - -class _Site: + +class _Site(ArchFloor._Floor): "The Site object" def __init__(self,obj): + ArchFloor._Floor.__init__(self,obj) self.Type = "Site" - obj.Proxy = self - self.Object = obj - - def execute(self,obj): - self.Object = obj - - def onChanged(self,obj,prop): - pass - - def addObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if not child in g: - g.append(child) - self.Object.Group = g - - def removeObject(self,child): - if hasattr(self,"Object"): - g = self.Object.Group - if child in g: - g.remove(child) - self.Object.Group = g - -class _ViewProviderSite: + obj.setEditorMode('Height',2) + +class _ViewProviderSite(ArchFloor._ViewProviderFloor): "A View Provider for the Site object" def __init__(self,vobj): - vobj.Proxy = self + ArchFloor._ViewProviderFloor.__init__(self,vobj) def getIcon(self): return ":/icons/Arch_Site_Tree.svg" - def attach(self,vobj): - self.Object = vobj.Object - return - - def claimChildren(self): - return self.Object.Group - + FreeCADGui.addCommand('Arch_Site',_CommandSite()) diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 2327ad7cac..d257c4d92f 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Structure" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeStructure(baseobj=None,length=None,width=None,height=None,name="Structure"): +def makeStructure(baseobj=None,length=None,width=None,height=None,name=str(translate("Arch","Structure"))): '''makeStructure([obj],[length],[width],[heigth],[swap]): creates a structure element based on the given profile object and the given extrusion height. If no base object is given, you can also specify @@ -64,29 +65,31 @@ class _CommandStructure: 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Structure","Creates a structure object from scratch or from a selected object (sketch, wire, face or solid)")} def Activated(self): + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Structure"))) + FreeCADGui.doCommand("import Arch") sel = FreeCADGui.Selection.getSelection() if sel: - FreeCAD.ActiveDocument.openTransaction("Structure") for obj in sel: - makeStructure(obj) - FreeCAD.ActiveDocument.commitTransaction() + FreeCADGui.doCommand("Arch.makeStructure(FreeCAD.ActiveDocument."+obj.Name+")") else: - makeStructure() + FreeCADGui.doCommand("Arch.makeStructure()") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() class _Structure(ArchComponent.Component): "The Structure object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyLength","Length","Base", - "The length of this element, if not based on a profile") + str(translate("Arch","The length of this element, if not based on a profile"))) obj.addProperty("App::PropertyLength","Width","Base", - "The width of this element, if not based on a profile") + str(translate("Arch","The width of this element, if not based on a profile"))) obj.addProperty("App::PropertyLength","Height","Base", - "The height or extrusion depth of this element. Keep 0 for automatic") + str(translate("Arch","The height or extrusion depth of this element. Keep 0 for automatic"))) obj.addProperty("App::PropertyLinkList","Axes","Base", - "Axes systems this structure is built on") + str(translate("Arch","Axes systems this structure is built on"))) obj.addProperty("App::PropertyVector","Normal","Base", - "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)") + str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) self.Type = "Structure" def execute(self,obj): @@ -160,18 +163,13 @@ class _Structure(ArchComponent.Component): base = Part.Face(base) base = base.extrude(normal) for app in obj.Additions: - base = base.oldFuse(app.Shape) - app.ViewObject.hide() # to be removed + if hasattr(app,"Shape"): + if not app.Shape.isNull(): + base = base.fuse(app.Shape) + app.ViewObject.hide() # to be removed for hole in obj.Subtractions: - cut = False - if hasattr(hole,"Proxy"): - if hasattr(hole.Proxy,"Subvolume"): - if hole.Proxy.Subvolume: - print "cutting subvolume",hole.Proxy.Subvolume - base = base.cut(hole.Proxy.Subvolume) - cut = True - if not cut: - if hasattr(obj,"Shape"): + if hasattr(hole,"Shape"): + if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed if base: @@ -184,7 +182,9 @@ class _Structure(ArchComponent.Component): fsh.append(sh) obj.Shape = Part.makeCompound(fsh) else: - obj.Shape = base + if not base.isNull(): + base = base.removeSplitter() + obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl class _ViewProviderStructure(ArchComponent.ViewProviderComponent): diff --git a/src/Mod/Arch/ArchVRM.py b/src/Mod/Arch/ArchVRM.py index 2de8ad0b3b..2eefd8f279 100644 --- a/src/Mod/Arch/ArchVRM.py +++ b/src/Mod/Arch/ArchVRM.py @@ -574,7 +574,7 @@ class Renderer: svg += '" ' svg += 'stroke="#000000" ' svg += 'stroke-width="' + str(linewidth) + '" ' - svg += 'style="stroke-width:0.01;' + svg += 'style="stroke-width:' + str(linewidth) + ';' svg += 'stroke-miterlimit:1;' svg += 'stroke-linejoin:round;' svg += 'stroke-dasharray:none;' @@ -598,7 +598,7 @@ class Renderer: svg += '" ' svg += 'stroke="#000000" ' svg += 'stroke-width="' + str(linewidth) + '" ' - svg += 'style="stroke-width:0.01;' + svg += 'style="stroke-width:' + str(linewidth) + ';' svg += 'stroke-miterlimit:1;' svg += 'stroke-linejoin:round;' svg += 'stroke-dasharray:none;' diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index c26125136d..1d6f8e5e07 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore +from DraftTools import translate __title__="FreeCAD Wall" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeWall(baseobj=None,width=None,height=None,align="Center",name="Wall"): +def makeWall(baseobj=None,width=None,height=None,align="Center",name=str(translate("Arch","Wall"))): '''makeWall(obj,[width],[height],[align],[name]): creates a wall based on the given object, which can be a sketch, a draft object, a face or a solid. align can be "Center","Left" or "Right"''' @@ -117,10 +118,12 @@ class _CommandWall: if sel: import Draft if Draft.getType(sel[0]) != "Wall": - FreeCAD.ActiveDocument.openTransaction("Wall") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Wall"))) + FreeCADGui.doCommand('import Arch') for obj in sel: - makeWall(obj) + FreeCADGui.doCommand('Arch.makeWall(FreeCAD.ActiveDocument.'+obj.Name+')') FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() done = True if not done: import DraftTrackers @@ -146,32 +149,33 @@ class _CommandWall: add = False l = Part.Line(self.points[0],self.points[1]) self.tracker.finalize() - FreeCAD.ActiveDocument.openTransaction("Wall") + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Wall"))) + FreeCADGui.doCommand('import Arch') + FreeCADGui.doCommand('import Part') + FreeCADGui.doCommand('trace=Part.Line(FreeCAD.'+str(l.StartPoint)+',FreeCAD.'+str(l.EndPoint)+')') if not self.existing: self.addDefault(l) else: w = joinWalls(self.existing) if w: if areSameWallTypes([w,self]): - w.Base.addGeometry(l) + FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+w.Name+'.Base.addGeometry(trace)') else: - nw = self.addDefault(l) + self.addDefault(l) add = True else: self.addDefault(l) + if add: + FreeCADGui.doCommand('Arch.addComponents(FreeCAD.ActiveDocument.'+FreeCAD.ActiveDocument.Objects[-1].Name+',FreeCAD.ActiveDocument.'+w.Name+')') FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - if add: - import ArchCommands - ArchCommands.addComponents(nw,w) if self.continueCmd: self.Activated() def addDefault(self,l): - s = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") - s.addGeometry(l) - w = makeWall(s,width=self.Width,height=self.Height,align=self.Align) - return w + FreeCADGui.doCommand('base=FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","'+str(translate('Arch','WallTrace'))+'")') + FreeCADGui.doCommand('base.addGeometry(trace)') + FreeCADGui.doCommand('Arch.makeWall(base,width='+str(self.Width)+',height='+str(self.Height)+',align="'+str(self.Align)+'")') def update(self,point): "this function is called by the Snapper when the mouse is moved" @@ -191,11 +195,11 @@ class _CommandWall: def taskbox(self): "sets up a taskbox widget" w = QtGui.QWidget() - w.setWindowTitle("Wall options") + w.setWindowTitle(str(translate("Arch","Wall options"))) lay0 = QtGui.QVBoxLayout(w) lay1 = QtGui.QHBoxLayout() lay0.addLayout(lay1) - label1 = QtGui.QLabel("Width") + label1 = QtGui.QLabel(str(translate("Arch","Width"))) lay1.addWidget(label1) value1 = QtGui.QDoubleSpinBox() value1.setDecimals(2) @@ -203,7 +207,7 @@ class _CommandWall: lay1.addWidget(value1) lay2 = QtGui.QHBoxLayout() lay0.addLayout(lay2) - label2 = QtGui.QLabel("Height") + label2 = QtGui.QLabel(str(translate("Arch","Height"))) lay2.addWidget(label2) value2 = QtGui.QDoubleSpinBox() value2.setDecimals(2) @@ -211,14 +215,14 @@ class _CommandWall: lay2.addWidget(value2) lay3 = QtGui.QHBoxLayout() lay0.addLayout(lay3) - label3 = QtGui.QLabel("Alignment") + label3 = QtGui.QLabel(str(translate("Arch","Alignment"))) lay3.addWidget(label3) value3 = QtGui.QComboBox() items = ["Center","Left","Right"] value3.addItems(items) value3.setCurrentIndex(items.index(self.Align)) lay3.addWidget(value3) - value4 = QtGui.QCheckBox("Continue") + value4 = QtGui.QCheckBox(str(translate("Arch","Continue"))) lay0.addWidget(value4) QtCore.QObject.connect(value1,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) QtCore.QObject.connect(value2,QtCore.SIGNAL("valueChanged(double)"),self.setHeight) @@ -245,14 +249,17 @@ class _Wall(ArchComponent.Component): def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyLength","Width","Base", - "The width of this wall. Not used if this wall is based on a face") + str(translate("Arch","The width of this wall. Not used if this wall is based on a face"))) obj.addProperty("App::PropertyLength","Height","Base", - "The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid") + str(translate("Arch","The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid"))) obj.addProperty("App::PropertyEnumeration","Align","Base", - "The alignment of this wall on its base object, if applicable") + str(translate("Arch","The alignment of this wall on its base object, if applicable"))) obj.addProperty("App::PropertyVector","Normal","Base", - "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)") + str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) + obj.addProperty("App::PropertyBool","ForceWire","Base", + str(translate("Arch","If True, if this wall is based on a face, it will use its border wire as trace, and disconsider the face."))) obj.Align = ['Left','Right','Center'] + obj.ForceWire = False self.Type = "Wall" obj.Width = 0.1 obj.Height = 0 @@ -268,20 +275,23 @@ class _Wall(ArchComponent.Component): "returns a subvolume from a base object" import Part max_length = 0 + f = None for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w - f = Part.Face(f) - n = f.normalAt(0,0) - v1 = DraftVecUtils.scaleTo(n,width) - f.translate(v1) - v2 = DraftVecUtils.neg(v1) - v2 = DraftVecUtils.scale(v1,-2) - f = f.extrude(v2) - if delta: - f.translate(delta) - return f + if f: + f = Part.Face(f) + n = f.normalAt(0,0) + v1 = DraftVecUtils.scaleTo(n,width) + f.translate(v1) + v2 = DraftVecUtils.neg(v1) + v2 = DraftVecUtils.scale(v1,-2) + f = f.extrude(v2) + if delta: + f.translate(delta) + return f + return None def createGeometry(self,obj): "builds the wall shape" @@ -351,7 +361,7 @@ class _Wall(ArchComponent.Component): base = obj.Base.Shape.copy() if base.Solids: pass - elif base.Faces: + elif base.Faces and (not obj.ForceWire): if height: norm = normal.multiply(height) base = base.extrude(norm) @@ -364,30 +374,48 @@ class _Wall(ArchComponent.Component): else: temp = sh base = temp - base = base.removeSplitter() - - for app in obj.Additions: - base = base.oldFuse(app.Shape) - app.ViewObject.hide() #to be removed - for hole in obj.Subtractions: - if Draft.getType(hole) == "Window": - # window - if hole.Base and obj.Width: - f = self.getSubVolume(hole.Base,width) - base = base.cut(f) - elif Draft.isClone(hole,"Window"): - if hole.Objects[0].Base and width: - f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) - base = base.cut(f) - elif hasattr(hole,"Shape"): - if not hole.Shape.isNull(): - base = base.cut(hole.Shape) - hole.ViewObject.hide() # to be removed + elif base.Edges: + wire = Part.Wire(base.Edges) + sh = getbase(wire) + if sh: + base = sh + else: + FreeCAD.Console.PrintError(str(translate("Arch","Error: Invalid base object"))) if base: - obj.Shape = base - if not DraftGeomUtils.isNull(pl): - obj.Placement = pl + for app in obj.Additions: + if hasattr(app,"Shape"): + if app.Shape: + if not app.Shape.isNull(): + base = base.fuse(app.Shape) + app.ViewObject.hide() #to be removed + + for hole in obj.Subtractions: + if Draft.getType(hole) == "Window": + # window + if hole.Base and obj.Width: + f = self.getSubVolume(hole.Base,width) + if f: + base = base.cut(f) + elif Draft.isClone(hole,"Window"): + if hole.Objects[0].Base and width: + f = self.getSubVolume(hole.Objects[0].Base,width,hole.Placement.Base) + if f: + base = base.cut(f) + elif hasattr(hole,"Shape"): + if hole.Shape: + if not hole.Shape.isNull(): + base = base.cut(hole.Shape) + hole.ViewObject.hide() # to be removed + + if not base.isNull(): + try: + base = base.removeSplitter() + except: + FreeCAD.Console.PrintError(str(translate("Arch","Error removing splitter from wall shape"))) + obj.Shape = base + if not DraftGeomUtils.isNull(pl): + obj.Placement = pl class _ViewProviderWall(ArchComponent.ViewProviderComponent): "A View Provider for the Wall object" diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 35813ddfc8..2f593257fb 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -24,12 +24,13 @@ import FreeCAD,FreeCADGui,Draft,ArchComponent,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore,QtGui +from DraftTools import translate __title__="FreeCAD Wall" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" -def makeWindow(baseobj=None,width=None,name="Window"): +def makeWindow(baseobj=None,width=None,name=str(translate("Arch","Window"))): '''makeWindow(obj,[name]): creates a window based on the given object''' if baseobj: @@ -87,18 +88,38 @@ class _CommandWindow: def Activated(self): sel = FreeCADGui.Selection.getSelection() - FreeCAD.ActiveDocument.openTransaction("Create Window") - for obj in sel: - makeWindow(obj) - FreeCAD.ActiveDocument.commitTransaction() + if sel: + if Draft.getType(sel[0]) == "Wall": + FreeCADGui.activateWorkbench("SketcherWorkbench") + FreeCADGui.runCommand("Sketcher_NewSketch") + FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(sel[0],FreeCAD.ActiveDocument.Objects[-1],hide=False,nextCommand="Arch_Window") + FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver) + else: + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Window"))) + FreeCADGui.doCommand("import Arch") + for obj in sel: + FreeCADGui.doCommand("Arch.makeWindow(FreeCAD.ActiveDocument."+obj.Name+")") + if hasattr(obj,"Support"): + if obj.Support: + if isinstance(obj.Support,tuple): + s = obj.Support[0] + else: + s = obj.Support + w = FreeCAD.ActiveDocument.Objects[-1] # last created object + FreeCADGui.doCommand("Arch.removeComponents(FreeCAD.ActiveDocument."+w.Name+",host=FreeCAD.ActiveDocument."+s.Name+")") + elif Draft.isClone(w,"Window"): + if w.Objects[0].Inlist: + FreeCADGui.doCommand("Arch.removeComponents(FreeCAD.ActiveDocument."+w.Name+",host=FreeCAD.ActiveDocument."+w.Objects[0].Inlist[0].Name+")") + FreeCAD.ActiveDocument.commitTransaction() class _Window(ArchComponent.Component): "The Window object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) obj.addProperty("App::PropertyStringList","WindowParts","Base", - "the components of this window") + str(translate("Arch","the components of this window"))) self.Type = "Window" + obj.Proxy = self def execute(self,obj): self.createGeometry(obj) @@ -112,43 +133,45 @@ class _Window(ArchComponent.Component): pl = obj.Placement if obj.Base: if obj.Base.isDerivedFrom("Part::Feature"): - if obj.WindowParts and (len(obj.WindowParts)%5 == 0): - shapes = [] - for i in range(len(obj.WindowParts)/5): - wires = [] - wstr = obj.WindowParts[(i*5)+2].split(',') - for s in wstr: - j = int(s[4:]) - if obj.Base.Shape.Wires: - if len(obj.Base.Shape.Wires) >= j: - wires.append(obj.Base.Shape.Wires[j]) - if wires: - max_length = 0 - for w in wires: - if w.BoundBox.DiagonalLength > max_length: - max_length = w.BoundBox.DiagonalLength - ext = w - wires.remove(ext) - shape = Part.Face(ext) - norm = shape.normalAt(0,0) - thk = float(obj.WindowParts[(i*5)+3]) - if thk: - exv = DraftVecUtils.scaleTo(norm,thk) - shape = shape.extrude(exv) + if hasattr(obj,"WindowParts"): + if obj.WindowParts and (len(obj.WindowParts)%5 == 0): + shapes = [] + for i in range(len(obj.WindowParts)/5): + wires = [] + wstr = obj.WindowParts[(i*5)+2].split(',') + for s in wstr: + j = int(s[4:]) + if obj.Base.Shape.Wires: + if len(obj.Base.Shape.Wires) >= j: + wires.append(obj.Base.Shape.Wires[j]) + if wires: + max_length = 0 for w in wires: - f = Part.Face(w) - f = f.extrude(exv) - shape = shape.cut(f) - if obj.WindowParts[(i*5)+4]: - zof = float(obj.WindowParts[(i*5)+4]) - if zof: - zov = DraftVecUtils.scaleTo(norm,zof) - shape.translate(zov) - print shape - shapes.append(shape) - obj.Shape = Part.makeCompound(shapes) - if not DraftGeomUtils.isNull(pl): - obj.Placement = pl + if w.BoundBox.DiagonalLength > max_length: + max_length = w.BoundBox.DiagonalLength + ext = w + wires.remove(ext) + shape = Part.Face(ext) + norm = shape.normalAt(0,0) + thk = float(obj.WindowParts[(i*5)+3]) + if thk: + exv = DraftVecUtils.scaleTo(norm,thk) + shape = shape.extrude(exv) + for w in wires: + f = Part.Face(w) + f = f.extrude(exv) + shape = shape.cut(f) + if obj.WindowParts[(i*5)+4]: + zof = float(obj.WindowParts[(i*5)+4]) + if zof: + zov = DraftVecUtils.scaleTo(norm,zof) + shape.translate(zov) + print shape + shapes.append(shape) + if shapes: + obj.Shape = Part.makeCompound(shapes) + if not DraftGeomUtils.isNull(pl): + obj.Placement = pl class _ViewProviderWindow(ArchComponent.ViewProviderComponent): "A View Provider for the Window object" diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 1de87ed470..7f4c510795 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -69,13 +69,14 @@ class ArchWorkbench(Workbench): "Draft_Offset","Draft_Upgrade", "Draft_Downgrade","Draft_Trimex"] self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode", - "Draft_AddToGroup","Draft_SelectGroup", - "Draft_SelectPlane","Draft_ToggleSnap"] + "Draft_AddToGroup","Draft_SelectGroup", + "Draft_SelectPlane","Draft_ToggleSnap", + "Draft_ShowSnapBar","Draft_ToggleGrid"] self.meshtools = ["Arch_SplitMesh","Arch_MeshToShape", "Arch_SelectNonSolidMeshes","Arch_RemoveShape"] self.appendToolbar(str(DraftTools.translate("arch","Arch tools")),self.archtools) self.appendToolbar(str(DraftTools.translate("arch","Draft tools")),self.drafttools) - self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Tools"))],self.meshtools) + self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Conversion Tools"))],self.meshtools) self.appendMenu(str(DraftTools.translate("arch","&Architecture")),self.archtools) self.appendMenu(str(DraftTools.translate("arch","&Draft")),self.drafttools+self.draftcontexttools) FreeCADGui.addIconPath(":/icons") @@ -84,11 +85,15 @@ class ArchWorkbench(Workbench): Log ('Loading Arch module... done\n') def Activated(self): - FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.show() Msg("Arch workbench activated\n") def Deactivated(self): - FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Deactivated() Msg("Arch workbench deactivated\n") def ContextMenu(self, recipient): @@ -104,7 +109,8 @@ FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ") try: import collada except: - FreeCAD.Console.PrintError("pycollada not found, no collada support.\n") + from DraftTools import translate + FreeCAD.Console.PrintMessage(str(translate("arch","pycollada not found, collada support will be disabled.\n"))) else: FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 7b1c5dc8f3..fc2596bebc 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -108,7 +108,7 @@ def getParamType(param): return "float" elif param in ["selectBaseObjects","alwaysSnap","grid","fillmode","saveonexit","maxSnap", "SvgLinesBlack","dxfStdSize","showSnapBar","hideSnapBar","alwaysShowGrid", - "renderPolylineWidth"]: + "renderPolylineWidth","showPlaneTracker"]: return "bool" elif param in ["color","constructioncolor","snapcolor"]: return "unsigned" @@ -1782,7 +1782,6 @@ class _ViewProviderDimension: obj.Proxy = self obj.FontSize=getParam("textheight") obj.FontName=getParam("textfont") - obj.DisplayMode = ["2D","3D"] obj.ExtLines=0.3 obj.Override = '' @@ -1825,18 +1824,19 @@ class _ViewProviderDimension: if hasattr(obj.ViewObject,"DisplayMode"): if obj.ViewObject.DisplayMode == "3D": offset = DraftVecUtils.neg(offset) - if hasattr(obj.ViewObject,"TextPosition"): - if obj.ViewObject.TextPosition == Vector(0,0,0): - tbase = midpoint.add(offset) + if hasattr(obj.ViewObject,"TextPosition"): + if obj.ViewObject.TextPosition == Vector(0,0,0): + tbase = midpoint.add(offset) + else: + tbase = obj.ViewObject.TextPosition else: - tbase = obj.ViewObject.TextPosition + tbase = midpoint.add(offset) else: - tbase = midpoint.add(offset) + tbase = midpoint rot = FreeCAD.Placement(DraftVecUtils.getPlaneRotation(u,v,norm)).Rotation.Q return p1,p2,p3,p4,tbase,norm,rot def attach(self, obj): - obj.DisplayMode = ["2D","3D"] self.Object = obj.Object p1,p2,p3,p4,tbase,norm,rot = self.calcGeom(obj.Object) self.color = coin.SoBaseColor() @@ -1903,6 +1903,10 @@ class _ViewProviderDimension: self.onChanged(obj,"FontName") def updateData(self, obj, prop): + try: + dm = obj.ViewObject.DisplayMode + except: + dm = "2D" text = None if obj.Base and obj.LinkedVertices: if "Shape" in obj.Base.PropertiesList: @@ -1936,7 +1940,7 @@ class _ViewProviderDimension: self.text.string = self.text3d.string = text self.textpos.rotation = coin.SbRotation(rot[0],rot[1],rot[2],rot[3]) self.textpos.translation.setValue([tbase.x,tbase.y,tbase.z]) - if obj.ViewObject.DisplayMode == "2D": + if dm == "2D": self.coords.point.setValues([[p1.x,p1.y,p1.z], [p2.x,p2.y,p2.z], [p3.x,p3.y,p3.z], @@ -1958,6 +1962,7 @@ class _ViewProviderDimension: self.coord2.point.setValue((p3.x,p3.y,p3.z)) def onChanged(self, vp, prop): + self.Object = vp.Object if prop == "FontSize": self.font.size = vp.FontSize self.font3d.size = vp.FontSize*100 @@ -1969,13 +1974,20 @@ class _ViewProviderDimension: elif prop == "LineWidth": self.drawstyle.lineWidth = vp.LineWidth else: + self.drawstyle.lineWidth = vp.LineWidth self.updateData(vp.Object, None) def getDisplayModes(self,obj): return ["2D","3D"] def getDefaultDisplayMode(self): - return "2D" + if hasattr(self,"defaultmode"): + return self.defaultmode + else: + return "2D" + + def setDisplayMode(self,mode): + return mode def getIcon(self): if self.Object.Base: @@ -2034,10 +2046,12 @@ class _ViewProviderDimension: """ def __getstate__(self): - return None + return self.Object.ViewObject.DisplayMode def __setstate__(self,state): - return None + if state: + self.defaultmode = state + self.setDisplayMode(state) class _AngularDimension: "The AngularDimension object" diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 16468ad0c1..7ce4a57f00 100755 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -765,7 +765,7 @@ def isReallyClosed(wire): def getNormal(shape): "finds the normal of a shape, if possible" n = Vector(0,0,1) - if shape.ShapeType == "Face": + if (shape.ShapeType == "Face") and hasattr(shape,"normalAt"): n = shape.normalAt(0.5,0.5) elif shape.ShapeType == "Edge": if isinstance(shape.Curve,Part.Circle): diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 13517f4785..d68a981a74 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -78,11 +78,18 @@ class todo: try: name = str(name) FreeCAD.ActiveDocument.openTransaction(name) - func() + if isinstance(func,list): + for l in func: + FreeCADGui.doCommand(l) + else: + func() FreeCAD.ActiveDocument.commitTransaction() except: wrn = "[Draft.todo.commit] Unexpected error:", sys.exc_info()[0], "in ", f, "(", arg, ")" FreeCAD.Console.PrintWarning (wrn) + # restack Draft screen widgets after creation + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.restack() todo.commitlist = [] @staticmethod @@ -952,6 +959,10 @@ class DraftToolBar: self.wipeLine() elif txt.endsWith("s"): self.togglesnap() + elif txt.endsWith("["): + self.toggleradius(1) + elif txt.endsWith("]"): + self.toggleradius(-1) elif txt.endsWith("c"): if self.closeButton.isVisible(): self.closeLine() @@ -1151,6 +1162,11 @@ class DraftToolBar: if hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper.toggle() + def toggleradius(self,val): + if hasattr(FreeCADGui,"Snapper"): + par = Draft.getParam("snapRange") + Draft.setParam("snapRange",par+val) + FreeCADGui.Snapper.showradius() #--------------------------------------------------------------------------- # TaskView operations diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 1479a65688..ee2e2aab97 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -70,10 +70,12 @@ class Snapper: self.grid = None self.constrainLine = None self.trackLine = None + self.radiusTracker = None self.snapInfo = None self.lastSnappedObject = None self.active = True - self.trackers = [[],[],[],[]] # view, grid, snap, extline + self.forceGridOff = False + self.trackers = [[],[],[],[],[]] # view, grid, snap, extline, radius self.polarAngles = [90,45] @@ -125,10 +127,13 @@ class Snapper: def cstr(point): "constrains if needed" if constrain: - return self.constrain(point,lastpoint) + fpt = self.constrain(point,lastpoint) else: self.unconstrain() - return point + fpt = point + if self.radiusTracker: + self.radiusTracker.update(fpt) + return fpt snaps = [] self.snapInfo = None @@ -149,6 +154,7 @@ class Snapper: self.grid = self.trackers[1][i] self.tracker = self.trackers[2][i] self.extLine = self.trackers[3][i] + self.radiusTracker = self.trackers[4][i] else: if Draft.getParam("grid"): self.grid = DraftTrackers.gridTracker() @@ -156,19 +162,23 @@ class Snapper: self.grid = None self.tracker = DraftTrackers.snapTracker() self.extLine = DraftTrackers.lineTracker(dotted=True) + self.radiusTracker = DraftTrackers.radiusTracker() self.trackers[0].append(v) self.trackers[1].append(self.grid) self.trackers[2].append(self.tracker) self.trackers[3].append(self.extLine) + self.trackers[4].append(self.radiusTracker) # getting current snap Radius - if not self.radius: - self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) + self.radius = self.getScreenDist(Draft.getParam("snapRange"),screenpos) + if self.radiusTracker: + self.radiusTracker.update(self.radius) + self.radiusTracker.off() # set the grid - if self.grid and Draft.getParam("grid"): + if self.grid and (not self.forceGridOff): self.grid.set() - + # activate snap oldActive = False if Draft.getParam("alwaysSnap"): @@ -194,7 +204,7 @@ class Snapper: eline = None point,eline = self.snapToPolar(point,lastpoint) point,eline = self.snapToExtensions(point,lastpoint,constrain,eline) - + if not self.snapInfo: # nothing has been snapped, check fro grid snap @@ -222,6 +232,9 @@ class Snapper: snaps = [self.snapToVertex(self.snapInfo)] else: + + # first stick to the snapped object + point = self.snapToVertex(self.snapInfo)[0] # active snapping comp = self.snapInfo['Component'] @@ -243,18 +256,20 @@ class Snapper: if (not self.maxEdges) or (len(obj.Edges) <= self.maxEdges): if "Edge" in comp: # we are snapping to an edge - edge = obj.Shape.Edges[int(comp[4:])-1] - snaps.extend(self.snapToEndpoints(edge)) - snaps.extend(self.snapToMidpoint(edge)) - snaps.extend(self.snapToPerpendicular(edge,lastpoint)) - #snaps.extend(self.snapToOrtho(edge,lastpoint,constrain)) # now part of snapToPolar - snaps.extend(self.snapToIntersection(edge)) - snaps.extend(self.snapToElines(edge,eline)) + en = int(comp[4:])-1 + if len(obj.Shape.Edges) > en: + edge = obj.Shape.Edges[en] + snaps.extend(self.snapToEndpoints(edge)) + snaps.extend(self.snapToMidpoint(edge)) + snaps.extend(self.snapToPerpendicular(edge,lastpoint)) + #snaps.extend(self.snapToOrtho(edge,lastpoint,constrain)) # now part of snapToPolar + snaps.extend(self.snapToIntersection(edge)) + snaps.extend(self.snapToElines(edge,eline)) - if isinstance (edge.Curve,Part.Circle): - # the edge is an arc, we have extra options - snaps.extend(self.snapToAngles(edge)) - snaps.extend(self.snapToCenter(edge)) + if isinstance (edge.Curve,Part.Circle): + # the edge is an arc, we have extra options + snaps.extend(self.snapToAngles(edge)) + snaps.extend(self.snapToCenter(edge)) elif "Vertex" in comp: # directly snapped to a vertex @@ -321,7 +336,12 @@ class Snapper: view = Draft.get3DView() pt = view.getPoint(x,y) if hasattr(FreeCAD,"DraftWorkingPlane"): - dv = view.getViewDirection() + if view.getCameraType() == "Perspective": + camera = view.getCameraNode() + p = camera.getField("position").getValue() + dv = pt.sub(Vector(p[0],p[1],p[2])) + else: + dv = view.getViewDirection() return FreeCAD.DraftWorkingPlane.projectPoint(pt,dv) else: return pt @@ -370,15 +390,17 @@ class Snapper: else: if self.isEnabled('parallel'): if last: - de = Part.Line(last,last.add(DraftGeomUtils.vec(e))).toShape() - np = self.getPerpendicular(de,point) - if (np.sub(point)).Length < self.radius: - if self.tracker: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['parallel']) - self.tracker.on() - self.setCursor('parallel') - return np,de + ve = DraftGeomUtils.vec(e) + if not DraftVecUtils.isNull(ve): + de = Part.Line(last,last.add(ve)).toShape() + np = self.getPerpendicular(de,point) + if (np.sub(point)).Length < self.radius: + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['parallel']) + self.tracker.on() + self.setCursor('parallel') + return np,de return point,eline def snapToPolar(self,point,last): @@ -418,18 +440,19 @@ class Snapper: def snapToGrid(self,point): "returns a grid snap point if available" if self.grid: - if self.isEnabled("grid"): - np = self.grid.getClosestNode(point) - if np: - if self.radius != 0: - dv = point.sub(np) - if dv.Length <= self.radius: - if self.tracker: - self.tracker.setCoords(np) - self.tracker.setMarker(self.mk['grid']) - self.tracker.on() - self.setCursor('grid') - return np + if self.grid.Visible: + if self.isEnabled("grid"): + np = self.grid.getClosestNode(point) + if np: + if self.radius != 0: + dv = point.sub(np) + if dv.Length <= self.radius: + if self.tracker: + self.tracker.setCoords(np) + self.tracker.setMarker(self.mk['grid']) + self.tracker.on() + self.setCursor('grid') + return np return point def snapToEndpoints(self,shape): @@ -627,12 +650,18 @@ class Snapper: v.setCursor(cur) self.cursorMode = mode + def restack(self): + if self.grid: + self.grid.lowerTracker() + def off(self): "finishes snapping" if self.tracker: self.tracker.off() if self.extLine: self.extLine.off() + if self.radiusTracker: + self.radiusTracker.off() if self.grid: if not Draft.getParam("alwaysShowGrid"): self.grid.off() @@ -867,6 +896,13 @@ class Snapper: self.toolbarButtons[i].setEnabled(False) self.saveSnapModes() + def showradius(self): + "shows the snap radius indicator" + self.radius = self.getScreenDist(Draft.getParam("snapRange"),(400,300)) + if self.radiusTracker: + self.radiusTracker.update(self.radius) + self.radiusTracker.on() + def isEnabled(self,but): "returns true if the given button is turned on" for b in self.toolbarButtons: @@ -875,7 +911,7 @@ class Snapper: return False def show(self): - "shows the toolbar" + "shows the toolbar and the grid" if not hasattr(self,"toolbar"): self.makeSnapToolBar() mw = getMainWindow() @@ -883,13 +919,18 @@ class Snapper: if not bt: mw.addToolBar(self.toolbar) self.toolbar.show() + if FreeCADGui.ActiveDocument: + if not self.forceGridOff: + if not self.grid: + self.grid = DraftTrackers.gridTracker() + self.grid.set() def setGrid(self): "sets the grid, if visible" - if self.grid: + if self.grid and (not self.forceGridOff): if self.grid.Visible: self.grid.set() - + if not hasattr(FreeCADGui,"Snapper"): FreeCADGui.Snapper = Snapper() if not hasattr(FreeCAD,"DraftWorkingPlane"): diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 885e781c64..0407369277 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -30,7 +30,6 @@ __url__ = "http://free-cad.sourceforge.net" #--------------------------------------------------------------------------- import os, FreeCAD, FreeCADGui, WorkingPlane, math, re, importSVG, Draft, Draft_rc, DraftVecUtils -from functools import partial from FreeCAD import Vector from DraftGui import todo,QtCore,QtGui from DraftSnap import * @@ -144,22 +143,6 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): cmod = hasMod(args,MODCONSTRAIN) point = FreeCADGui.Snapper.snap(args["Position"],lastpoint=last,active=amod,constrain=cmod) info = FreeCADGui.Snapper.snapInfo - # project onto working plane if needed - if (not plane.weak) and workingplane: - # working plane was explicitely selected - project onto it - viewDirection = view.getViewDirection() - if view.getCameraType() == "Perspective": - camera = view.getCameraNode() - p = camera.getField("position").getValue() - # view is from camera to point: - viewDirection = point.sub(Vector(p[0],p[1],p[2])) - # if we are not snapping to anything, project along view axis, - # otherwise perpendicularly - if view.getObjectInfo((args["Position"][0],args["Position"][1])): - pass - # point = plane.projectPoint(point) - else: - point = plane.projectPoint(point, viewDirection) ctrlPoint = Vector(point) mask = FreeCADGui.Snapper.affinity if target.node: @@ -170,8 +153,19 @@ def getPoint(target,args,mobile=False,sym=False,workingplane=True): else: ui.displayPoint(point, plane=plane, mask=mask) return point,ctrlPoint,info -def getSupport(args): +def getSupport(args=None): "returns the supporting object and sets the working plane" + if not args: + sel = FreeCADGui.Selection.getSelectionEx() + if len(sel) == 1: + sel = sel[0] + if sel.HasSubObjects: + if len(sel.SubElementNames) == 1: + if "Face" in sel.SubElementNames[0]: + plane.alignToFace(sel.SubObjects[0]) + return sel.Object + return None + snapped = Draft.get3DView().getObjectInfo((args["Position"][0],args["Position"][1])) if not snapped: return None obj = None @@ -205,11 +199,125 @@ def setMod(args,mod,state): elif mod == "alt": args["AltDown"] = state + + + +#--------------------------------------------------------------------------- +# Base Class +#--------------------------------------------------------------------------- + +class DraftTool: + "The base class of all Draft Tools" + + def __init__(self): + self.commitList = [] + + def IsActive(self): + if FreeCADGui.ActiveDocument: + return True + else: + return False + + def Activated(self,name="None"): + if FreeCAD.activeDraftCommand: + FreeCAD.activeDraftCommand.finish() + + global Part, DraftGeomUtils + import Part, DraftGeomUtils + + self.ui = None + self.call = None + self.support = None + self.commitList = [] + self.doc = FreeCAD.ActiveDocument + if not self.doc: + self.finish() + return + + FreeCAD.activeDraftCommand = self + self.view = Draft.get3DView() + self.ui = FreeCADGui.draftToolBar + self.ui.sourceCmd = self + self.ui.setTitle(name) + self.ui.show() + rot = self.view.getCameraNode().getField("orientation").getValue() + upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) + plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) + self.node = [] + self.pos = [] + self.constrain = None + self.obj = None + self.extendedCopy = False + self.ui.setTitle(name) + self.featureName = name + #self.snap = snapTracker() + #self.extsnap = lineTracker(dotted=True) + self.planetrack = None + if Draft.getParam("showPlaneTracker"): + self.planetrack = PlaneTracker() + + def finish(self): + self.node = [] + #self.snap.finalize() + #self.extsnap.finalize() + FreeCAD.activeDraftCommand = None + if self.ui: + self.ui.offUi() + self.ui.sourceCmd = None + #self.ui.cross(False) + msg("") + if self.planetrack: + self.planetrack.finalize() + if self.support: + plane.restore() + FreeCADGui.Snapper.off() + if self.call: + self.view.removeEventCallback("SoEvent",self.call) + self.call = None + if self.commitList: + todo.delayCommit(self.commitList) + self.commitList = [] + + def commit(self,name,func): + "stores actions to be committed to the FreeCAD document" + # print "committing" + self.commitList.append((name,func)) + + def getStrings(self): + "returns a couple of useful strings fro building python commands" + + # current plane rotation + p = plane.getRotation() + qr = p.Rotation.Q + qr = '('+str(qr[0])+','+str(qr[1])+','+str(qr[2])+','+str(qr[3])+')' + + # support object + if self.support: + sup = 'FreeCAD.ActiveDocument.getObject("' + self.support.Name + '")' + else: + sup = 'None' + + # contents of self.node + points='[' + for n in self.node: + if len(points) > 1: + points += ',' + points += DraftVecUtils.toString(n) + points += ']' + + # fill mode + if self.ui: + fil = str(bool(self.ui.fillmode)) + else: + fil = "True" + + return qr,sup,points,fil + #--------------------------------------------------------------------------- # Helper tools -#--------------------------------------------------------------------------- - -class SelectPlane: +#--------------------------------------------------------------------------- + +class SelectPlane(DraftTool): "The Draft_SelectPlane FreeCAD command definition" def GetResources(self): @@ -217,27 +325,24 @@ class SelectPlane: 'Accel' : "W, P", 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "SelectPlane"), 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_SelectPlane", "Select a working plane for geometry creation")} - - def IsActive(self): - if FreeCADGui.ActiveDocument: - return True - else: - return False def Activated(self): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() + DraftTool.Activated(self) self.offset = 0 - self.ui = None - self.call = None - self.doc = FreeCAD.ActiveDocument if self.doc: - FreeCAD.activeDraftCommand = self - self.view = Draft.get3DView() - self.ui = FreeCADGui.draftToolBar + sel = FreeCADGui.Selection.getSelectionEx() + if len(sel) == 1: + sel = sel[0] + if sel.HasSubObjects: + if len(sel.SubElementNames) == 1: + if "Face" in sel.SubElementNames[0]: + self.ui = FreeCADGui.draftToolBar + plane.alignToFace(sel.SubObjects[0], self.offset) + self.display(plane.axis) + self.finish() + return self.ui.selectPlaneUi() msg(translate("draft", "Pick a face to define the drawing plane\n")) - self.ui.sourceCmd = self if plane.alignToSelection(self.offset): FreeCADGui.Selection.clearSelection() self.display(plane.axis) @@ -306,88 +411,25 @@ class SelectPlane: self.ui.wplabel.setText(plv+suffix) FreeCADGui.Snapper.setGrid() - def finish(self): - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - #--------------------------------------------------------------------------- # Geometry constructors #--------------------------------------------------------------------------- - -class Creator: + +class Creator(DraftTool): "A generic Draft Creator Tool used by creation tools such as line or arc" def __init__(self): - self.commitList = [] - + DraftTool.__init__(self) + def Activated(self,name="None"): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() - global Part, DraftGeomUtils - import Part, DraftGeomUtils - self.ui = None - self.call = None - self.doc = None - self.support = None - self.commitList = [] - self.doc = FreeCAD.ActiveDocument - self.view = Draft.get3DView() - self.featureName = name - if not self.doc: - self.finish() - else: - FreeCAD.activeDraftCommand = self - self.ui = FreeCADGui.draftToolBar - self.ui.sourceCmd = self - self.ui.setTitle(name) - self.ui.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) - self.node = [] - self.pos = [] - self.constrain = None - self.obj = None - self.snap = snapTracker() - self.extsnap = lineTracker(dotted=True) - self.planetrack = PlaneTracker() - - def IsActive(self): - if FreeCADGui.ActiveDocument: - return True - else: - return False - - def finish(self): - self.snap.finalize() - self.extsnap.finalize() - self.node=[] - self.planetrack.finalize() - if self.support: plane.restore() - FreeCADGui.Snapper.off() - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - self.ui.sourceCmd = None - msg("") - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - self.call = None - if self.commitList: - todo.delayCommit(self.commitList) - self.commitList = [] - - def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" - self.commitList.append((name,func)) - + DraftTool.Activated(self) + self.support = getSupport() + class Line(Creator): "The Line FreeCAD command definition" def __init__(self, wiremode=False): + Creator.__init__(self) self.isWire = wiremode def GetResources(self): @@ -415,13 +457,17 @@ class Line(Creator): def finish(self,closed=False,cont=False): "terminates the operation and closes the poly if asked" if self.obj: + # remove temporary object, if any old = self.obj.Name todo.delay(self.doc.removeObject,old) self.obj = None if (len(self.node) > 1): + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create DWire"), - partial(Draft.makeWire,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+pts, + 'Draft.makeWire(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) if self.ui: self.linetrack.finalize() self.constraintrack.finalize() @@ -447,7 +493,8 @@ class Line(Creator): if (arg["Position"] == self.pos): self.finish(False,cont=True) else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.pos = arg["Position"] self.node.append(point) @@ -483,7 +530,8 @@ class Line(Creator): if (len(self.node) == 1): self.linetrack.on() msg(translate("draft", "Pick next point:\n")) - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) elif (len(self.node) == 2): last = self.node[len(self.node)-2] newseg = Part.Line(last,point).toShape() @@ -506,7 +554,8 @@ class Line(Creator): self.obj.ViewObject.Visibility = False self.node = [self.node[-1]] self.linetrack.p1(self.node[0]) - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) msg(translate("draft", "Pick next point:\n")) def numericInput(self,numx,numy,numz): @@ -569,7 +618,8 @@ class BSpline(Line): if (arg["Position"] == self.pos): self.finish(False,cont=True) else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.pos = arg["Position"] self.node.append(point) @@ -599,7 +649,8 @@ class BSpline(Line): def drawUpdate(self,point): if (len(self.node) == 1): self.bsplinetrack.on() - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) msg(translate("draft", "Pick next point:\n")) else: spline = Part.BSplineCurve() @@ -615,9 +666,12 @@ class BSpline(Line): old = self.obj.Name self.doc.removeObject(old) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create BSpline"), - partial(Draft.makeBSpline,self.node,closed, - face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'points='+pts, + 'Draft.makeBSpline(points,closed='+str(closed)+',face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" if self.ui: @@ -728,12 +782,15 @@ class Rectangle(Creator): if abs(DraftVecUtils.angle(p4.sub(p1),plane.u,plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1),plane.v,plane.axis)) > 1: height = -height - p = plane.getRotation() - p.move(p1) try: + # building command string + rot,sup,pts,fil = self.getStrings() self.commit(translate("draft","Create Rectangle"), - partial(Draft.makeRectangle,length,height, - p,self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(p1), + 'Draft.makeRectangle(length='+str(length)+',height='+str(height)+',placement=pl,face='+fil+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -752,7 +809,8 @@ class Rectangle(Creator): if (arg["Position"] == self.pos): self.finish() else: - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) point,ctrlPoint,info = getPoint(self,arg) self.appendPoint(point) @@ -771,7 +829,8 @@ class Rectangle(Creator): self.ui.setRelative() self.rect.setorigin(point) self.rect.on() - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) class Arc(Creator): @@ -948,7 +1007,8 @@ class Arc(Creator): if not DraftVecUtils.isNull(viewdelta): point = point.add(DraftVecUtils.neg(viewdelta)) if (self.step == 0): # choose center - self.support = getSupport(arg) + if not self.support: + self.support = getSupport(arg) if hasMod(arg,MODALT): snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1])) if snapped: @@ -976,7 +1036,8 @@ class Arc(Creator): self.step = 1 self.linetrack.on() msg(translate("draft", "Pick radius:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): # choose radius if self.closedCircle: self.ui.cross(False) @@ -1000,23 +1061,30 @@ class Arc(Creator): def drawArc(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() if self.closedCircle: try: - self.commit(translate("draft","Create Circle"), - partial(Draft.makeCircle,self.rad,p, - self.ui.fillmode,support=self.support)) + # building command string + self.commit(translate("draft","Create Circle"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',support='+sup+')']) except: - print "Draft: error delaying commit" + print "Draft: error delaying commit" else: sta = math.degrees(self.firstangle) end = math.degrees(self.firstangle+self.angle) if end < sta: sta,end = end,sta try: - self.commit(translate("draft","Create Arc"), - partial(Draft.makeCircle,self.rad,p,self.ui.fillmode, - sta,end,support=self.support)) + # building command string + self.commit(translate("draft","Create Arc"), + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makeCircle(radius='+str(self.rad)+',placement=pl,face='+fil+',startangle='+str(sta)+',endangle='+str(end)+',support='+sup+')']) except: print "Draft: error delaying commit" self.finish(cont=True) @@ -1209,7 +1277,8 @@ class Polygon(Creator): if not DraftVecUtils.isNull(viewdelta): point = point.add(DraftVecUtils.neg(viewdelta)) if (self.step == 0): # choose center - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) if hasMod(arg,MODALT): snapped=self.view.getObjectInfo((arg["Position"][0],arg["Position"][1])) if snapped: @@ -1237,18 +1306,22 @@ class Polygon(Creator): self.step = 1 self.linetrack.on() msg(translate("draft", "Pick radius:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): # choose radius self.ui.cross(False) self.drawPolygon() def drawPolygon(self): "actually draws the FreeCAD object" - p = plane.getRotation() - p.move(self.center) + rot,sup,pts,fil = self.getStrings() + # building command string self.commit(translate("draft","Create Polygon"), - partial(Draft.makePolygon,self.ui.numFaces.value(),self.rad, - True,p,face=self.ui.fillmode,support=self.support)) + ['import Draft', + 'pl=FreeCAD.Placement()', + 'pl.Rotation.Q='+rot, + 'pl.Base='+DraftVecUtils.toString(self.center), + 'Draft.makePolygon('+str(self.ui.numFaces.value())+',radius='+str(self.rad)+',inscribed=True,placement=pl,face='+fil+',support='+sup+')']) self.finish(cont=True) def numericInput(self,numx,numy,numz): @@ -1313,8 +1386,16 @@ class Text(Creator): def createObject(self): "creates an object in the current doc" + tx = '[' + for l in self.text: + if len(tx) > 1: + tx += ',' + tx += '"'+str(l)+'"' + tx += ']' self.commit(translate("draft","Create Text"), - partial(Draft.makeText,self.text,self.node[0])) + ['import Draft', + 'Draft.makeText('+tx+',point='+DraftVecUtils.toString(self.node[0])+')']) + self.finish(cont=True) def action(self,arg): @@ -1416,28 +1497,28 @@ class Dimension(Creator): pt = o.ViewObject.RootNode.getChildren()[1].getChildren()[0].getChildren()[0].getChildren()[3] p3 = Vector(pt.point.getValues()[2].getValue()) self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,p1,p2,p3)) - self.commit(translate("draft","Delete Measurement"), - partial(FreeCAD.ActiveDocument.removeObject,o.Name)) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(p1)+','+DraftVecUtils.toString(p2)+','+DraftVecUtils.toString(p3)+')', + 'FreeCAD.ActiveDocument.removeObject("'+o.Name+'")']) def createObject(self): "creates an object in the current doc" if self.angledata: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeAngularDimension,self.center, - self.angledata,self.node[-1])) + ['import Draft', + 'Draft.makeAngularDimension(center='+DraftVecUtils.toString(self.center)+',angles=['+str(self.angledata[0])+','+str(self.angledata[1])+'],p3='+DraftVecUtils.toString(self.node[-1])+')']) elif self.link and (not self.arcmode): self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.link[0],self.link[1], - self.link[2],self.node[2])) + ['import Draft', + 'Draft.makeDimension(FreeCAD.ActiveDocument.'+self.link[0].Name+','+str(self.link[1])+','+str(self.link[2])+','+DraftVecUtils.toString(self.node[2])+')']) elif self.arcmode: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.link[0],self.link[1], - self.arcmode,self.node[2])) + ['import Draft', + 'Draft.makeDimension(FreeCAD.ActiveDocument.'+self.link[0].Name+','+str(self.link[1])+',"'+str(self.arcmode)+'",'+DraftVecUtils.toString(self.node[2])+')']) else: self.commit(translate("draft","Create Dimension"), - partial(Draft.makeDimension,self.node[0],self.node[1], - self.node[2])) + ['import Draft', + 'Draft.makeDimension('+DraftVecUtils.toString(self.node[0])+','+DraftVecUtils.toString(self.node[1])+','+DraftVecUtils.toString(self.node[2])+')']) if self.ui.continueMode: self.cont = self.node[2] if not self.dir: @@ -1544,7 +1625,8 @@ class Dimension(Creator): elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): point,ctrlPoint,info = getPoint(self,arg) - if not self.node: self.support = getSupport(arg) + if (not self.node) and (not self.support): + self.support = getSupport(arg) if hasMod(arg,MODALT) and (len(self.node)<3): print "snapped: ",info if info: @@ -1596,13 +1678,14 @@ class Dimension(Creator): if self.dir: point = self.node[0].add(DraftVecUtils.project(point.sub(self.node[0]),self.dir)) self.node.append(point) - print "node",self.node + #print "node",self.node self.dimtrack.update(self.node) if (len(self.node) == 2): self.point2 = self.node[1] if (len(self.node) == 1): self.dimtrack.on() - self.planetrack.set(self.node[0]) + if self.planetrack: + self.planetrack.set(self.node[0]) elif (len(self.node) == 2) and self.cont: self.node.append(self.cont) self.createObject() @@ -1637,73 +1720,18 @@ class Dimension(Creator): # Modifier functions #--------------------------------------------------------------------------- -class Modifier: +class Modifier(DraftTool): "A generic Modifier Tool, used by modification tools such as move" - - def __init__(self): - self.commitList = [] + def __init__(self): + DraftTool.__init__(self) + def IsActive(self): if Draft.getSelection(): return True else: return False - def Activated(self,name="None"): - if FreeCAD.activeDraftCommand: - FreeCAD.activeDraftCommand.finish() - global Part, DraftGeomUtils - import Part, DraftGeomUtils - self.ui = None - self.call = None - self.commitList = [] - self.doc = FreeCAD.ActiveDocument - if not self.doc: - self.finish() - else: - FreeCAD.activeDraftCommand = self - self.view = Draft.get3DView() - self.ui = FreeCADGui.draftToolBar - FreeCADGui.draftToolBar.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) - self.node = [] - self.ui.sourceCmd = self - self.constrain = None - self.obj = None - self.extendedCopy = False - self.ui.setTitle(name) - self.featureName = name - #self.snap = snapTracker() - #self.extsnap = lineTracker(dotted=True) - self.planetrack = PlaneTracker() - - def finish(self): - self.node = [] - #self.snap.finalize() - #self.extsnap.finalize() - FreeCAD.activeDraftCommand = None - if self.ui: - self.ui.offUi() - self.ui.sourceCmd=None - self.ui.cross(False) - msg("") - self.planetrack.finalize() - FreeCADGui.Snapper.off() - if self.call: - self.view.removeEventCallback("SoEvent",self.call) - self.call = None - if self.commitList: - todo.delayCommit(self.commitList) - self.commitList = [] - - def commit(self,name,func): - "stores partial actions to be committed to the FreeCAD document" - # print "committing" - self.commitList.append((name,func)) - - class Move(Modifier): "The Draft_Move FreeCAD command definition" @@ -1755,10 +1783,20 @@ class Move(Modifier): def move(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: - self.commit(translate("draft","Copy"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Copy"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) else: - self.commit(translate("draft","Move"),partial(Draft.move,self.sel,delta,copy)) + self.commit(translate("draft","Move"), + ['import Draft', + 'Draft.move('+sel+','+DraftVecUtils.toString(delta)+',copy='+str(copy)+')']) self.doc.recompute() def action(self,arg): @@ -1792,7 +1830,8 @@ class Move(Modifier): self.ghost.on() self.linetrack.p1(point) msg(translate("draft", "Pick end point:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) else: last = self.node[0] if self.ui.isCopy.isChecked() or hasMod(arg,MODALT): @@ -1843,19 +1882,21 @@ class ApplyStyle(Modifier): if self.ui: self.sel = Draft.getSelection() if (len(self.sel)>0): + c = ['import Draft'] for ob in self.sel: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') + self.commit(translate("draft","Change Style"),c) def formatGroup(self,grpob): + c=[] for ob in grpob.Group: if (ob.Type == "App::DocumentObjectGroup"): - self.formatGroup(ob) + c.extend(self.formatGroup(ob)) else: - self.commit(translate("draft","Change Style"),partial(Draft.formatObject,ob)) - + c.append('Draft.formatObject(FreeCAD.ActiveDocument.'+ob.Name+')') class Rotate(Modifier): "The Draft_Rotate FreeCAD command definition" @@ -1913,14 +1954,20 @@ class Rotate(Modifier): def rot (self,angle,copy=False): "rotating the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) else: self.commit(translate("draft","Rotate"), - partial(Draft.rotate,self.sel, - math.degrees(angle),self.center,plane.axis,copy)) + ['import Draft', + 'Draft.rotate('+sel+','+str(math.degrees(angle))+','+DraftVecUtils.toString(self.center)+',axis='+DraftVecUtils.toString(plane.axis)+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" @@ -1999,7 +2046,8 @@ class Rotate(Modifier): self.linetrack.on() self.step = 1 msg(translate("draft", "Pick base angle:\n")) - self.planetrack.set(point) + if self.planetrack: + self.planetrack.set(point) elif (self.step == 1): self.ui.labelRadius.setText("Rotation") self.rad = DraftVecUtils.dist(point,self.center) @@ -2115,7 +2163,8 @@ class Offset(Modifier): self.call = self.view.addEventCallback("SoEvent",self.action) msg(translate("draft", "Pick distance:\n")) self.ui.cross(True) - self.planetrack.set(self.shape.Vertexes[0].Point) + if self.planetrack: + self.planetrack.set(self.shape.Vertexes[0].Point) self.running = True def action(self,arg): @@ -2181,13 +2230,18 @@ class Offset(Modifier): occmode = self.ui.occOffset.isChecked() if hasMod(arg,MODALT) or self.ui.isCopy.isChecked(): copymode = True if self.npts: + print "offset:npts=",self.npts self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.npts,copymode,occ=False)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+DraftVecUtils.toString(self.ntps)+',copy='+str(copymode)+')']) elif self.dvec: + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) if hasMod(arg,MODALT): self.extendedCopy = True else: @@ -2209,9 +2263,13 @@ class Offset(Modifier): copymode = False occmode = self.ui.occOffset.isChecked() if self.ui.isCopy.isChecked(): copymode = True + if isinstance(self.dvec,float): + d = str(self.dvec) + else: + d = DraftVecUtils.toString(self.dvec) self.commit(translate("draft","Offset"), - partial(Draft.offset,self.sel, - self.dvec,copymode,occ=occmode)) + ['import Draft', + 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) self.finish() @@ -2933,12 +2991,20 @@ class Scale(Modifier): def scale(self,delta,copy=False): "moving the real shapes" + sel = '[' + for o in self.sel: + if len(sel) > 1: + sel += ',' + sel += 'FreeCAD.ActiveDocument.'+o.Name + sel += ']' if copy: self.commit(translate("draft","Copy"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) else: self.commit(translate("draft","Scale"), - partial(Draft.scale,self.sel,delta,self.node[0],copy)) + ['import Draft', + 'Draft.scale('+sel+',delta='+DraftVecUtils.toString(delta)+',center='+DraftVecUtils.toString(self.node[0])+',copy='+str(copy)+')']) def action(self,arg): "scene event handler" @@ -3190,7 +3256,8 @@ class Edit(Modifier): plane.save() if "Shape" in self.obj.PropertiesList: plane.alignToFace(self.obj.Shape) - self.planetrack.set(self.editpoints[0]) + if self.planetrack: + self.planetrack.set(self.editpoints[0]) else: msg(translate("draft", "This object type is not editable\n"),'warning') self.finish() @@ -3736,7 +3803,29 @@ class Draft_Clone(): return True else: return False - + + +class ToggleGrid(): + "The Draft ToggleGrid command definition" + + def GetResources(self): + return {'Pixmap' : 'Snap_Grid', + 'Accel' : "G,R", + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ToggleGrid", "Toggle Grid"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_ToggleGrid", "Toggles the Draft gid on/off")} + + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if FreeCADGui.Snapper.grid: + if FreeCADGui.Snapper.grid.Visible: + FreeCADGui.Snapper.grid.off() + FreeCADGui.Snapper.forceGridOff=True + else: + FreeCADGui.Snapper.grid.on() + FreeCADGui.Snapper.forceGridOff=False + else: + FreeCADGui.Snapper.show() + #--------------------------------------------------------------------------- # Adds the icons & commands to the FreeCAD command manager, and sets defaults #--------------------------------------------------------------------------- @@ -3784,6 +3873,7 @@ FreeCADGui.addCommand('Draft_SelectGroup',SelectGroup()) FreeCADGui.addCommand('Draft_Shape2DView',Shape2DView()) FreeCADGui.addCommand('Draft_ToggleSnap',ToggleSnap()) FreeCADGui.addCommand('Draft_ShowSnapBar',ShowSnapBar()) +FreeCADGui.addCommand('Draft_ToggleGrid',ToggleGrid()) # a global place to look for active draft Command FreeCAD.activeDraftCommand = None diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index 5223ebf667..c36d031522 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -81,6 +81,22 @@ class Tracker: self.switch.whichChild = -1 self.Visible = False + def lowerTracker(self): + '''lowers the tracker to the bottom of the scenegraph, so + it doesn't obscure the other objects''' + if self.switch: + sg=Draft.get3DView().getSceneGraph() + sg.removeChild(self.switch) + sg.addChild(self.switch) + + def raiseTracker(self): + '''raises the tracker to the top of the scenegraph, so + it obscures the other objects''' + if self.switch: + sg=Draft.get3DView().getSceneGraph() + sg.removeChild(self.switch) + sg.insertChild(self.switch,0) + class snapTracker(Tracker): "A Snap Mark tracker, used by tools that support snapping" def __init__(self): @@ -617,29 +633,21 @@ class gridTracker(Tracker): def set(self): Q = FreeCAD.DraftWorkingPlane.getRotation().Rotation.Q + P = FreeCAD.DraftWorkingPlane.position self.trans.rotation.setValue([Q[0],Q[1],Q[2],Q[3]]) + self.trans.translation.setValue([P.x,P.y,P.z]) self.on() def getClosestNode(self,point): "returns the closest node from the given point" # get the 2D coords. - point = FreeCAD.DraftWorkingPlane.projectPoint(point) - u = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.u) - lu = u.Length - if u.getAngle(FreeCAD.DraftWorkingPlane.u) > 1.5: - lu = -lu - v = DraftVecUtils.project(point,FreeCAD.DraftWorkingPlane.v) - lv = v.Length - if v.getAngle(FreeCAD.DraftWorkingPlane.v) > 1.5: - lv = -lv - # print "u = ",u," v = ",v - # find nearest grid node - pu = (round(lu/self.space,0))*self.space - pv = (round(lv/self.space,0))*self.space - rot = FreeCAD.Rotation() - rot.Q = self.trans.rotation.getValue().getValue() - return rot.multVec(Vector(pu,pv,0)) - + # point = FreeCAD.DraftWorkingPlane.projectPoint(point) + pt = FreeCAD.DraftWorkingPlane.getLocalCoords(point) + pu = (round(pt.x/self.space,0))*self.space + pv = (round(pt.y/self.space,0))*self.space + pt = FreeCAD.DraftWorkingPlane.getGlobalCoords(Vector(pu,pv,0)) + return pt + class boxTracker(Tracker): "A box tracker, can be based on a line object" def __init__(self,line=None,width=0.1,height=1): @@ -701,3 +709,27 @@ class boxTracker(Tracker): self.update() else: return self.cube.depth.getValue() + +class radiusTracker(Tracker): + "A tracker that displays a transparent sphere to inicate a radius" + def __init__(self,position=FreeCAD.Vector(0,0,0),radius=1): + self.trans = coin.SoTransform() + self.trans.translation.setValue([position.x,position.y,position.z]) + m = coin.SoMaterial() + m.transparency.setValue(0.9) + m.diffuseColor.setValue([0,1,0]) + self.sphere = coin.SoSphere() + self.sphere.radius.setValue(radius) + self.baseline = None + Tracker.__init__(self,children=[self.trans,m,self.sphere]) + + def update(self,arg1,arg2=None): + if isinstance(arg1,FreeCAD.Vector): + self.trans.translation.setValue([arg1.x,arg1.y,arg1.z]) + else: + self.sphere.radius.setValue(arg1) + if arg2 != None: + if isinstance(arg2,FreeCAD.Vector): + self.trans.translation.setValue([arg2.x,arg2.y,arg2.z]) + else: + self.sphere.radius.setValue(arg2) diff --git a/src/Mod/Draft/DraftVecUtils.py b/src/Mod/Draft/DraftVecUtils.py index 1f10a2c7ea..678965e85a 100644 --- a/src/Mod/Draft/DraftVecUtils.py +++ b/src/Mod/Draft/DraftVecUtils.py @@ -40,6 +40,10 @@ def typecheck (args_and_types, name="?"): FreeCAD.Console.PrintWarning("typecheck[" + str(name) + "]: " + str(v) + " is not " + str(t) + "\n") raise TypeError("fcvec." + str(name)) +def toString(u): + "returns a string containing a python command to recreate this vector" + return "FreeCAD.Vector("+str(u.x)+","+str(u.y)+","+str(u.z)+")" + def tup(u,array=False): "returns a tuple (x,y,z) with the vector coords, or an array if array=true" typecheck ([(u,Vector)], "tup"); diff --git a/src/Mod/Draft/Draft_rc.py b/src/Mod/Draft/Draft_rc.py index e1f587ddd4..4028d367e7 100644 --- a/src/Mod/Draft/Draft_rc.py +++ b/src/Mod/Draft/Draft_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Tue May 22 16:46:43 2012 +# Created: Wed Jun 6 16:21:23 2012 # by: The Resource Compiler for PyQt (Qt v4.8.1) # # WARNING! All changes made in this file will be lost! @@ -117,79 +117,150 @@ qt_resource_data = "\ \x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\ \x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ \ -\x00\x00\x04\x69\ -\x00\ -\x00\x15\x07\x78\x9c\xbd\x58\xdb\x6e\x1b\x37\x10\x7d\xef\x57\x2c\ -\x36\x2f\x2d\xb0\xa1\x78\xbf\xb8\x52\x7e\xa1\x0f\x45\x3e\x40\xb0\ -\xd7\xb2\x10\x47\x0e\x24\xb5\x49\xfa\xf5\x1d\xde\x96\xb4\xe1\x59\ -\xcf\x4b\x2c\x01\xc2\x90\x7b\x20\x9e\x1d\xce\x99\x19\x72\x7b\xf9\ -\xf7\xf0\xe9\xb7\x61\xd8\xde\xcd\xf7\x97\x68\x80\xf9\x6d\x7f\xbd\ -\xce\xe7\x53\x1a\xc0\xe7\x78\xb7\x1b\x6f\x9f\x4e\xb7\xe7\xf9\x3a\ -\x8f\x75\xb2\x60\x3e\x9f\x8e\xd7\xcb\x6e\xfc\xe7\x32\x9f\xff\xfe\ -\xb6\xbf\x9d\xff\x3a\x7d\xbe\x34\xd0\x8f\xdd\xc8\x97\xc1\xcf\x7e\ -\xf0\xfd\x78\x77\x7d\xd8\x8d\xcc\x2c\x33\x0f\xf3\xf1\xf0\x70\x4d\ -\x53\x9f\xca\xdc\xf6\x50\x1f\x0e\xc3\xe5\xfa\xf3\x71\xde\x8d\xf7\ -\xc7\xc7\xc7\x9b\xd3\xd3\x69\xfe\x13\x66\xce\x4f\x5f\xe6\x9b\x0f\ -\x3c\x7d\xea\xf8\x63\xfa\xe7\x9b\xee\x8f\x87\xe1\x7a\xde\x9f\x2e\ -\xf7\x4f\xe7\xaf\xbb\xf1\x72\xbb\x7f\x9c\x7f\x67\x5c\xfc\xd1\x3d\ -\x8f\x2f\x78\x50\x9a\x8b\x65\xe5\xec\x84\x87\x06\x19\x06\xc0\x7c\ -\x1d\x0c\x53\xc6\x09\x2d\xfd\xa4\x98\x91\xd2\x07\x6d\x87\xfd\x20\ -\x98\x52\x41\x7a\xe3\xa6\xc5\x1a\xf8\x20\xe0\xfb\x51\x32\xeb\x60\ -\x28\xcc\xc4\x57\x60\x3d\xea\xbf\xb1\x5f\x35\x52\x8b\x4c\xa4\xd7\ -\x7c\x1c\x36\x6f\xf1\x13\x9a\x09\x0e\x04\xd5\x64\x99\x97\x56\x5b\ -\x2b\x80\x9f\x62\x32\x72\x36\x76\x5a\xac\xca\xcf\xb2\x38\x74\x42\ -\xc0\xca\x38\xac\x47\xa1\xfc\x24\x8d\x9f\x97\xd9\x7f\x9c\x05\xa5\ -\x2c\x0f\xc0\x8f\xb3\xe4\x53\xe3\xd4\xd4\xcc\xca\x50\x94\xb5\x61\ -\xe9\x35\x5c\x07\x43\x19\x6a\x02\x43\xe9\x59\xd9\x61\x61\x99\xe4\ -\x02\x5c\x08\x0c\x25\x93\x2a\xf2\x0e\xd3\x62\x55\x7e\x9a\x69\x0b\ -\x70\x0b\x0b\xe3\xa8\x0e\x84\xb2\xb3\x04\x76\x40\xc9\xc4\x68\x97\ -\x93\xe4\x8c\x7b\xa3\x82\x4e\xfe\xd3\xa1\x6c\x7b\x33\x2b\x3f\xf0\ -\xb3\xcf\xa4\xf8\x2a\xb0\xc7\xa1\x1c\x3d\x81\xa3\xd2\xcc\xc5\x97\ -\xd7\x93\x74\x20\x12\xe7\xa4\x49\x1c\x65\x8e\xf1\xb4\xb4\x8a\x6a\ -\x0b\xae\x71\x34\x49\x57\x89\x22\x8e\xeb\x60\x18\x43\x43\x51\x89\ -\xe6\x4c\xc4\x35\xe4\x14\x98\x82\xa8\xe6\x5e\x24\x86\xa2\xea\x13\ -\x48\xe4\xfd\xf3\x8d\x61\xa6\x95\x18\xe2\xb8\x0e\x86\x32\xa4\xe8\ -\x44\x97\xdd\xd0\x93\xf2\xcc\x69\x2b\x8a\x4e\xea\x72\xaf\x32\xd4\ -\x29\xc6\xb2\x0f\x51\x5c\x07\x43\x19\x52\x74\xa2\x04\x13\x96\xc7\ -\x94\x05\x5c\x8d\xb3\x59\x27\x8a\x25\xc7\xc0\xec\x62\x55\x7e\x8e\ -\xc5\x61\x8c\xbc\x15\x54\x07\x42\xd9\x51\x74\x22\x8b\x00\x55\xf4\ -\x9f\x34\x86\x2b\x93\xf2\x74\x64\x9c\xb4\x5d\xad\xca\xae\x64\x3c\ -\x9b\xd2\x34\x86\xea\x40\x28\x3b\x8a\x42\xb4\x60\x21\xee\x01\x14\ -\x02\x58\x42\x3b\x1f\xb2\x42\x7c\x76\x46\x8c\xfc\x6a\xb6\x2c\x98\ -\x44\x91\x15\x82\xe3\x3a\x18\xc6\xd0\x52\x14\xa2\x2c\xcb\x69\x42\ -\x1a\xe6\xa2\xfb\x5c\x22\x58\x8a\x5f\x0c\xfc\x6a\xb6\xf0\x73\xd5\ -\x37\x6b\xb8\x0e\x86\x12\xa4\x08\x24\x26\xc2\x49\x4b\x50\x89\x13\ -\x2e\x6b\xc3\xc2\x4c\xfa\x6d\x3e\x8b\x33\xaf\x3c\x29\x0f\x50\x02\ -\x94\xf8\x17\x8e\x15\x89\x89\xc0\x82\x29\x75\x82\xd0\x09\x90\x1a\ -\x81\x35\x76\x94\xf8\x17\x25\xc2\xec\x24\x3c\xe8\xd4\x38\x23\x73\ -\x9d\x48\xda\x4f\x99\xab\x9a\x6d\x03\x7d\x49\x6a\xab\xb8\x0e\x86\ -\x32\xa4\x68\x00\xaa\xab\xcd\x21\x06\x9a\x0f\x20\x01\x9d\x77\x31\ -\x67\x78\x19\x57\xae\x66\xdb\xcf\xa4\xc6\x5c\xc8\x70\x5c\x07\xc3\ -\x18\x3a\x92\x06\x96\x72\x23\x14\xb3\x42\x8a\x52\x25\xde\x66\xe8\ -\x69\x0c\xd7\x72\xb0\xa3\x88\x00\x2a\x6d\x52\xbf\x8d\x4a\x70\x22\ -\x28\x29\x89\xdd\x94\xa4\x75\x53\x72\x8d\x21\x45\x25\x7a\xf1\x61\ -\xec\xab\x52\xc3\x9c\x54\x62\x4a\xfd\x5d\xac\x96\x87\xb9\x5a\xda\ -\x65\x0c\xd5\x81\x50\x76\x14\x95\xa8\x90\xbb\x51\x37\x41\xbd\xe0\ -\xbc\xa4\xb9\x9a\x43\xd5\xb4\x58\x8d\x9d\xa9\x1b\x87\xa3\x3a\x10\ -\xca\x8e\xa2\x10\x59\x8a\xe1\x73\xdf\x41\xca\xcb\x82\x9c\x16\xab\ -\xb2\x33\x4d\x9a\x38\xca\xbc\xad\x5f\x4f\x51\x47\xd7\x89\x7a\xc8\ -\x35\x10\x7b\x79\x67\x75\xa9\xec\x8b\xd5\xf2\x5f\x7a\x9b\xa4\x4b\ -\x1c\xd6\xa3\x50\x7e\x24\x6d\xe4\x18\x89\x1d\x80\x62\xf0\x4a\x5c\ -\xe5\xfc\xe2\x79\x69\x21\xab\xd5\x94\x61\x3b\xf1\x62\xb0\x1e\x85\ -\xf2\x23\x29\x23\x9f\x33\x54\x4c\xd0\x9d\xfb\x7e\x79\x83\xe2\x49\ -\xc2\x50\x85\x1c\xb4\x01\x29\xf0\xb2\xef\xf2\x01\x21\x95\xf5\x6a\ -\x36\xef\x85\x7a\xc0\x59\xc3\x75\x30\x94\x20\x49\x1b\xa6\x64\xac\ -\x98\x1b\x8c\xaf\xc7\xc8\x77\x29\x6f\x81\x22\x0f\xb9\x64\x01\xcd\ -\x78\x23\xf8\x2e\x1e\x0c\x14\x7d\x80\x6a\x4b\x12\x85\xfe\xc5\xf0\ -\xf7\x6d\xf1\x02\xe9\x84\x01\x39\x59\x7b\x13\xe0\x94\x60\xfa\xbb\ -\x02\xe3\x9c\x07\xc1\xbc\x42\x10\x8e\x24\x06\x6a\x58\x29\xbf\x18\ -\xac\x43\xa1\xfc\x28\x22\xd1\x70\xba\x9d\xb4\x01\x92\x3c\xb7\x57\ -\xa9\xad\x8c\x3f\x2d\xe5\xe5\x3a\xf6\x7c\x3a\xcf\xa2\x4b\x93\x0e\ -\x10\xe5\x1a\x25\xc0\x01\xca\x05\x58\x5e\xbc\xdf\x35\x4a\xe0\x94\ -\xf8\x8f\xa7\xd6\x00\x1f\x68\x4d\x80\x62\xbd\x46\x79\x76\xb8\xaf\ -\xe6\xcb\x4b\x00\xf5\xf2\x16\xe0\x05\xb0\xc7\xa1\x1c\x7b\x09\x6c\ -\x37\x87\x72\xb1\xb9\x29\xb7\x96\xe9\xc2\x73\x93\x6f\x3c\xb7\x9b\ -\x78\x03\xfa\x3f\xe0\xde\x25\x35\ +\x00\x00\x08\xd6\ +\x3c\ +\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x63\x6f\x6e\x63\x72\x65\x74\x65\x22\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\ +\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\ +\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\x35\x22\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ +\x2e\x35\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ +\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\ +\x23\x30\x30\x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\ +\x77\x69\x64\x74\x68\x3a\x2e\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x73\x63\ +\x61\x6c\x65\x28\x2e\x30\x31\x29\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x67\x33\x34\x30\x31\x22\x3e\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ +\x6d\x20\x35\x2e\x33\x35\x37\x31\x34\x32\x38\x2c\x33\x2e\x35\x32\ +\x32\x38\x39\x34\x36\x20\x61\x20\x31\x2e\x33\x33\x39\x32\x38\x35\ +\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\ +\x31\x20\x2d\x32\x2e\x36\x37\x38\x35\x37\x31\x35\x2c\x30\x20\x31\ +\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\ +\x35\x37\x20\x30\x20\x31\x20\x31\x20\x32\x2e\x36\x37\x38\x35\x37\ +\x31\x35\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\ +\x31\x30\x37\x31\x34\x33\x2c\x36\x2e\x38\x32\x36\x34\x36\x36\x31\ +\x20\x61\x20\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x2c\x33\x2e\x32\ +\x31\x34\x32\x38\x35\x36\x20\x30\x20\x31\x20\x31\x20\x2d\x36\x2e\ +\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\x33\x2e\x32\x31\x34\x32\ +\x38\x35\x36\x2c\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x20\x30\x20\ +\x31\x20\x31\x20\x36\x2e\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\ +\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\ +\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\x38\x32\x31\x34\x32\ +\x38\x2c\x33\x30\x2e\x39\x33\x33\x36\x30\x39\x20\x61\x20\x30\x2e\ +\x37\x31\x34\x32\x38\x35\x37\x33\x2c\x30\x2e\x37\x31\x34\x32\x38\ +\x35\x37\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x34\x32\x38\ +\x35\x37\x31\x2c\x30\x20\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\ +\x2c\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\x20\x30\x20\x31\x20\ +\x31\x20\x31\x2e\x34\x32\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\ +\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\ +\x64\x3d\x22\x6d\x20\x32\x38\x2e\x35\x37\x31\x34\x32\x38\x2c\x31\ +\x36\x2e\x32\x30\x31\x34\x36\x36\x20\x61\x20\x32\x2e\x32\x33\x32\ +\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x20\x30\ +\x20\x31\x20\x31\x20\x2d\x34\x2e\x34\x36\x34\x32\x38\x36\x2c\x30\ +\x20\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\ +\x31\x34\x32\x39\x20\x30\x20\x31\x20\x31\x20\x34\x2e\x34\x36\x34\ +\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\x2e\ +\x32\x35\x30\x30\x30\x30\x32\x2c\x32\x30\x2e\x30\x38\x35\x33\x39\ +\x34\x20\x61\x20\x30\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x2c\x30\ +\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x20\x30\x20\x31\x20\x31\x20\ +\x2d\x30\x2e\x39\x38\x32\x31\x34\x32\x39\x2c\x30\x20\x30\x2e\x34\ +\x39\x31\x30\x37\x31\x34\x33\x2c\x30\x2e\x34\x39\x31\x30\x37\x31\ +\x34\x33\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x39\x38\x32\x31\x34\ +\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x33\x34\x2e\ +\x37\x33\x32\x31\x34\x34\x2c\x32\x37\x2e\x35\x32\x37\x37\x32\x35\ +\x20\x61\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x31\x33\x2c\x30\x2e\ +\x34\x33\x33\x34\x30\x31\x39\x37\x20\x30\x20\x31\x20\x31\x20\x2d\ +\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\x30\x20\x30\x2e\x32\x36\x37\ +\x38\x35\x37\x31\x33\x2c\x30\x2e\x34\x33\x33\x34\x30\x31\x39\x37\ +\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\ +\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ +\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x31\x37\x38\ +\x35\x37\x32\x2c\x39\x2e\x33\x37\x31\x31\x30\x38\x31\x20\x61\x20\ +\x30\x2e\x31\x33\x33\x39\x32\x38\x35\x37\x2c\x30\x2e\x32\x32\x33\ +\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x32\ +\x36\x37\x38\x35\x37\x2c\x30\x20\x30\x2e\x31\x33\x33\x39\x32\x38\ +\x35\x37\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\ +\x31\x20\x31\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x2c\x30\x20\x7a\ +\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\ +\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x39\x38\x32\x31\x34\x34\ +\x2c\x33\x38\x2e\x37\x34\x36\x31\x30\x39\x20\x61\x20\x30\x2e\x32\ +\x32\x33\x32\x31\x34\x32\x38\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\ +\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x34\x34\x36\x34\ +\x32\x38\x2c\x30\x20\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x2c\ +\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\ +\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x2c\x30\x20\x7a\x22\x2f\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ +\x3d\x22\x6d\x20\x33\x31\x2e\x31\x36\x30\x37\x31\x35\x2c\x34\x30\ +\x2e\x35\x37\x36\x34\x36\x36\x20\x61\x20\x33\x2e\x39\x32\x38\x35\ +\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x20\x30\x20\ +\x31\x20\x31\x20\x2d\x37\x2e\x38\x35\x37\x31\x34\x33\x2c\x30\x20\ +\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\ +\x37\x31\x35\x20\x30\x20\x31\x20\x31\x20\x37\x2e\x38\x35\x37\x31\ +\x34\x33\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x32\x32\x2e\ +\x32\x33\x32\x31\x34\x33\x2c\x33\x38\x2e\x32\x35\x35\x30\x33\x35\ +\x20\x61\x20\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x2c\x31\x2e\x36\ +\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x33\x2e\ +\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x31\x2e\x36\x30\x37\x31\x34\ +\x32\x38\x2c\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\ +\x20\x31\x20\x33\x2e\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\ +\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ +\x20\x64\x3d\x22\x6d\x20\x34\x31\x2e\x39\x36\x34\x32\x38\x37\x2c\ +\x31\x31\x2e\x36\x34\x37\x38\x39\x35\x20\x61\x20\x30\x2e\x38\x39\ +\x32\x38\x35\x37\x31\x33\x2c\x30\x2e\x38\x39\x32\x38\x35\x37\x31\ +\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x37\x38\x35\x37\x31\ +\x34\x2c\x30\x20\x30\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x2c\x30\ +\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x20\x30\x20\x31\x20\x31\x20\ +\x31\x2e\x37\x38\x35\x37\x31\x34\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\ +\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\ +\x22\x6d\x20\x33\x36\x2e\x30\x37\x31\x34\x33\x2c\x32\x35\x2e\x37\ +\x35\x35\x30\x33\x37\x20\x61\x20\x30\x2e\x33\x35\x37\x31\x34\x32\ +\x38\x37\x2c\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x20\x30\x20\ +\x31\x20\x31\x20\x2d\x30\x2e\x37\x31\x34\x32\x38\x36\x2c\x30\x20\ +\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x2c\x30\x2e\x33\x35\x37\ +\x31\x34\x32\x38\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x37\x31\ +\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\ +\x2e\x32\x35\x2c\x34\x32\x2e\x39\x38\x37\x31\x37\x39\x20\x61\x20\ +\x30\x2e\x36\x32\x35\x2c\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\ +\x31\x20\x2d\x31\x2e\x32\x35\x2c\x30\x20\x30\x2e\x36\x32\x35\x2c\ +\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x32\x35\ +\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ +\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x37\x2e\x33\x32\ +\x31\x34\x32\x38\x2c\x31\x39\x2e\x39\x35\x31\x34\x36\x36\x20\x61\ +\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\ +\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x2d\x32\x2e\x36\x37\ +\x38\x35\x37\x31\x2c\x30\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\ +\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\ +\x20\x32\x2e\x36\x37\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ +\x3d\x22\x6d\x20\x31\x31\x2e\x39\x36\x34\x32\x38\x36\x2c\x31\x38\ +\x2e\x31\x36\x35\x37\x35\x32\x20\x61\x20\x30\x2e\x34\x34\x36\x34\ +\x32\x38\x35\x37\x2c\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x20\ +\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x38\x39\x32\x38\x35\x37\x2c\ +\x30\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x2c\x30\x2e\x34\ +\x34\x36\x34\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\ +\x38\x39\x32\x38\x35\x37\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\ +\x20\x31\x36\x2e\x36\x30\x37\x31\x34\x33\x2c\x37\x2e\x38\x39\x37\ +\x38\x39\x34\x39\x20\x61\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x32\ +\x37\x2c\x30\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x20\x30\x20\x31\ +\x20\x31\x20\x2d\x31\x2e\x30\x37\x31\x34\x32\x39\x2c\x30\x20\x30\ +\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x2c\x30\x2e\x35\x33\x35\x37\ +\x31\x34\x32\x37\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x30\x37\x31\ +\x34\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ +\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\ +\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\ +\x2f\x73\x76\x67\x3e\ \x00\x00\x74\xe7\ \x3c\ \xb8\x64\x18\xca\xef\x9c\x95\xcd\x21\x1c\xbf\x60\xa1\xbd\xdd\x42\ @@ -26696,258 +26767,263 @@ qt_resource_data = "\ \x33\x2e\x72\x9f\xc2\xcc\xa8\x77\x7d\x66\xc6\x1b\xcc\xef\xf2\x48\ \xa6\x8c\x0e\x28\xa3\x2d\x9e\x88\x63\x66\x74\x00\x9f\x1b\xd5\x84\ \x37\x0f\xfe\x01\xbd\x89\x17\xfc\ -\x00\x00\x0f\x9f\ +\x00\x00\x0f\xe1\ \x00\ -\x00\xa2\x2c\x78\x9c\xed\x1d\x6b\x6f\xdb\x38\xf2\x7b\x7e\x05\x91\ -\x0f\x45\x0e\xc8\xc6\xb1\xf3\x6e\x1d\x2f\xda\xf4\x09\x74\x77\xbb\ -\x75\xda\xbd\xbd\x2f\x0b\x5a\xa2\x6d\x5e\x65\xd1\x4b\xd2\x49\xbc\ -\xb8\x1f\x7f\x33\xa4\x64\x4b\xb2\x2c\xc7\xa6\x65\x39\xad\x8b\x02\ -\xb1\x48\x8a\x1c\x0e\xe7\xc5\x99\x21\xd5\xfc\xf9\x61\x10\x90\x3b\ -\x26\x15\x17\xe1\xf5\x7e\xfd\xe8\x78\x9f\xb0\xd0\x13\x3e\x0f\x7b\ -\xd7\xfb\x5f\x6e\xdf\xfe\x74\xb9\xff\x73\x6b\xaf\x39\xe2\xd3\x46\ -\xa7\xd0\xa8\xb5\x47\x9a\x5e\x40\x95\x6a\xbd\x1b\xf1\xe7\xcf\x5f\ -\x73\x1a\x88\x1e\xfc\x0d\x7a\x6d\xa6\x35\xbc\xac\x5e\x4b\xda\xd5\ -\xcd\x9a\x6d\x04\xad\xef\xb9\xdf\x63\x9a\x98\xe7\xeb\xfd\xdf\xff\ -\x30\x8f\xfb\x24\xa4\x03\x76\xbd\x5f\xd8\x09\x0e\x46\x9a\x43\x29\ -\x86\x4c\xea\x71\xf4\x46\x8f\x89\x01\xd3\x72\x6c\x2a\x49\x53\x32\ -\x4f\x9b\x5f\xa4\xf9\xd0\x3a\x6e\xd6\x1e\xa2\x87\x31\x3e\x8c\xa3\ -\x07\x00\x41\xf7\x5b\x67\x57\x50\x64\x7f\xda\xe2\x3e\xe3\xbd\xbe\ -\x6e\x9d\x9f\x34\x9a\xb5\xe8\xb7\xe9\xb3\x16\x77\xda\xac\xc5\x83\ -\xe7\x41\x72\xcf\x43\x5f\xdc\xdf\x72\x1d\xb0\x08\x18\xa5\x25\x00\ -\xdf\x7a\xc7\x42\x26\x69\x40\x54\x34\x99\x66\x2d\xaa\x98\xed\x32\ -\xa0\x63\x31\x9a\x22\xe7\xeb\x2b\xf1\xf0\xd1\x14\x45\x3d\x66\x86\ -\x54\x43\xea\x41\x47\xfb\xd1\x04\xc2\xd1\xa0\xc3\x64\xeb\xbc\x59\ -\x8b\x7e\x59\xf0\x93\x23\xcc\x74\x31\xa0\xb2\xc7\xc3\x4c\x0f\x57\ -\x85\x3d\x70\xcd\x06\x53\x4c\x26\x17\xf3\x9d\x14\xa3\x21\xc0\x1c\ -\x2f\x67\x2f\x7e\xb6\xcd\x67\x06\xd7\x53\x64\xe5\xe0\xcb\x2c\x3a\ -\x69\xe7\x60\x6d\x16\xa8\x42\xdc\x45\xa3\x01\xe1\x6a\xee\xd1\xc0\ -\x96\xfe\xd5\x98\x0e\x3c\x9d\x51\x4e\x47\xef\x67\x3a\xea\x0b\xc9\ -\xff\x11\xa1\x9e\x74\x55\xbf\x9a\xf4\x95\xed\x6d\x16\x49\x1f\x69\ -\x87\x05\x71\x57\x01\x3e\xa4\xdf\xcf\x41\x13\x7b\xd0\xa9\x06\x13\ -\x54\x59\x14\xf1\x50\x33\xd9\xa5\x1e\x23\x03\xe1\xb3\x0c\xa2\xf2\ -\xb1\x65\x0b\x2d\x64\x09\xd0\x6b\x69\xd8\x17\x4c\xc5\x70\xeb\x27\ -\xc9\xba\x37\x62\xd0\x11\xc9\x75\xc7\x8a\x21\x54\x78\x58\xd1\x11\ -\x0f\x7f\x9d\x16\x4f\x50\x88\xe0\x96\x0f\xf3\xe7\x78\xdb\xe7\x8a\ -\xc0\x7f\xdd\x67\xe4\xcb\x07\x33\x45\x98\x31\xb9\xef\x73\xaf\x6f\ -\x0a\x2d\x12\xa0\x7c\x14\x30\x72\xcf\x83\x80\xdc\x0b\xf9\xed\x39\ -\xb9\x85\x5e\x3b\x54\xda\x37\x4c\xf9\x30\x40\x24\xd1\x20\xa6\xad\ -\x98\x23\xb1\x3f\x0a\x4f\x43\x2a\xa9\x66\x44\xdb\x17\x0f\x71\x0c\ -\xe8\x52\x53\xf5\x2d\xdd\xcf\x48\x31\x33\xf2\x5b\xc9\xd8\xcd\xcb\ -\xd7\xe4\x16\x5a\xdc\x71\x76\x4f\xd4\x58\x01\xc6\x48\x57\x48\x33\ -\x0a\xd7\x0a\xdb\x4a\xbb\x42\xd4\xd3\x20\x37\x1f\xbd\x3c\x33\x58\ -\x42\x84\xbe\x09\x51\xd6\x11\xa5\x7d\x80\xfd\x7a\xff\x38\x83\x32\ -\x2f\xea\xfb\x0b\xff\xc5\x50\x82\xe7\x32\xd6\x27\xaa\xfb\x8b\x87\ -\x82\x81\x6a\xb1\x8c\x7f\xf4\x68\x19\xba\x7a\x0c\xc9\x4f\xe9\xc1\ -\xae\x4e\x1e\x1e\xe7\x0d\x57\xcb\x8e\xe7\x06\x40\xb4\xda\x2e\x10\ -\x2c\xe4\xbd\x66\xcd\x8a\xa1\x89\x8c\x4a\x55\x3b\x4b\xac\x86\x9b\ -\xc0\x6a\xac\x2a\xaf\x58\x97\x8e\x02\xe8\x5a\x04\x22\x77\x05\x4b\ -\x17\x54\x30\xee\xab\x91\xd6\x22\xcc\x91\x55\x50\xd7\xb1\x75\x2b\ -\x0b\x2b\x94\x0a\x7e\x72\x92\x46\x16\x84\x20\x1a\x44\xe7\xbf\x60\ -\x46\x64\xd5\x58\x11\xcd\x64\xc6\x35\xdd\x65\x99\x10\xcb\x32\x54\ -\x2a\x99\x8f\xc6\x0e\xfe\x49\x57\xf4\x40\x5a\x85\x58\x65\x7f\xa4\ -\x2b\x3b\xc1\x88\x61\x9d\xf9\x9b\x26\xe8\x99\x41\xd6\x2e\xae\x22\ -\x72\xd8\x4e\x69\xe5\x4c\x7d\x79\x0c\xb4\x40\x1f\xce\x65\xa0\x76\ -\x48\x87\xdb\xce\x3d\x8b\xa4\xc3\xf2\xfc\xa3\x70\xd6\x6a\x0c\xb6\ -\x44\xb0\x63\xa0\x14\xf8\x11\x2e\x10\x41\x3f\x1e\x13\xad\x6a\x35\ -\xdf\x88\x10\x7e\x8d\x8c\x39\xb6\xf5\xcc\x74\xb2\x1e\xcb\x79\x96\ -\xa9\x22\x85\x44\x3a\x0c\xda\x12\x5f\xd2\xfb\x30\x32\x78\x39\x62\ -\x25\x81\x22\x34\x7c\x8f\x36\xc3\x77\xa7\xa7\xf3\x19\xaf\xde\x38\ -\x2b\x60\xbd\xc6\xd9\x59\x65\xda\x6b\x8a\xab\x1f\x8f\x09\x17\xd0\ -\xe7\x42\x53\x30\xe0\x60\x1d\xa1\x17\xa6\x02\x1e\x6c\x0f\x79\x98\ -\xb7\x6d\x55\x50\xde\x99\xba\x2d\xf2\x67\xf6\x58\x35\x36\x99\xe0\ -\xfa\x4c\xc1\x3b\x0a\x74\x9e\x19\x39\x72\xd8\x34\x52\xae\x9b\xa5\ -\x7a\x5d\x82\xe4\x13\x8b\xf6\x44\x49\xbd\xe4\xfd\xd5\x89\xe3\xfe\ -\xea\xd8\x49\xb5\x51\x6e\x84\xf6\x56\x7a\x82\xce\x5c\xf4\x19\x23\ -\x93\x09\xa2\xd2\x82\x39\xf2\x2e\x67\x92\x7c\x63\xe3\x8d\xf8\x55\ -\x60\x40\x2f\x06\x60\x5b\x49\xdf\xcd\xb9\xa1\xfa\x1c\x07\xab\xca\ -\xb7\xe2\x69\x19\x54\x37\x3a\x0d\x9c\xa6\xbe\x02\x1b\xa1\x07\x1f\ -\xe8\x37\x2b\x45\xda\xa6\x78\x01\xaf\x40\x6b\x06\x8d\xd1\xec\xc8\ -\x50\x0f\x03\x15\xd0\xfa\x5d\x3f\x7f\xfe\x7e\xd2\x63\xb3\x66\x0a\ -\x97\x26\x55\xc5\xff\x61\xef\x79\xa8\xe7\x93\x2a\xb6\xc8\xa0\xd3\ -\x46\x55\x4e\xd3\xf1\x95\x49\x6d\x14\x5b\x69\x1c\xa7\xc2\x2c\x53\ -\xb0\xb2\x1d\xce\x11\x59\x16\x75\x6b\xb5\x65\x1a\x75\x97\x6d\xf9\ -\xb6\x4a\xdc\x73\x37\x89\xab\xa2\xb9\xad\x41\xd2\x7a\x23\x29\x81\ -\x64\x3f\x84\x3e\x7b\xc8\x37\x5f\xea\x1b\x31\x5f\x60\x36\x38\xab\ -\x9d\x04\xb7\xa5\x3b\x09\x3e\x2d\x58\xb3\x04\x2f\xe6\xbd\x9d\x04\ -\x5f\xbb\x04\x5f\x35\x32\xf1\x32\xd0\x5b\x2b\xc0\x2f\xdc\x04\x38\ -\xb5\x53\xdb\x88\xfc\xde\xcc\xf6\x13\x66\x63\x18\x7d\x27\xbe\x77\ -\xe2\x7b\x9b\xa2\xaa\xf5\x24\x15\xad\x92\x07\xe2\xb6\xed\x8f\xdc\ -\xb5\x26\xeb\xc6\xbc\x52\x81\x34\xfb\xc8\x43\xf6\xc6\xe7\x7a\x46\ -\x9a\xa1\xcb\x88\x41\x85\x4b\x74\x28\xcf\xa1\x3d\x9d\xad\xf1\xaf\ -\xa5\x1c\xd7\x71\x72\xd8\xea\x12\xef\x71\x28\xdf\x88\xef\x21\x39\ -\x33\x33\x69\xbb\xc2\xdb\x29\x03\xab\xe6\xc4\xd3\xc7\x33\xe2\x54\ -\x11\xf7\x99\xf7\x2d\x57\x11\x63\x85\x93\x3f\xd8\xf4\x00\x64\x8b\ -\xe4\xdb\x25\x00\x21\xb9\xa7\xa1\x26\x5a\x4c\x52\x89\x4c\xe0\xa0\ -\x96\xf0\x15\x4b\x31\x30\x15\x51\x32\x12\xa1\x2a\x26\xf9\x32\xa8\ -\xb9\x4d\xef\x00\x06\xab\xde\xa3\x18\x11\x0d\xfd\x84\xef\x9a\x7a\ -\x52\x28\x45\x14\x53\x98\xfc\xe9\xe0\xbb\x5e\x26\xaa\x09\x40\x89\ -\x90\x3d\xf0\xad\x55\xf5\x55\x93\xf9\x45\x19\x64\xee\xe2\xa2\xfd\ -\xd0\x8d\xa8\x5c\x11\xd3\x1d\xf3\x0f\x81\x9c\x86\xe3\x44\xf2\x5c\ -\x87\x81\x11\x3a\xd4\x31\x45\x81\x95\x3b\x00\x52\x3b\x24\x02\xa8\ -\x5d\xde\x73\xc5\xe2\x22\x65\xdb\xd3\xe0\x9e\x8e\x81\xf2\x34\x95\ -\x98\xea\x48\x42\xf1\xd3\xa4\xc7\x32\x58\xe1\x5d\x20\x3a\x34\x20\ -\x6b\x18\x23\xc2\x40\x66\x98\x0e\xe0\xaf\x05\x82\x1c\x3a\x36\x3f\ -\xcb\xd5\x19\xc3\xf1\x60\x8b\x33\x01\xab\xe6\x9f\xfa\x65\x29\x0c\ -\xe4\x10\xb3\xff\x55\xc8\x01\x0d\x82\xf1\x21\x01\x44\x32\x69\xc8\ -\x10\x43\x1d\x51\xcc\xf0\x30\x52\x16\x43\xce\x14\x41\xe0\x14\x0b\ -\xa0\x9c\xf9\x47\x24\x66\x3d\x31\x34\xb6\x4f\x92\x03\xf1\x9d\x0e\ -\x05\xce\x8a\x63\xfe\x31\x23\xc6\x6f\x03\x5f\x29\xcd\xa8\xef\x10\ -\xe5\x2f\xd0\x2d\x66\x90\x34\x00\xa9\xc9\x6d\x46\x99\x18\x28\x5e\ -\x01\x10\xbf\xc5\xe1\xd7\x1d\x4f\xe4\xf1\xc4\x59\x29\x2c\x71\xb9\ -\x5e\x9d\x62\x4c\xa8\xa4\x76\x40\x27\x35\x98\x53\x60\x2c\x28\x9d\ -\xe0\x97\x28\xa3\x05\xb3\x5b\xa0\xd4\x30\x49\x28\x74\xe2\xfd\x8e\ -\x75\x70\x0f\xcd\x3b\x61\x30\x86\x17\x58\x48\x00\x74\xb0\x73\xa0\ -\xe8\xe6\xf6\xf3\xc7\x52\x98\xe2\x65\x1a\xee\x18\xdc\x03\x9f\x2b\ -\xda\x09\xa6\x5e\x77\x74\xd8\xfc\xeb\x29\xab\x20\xbb\x40\xed\x2d\ -\x76\xb7\xaf\xdd\xe9\x5b\xbc\xc3\xdd\x39\x7d\xd7\xed\xf4\xad\x3b\ -\x3a\x48\x25\xf5\xf9\x48\x4d\x32\x4d\x8d\x2c\x00\x9e\x54\x43\xe6\ -\x71\xb0\x04\x87\x02\x10\xa9\x8e\xf0\x24\x12\x16\x1f\xdb\x3c\x1e\ -\x41\x80\x55\x35\x0d\x3d\x46\x0e\x78\xd8\xe5\x21\xc0\xed\xc0\xa8\ -\x0b\x62\x8b\x92\x86\xbd\x2a\xdc\x39\x0b\x32\xa4\x16\x90\xba\x1a\ -\x75\xbb\x3c\xeb\x29\x8e\xe6\x30\x7c\xd8\x8c\xda\x07\xec\x7d\xb6\ -\xc8\x7b\xa2\xd2\xa7\xec\x93\x20\x4b\xf8\x2c\x97\xd0\xf7\x0e\x07\ -\xbe\xf2\xf4\xfd\x84\x2f\x8d\xd2\x06\x15\x4e\x84\xe7\x8d\x24\xa1\ -\x3d\x8a\xa6\x6b\xc2\xb0\xd5\x7d\x50\x9b\x12\x9d\x2a\x34\x34\x86\ -\x2f\x0f\x7d\xee\x51\x34\x72\x6d\x64\x82\x88\x2e\x61\x30\x2b\x07\ -\x1f\xc6\xa2\x44\x00\xfa\xc0\x07\xa3\x41\x29\x5a\xbb\x4b\x03\xb5\ -\x09\xb5\x0d\x73\xf8\x91\x74\xf6\x4e\x63\xcf\xc7\x74\x19\x61\xda\ -\xe2\x1d\x32\x0b\xd1\x06\x5e\x3f\xfd\x17\x8a\x9d\x5f\x2c\xd7\x66\ -\xa5\x04\xaa\xfc\x0e\x33\x71\x05\xee\x33\x09\x62\x24\x69\x27\x94\ -\x21\x43\xe6\x00\xb2\x85\xda\xbf\x38\xbb\xa1\xac\x55\x8c\xc4\x6b\ -\x7e\x00\xfa\xea\xea\x6a\xf5\x10\x74\x51\x5e\xf5\xe9\x66\x22\xdb\ -\x56\xec\xbe\xb1\x0b\xfe\x44\x65\x6f\xd9\x16\x4b\xbd\x14\x8b\xc5\ -\x61\x13\x01\x16\x4b\xca\xd7\xd6\xb6\xbb\x7a\x1b\xb8\x99\xb8\x19\ -\xfa\xc2\x9c\xb0\x61\x21\xbb\x03\xb6\x46\x0f\x04\x06\x7f\xca\x94\ -\x24\x6d\x18\x32\xbe\x46\x21\x01\xd2\x53\x76\x27\x20\x16\x71\x2e\ -\xaf\xe8\xf7\x7b\xcc\x66\x09\xa2\x5d\xf5\x08\xdc\x7b\xd0\x65\xf1\ -\x25\x08\x49\x62\xb5\x7e\xd9\x91\x72\x88\x7a\x2c\xb1\x98\x7d\x80\ -\xe2\xa9\x2f\x66\xc9\xc2\xae\x9c\x00\x85\x43\x4a\x70\xde\xee\x2c\ -\x15\x56\x00\x71\xc6\x6c\xbc\xba\x0b\x8f\x60\x2f\x4d\x23\xd7\x47\ -\xe4\xb7\x38\xc8\x67\x04\xe5\x38\xfb\xc6\x3d\x07\x28\xe5\x32\x99\ -\x2b\x4b\xd0\xfc\x5b\x1c\x6b\x72\xea\x71\xec\x1e\x4e\x5f\x82\xd2\ -\x11\x15\xbb\x40\xdc\x7c\x32\x77\xbd\x40\xc7\xf5\x42\x0a\xbc\x31\ -\x06\xdd\x0b\xc3\x80\x86\xdb\x79\x83\x4e\xf1\x0c\x97\x20\xc5\x88\ -\xec\xff\xf8\xb4\xad\xb4\xe8\x96\x42\xf8\xab\xc8\x5f\xc0\xcd\x24\ -\x30\xfe\xfb\x4f\x72\x70\x2b\x86\xb9\x8e\xe0\x0d\x41\xf0\x1f\x72\ -\xf0\x56\x02\x6f\x55\x08\xc3\x9f\x00\x43\x1b\x74\xbb\x13\x08\x55\ -\x8b\xa4\x7a\x39\xc9\x35\x0e\xe9\x8f\xc9\x6d\x06\x25\x3d\xc9\xfd\ -\x94\xfe\x34\x91\xcb\x28\xd2\x59\x86\xfe\xfc\x02\xbb\x15\x1c\xf4\ -\x49\xfb\x35\xed\x04\xb6\x53\xf0\xad\x3d\x10\x59\xbc\xa5\xdd\xb9\ -\x35\x5d\xdc\x9a\x4b\xb0\xfc\x82\xb4\xee\x4a\xbc\x9d\x59\x97\x85\ -\xdd\x0b\x26\x64\x8a\xcd\x4f\xe8\x30\x72\xc7\x15\xc7\x8c\x04\x23\ -\x5e\xa6\x4d\xd1\x62\xea\xb0\xd0\xeb\xe3\x4e\x00\xaf\xb6\xbb\x63\ -\x09\xf3\x3e\x91\x4b\x31\x32\x89\x14\x34\xce\xea\x2b\x33\x99\xa2\ -\x2f\x72\x2f\x65\x2b\x2b\xa7\x01\x86\x7b\xf7\x7d\x89\x93\x47\x58\ -\xda\xc5\xc9\xa1\xa5\xd1\xf2\xfc\x74\x4d\x24\xd9\xe8\x22\xd4\x0a\ -\x0c\xf7\xd7\x62\x04\x13\x9e\xe7\x28\xf7\x4d\x6d\xec\x2e\xdf\xc2\ -\xa0\x87\x39\xe0\x6b\x91\x07\xbc\xae\xef\x19\x30\x2c\xa3\xc0\xd4\ -\x46\x12\x60\xfa\xf7\xea\x0c\xe5\x33\x8f\x0f\x00\xf2\x7c\x87\xba\ -\x83\x3f\x3d\x3f\x00\x60\x71\x8d\x01\x80\xab\xa3\xab\xcb\xab\xc9\ -\xbf\x8b\xcb\x46\xb3\x16\x55\x2e\x3d\x54\x5e\x44\x20\xea\xab\x7e\ -\x74\x9c\xfe\xb7\xfa\x28\x4b\xda\x2f\xed\x98\xda\x7f\x24\xb9\x53\ -\x4d\xac\xa9\x28\x4a\xc7\x43\xc3\x20\x8a\xa0\x3f\xff\xf1\x67\x8c\ -\x36\x17\x9e\x2b\xce\xc1\xa8\x28\xc8\xca\x43\x8b\xb4\x38\x2c\x62\ -\x2f\x1e\xd3\x7d\x0e\xb6\x88\x3c\x22\x6d\x4c\xba\xea\x8e\x09\xd8\ -\x11\x8c\x60\xf8\x02\x6c\x86\x31\x51\x7f\x8f\xa8\x64\x6a\x22\xa2\ -\x06\x71\x37\x0e\xb9\x99\x05\xc1\xbe\xfa\xf1\x46\x82\x7d\xc8\xcb\ -\x6f\x2c\xed\x3c\x51\x4e\x2e\x7b\x0b\xee\x78\x8d\xd2\x82\xa4\xe3\ -\xb9\xbc\xfd\x01\xaf\x6b\x0e\x31\xf5\x4f\x02\x35\xe2\x81\x27\x12\ -\x00\x93\xe7\x1e\x8c\xad\x98\xc7\x8b\x6d\x8a\x48\x53\xb6\x61\x47\ -\xf4\xd8\x5d\xd7\x79\xf1\xae\xab\x7e\x7e\x71\x71\xd1\xa8\x9f\xb9\ -\xec\xbd\x96\x37\x51\xa6\xa9\x10\xb1\x51\x81\x27\x81\x78\xbc\x4c\ -\x9e\x10\xd2\xe7\x21\xd5\x0c\x8f\x3f\x30\x69\xf6\xb4\x8a\x1c\x60\ -\xa6\x06\x7b\x38\x22\x27\xe4\x9a\x1c\x83\xba\xae\x3b\x24\x68\x16\ -\x88\x8b\xf3\x8d\x48\x8b\x09\x2d\x3e\x59\x69\xb1\xac\xfb\xa2\x58\ -\x7f\xed\xdc\x17\x6b\xcf\xa3\x5e\x55\x5e\xbe\xe6\x03\x16\x9a\x73\ -\xa1\x4f\x40\x62\x16\x6f\x60\x0b\x93\x8b\x2e\xcb\x49\x2d\xda\x88\ -\xf8\xf0\xf9\xe0\xd3\x93\x97\x20\x65\xdb\x1b\x4b\x9c\x7d\xca\x63\ -\xa0\x05\x29\x44\x73\xf9\xe7\x56\x04\xa0\xb4\x42\xaf\xd4\xc8\x63\ -\xb1\xac\x2d\x66\xfd\x9d\xac\x5d\x8b\xab\x78\x09\x67\xd1\x0f\x60\ -\xd6\x4d\xef\xf2\x30\xc2\x11\xb3\x8e\x7c\xcc\xcb\xe8\x8e\x42\xcf\ -\x1a\x70\xba\x4f\xb5\x49\x92\xa3\x44\xc7\x2c\x72\xb4\xf7\x15\x5b\ -\x47\xa9\xfe\x3e\xef\x76\x61\xa7\x08\xe5\xb8\x39\x0c\x60\xbf\x68\ -\x92\x53\x6c\x87\xf1\x16\x53\x4b\x66\xf2\xff\xa9\x22\x0a\x00\x73\ -\xd8\x33\x16\x1c\x68\xa9\xad\x42\xf0\x61\x0f\xc8\x41\xb3\x2c\x8a\ -\x22\x3f\x12\x98\xac\xf5\x35\x79\x98\x0a\xfc\x58\x30\xca\xd9\xe6\ -\xfd\x58\x7a\x2a\xf2\x76\xba\x28\x4f\x17\x9d\xbb\xa9\xa2\x05\x19\ -\x5e\x0b\x13\x62\xb0\x05\xb1\xb2\xa0\x3a\xa5\x54\x9c\xdd\xb8\x53\ -\x4a\x9b\x56\x4a\x0b\x92\x17\xbe\x07\xb5\x14\xd3\xbf\x1d\xcf\x9c\ -\xf5\x40\x56\x50\xe6\x3a\x1c\x7f\xb2\xcf\x59\xaf\x1f\x61\x22\x89\ -\x1b\x15\x44\x14\x70\x7a\x31\xa3\xef\x44\x71\xee\xb6\xc0\xf1\x6b\ -\x59\x0b\xee\xd3\x79\x9c\x2c\xee\x02\x44\xd5\x49\xe2\x62\x37\xe3\ -\x4e\x12\xaf\x45\x12\x2f\xbc\x3b\x6f\xf5\xd4\xb1\xbc\x9b\xf3\x90\ -\xa2\xcc\xbb\x93\xef\x15\xda\xcc\x8e\x3c\x79\x77\xb4\xf7\x01\xe0\ -\xa5\x21\x9a\xd3\x34\xf1\xa6\x1a\x79\x7d\xb4\xab\x9f\xfd\x3d\x12\ -\xfa\xc5\x4b\xc9\x69\x60\x7f\x62\x86\x5a\x3c\x90\xd2\xe3\x20\xdb\ -\x54\xd1\x50\xc5\x2d\xa3\x12\x26\x79\xd7\xfe\xdc\x03\x78\xec\xaf\ -\x81\x08\x45\xdc\x0c\x81\x24\x5d\x3a\xe0\xc1\x38\x6f\xdc\xc3\xf7\ -\x2c\xb8\x63\xf8\x85\xcf\xc3\x69\xe7\xf6\x25\x03\xaa\xd9\x2f\x50\ -\x0b\xcc\x5e\xce\xfb\xcf\x5f\x89\xc0\xb7\xcf\xa5\xe4\xa8\xe0\x18\ -\xab\x77\x4c\x03\xde\x0b\x61\x35\x66\x7a\x07\x26\x47\xfe\x7a\x89\ -\xf5\x9f\x91\x86\xff\x37\x79\xbc\x95\x94\x03\xe9\xf4\xa6\x25\x5f\ -\x6f\x18\xfa\xcc\x01\x0c\xa6\x97\x87\x61\x49\xa5\x62\x25\xd6\x4e\ -\xa5\xe4\xaa\x94\x25\xce\xb0\xe5\xba\x6a\x57\xbd\xa9\x7e\xaa\x53\ -\x06\xc3\x00\x3f\x76\xaa\xfa\x8c\x55\xa8\x58\x16\x4c\x64\xa7\x59\ -\xd6\xa2\x59\xde\xf2\x80\xdd\xf4\x85\x00\x19\x3b\xa3\x5c\xba\x50\ -\xe7\xd9\xba\x45\xf6\x3d\x0f\x97\xb5\xef\x4f\x8e\x8b\x11\xe4\x84\ -\x9f\xe5\xe3\x88\x7e\x96\xfa\xa3\x1b\x38\x4d\xa2\xa2\x87\x6e\x22\ -\x9b\xab\x88\x5f\x69\x8a\x32\xa9\x97\x64\x10\x37\xa1\x69\xa1\xda\ -\x09\xcd\x39\x42\x73\x89\x5b\x5d\x73\x84\xa6\x7b\x70\xeb\x19\x1d\ -\x0c\x5f\x90\x8f\x8c\xfa\x20\xd0\xa8\x94\xe2\xde\x9a\x13\xdb\x78\ -\x60\x68\x6d\xc7\x85\xf8\xc0\x7e\x79\x73\x5b\xa9\xd2\xed\xa8\xca\ -\x6b\xa1\xc9\x59\x75\x27\x65\x70\xf8\x8b\x6a\x87\xbf\xaa\x6e\xf8\ -\x1b\x2e\x3d\xd8\x19\x54\x88\xff\x08\x82\x0a\x97\x20\x82\xa0\xc2\ -\x55\x68\x83\x68\xe9\x57\xb9\x08\x16\x80\x0a\xd7\xc0\x02\x50\xe1\ -\x12\xbc\xa2\xde\x37\x55\xf5\x32\x4c\x81\xa8\x70\x29\xa6\x40\x38\ -\x2d\x47\xe5\xa6\x8a\x63\xf8\xa6\x5e\x9c\xfb\x34\xdf\x5a\xf9\x0a\ -\xad\xb8\x47\x83\x84\xef\xc6\xfa\x0f\x13\x3b\xa9\x6d\x34\x57\xd6\ -\xf5\x9d\xe3\xc4\x34\x31\x5d\xd0\xf8\xbb\x62\x4c\x44\x9e\xad\xe8\ -\x20\x13\x6c\x78\x92\x48\xa2\x92\x91\xbb\x08\x79\x47\x24\xde\x26\ -\x43\xaf\x01\xeb\xea\x43\xbc\x47\xd6\x1e\x75\xc2\x1e\x3f\xb4\x7f\ -\xc3\xfb\xc8\x43\x9f\x4a\x97\x2b\x93\x97\xb3\xc2\x52\x0b\xf8\x3d\ -\x9a\x62\x1f\x01\xd1\xe4\x20\x89\xdb\x0a\x8f\x30\x7f\x9e\x17\xf9\ -\x7c\x22\x12\xc8\x51\xfc\xac\xfa\x55\xe7\x97\x81\x49\xc9\x85\x9d\ -\x75\xfb\xeb\x3b\x02\x84\x86\x8f\xc0\x43\xc2\x2b\x5d\xf2\x2c\xf0\ -\x33\x15\x9f\xc1\xdc\xf9\x99\x36\xea\x67\x5a\x90\xb5\xfe\xa4\xfd\ -\x4c\xef\xf1\xe4\x0a\xde\xee\x85\xe1\x12\x15\x1d\x67\xa1\xa0\x6a\ -\x24\xf3\xb4\x90\x63\xbc\x4a\x50\x47\x1f\x30\x46\x26\x41\xbc\xa8\ -\x64\xe1\xb3\x40\xbf\x18\x5a\xce\x79\xd6\xd3\x2f\xd0\x69\x85\xb7\ -\x0b\x4f\x33\xa2\xe2\x38\x8c\xef\x33\x1f\x3d\x58\xa8\x92\x62\x91\ -\x19\xc5\x70\xa0\x19\xa8\xab\xa8\x97\xcd\x7c\xad\x25\x1a\x0c\x49\ -\x60\x5b\xf5\x93\x9b\x4c\x4e\x57\xa6\xfa\x9a\xb6\x4b\x34\x4a\xb4\ -\x88\xe4\xf7\x24\x20\x66\x0f\xf0\x5d\xef\x9f\xef\x93\x01\x95\x3d\ -\x1e\x5e\xef\xd7\xeb\xfb\x98\x42\xd6\x1c\xf2\x87\x01\x1d\xc6\x39\ -\x70\xad\xbf\x3f\x99\xe7\xb7\x52\x0c\x7e\x01\x63\xa5\x2d\x46\x12\ -\x73\xa6\x32\xad\xe0\x3d\x6f\xa4\xb4\x18\xd8\x11\x95\x81\x24\x59\ -\x62\xa1\x34\xec\xda\x32\xdc\x9a\xe0\x54\xc0\x9f\x29\x37\x4d\x40\ -\xb2\xb3\xd0\x57\xad\xdf\xff\x30\xef\x81\xa8\x8b\x0a\xf6\x2c\x37\ -\xa1\xeb\x0b\x7b\xa8\x61\x07\xaf\x39\x0d\x44\xef\xa8\x8f\xcc\x65\ -\x2a\x0c\x02\xb2\xe3\x16\x03\x72\x83\x1f\x1b\x7a\x35\xd2\xda\x98\ -\x35\x39\x80\x7c\x1a\xa9\x7e\x5c\x3f\x0f\x18\x0b\xac\x72\x83\x24\ -\x23\xc0\x72\xa1\x99\xc5\xdd\x3c\x90\xb0\xb7\xb5\x81\x15\x65\xe7\ -\xe4\x23\x68\x52\xb9\x11\x50\x16\xad\xd7\xec\xa2\x6e\x06\xac\xe8\ -\x16\x86\x7c\x14\x4d\x6b\x37\x84\x23\xbb\xdb\x99\x03\xcc\xa4\x76\ -\x23\xc0\xc4\x59\x05\xf9\xc0\x4c\x6b\x37\x02\x4c\x2a\xd9\x2c\x1f\ -\xa2\x4c\x13\x77\xb0\xd2\x05\xd8\x53\x53\x32\x65\x04\xa9\x32\x22\ -\x17\x74\x6f\xc8\x6c\xca\xb1\x9d\xc5\xe4\xd9\x8e\xaa\x00\x00\xe8\ -\x3b\xe7\x2e\x1f\x0c\xa4\x87\xd1\xb8\x68\x9d\xf4\x42\x1a\xb4\xbc\ -\x00\xcf\xb2\xfa\x07\x78\x7c\x16\x77\x30\xb6\xd4\xb4\x00\x13\x80\ -\xf1\xbb\x64\x5f\x99\x1b\x02\x9a\xb5\x49\x13\xdb\x65\x20\x74\x0b\ -\xd4\xe0\x1b\x7b\x58\x77\xd2\x27\x16\x5b\x8c\xe0\xb7\x09\x22\x8d\ -\x84\xbf\x89\x1e\x0f\xd1\xd4\x34\xd3\x4b\x85\x3d\x9a\x0f\xad\x2b\ -\x00\xf8\x21\x7e\x1c\xb7\x4e\x2e\xae\x9a\xb5\x71\xac\xce\xf0\xed\ -\xd9\x8e\x7c\x86\xdf\x53\x31\x86\x71\xb6\xb7\xd3\xfa\x45\xba\xbb\ -\xcb\x7a\x5e\x77\xf6\xa7\x8a\x96\x26\x85\xda\x8d\x62\x7a\x72\x2a\ -\xba\x5c\x1c\x23\x4e\x53\x38\x3e\x71\xc1\xf1\xd9\x45\x3d\xd3\xdd\ -\x69\x69\x38\x3e\x5d\x1f\x8e\xcf\xcb\xc5\xf1\x79\x9a\xf0\x1a\x17\ -\xc7\xeb\xc4\x71\xe3\xa2\xb1\x85\x38\x8e\x2f\x4e\x2f\x19\xb3\x8d\ -\xb5\x62\xb6\x7e\xbe\x31\xcc\xae\x2e\x21\xe2\x8b\x6d\xca\xc5\xec\ -\xd9\x65\x9a\x91\xcf\xcf\x5c\x30\x7b\x72\x76\xfa\x04\x64\x6f\x7c\ -\x75\x47\xb9\x98\x3d\x39\xc9\x60\xf6\x62\x9d\x34\x5b\x26\x66\x2f\ -\xdc\x25\xee\xf4\x26\xe8\x72\x91\x5c\xcf\xd0\x6f\xe3\xca\x49\x32\ -\x9c\xa2\xbf\x26\xd5\x5d\xae\x9a\xdc\x16\xdb\x21\x71\x95\x5b\xc9\ -\xd6\x43\xd6\x42\x73\x92\x12\x8d\xc6\xf9\x63\x8c\x91\xf9\x58\x4e\ -\x3e\x42\x7d\xb3\x36\xe2\xad\xbd\xff\x03\x0c\xba\x22\x38\ +\x00\xa3\x4a\x78\x9c\xed\x1d\x6b\x6f\xdb\x38\xf2\x7b\x7e\x05\x91\ +\x0f\x45\x0f\xc8\xc6\xb1\xf3\x6e\x1d\x2f\xda\xf4\x09\x74\x77\xbb\ +\x75\xda\xde\xde\x97\x05\x2d\xd1\x36\xaf\xb2\xe8\x8a\x74\x12\x2f\ +\xee\xc7\xdf\x0c\x49\x59\x8f\xc8\x72\x64\x59\x96\xdd\xba\x28\x10\ +\x89\xa4\xc8\xe1\x70\x5e\x9c\x19\xd2\xed\x5f\xef\x47\x1e\xb9\x65\ +\x81\xe4\xc2\xbf\xda\x6f\x1e\x1e\xed\x13\xe6\x3b\xc2\xe5\xfe\xe0\ +\x6a\xff\xf3\xcd\x9b\x5f\x2e\xf6\x7f\xed\xec\xb5\x27\x3c\x6a\x74\ +\x02\x8d\x3a\x7b\xa4\xed\x78\x54\xca\xce\xdb\x09\x7f\xf6\xec\x15\ +\xa7\x9e\x18\xc0\x5f\x6f\xd0\x65\x4a\xc1\xc7\xf2\x55\x40\xfb\xaa\ +\xdd\x30\x8d\xa0\xf5\x1d\x77\x07\x4c\x11\xfd\x7e\xb5\xff\xe7\x57\ +\xfd\xba\x4f\x7c\x3a\x62\x57\xfb\xb9\x9d\xe0\x60\xa4\x3d\x0e\xc4\ +\x98\x05\x6a\x6a\xbf\x18\x30\x31\x62\x2a\x98\xea\x4a\xd2\x0e\x98\ +\xa3\xf4\x13\x69\xdf\x77\x8e\xda\x8d\x7b\xfb\x32\xc5\x97\xa9\x7d\ +\x01\x10\xd4\xb0\x73\x7a\x09\x45\xe6\xd1\x14\x0f\x19\x1f\x0c\x55\ +\xe7\xec\xb8\xd5\x6e\xd8\x67\xdd\x67\x23\xec\xb4\xdd\x08\x07\xcf\ +\x82\xe4\x8e\xfb\xae\xb8\xbb\xe1\xca\x63\x16\x18\xa9\x02\x00\xbe\ +\xf3\x96\xf9\x2c\xa0\x1e\x91\x76\x32\xed\x86\xad\x78\xd8\xa5\x47\ +\xa7\x62\x12\x21\xe7\xcb\x4b\x71\xff\x41\x17\xd9\x1e\x53\x43\xca\ +\x31\x75\xa0\xa3\x7d\x3b\x01\x7f\x32\xea\xb1\xa0\x73\xd6\x6e\xd8\ +\x27\x03\x7e\x7c\x84\x07\x5d\x8c\x68\x30\xe0\x7e\xaa\x87\xcb\xdc\ +\x1e\xb8\x62\xa3\x08\x93\xf1\xc5\x7c\x1b\x88\xc9\x18\x60\x0e\x97\ +\x73\x10\xbe\x9b\xe6\x0f\x06\x57\x11\xb2\x32\xf0\xa5\x17\x9d\x74\ +\x33\xb0\xf6\x10\xa8\x5c\xdc\xd9\xd1\x80\x70\x15\x77\xa8\x67\x4a\ +\xff\x6e\x45\x03\x47\x33\xca\xe8\xe8\xdd\x83\x8e\x86\x22\xe0\xff\ +\x08\x5f\xcd\xba\x6a\x5e\xce\xfa\x4a\xf7\xf6\x10\x49\x1f\x68\x8f\ +\x79\x61\x57\x1e\xbe\x24\xbf\xcf\x40\x13\xbb\x57\x89\x06\x33\x54\ +\x19\x14\x71\x5f\xb1\xa0\x4f\x1d\x46\x46\xc2\x65\x29\x44\x65\x63\ +\xcb\x14\x1a\xc8\x62\xa0\x37\x92\xb0\x2f\x98\x8a\xe6\xd6\x8f\x01\ +\xeb\x5f\x8b\x51\x4f\xc4\xd7\x1d\x2b\xc6\x50\xe1\x60\x45\x4f\xdc\ +\xff\x7d\x92\x3f\x41\x21\xbc\x1b\x3e\xce\x9e\xe3\xcd\x90\x4b\x02\ +\xff\xd5\x90\x91\xcf\xef\xf5\x14\x61\xc6\xe4\x6e\xc8\x9d\xa1\x2e\ +\x34\x48\x80\xf2\x89\xc7\xc8\x1d\xf7\x3c\x72\x27\x82\x6f\xcf\xc8\ +\x0d\xf4\xda\xa3\x81\xf9\x42\x97\x8f\x3d\x44\x12\xf5\x42\xda\x0a\ +\x39\x12\xfb\xa3\xf0\x36\xa6\x01\x55\x8c\x28\xf3\xe1\x01\x8e\x01\ +\x5d\x2a\x2a\xbf\x25\xfb\x99\x48\xa6\x47\x7e\x13\x30\x76\xfd\xe2\ +\x15\xb9\x81\x16\xb7\x9c\xdd\x11\x39\x95\x80\x31\xd2\x17\x81\x1e\ +\x85\x2b\x89\x6d\x03\xb3\x42\xd4\x51\x20\x37\x1f\xbd\x3c\x0f\xb0\ +\x84\x08\x7d\xed\xa3\xac\x23\x52\xb9\x00\xfb\xd5\xfe\x51\x0a\x65\ +\x8e\xed\xfb\x33\xff\x4d\x53\x82\x53\x66\xac\x8f\x54\x0d\x17\x0f\ +\x05\x03\x35\x42\x19\xff\xe8\xd1\x52\x74\xf5\x18\x92\x8f\xe8\xc1\ +\xac\x4e\x16\x1e\xe7\x0d\xd7\x48\x8f\x57\x0e\x00\xbb\xda\x65\x20\ +\x58\xc8\x7b\xed\x86\x11\x43\x33\x19\x95\xa8\x2e\x2d\xb1\x5a\xe5\ +\x04\x56\x6b\x59\x79\xc5\xfa\x74\xe2\x41\xd7\xc2\x13\x99\x2b\x58\ +\xb9\xa0\x82\x71\x5f\x4e\x94\x12\x7e\x86\xac\x82\xba\x9e\xa9\x5b\ +\x5a\x58\xa1\x54\x70\xe3\x93\xd4\xb2\xc0\x07\xd1\x20\x7a\xff\x05\ +\x33\x22\xad\xc6\xf2\x68\x26\x35\xae\xee\x2e\xcd\x84\x58\x96\xa2\ +\xd2\x80\xb9\x68\xec\xe0\x9f\x64\xc5\x00\xa4\x95\x8f\x55\xe6\x21\ +\x59\xd9\xf3\x26\x0c\xeb\xf4\xdf\x24\x41\x3f\x18\x64\xe5\xe2\xca\ +\x92\xc3\x66\x4a\xab\xd2\xd4\x97\xc5\x40\x0b\xf4\xe1\x5c\x06\xea\ +\xfa\x74\xbc\xe9\xdc\xb3\x48\x3a\x14\xe7\x1f\x89\xb3\x96\x53\xb0\ +\x25\xbc\x1d\x03\x25\xc0\xb7\xb8\x40\x04\xfd\x7c\x4c\xb4\xac\xd5\ +\x7c\x2d\x7c\x78\x9a\x68\x73\x6c\xe3\x99\xe9\x78\x35\x96\xf3\x43\ +\xa6\xb2\x0a\x89\xf4\x18\xb4\x25\x6e\x40\xef\x7c\x6b\xf0\x72\xc4\ +\x4a\x0c\x45\x68\xf8\x1e\xae\x87\xef\x4e\x4e\xe6\x33\x5e\xb3\x75\ +\x9a\xc3\x7a\xad\xd3\xd3\xda\xb4\x57\x84\xab\x9f\x8f\x09\x17\xd0\ +\xe7\x42\x53\xd0\xe3\x60\x1d\xa1\x17\xa6\x06\x1e\xec\x8e\xb9\x9f\ +\xb5\x6d\x95\x50\xde\x8b\xdc\x16\xd9\x33\x7b\xac\x1a\x9b\x4d\x70\ +\x75\xa6\xe0\x2d\x05\x3a\x4f\x8d\x6c\x1d\x36\xad\x84\xeb\xa6\x50\ +\xaf\x05\x48\x3e\xb6\x68\x5b\x4a\xea\x15\xef\xaf\x8e\x4b\xee\xaf\ +\x8e\x4a\xa9\x36\xca\xb5\xd0\xde\x48\x4f\xd0\x69\x19\x7d\xc6\xc8\ +\x6c\x82\xa8\xb4\x60\x8e\xbc\xcf\x59\x40\xbe\xb1\xe9\x5a\xfc\x2a\ +\x30\xa0\x13\x02\xb0\xa9\xa4\x5f\xce\xb9\x21\x87\x1c\x07\xab\xcb\ +\xb7\xe2\xa8\xc0\xab\x6f\x74\xea\x95\x9a\xfa\x12\x6c\x84\x1e\x7c\ +\xa0\xdf\xb4\x14\xe9\xea\xe2\x05\xbc\x02\xad\x19\x34\x46\xb3\x23\ +\x45\x3d\x0c\x54\x40\xe7\x4f\xf5\xec\xd9\xbb\x59\x8f\xed\x86\x2e\ +\x2c\x4c\xaa\x92\xff\xc3\xde\x71\x5f\xcd\x27\x55\x6c\x91\x42\xa7\ +\x89\xaa\x9c\x24\xe3\x2b\xb3\x5a\x1b\x5b\x69\x1d\x25\xc2\x2c\x11\ +\x58\xe9\x0e\xe7\x88\x2c\x83\xba\x95\xda\x32\xad\x66\x99\x6d\xf9\ +\xa6\x4a\xdc\xb3\x72\x12\x57\xda\xb9\xad\x40\xd2\x3a\x93\x20\x00\ +\x92\x7d\xef\xbb\xec\x3e\xdb\x7c\x69\xae\xc5\x7c\x81\xd9\xe0\xac\ +\x76\x12\xdc\x94\xee\x24\x78\x54\xb0\x62\x09\x9e\xcf\x7b\x3b\x09\ +\xbe\x72\x09\xbe\x6c\x64\xe2\x85\xa7\x36\x56\x80\x9f\x97\x13\xe0\ +\xd4\x4c\x6d\x2d\xf2\x7b\x3d\xdb\x4f\x98\x8d\x66\xf4\x9d\xf8\xde\ +\x89\xef\x4d\x8a\xaa\x36\xe3\x54\xb4\x4c\x1e\x48\xb9\x6d\xbf\x75\ +\xd7\xea\xac\x1b\xfd\x49\x0d\xd2\xec\x03\xf7\xd9\x6b\x97\xab\x07\ +\xd2\x0c\x5d\x46\x0c\x2a\xca\x44\x87\xb2\x1c\xda\xd1\x6c\xb5\x7f\ +\x2d\xe1\xb8\x0e\x93\xc3\x96\x97\x78\x8f\x43\xf9\x5a\x7c\x0f\xf1\ +\x99\xe9\x49\x9b\x15\xde\x4c\x19\x58\x37\x27\x9e\x3c\x9e\x11\x23\ +\x45\x3c\x64\xce\xb7\x4c\x45\x8c\x15\xa5\xfc\xc1\xba\x07\x20\x5b\ +\x24\xdf\x3e\x01\x08\xc9\x1d\xf5\x15\x51\x62\x96\x4a\xa4\x03\x07\ +\x8d\x98\xaf\x38\x10\x23\x5d\x61\x93\x91\x08\x95\x21\xc9\x57\x41\ +\xcd\x5d\x7a\x0b\x30\x18\xf5\x6e\x63\x44\xd4\x77\x63\xbe\x6b\xea\ +\x04\x42\x4a\x22\x99\xc4\xe4\xcf\x12\xbe\xeb\x22\x51\x4d\x00\x4a\ +\xf8\xec\x9e\x6f\xac\xaa\xaf\x9b\xcc\xcf\xab\x20\xf3\x32\x2e\xda\ +\xf7\x7d\x4b\xe5\x92\xe8\xee\x98\x7b\x00\xe4\x34\x9e\xc6\x92\xe7\ +\x7a\x0c\x8c\xd0\xb1\x0a\x29\x0a\xac\xdc\x11\x90\xda\x01\x11\x40\ +\xed\xc1\x1d\x97\x2c\x2c\x92\xa6\x3d\xf5\xee\xe8\x14\x28\x4f\xd1\ +\x00\x53\x1d\x89\x2f\x7e\x99\xf5\x58\x05\x2b\xbc\xf5\x44\x8f\x7a\ +\x64\x05\x63\x58\x0c\xa4\x86\xe9\x01\xfe\x3a\x20\xc8\xa1\x63\xfd\ +\x58\xad\xce\x18\x4f\x47\x1b\x9c\x09\x58\x37\xff\x34\x2f\x2a\x61\ +\xa0\x12\x31\xfb\xdf\x45\x30\xa2\x9e\x37\x3d\x20\x80\x48\x16\x68\ +\x32\xc4\x50\x87\x8d\x19\x1e\x58\x65\x31\xe6\x4c\x12\x04\x4e\x32\ +\x0f\xca\x99\x7b\x48\x42\xd6\x13\x63\x6d\xfb\xc4\x39\x10\xbf\xe9\ +\x51\xe0\xac\x30\xe6\x1f\x32\x62\xf8\x35\xf0\x95\x54\x8c\xba\x25\ +\xa2\xfc\x39\xba\x45\x0f\x92\x04\x20\x31\xb9\xf5\x28\x13\x0d\xc5\ +\x4b\x00\xe2\x8f\x30\xfc\xba\xe3\x89\x2c\x9e\x38\xad\x84\x25\x2e\ +\x56\xab\x53\xb4\x09\x15\xd7\x0e\xe8\xa4\x06\x73\x0a\x8c\x05\xa9\ +\x62\xfc\x62\x33\x5a\x30\xbb\x05\x4a\x35\x93\xf8\x42\xc5\xbe\xef\ +\x19\x07\xf7\x58\x7f\xe3\x7b\x53\xf8\x80\xf9\x04\x40\x07\x3b\x07\ +\x8a\xae\x6f\x3e\x7d\xa8\x84\x29\x5e\x24\xe1\x0e\xc1\x7d\xea\x72\ +\x49\x7b\x5e\xe4\x75\x47\x87\xcd\xbf\xb6\x59\x05\x99\x05\xea\x6e\ +\xb0\xbb\x7d\xe5\x4e\xdf\xfc\x1d\xee\xce\xe9\xbb\x6a\xa7\x6f\xb3\ +\xa4\x83\x34\xa0\x2e\x9f\xc8\x59\xa6\xa9\x96\x05\xc0\x93\x72\xcc\ +\x1c\x0e\x96\xe0\x58\x00\x22\xe5\x21\x9e\x44\xc2\xe2\x23\x93\xc7\ +\x23\x08\xb0\xaa\xa2\xbe\xc3\xc8\x53\xee\xf7\xb9\x0f\x70\x97\x60\ +\xd4\x05\xb1\xc5\x80\xfa\x83\x3a\xdc\x39\x0b\x32\xa4\x16\x90\xba\ +\x9c\xf4\xfb\x3c\xed\x29\xb6\x73\x18\xdf\xaf\x47\xed\x03\xf6\x3e\ +\x19\xe4\x6d\xa9\xf4\xa9\xfa\x24\x48\x01\x9f\x65\x01\x7d\x5f\xe2\ +\xc0\x57\x96\xbe\x9f\xf1\xa5\x56\xda\xa0\xc2\x89\x70\x9c\x49\x40\ +\xe8\x80\xa2\xe9\x1a\x33\x6c\xd5\x10\xd4\x66\x80\x4e\x15\xea\x6b\ +\xc3\x97\xfb\x2e\x77\x28\x1a\xb9\x26\x32\x41\x44\x9f\x30\x98\x55\ +\x09\x1f\xc6\xa2\x44\x00\x7a\xcf\x47\x93\x51\x25\x5a\xbb\x4f\x3d\ +\xb9\x0e\xb5\x0d\x73\xf8\x99\x74\xf6\x4e\x63\xcf\xc7\x74\x15\x61\ +\xda\xfc\x1d\x32\xf3\xd1\x06\x5e\x3d\xfd\xe7\x8a\x9d\xdf\x0c\xd7\ +\xa6\xa5\x04\xaa\xfc\x1e\xd3\x71\x05\xee\xb2\x00\xc4\x48\xdc\x4e\ +\xa8\x42\x86\xcc\x01\x64\x03\xb5\x7f\x7e\x76\x43\x55\xab\x68\xc5\ +\x6b\x76\x00\xfa\xf2\xf2\x72\xf9\x10\x74\x5e\x5e\xf5\xc9\x7a\x22\ +\xdb\x46\xec\xbe\x36\x0b\xbe\xa5\xb2\xb7\x6a\x8b\xa5\x59\x89\xc5\ +\x52\x62\x13\x01\x16\x4b\xc2\xd7\xd6\x35\xbb\x7a\x13\xb8\x99\xb9\ +\x19\x86\x42\x9f\xb0\x61\x3e\xbb\x05\xb6\x46\x0f\x04\x06\x7f\xaa\ +\x94\x24\x5d\x18\x32\xbc\x46\x21\x06\xd2\x36\xbb\x13\x10\x8b\x38\ +\x97\x97\xf4\xc7\x3d\x66\x53\x80\x68\x97\x3d\x02\xf7\x0e\x74\x59\ +\x78\x09\x42\x9c\x58\x8d\x5f\x76\x22\x4b\x44\x3d\x0a\x2c\xe6\x10\ +\xa0\xd8\xf6\xc5\xac\x3a\x42\x51\xe0\xa4\x7e\x01\xba\x69\x2e\xc8\ +\x74\xce\x93\x76\x3c\x26\xed\x28\xb1\x20\xd8\x5d\x96\x76\x9d\x0c\ +\xa3\x50\x32\xde\xc4\x81\x65\x63\x8f\xfa\x8c\xc4\x0c\x69\x02\x52\ +\x8f\xd1\x40\x12\x77\x82\x9d\x86\x1e\x5a\x82\x60\xe8\x06\xd5\x6c\ +\xcf\x50\x20\x7e\xb5\x30\x7d\xd4\x30\xa9\x80\xc2\x54\x4a\xc8\xc4\ +\x82\xc2\x4b\x8f\x7a\x13\x0e\xba\x23\xfa\x2c\xa2\xaf\x26\x2a\x57\ +\x22\x0f\x3e\xcb\x25\x91\x88\xa5\x19\x6a\xc6\x24\x8d\x3e\xbc\xc2\ +\x26\x21\x4a\xd7\x38\x24\x7f\x84\x91\x6d\x6d\x1d\x4c\xd3\x5f\xdc\ +\x71\x80\x32\x28\x92\xae\x55\x80\xe2\xdf\xe0\x58\xb3\xa3\xbe\xd3\ +\xf2\x39\x24\x05\xc8\x1d\x51\xb1\x8b\x3e\xcf\x27\xf3\xb2\xb7\x46\ +\x95\xbd\x85\x25\x21\x9c\x37\x32\xf3\x39\x7f\x86\x05\x48\xd1\x92\ +\xfd\xd7\x8f\x9b\x4a\x8b\xe5\xf2\x66\x7f\x17\xd9\x0b\xb8\x9e\xac\ +\xdd\x7f\xff\x45\x9e\xde\x88\x71\x66\xf4\x63\x4d\x10\xfc\x87\x3c\ +\x7d\x13\x00\x6f\xd5\x08\xc3\x5f\x00\x43\x17\x0c\xda\x52\x20\xd4\ +\x2d\x92\x9a\xd5\x64\x94\x95\xc8\xf9\x7d\x9f\xb0\x36\x07\x01\x77\ +\x13\xfa\x53\x87\xeb\xad\xf1\x58\x85\xfe\xfc\x0c\x5b\x74\x1c\x74\ +\xab\x9d\xf9\x66\x02\x9b\x29\xf8\x56\x1e\x7d\xcf\xf7\xe3\xec\x7c\ +\xf9\x65\x7c\xf9\x45\x76\x98\xf9\x67\x19\x6a\x71\xf1\xa7\xfd\x74\ +\xc6\x01\x12\x93\x29\x26\x29\xa7\xc7\xc8\x2d\x97\x1c\xd3\x70\xb4\ +\x78\x89\x9a\xa2\xc5\xd4\x63\xbe\x33\xc4\x9d\x00\xde\xe7\x78\xcb\ +\x62\xe6\x7d\x2c\x81\x68\xa2\xb3\x87\x68\x98\xca\x5a\x65\x06\x11\ +\xec\x29\xd7\x62\xcd\xdb\x44\x1e\x18\xee\xed\x8f\x25\x4e\x1e\x61\ +\x69\xe7\x67\x44\x57\x46\xcb\xf3\x73\x94\x91\x64\xed\xed\xbf\x35\ +\x18\xee\xaf\xc4\x04\x26\x3c\x2f\x3a\xe4\xea\xda\x30\x46\xb4\x81\ +\x91\x3e\x7d\xaa\xdd\x20\x0f\x78\x5d\xdd\x31\x60\x58\x46\x81\xa9\ +\xb5\x24\xc0\x33\x0f\xcb\x33\x94\xcb\x1c\x3e\x02\xc8\xb3\xa3\x48\ +\x25\x82\x48\xd9\x51\x2f\x83\x6b\x8c\x7a\x5d\x1e\x5e\x5e\x5c\xce\ +\xfe\x9d\x5f\xb4\xda\x0d\x5b\x59\x78\xa8\xac\x30\x98\xed\xab\x79\ +\x78\x94\xfc\xb7\xfc\x28\x05\xed\x97\x6e\x48\xed\x3f\x93\xdc\xa9\ +\x27\xc0\x9a\x17\x9a\xe6\xbe\x66\x10\x49\x30\x88\xf5\xf8\x83\x75\ +\xeb\x8b\x49\xe7\x27\x1e\xd5\x94\x59\xc0\x7d\x83\xb4\x30\x16\x68\ +\x6e\xdb\x53\x43\x8e\x5e\xe0\x43\xd2\xc5\x4c\xc3\xfe\x94\x80\x1d\ +\xc1\x08\xba\xa8\xc1\x66\x98\x12\xf9\x7d\x42\x03\x26\x67\x22\x6a\ +\x14\x76\x53\x22\x21\x39\x27\xc2\xdd\x3c\x5a\x4b\x84\x1b\x79\xf9\ +\xb5\xa1\x9d\x2d\xe5\xe4\xaa\xb7\xe0\x25\xef\x0e\x5b\x90\x69\x3f\ +\x97\xb7\xdf\xe3\x1d\xe5\x3e\xe6\xbb\x06\x40\x8d\x78\xca\x8f\x78\ +\xc0\xe4\x99\xa7\xc1\x6b\xe6\xf1\x7c\x9b\xc2\x6a\xca\x2e\xec\x88\ +\x1e\xbb\xeb\x3a\xcb\xdf\x75\x35\xcf\xce\xcf\xcf\x5b\xcd\xd3\x32\ +\x7b\xaf\xe2\x26\x4a\x94\xff\x13\x1a\x15\x78\xfc\x8d\x87\xcb\xe4\ +\x08\x11\xb8\xdc\xa7\x8a\xc9\x58\xd4\x8c\x3c\xc5\xf4\x24\x76\x7f\ +\x48\x8e\xc9\x15\x39\x02\x75\xdd\x2c\x91\x95\x9c\x23\x2e\xce\xd6\ +\x22\x2d\x66\xb4\xb8\xb5\xd2\xa2\xa8\xfb\x22\x5f\x7f\xed\xdc\x17\ +\x2b\x3f\x3c\xb0\xac\xbc\x7c\xc5\x47\xcc\xd7\x87\xa1\xb7\x40\x62\ +\xe6\x6f\x60\x73\x33\xea\x2e\xaa\xc9\xa7\x5b\x8b\xf8\x70\xf9\xe8\ +\xe3\xd6\x4b\x90\xaa\xed\x8d\x02\x07\xfe\xb2\x18\x68\x41\xde\xdc\ +\x5c\xfe\xb9\x11\x1e\x28\x2d\xdf\xa9\x34\xf2\x98\x2f\x6b\xf3\x59\ +\x7f\x27\x6b\x57\xe2\x2a\x2e\xe0\x2c\xfa\x09\xcc\xba\xe8\x02\x1b\ +\x2d\x1c\x31\xd5\xce\xc5\xbc\x8c\xfe\xc4\x77\x8c\x01\xa7\x86\x54\ +\xe9\xcc\x50\x4a\x54\xc8\x22\x87\x7b\x5f\xb0\xb5\x3d\xdf\xe2\xf2\ +\x7e\x1f\x76\x8a\x50\x8e\x9b\x43\x0f\xf6\x8b\x3a\x39\xc5\x74\x18\ +\x6e\x31\x55\xc0\xf4\xa1\x17\x2a\x89\x04\xc0\x4a\xec\x19\x73\x4e\ +\x71\x35\x96\x21\x78\x7f\x00\xe4\xa0\x58\x1a\x45\xd6\x8f\x04\x26\ +\x6b\x73\x45\x1e\xa6\x1c\x3f\x16\x8c\x72\xba\x7e\x3f\x96\x8a\x44\ +\xde\x4e\x17\x65\xe9\xa2\xb3\x72\xaa\x68\x41\x86\xd7\xc2\x84\x18\ +\x6c\x41\x8c\x2c\xd8\x70\xaf\x7a\x7e\x40\xfd\x47\x10\x95\xe1\x9a\ +\x98\xf1\xf4\xa1\x1b\x5c\x1e\xa9\xef\x25\x72\x67\xb6\xf7\x6a\xf7\ +\xb6\x33\xe9\xd0\xaa\xc1\xcb\x8d\xd3\x0b\x89\x6f\x4b\xc5\x43\x51\ +\x03\x2c\xdf\x91\xb3\x33\xc0\x56\xbd\xd9\x5d\x70\xb3\xd3\xe3\x04\ +\x64\x1f\xf0\xbd\x89\x37\x0b\x2e\x9f\x63\x94\x75\xaf\x20\xce\x52\ +\x7f\x3b\xfb\x35\x47\x93\x02\x90\x25\x84\x0e\xf7\xde\x03\xbc\xd4\ +\x47\xbb\x8b\xc6\xbe\x94\x13\x67\x88\x06\xd8\x93\xef\x13\xa1\x9e\ +\xbf\x08\x38\xf5\xcc\x23\xa6\x32\x85\x03\x49\x35\xf5\xd2\x4d\x25\ +\xf5\x65\xd8\xd2\x96\xb0\x80\xf7\xcd\xe3\x1e\xc0\x63\x9e\x46\xc2\ +\x17\x61\x33\x04\x92\xf4\xe9\x88\x7b\xd3\xac\x71\x0f\xde\x31\xef\ +\x96\xe1\xef\x9f\x1e\x44\x9d\x9b\x8f\x34\xa8\xda\xb0\xa4\x06\x98\ +\xbd\x8c\xef\x9f\xbd\x14\x9e\x6b\xde\x2b\x49\x66\xc0\x31\x96\xef\ +\x98\x7a\x7c\xe0\xc3\x6a\x3c\xe8\x1d\x08\x0f\xc5\xc4\x0b\xac\xff\ +\x84\xac\xf8\xbf\xd9\xeb\x4d\x40\x39\x90\xce\x20\x2a\xf9\x72\xcd\ +\xd0\xb9\x0a\x60\x30\x55\x1c\x86\x82\x92\xde\x70\xd1\x96\xca\xf9\ +\xaa\x5d\x12\x05\x4e\xf8\x65\xfa\xf4\x96\xbd\xc7\x3f\x92\x73\xa3\ +\xb1\x87\x3f\x05\x2b\x87\x8c\x55\x2a\xec\xf2\xf5\xe3\x82\x89\xec\ +\x14\xe4\x4a\x3c\x14\x6f\xb8\xc7\xae\x87\x42\x80\x8c\x7d\xa0\x5c\ +\xfa\x50\xe7\x98\xba\x45\x46\x37\xf7\x8b\x1a\xdd\xc7\x47\xf9\x08\ +\x2a\x85\x9f\xe2\x01\x27\x37\x4d\xfd\xf6\x7e\x52\x9d\xd1\xe6\xa0\ +\x3f\xc1\x24\xb5\xe1\x6f\x58\x85\xe7\xb5\x8a\x31\x48\x39\xa1\x69\ +\xa0\xda\x09\xcd\x39\x42\xb3\xc0\x9d\xb7\x19\x42\xb3\x7c\x14\xe4\ +\x09\x1d\x8d\x9f\x93\x0f\x8c\xba\x20\xd0\x68\x10\x88\x3b\x63\x4e\ +\x6c\xe2\xc9\x92\x95\x9d\x2b\xe1\x23\xf3\xbb\xa4\x9b\x4a\x95\xe5\ +\xce\x34\xbc\x12\x8a\x9c\xd6\x77\xa4\x02\x87\x3f\xaf\x77\xf8\xcb\ +\xfa\x86\xbf\xe6\x81\x03\x3b\x83\x1a\xf1\x6f\x21\xa8\x71\x09\x2c\ +\x04\x35\xae\x42\x17\x44\xcb\xb0\xce\x45\x30\x00\xd4\xb8\x06\x06\ +\x80\x1a\x97\xe0\x25\x75\xbe\xc9\xba\x97\x21\x02\xa2\xc6\xa5\x88\ +\x80\x28\xb5\x1c\xb5\x9b\x2a\x25\xfd\xfc\xcd\xfc\x24\x99\xf9\xd6\ +\xca\x17\x68\xc5\x1d\xea\xc5\x7c\x37\xc6\xa7\x15\xdb\x49\x6d\xa2\ +\xb9\xb2\xaa\x5f\x81\x8e\x5f\xc0\x20\xfa\xc6\xdf\x15\x62\xc2\x7a\ +\xb6\xec\x89\x17\xd8\xf0\xc4\x91\x44\x03\x46\x6e\x2d\xf2\x0e\x49\ +\xb8\x4d\x86\x5e\x3d\xd6\x57\x07\x78\xcb\xae\x39\x13\x83\x3d\xbe\ +\xef\xfe\x81\xb7\xb5\xfb\x2e\x0d\xca\x5c\x28\x5d\xcc\x0a\x4b\x2c\ +\xe0\x8f\x68\x8a\x7d\x00\x44\x93\xa7\x71\xdc\xd6\x78\xd6\xf5\xd3\ +\xbc\x10\xd9\x96\x48\xa0\x92\xe2\x67\xd9\xdf\xbc\x7e\xe1\xe9\xdc\ +\x4d\xd8\x59\x77\xbf\xbc\x25\x40\x68\xf8\x0a\x3c\x24\x9c\xca\x25\ +\xcf\x02\x3f\x53\xfe\x61\xbd\x9d\x9f\x69\xad\x7e\xa6\x05\xe9\xcd\ +\x5b\xed\x67\x7a\x87\x47\x1c\xf0\xee\x33\x0c\x97\x48\x7b\xee\x81\ +\x82\xaa\x09\x98\xa3\x44\x30\xc5\x8b\x16\x95\xfd\x79\x67\x64\x12\ +\xc4\x8b\x8c\x17\x3e\xf1\xd4\xf3\xb1\xe1\x9c\x27\x03\xf5\x1c\x9d\ +\x56\x78\xf7\x72\x94\x3a\x13\xc6\x61\x5c\x97\xb9\xe8\xc1\x42\x95\ +\x14\x8a\x4c\x1b\xc3\x81\x66\xa0\xae\x6c\x2f\xeb\xf9\x2d\x1b\x3b\ +\x18\x92\xc0\xa6\xea\xa7\x72\x32\x39\x59\x99\xe8\x2b\x6a\x17\x6b\ +\x14\x6b\x61\xe5\xf7\x2c\x20\x66\x4e\x7a\x5d\xed\x9f\xed\x93\x11\ +\x0d\x06\xdc\xbf\xda\x6f\x36\xf7\x31\xd7\xa8\x3d\xe6\xf7\x23\x3a\ +\x0e\x93\xa5\x3a\xdf\x3f\xea\xf7\x37\x81\x18\xfd\x06\xc6\x4a\x57\ +\x4c\x02\x4c\xae\x49\xb5\x82\xef\x9c\x89\x54\x62\x64\x46\x94\x1a\ +\x92\x78\x89\x81\x52\xb3\x6b\x47\x73\x6b\x8c\x53\x01\x7f\xba\x5c\ +\x37\x01\xc9\xce\x7c\x57\x76\xfe\xfc\xaa\xbf\x03\x51\x67\x0b\xf6\ +\x0c\x37\xa1\xeb\x0b\x7b\x68\x60\x07\xaf\x38\xf5\xc4\xe0\x70\x88\ +\xcc\xa5\x2b\x34\x02\xd2\xe3\xe6\x03\x72\x8d\x3f\xc5\xf4\x72\xa2\ +\x94\x36\x6b\x32\x00\xf9\x38\x91\xc3\xb0\x7e\x1e\x30\x06\x58\x59\ +\x0e\x92\x94\x00\xcb\x84\xe6\x21\xee\xe6\x81\x84\xbd\xad\x0c\x2c\ +\x9b\x32\x93\x8d\xa0\x59\xe5\x5a\x40\x59\xb4\x5e\x0f\x17\x75\x3d\ +\x60\xd9\xe3\xfa\xd9\x28\x8a\x6a\xd7\x84\x23\xb3\xdb\x99\x03\xcc\ +\xac\x76\x2d\xc0\x84\x59\x05\xd9\xc0\x44\xb5\x6b\x01\x26\x91\x01\ +\x96\x0d\x51\xaa\x49\x79\xb0\x92\x05\xd8\x53\x3b\x60\x52\x0b\x52\ +\xa9\x45\x2e\xe8\x5e\x9f\x99\xdc\x54\x33\x8b\xd9\xbb\x19\x55\x02\ +\x00\xd0\x77\xc6\xa5\x2f\x18\x48\xf7\xed\xb8\x68\x9d\x0c\x7c\xea\ +\x75\x1c\x0f\x0f\x3d\xba\x4f\xf1\x9c\x25\xee\x60\x4c\xa9\x6e\x01\ +\x26\x00\xe3\xb7\xf1\xbe\x52\x47\xc9\xdb\x8d\x59\x13\xd3\xa5\x27\ +\x54\x07\xd4\xe0\x6b\x73\xaa\x73\xd6\x27\x16\x1b\x8c\xe0\x2f\x37\ +\x58\x8d\x84\xcf\x44\x4d\xc7\x68\x6a\xea\xe9\x25\xc2\x1e\xed\xfb\ +\xce\x25\x00\x7c\x1f\xbe\x4e\x3b\xc7\xe7\x97\xed\xc6\x34\x54\x67\ +\xf8\xf5\xc3\x8e\x5c\x86\xbf\x36\xa3\x0d\xe3\x74\x6f\x27\xcd\xf3\ +\x64\x77\x17\xcd\xac\xee\xcc\xa3\xb4\x4b\x93\x40\xed\x5a\x31\x3d\ +\x3b\x3e\x5b\x2d\x8e\x11\xa7\x09\x1c\x1f\x97\xc1\xf1\xe9\x79\x33\ +\xd5\xdd\x49\x65\x38\x3e\x59\x1d\x8e\xcf\xaa\xc5\xf1\x59\x92\xf0\ +\x5a\xe7\x47\xab\xc4\x71\xeb\xbc\xb5\x81\x38\x0e\xaf\x95\xaf\x18\ +\xb3\xad\x95\x62\xb6\x79\xb6\x36\xcc\x2e\x2f\x21\xc2\x1b\x50\xaa\ +\xc5\xec\xe9\x45\x92\x91\xcf\x4e\xcb\x60\xf6\xf8\xf4\x64\x0b\x64\ +\x6f\x78\xc7\x43\xb5\x98\x3d\x3e\x4e\x61\xf6\x7c\x95\x34\x5b\x25\ +\x66\xcf\xcb\x4b\xdc\xe8\x9e\xec\x6a\x91\xdc\x4c\xd1\x6f\xeb\xb2\ +\x94\x64\x38\x41\x7f\x4d\xa2\xbb\x4c\x35\xb9\x29\xb6\x43\xec\xce\ +\xaf\x8a\xad\x87\xb4\x85\x56\x4a\x4a\xb4\x5a\x67\x8f\x31\x46\xe6\ +\x63\x39\xfe\x0a\xf5\xed\xc6\x84\x77\xf6\xfe\x0f\x9b\x41\x85\x54\ +\ \x00\x00\x07\xb5\ \x00\ \x00\x1a\x6e\x78\x9c\xed\x58\x5d\x6f\xe3\xb8\x15\x7d\xcf\xaf\x50\ @@ -37775,83 +37851,83 @@ qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x35\x00\x00\x00\x1b\ \x00\x00\x00\x38\x00\x02\x00\x00\x00\x05\x00\x00\x00\x16\ \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x11\x00\x00\x00\x05\ -\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x43\xc1\ -\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x40\x4a\ -\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x79\xe8\ -\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xdd\x0d\ -\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xa8\x51\ -\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x06\xcc\ -\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x73\x1f\ -\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x96\ -\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x10\xb8\ -\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x39\x0a\ -\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x5e\x55\ -\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2a\x0f\ -\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe4\x37\ -\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2b\x46\ -\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x7f\x81\ -\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x5d\x63\ -\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x0e\x32\ +\x00\x00\x02\x8e\x00\x01\x00\x00\x00\x01\x00\x06\x48\x2e\ +\x00\x00\x02\x02\x00\x01\x00\x00\x00\x01\x00\x05\x44\xb7\ +\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x03\x7e\x55\ +\x00\x00\x02\x56\x00\x01\x00\x00\x00\x01\x00\x05\xe1\x7a\ +\x00\x00\x02\x3a\x00\x01\x00\x00\x00\x01\x00\x05\xac\xbe\ +\x00\x00\x01\x3e\x00\x01\x00\x00\x00\x01\x00\x02\x0b\x39\ +\x00\x00\x02\x1e\x00\x01\x00\x00\x00\x01\x00\x05\x77\x8c\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x03\ +\x00\x00\x02\x72\x00\x01\x00\x00\x00\x01\x00\x06\x15\x25\ +\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x3d\x77\ +\x00\x00\x01\x22\x00\x00\x00\x00\x00\x01\x00\x01\x62\xc2\ +\x00\x00\x01\xae\x00\x01\x00\x00\x00\x01\x00\x04\x2e\x7c\ +\x00\x00\x01\x76\x00\x00\x00\x00\x00\x01\x00\x02\xe8\xa4\ +\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\x2f\xb3\ +\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\x83\xee\ +\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x04\x61\xd0\ +\x00\x00\x01\xe6\x00\x01\x00\x00\x00\x01\x00\x05\x12\x9f\ \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x00\xb0\x00\x01\x00\x00\x00\x01\x00\x00\x06\x29\ +\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x06\x29\ \x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x01\x64\ \x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc4\ \x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x03\x12\ -\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x6c\x16\ -\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xbd\xc3\ -\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x1f\xe0\ -\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xde\xc8\ -\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x08\xaa\ -\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x89\x60\ -\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe1\xfe\ -\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb3\x2f\ -\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x59\x6f\ -\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xfa\xd8\ -\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xd6\xe5\ -\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xe7\xc2\ -\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x76\xca\ -\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xdc\x64\ -\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa2\x25\ -\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xcc\x79\ -\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc1\x0e\ -\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x98\x0d\ -\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x23\xc9\ -\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xad\x18\ -\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xb7\x7a\ -\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x11\xb0\ -\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x90\x54\ -\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x08\xb6\ -\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x69\xb7\ -\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x35\x39\ -\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x80\x6a\ -\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xc9\xd1\ -\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xf7\xcc\ -\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xe9\x62\ -\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x4c\x78\ -\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa0\x14\ -\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x06\xb1\ -\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\x9d\xf1\ -\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb5\x34\ -\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x28\x89\ -\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xc9\x9b\ -\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x5e\xb7\ -\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc0\x20\ -\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x44\xde\ -\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x06\xfe\x69\ -\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xdb\x89\ -\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x2f\xba\ -\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x5f\xe4\ -\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x07\xfd\x44\ -\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x10\x0a\ -\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd4\x46\ -\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x93\x41\ -\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xef\x83\ -\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xed\x72\ -\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x3d\x1e\ -\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x98\xb1\ -\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x2f\xda\ -\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x77\x44\ -\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x80\xb1\ +\x00\x00\x06\x02\x00\x01\x00\x00\x00\x01\x00\x07\x70\xc5\ +\x00\x00\x03\xb0\x00\x00\x00\x00\x00\x01\x00\x06\xc2\x72\ +\x00\x00\x08\x38\x00\x01\x00\x00\x00\x01\x00\x08\x24\x8f\ +\x00\x00\x0a\x7e\x00\x01\x00\x00\x00\x01\x00\x08\xe3\x77\ +\x00\x00\x04\xba\x00\x01\x00\x00\x00\x01\x00\x07\x0d\x59\ +\x00\x00\x06\x4a\x00\x00\x00\x00\x00\x01\x00\x07\x8e\x0f\ +\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x07\xe6\xad\ +\x00\x00\x06\xbe\x00\x00\x00\x00\x00\x01\x00\x07\xb7\xde\ +\x00\x00\x08\xd2\x00\x01\x00\x00\x00\x01\x00\x08\x5e\x1e\ +\x00\x00\x0a\xce\x00\x01\x00\x00\x00\x01\x00\x08\xff\x87\ +\x00\x00\x03\xf6\x00\x01\x00\x00\x00\x01\x00\x06\xdb\x94\ +\x00\x00\x07\x84\x00\x01\x00\x00\x00\x01\x00\x07\xec\x71\ +\x00\x00\x06\x24\x00\x00\x00\x00\x00\x01\x00\x07\x7b\x79\ +\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x06\xe1\x13\ +\x00\x00\x06\x92\x00\x01\x00\x00\x00\x01\x00\x07\xa6\xd4\ +\x00\x00\x03\xd2\x00\x01\x00\x00\x00\x01\x00\x06\xd1\x28\ +\x00\x00\x0a\x08\x00\x00\x00\x00\x00\x01\x00\x08\xc5\xbd\ +\x00\x00\x03\x30\x00\x01\x00\x00\x00\x01\x00\x06\x9c\xbc\ +\x00\x00\x05\x0a\x00\x01\x00\x00\x00\x01\x00\x07\x28\x78\ +\x00\x00\x09\xc0\x00\x01\x00\x00\x00\x01\x00\x08\xb1\xc7\ +\x00\x00\x09\xe2\x00\x01\x00\x00\x00\x01\x00\x08\xbc\x29\ +\x00\x00\x04\xe8\x00\x00\x00\x00\x00\x01\x00\x07\x16\x5f\ +\x00\x00\x02\xfe\x00\x01\x00\x00\x00\x01\x00\x06\x95\x03\ +\x00\x00\x07\xee\x00\x01\x00\x00\x00\x01\x00\x08\x0d\x65\ +\x00\x00\x09\x2c\x00\x00\x00\x00\x00\x01\x00\x08\x6e\x66\ +\x00\x00\x05\x5e\x00\x01\x00\x00\x00\x01\x00\x07\x39\xe8\ +\x00\x00\x09\x50\x00\x00\x00\x00\x00\x01\x00\x08\x85\x19\ +\x00\x00\x07\x18\x00\x00\x00\x00\x00\x01\x00\x07\xce\x80\ +\x00\x00\x04\x6c\x00\x01\x00\x00\x00\x01\x00\x06\xfc\x7b\ +\x00\x00\x0a\x9e\x00\x00\x00\x00\x00\x01\x00\x08\xee\x11\ +\x00\x00\x05\xb8\x00\x00\x00\x00\x00\x01\x00\x07\x51\x27\ +\x00\x00\x03\x5c\x00\x00\x00\x00\x00\x01\x00\x06\xa4\xc3\ +\x00\x00\x0a\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x0b\x60\ +\x00\x00\x09\x98\x00\x00\x00\x00\x00\x01\x00\x08\xa2\xa0\ +\x00\x00\x03\x80\x00\x01\x00\x00\x00\x01\x00\x06\xb9\xe3\ +\x00\x00\x08\x5a\x00\x01\x00\x00\x00\x01\x00\x08\x2d\x38\ +\x00\x00\x0a\x2e\x00\x00\x00\x00\x00\x01\x00\x08\xce\x4a\ +\x00\x00\x05\xe0\x00\x01\x00\x00\x00\x01\x00\x07\x63\x66\ +\x00\x00\x06\xf0\x00\x01\x00\x00\x00\x01\x00\x07\xc4\xcf\ +\x00\x00\x08\xb0\x00\x00\x00\x00\x00\x01\x00\x08\x49\x8d\ +\x00\x00\x04\x90\x00\x01\x00\x00\x00\x01\x00\x07\x03\x18\ +\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x07\xe0\x38\ +\x00\x00\x05\x3e\x00\x01\x00\x00\x00\x01\x00\x07\x34\x69\ +\x00\x00\x08\xfc\x00\x01\x00\x00\x00\x01\x00\x08\x64\x93\ +\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x08\x01\xf3\ +\x00\x00\x08\x10\x00\x01\x00\x00\x00\x01\x00\x08\x14\xb9\ +\x00\x00\x0a\x54\x00\x01\x00\x00\x00\x01\x00\x08\xd8\xf5\ +\x00\x00\x09\x74\x00\x01\x00\x00\x00\x01\x00\x08\x97\xf0\ +\x00\x00\x04\x3c\x00\x01\x00\x00\x00\x01\x00\x06\xf4\x32\ +\x00\x00\x07\xa6\x00\x00\x00\x00\x00\x01\x00\x07\xf2\x21\ +\x00\x00\x05\x8c\x00\x00\x00\x00\x00\x01\x00\x07\x41\xcd\ +\x00\x00\x06\x72\x00\x01\x00\x00\x00\x01\x00\x07\x9d\x60\ +\x00\x00\x08\x7e\x00\x00\x00\x00\x00\x01\x00\x08\x34\x89\ +\x00\x00\x02\xaa\x00\x01\x00\x00\x00\x01\x00\x06\x7b\xb1\ +\x00\x00\x02\xd6\x00\x01\x00\x00\x00\x01\x00\x06\x85\x1e\ " def qInitResources(): diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py index 84d0b9b868..b4b83ee359 100644 --- a/src/Mod/Draft/InitGui.py +++ b/src/Mod/Draft/InitGui.py @@ -192,26 +192,32 @@ class DraftWorkbench (Workbench): "Draft_Clone"] self.treecmdList = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup", "Draft_SelectGroup","Draft_SelectPlane","Draft_ToggleSnap", - "Draft_ShowSnapBar"] + "Draft_ShowSnapBar","Draft_ToggleGrid"] self.lineList = ["Draft_UndoLine","Draft_FinishLine","Draft_CloseLine"] self.appendToolbar(str(DraftTools.translate("draft","Draft creation tools")),self.cmdList) self.appendToolbar(str(DraftTools.translate("draft","Draft modification tools")),self.modList) self.appendMenu(str(DraftTools.translate("draft","&Draft")),self.cmdList+self.modList) - self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Display options"))],self.treecmdList) + self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Context tools"))],self.treecmdList) self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Wire tools"))],self.lineList) def Activated(self): - FreeCADGui.draftToolBar.Activated() - + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Activated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.show() + Msg("Draft workbench activated\n") + def Deactivated(self): - FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"draftToolBar"): + FreeCADGui.draftToolBar.Deactivated() + Msg("Draft workbench deactivated\n") def ContextMenu(self, recipient): if (recipient == "View"): if (FreeCAD.activeDraftCommand == None): if (FreeCADGui.Selection.getSelection()): self.appendContextMenu("Draft",self.cmdList+self.modList) - self.appendContextMenu("Display options",self.treecmdList) + self.appendContextMenu("Draft context tools",self.treecmdList) else: self.appendContextMenu("Draft",self.cmdList) else: diff --git a/src/Mod/Draft/Resources/patterns/concrete.svg b/src/Mod/Draft/Resources/patterns/concrete.svg index 8c2fd6c616..2aa5e9565e 100644 --- a/src/Mod/Draft/Resources/patterns/concrete.svg +++ b/src/Mod/Draft/Resources/patterns/concrete.svg @@ -11,102 +11,22 @@ style="fill:none; stroke:#000000; stroke-width:.5" transform="scale(.01)" id="g3401"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/Resources/ui/userprefs-base.ui b/src/Mod/Draft/Resources/ui/userprefs-base.ui index 1928878206..ebf2bdbd97 100755 --- a/src/Mod/Draft/Resources/ui/userprefs-base.ui +++ b/src/Mod/Draft/Resources/ui/userprefs-base.ui @@ -562,6 +562,26 @@ + + + + + + if checked, a widget indicating the current working plane orientation appears during drawing operations + + + Show Working Plane tracker + + + showPlaneTracker + + + Mod/Draft + + + + + @@ -873,19 +893,6 @@ Values with differences below this value will be treated as same. - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -908,17 +915,6 @@ Values with differences below this value will be treated as same. - - - - - - - - Default text font - - - @@ -932,6 +928,13 @@ Values with differences below this value will be treated as same. + + + + Default text font + + + diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index e41eaabf76..6c1d6066b9 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -86,8 +86,29 @@ class plane: def projectPoint(self, p, direction=None): '''project point onto plane, default direction is orthogonal''' - if not direction: direction = self.axis + if not direction: + direction = self.axis + lp = self.getLocalCoords(p) + gp = self.getGlobalCoords(Vector(lp.x,lp.y,0)) + a = direction.getAngle(gp.sub(p)) + if a > math.pi/2: + direction = DraftVecUtils.neg(direction) + a = math.pi - a + ld = self.getLocalRot(direction) + gd = self.getGlobalRot(Vector(ld.x,ld.y,0)) + hyp = abs(math.tan(a) * lp.z) + return gp.add(DraftVecUtils.scaleTo(gd,hyp)) + + def projectPointOld(self, p, direction=None): + '''project point onto plane, default direction is orthogonal. Obsolete''' + if not direction: + direction = self.axis t = Vector(direction) + #t.normalize() + a = round(t.getAngle(self.axis),DraftVecUtils.precision()) + pp = round((math.pi)/2,DraftVecUtils.precision()) + if a == pp: + return p t.multiply(self.offsetToPoint(p, direction)) return p.add(t) @@ -196,6 +217,31 @@ class plane: def getLocalCoords(self,point): "returns the coordinates of a given point on the working plane" + pt = point.sub(self.position) + xv = DraftVecUtils.project(pt,self.u) + x = xv.Length + if xv.getAngle(self.u) > 1: + x = -x + yv = DraftVecUtils.project(pt,self.v) + y = yv.Length + if yv.getAngle(self.v) > 1: + y = -y + zv = DraftVecUtils.project(pt,self.axis) + z = zv.Length + if zv.getAngle(self.axis) > 1: + z = -z + return Vector(x,y,z) + + def getGlobalCoords(self,point): + "returns the global coordinates of the given point, taken relatively to this working plane" + vx = DraftVecUtils.scale(self.u,point.x) + vy = DraftVecUtils.scale(self.v,point.y) + vz = DraftVecUtils.scale(self.axis,point.z) + pt = (vx.add(vy)).add(vz) + return pt.add(self.position) + + def getLocalRot(self,point): + "Same as getLocalCoords, but discards the WP position" xv = DraftVecUtils.project(point,self.u) x = xv.Length if xv.getAngle(self.u) > 1: @@ -210,13 +256,14 @@ class plane: z = -z return Vector(x,y,z) - def getGlobalCoords(self,point): - "returns the global coordinates of the given point, taken relatively to this working plane" + def getGlobalRot(self,point): + "Same as getGlobalCoords, but discards the WP position" vx = DraftVecUtils.scale(self.u,point.x) vy = DraftVecUtils.scale(self.v,point.y) vz = DraftVecUtils.scale(self.axis,point.z) - return (vx.add(vy)).add(vz) - + pt = (vx.add(vy)).add(vz) + return pt + def getClosestAxis(self,point): "returns which of the workingplane axes is closest from the given vector" ax = point.getAngle(self.u) diff --git a/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp b/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp index 07aa2c4717..a4cb65ef90 100644 --- a/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp +++ b/src/Mod/Drawing/Gui/AppDrawingGuiPy.cpp @@ -139,6 +139,10 @@ exporter(PyObject *self, PyObject *args) str_out.close(); break; } + else { + PyErr_SetString(PyExc_TypeError, "Export as SVG of this object type is not supported by Drawing module"); + return 0; + } } } } PY_CATCH; diff --git a/src/Mod/Mesh/App/Core/Segmentation.cpp b/src/Mod/Mesh/App/Core/Segmentation.cpp index 01d99fb11f..9d0d69f325 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.cpp +++ b/src/Mod/Mesh/App/Core/Segmentation.cpp @@ -46,6 +46,16 @@ void MeshSurfaceSegment::AddSegment(const std::vector& segm) } } +MeshSegment MeshSurfaceSegment::FindSegment(unsigned long index) const +{ + for (std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + if (std::find(it->begin(), it->end(), index) != it->end()) + return *it; + } + + return MeshSegment(); +} + // -------------------------------------------------------- MeshDistancePlanarSegment::MeshDistancePlanarSegment(const MeshKernel& mesh, unsigned long minFacets, float tol) diff --git a/src/Mod/Mesh/App/Core/Segmentation.h b/src/Mod/Mesh/App/Core/Segmentation.h index 1b29f4bff9..9416028b69 100644 --- a/src/Mod/Mesh/App/Core/Segmentation.h +++ b/src/Mod/Mesh/App/Core/Segmentation.h @@ -46,6 +46,7 @@ public: virtual void AddFacet(const MeshFacet& rclFacet); void AddSegment(const std::vector&); const std::vector& GetSegments() const { return segments; } + MeshSegment FindSegment(unsigned long) const; protected: std::vector segments; diff --git a/src/Mod/Mesh/App/Makefile.am b/src/Mod/Mesh/App/Makefile.am index 27537e0605..5ba606444c 100644 --- a/src/Mod/Mesh/App/Makefile.am +++ b/src/Mod/Mesh/App/Makefile.am @@ -359,7 +359,7 @@ Mesh_la_DEPENDENCIES = libMesh.la # set the include path found by configure AM_CXXFLAGS = -I$(top_srcdir)/src/3rdParty -I$(top_srcdir)/src -I$(top_builddir)/src $(GTS_CFLAGS) \ - $(all_includes) $(QT4_CORE_CXXFLAGS) + $(all_includes) -I$(EIGEN3_INC) $(QT4_CORE_CXXFLAGS) includedir = @includedir@/Mod/Mesh/App libdir = $(prefix)/Mod/Mesh diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 875f04cd4c..716597e2b1 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -134,6 +134,7 @@ void PartExport initPart() Part::TopoShape ::init(); Part::PropertyPartShape ::init(); Part::PropertyGeometryList ::init(); + Part::PropertyShapeHistory ::init(); Part::PropertyFilletEdges ::init(); Part::Feature ::init(); diff --git a/src/Mod/Part/App/FeaturePartBoolean.cpp b/src/Mod/Part/App/FeaturePartBoolean.cpp index f21296a8b2..2a1781b6cf 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.cpp +++ b/src/Mod/Part/App/FeaturePartBoolean.cpp @@ -23,9 +23,14 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include +# include #endif #include "FeaturePartBoolean.h" +#include "modelRefine.h" +#include +#include using namespace Part; @@ -37,6 +42,9 @@ Boolean::Boolean(void) { ADD_PROPERTY(Base,(0)); ADD_PROPERTY(Tool,(0)); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short Boolean::mustExecute() const @@ -66,10 +74,32 @@ App::DocumentObjectExecReturn *Boolean::execute(void) TopoDS_Shape BaseShape = base->Shape.getValue(); TopoDS_Shape ToolShape = tool->Shape.getValue(); - TopoDS_Shape resShape = runOperation(BaseShape, ToolShape); - if (resShape.IsNull()) + std::auto_ptr mkBool(makeOperation(BaseShape, ToolShape)); + if (!mkBool->IsDone()) { + return new App::DocumentObjectExecReturn("Boolean operation failed"); + } + TopoDS_Shape resShape = mkBool->Shape(); + if (resShape.IsNull()) { return new App::DocumentObjectExecReturn("Resulting shape is invalid"); + } + + std::vector history; + history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, BaseShape)); + history.push_back(buildHistory(*mkBool.get(), TopAbs_FACE, resShape, ToolShape)); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + history[0] = joinHistory(history[0], hist); + history[1] = joinHistory(history[1], hist); + } + this->Shape.setValue(resShape); + this->History.setValues(history); return App::DocumentObject::StdReturn; } catch (...) { diff --git a/src/Mod/Part/App/FeaturePartBoolean.h b/src/Mod/Part/App/FeaturePartBoolean.h index 742af5098e..2730af82f2 100644 --- a/src/Mod/Part/App/FeaturePartBoolean.h +++ b/src/Mod/Part/App/FeaturePartBoolean.h @@ -27,6 +27,8 @@ #include #include "PartFeature.h" +class BRepAlgoAPI_BooleanOperation; + namespace Part { @@ -39,6 +41,7 @@ public: App::PropertyLink Base; App::PropertyLink Tool; + PropertyShapeHistory History; /** @name methods overide Feature */ //@{ @@ -53,7 +56,7 @@ public: } protected: - virtual TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const = 0; + virtual BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const = 0; }; } diff --git a/src/Mod/Part/App/FeaturePartBox.h b/src/Mod/Part/App/FeaturePartBox.h index 0349bb2111..086fa40cd4 100644 --- a/src/Mod/Part/App/FeaturePartBox.h +++ b/src/Mod/Part/App/FeaturePartBox.h @@ -32,7 +32,7 @@ namespace Part { -class Box :public Part::Primitive +class PartExport Box :public Part::Primitive { PROPERTY_HEADER(Part::Box); diff --git a/src/Mod/Part/App/FeaturePartCommon.cpp b/src/Mod/Part/App/FeaturePartCommon.cpp index 828512bc35..01ef09e417 100644 --- a/src/Mod/Part/App/FeaturePartCommon.cpp +++ b/src/Mod/Part/App/FeaturePartCommon.cpp @@ -25,11 +25,14 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include #endif #include "FeaturePartCommon.h" - +#include "modelRefine.h" +#include +#include #include using namespace Part; @@ -41,14 +44,10 @@ Common::Common(void) { } -TopoDS_Shape Common::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Common::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a section operation: - BRepAlgoAPI_Common mkCommon(base, tool); - // Let's check if the section has been successful - if (!mkCommon.IsDone()) - throw Base::Exception("Intersection failed"); - return mkCommon.Shape(); + return new BRepAlgoAPI_Common(base, tool); } // ---------------------------------------------------- @@ -60,6 +59,9 @@ MultiCommon::MultiCommon(void) { ADD_PROPERTY(Shapes,(0)); Shapes.setSize(0); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short MultiCommon::mustExecute() const @@ -82,18 +84,50 @@ App::DocumentObjectExecReturn *MultiCommon::execute(void) } if (s.size() >= 2) { - TopoDS_Shape res = s.front(); - for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { - // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Common mkCommon(res, *it); - // Let's check if the fusion has been successful - if (!mkCommon.IsDone()) - throw Base::Exception("Intersection failed"); - res = mkCommon.Shape(); + try { + std::vector history; + TopoDS_Shape resShape = s.front(); + for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { + // Let's call algorithm computing a fuse operation: + BRepAlgoAPI_Common mkCommon(resShape, *it); + // Let's check if the fusion has been successful + if (!mkCommon.IsDone()) + throw Base::Exception("Intersection failed"); + resShape = mkCommon.Shape(); + + ShapeHistory hist1 = buildHistory(mkCommon, TopAbs_FACE, resShape, mkCommon.Shape1()); + ShapeHistory hist2 = buildHistory(mkCommon, TopAbs_FACE, resShape, mkCommon.Shape2()); + if (history.empty()) { + history.push_back(hist1); + history.push_back(hist2); + } + else { + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist1); + history.push_back(hist2); + } + } + if (resShape.IsNull()) + throw Base::Exception("Resulting shape is invalid"); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist); + } + + this->Shape.setValue(resShape); + this->History.setValues(history); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + return new App::DocumentObjectExecReturn(e->GetMessageString()); } - if (res.IsNull()) - throw Base::Exception("Resulting shape is invalid"); - this->Shape.setValue(res); } else { throw Base::Exception("Not enough shape objects linked"); diff --git a/src/Mod/Part/App/FeaturePartCommon.h b/src/Mod/Part/App/FeaturePartCommon.h index 295ec2e4f2..ccb568aed9 100644 --- a/src/Mod/Part/App/FeaturePartCommon.h +++ b/src/Mod/Part/App/FeaturePartCommon.h @@ -41,7 +41,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; @@ -53,6 +53,7 @@ public: MultiCommon(); App::PropertyLinkList Shapes; + PropertyShapeHistory History; /** @name methods override feature */ //@{ diff --git a/src/Mod/Part/App/FeaturePartCut.cpp b/src/Mod/Part/App/FeaturePartCut.cpp index 1d48c1783f..6b6d792c5e 100644 --- a/src/Mod/Part/App/FeaturePartCut.cpp +++ b/src/Mod/Part/App/FeaturePartCut.cpp @@ -40,12 +40,8 @@ Cut::Cut(void) { } -TopoDS_Shape Cut::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Cut::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a cut operation: - BRepAlgoAPI_Cut mkCut(base, tool); - // Let's check if the cut has been successful - if (!mkCut.IsDone()) - throw Base::Exception("Cut failed"); - return mkCut.Shape(); + return new BRepAlgoAPI_Cut(base, tool); } diff --git a/src/Mod/Part/App/FeaturePartCut.h b/src/Mod/Part/App/FeaturePartCut.h index 63b0de61a6..11187781c9 100644 --- a/src/Mod/Part/App/FeaturePartCut.h +++ b/src/Mod/Part/App/FeaturePartCut.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/App/FeaturePartFuse.cpp b/src/Mod/Part/App/FeaturePartFuse.cpp index d0f4a0e972..ba73877ea5 100644 --- a/src/Mod/Part/App/FeaturePartFuse.cpp +++ b/src/Mod/Part/App/FeaturePartFuse.cpp @@ -29,7 +29,9 @@ #include "FeaturePartFuse.h" - +#include "modelRefine.h" +#include +#include #include using namespace Part; @@ -41,14 +43,10 @@ Fuse::Fuse(void) { } -TopoDS_Shape Fuse::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Fuse::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(base, tool); - // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - throw Base::Exception("Fusion failed"); - return mkFuse.Shape(); + return new BRepAlgoAPI_Fuse(base, tool); } // ---------------------------------------------------- @@ -60,6 +58,9 @@ MultiFuse::MultiFuse(void) { ADD_PROPERTY(Shapes,(0)); Shapes.setSize(0); + ADD_PROPERTY_TYPE(History,(ShapeHistory()), "Boolean", (App::PropertyType) + (App::Prop_Output|App::Prop_Transient|App::Prop_Hidden), "Shape history"); + History.setSize(0); } short MultiFuse::mustExecute() const @@ -83,18 +84,44 @@ App::DocumentObjectExecReturn *MultiFuse::execute(void) if (s.size() >= 2) { try { - TopoDS_Shape res = s.front(); + std::vector history; + TopoDS_Shape resShape = s.front(); for (std::vector::iterator it = s.begin()+1; it != s.end(); ++it) { // Let's call algorithm computing a fuse operation: - BRepAlgoAPI_Fuse mkFuse(res, *it); + BRepAlgoAPI_Fuse mkFuse(resShape, *it); // Let's check if the fusion has been successful if (!mkFuse.IsDone()) throw Base::Exception("Fusion failed"); - res = mkFuse.Shape(); + resShape = mkFuse.Shape(); + + ShapeHistory hist1 = buildHistory(mkFuse, TopAbs_FACE, resShape, mkFuse.Shape1()); + ShapeHistory hist2 = buildHistory(mkFuse, TopAbs_FACE, resShape, mkFuse.Shape2()); + if (history.empty()) { + history.push_back(hist1); + history.push_back(hist2); + } + else { + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist1); + history.push_back(hist2); + } } - if (res.IsNull()) + if (resShape.IsNull()) throw Base::Exception("Resulting shape is invalid"); - this->Shape.setValue(res); + + Base::Reference hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); + if (hGrp->GetBool("RefineModel", false)) { + TopoDS_Shape oldShape = resShape; + BRepBuilderAPI_RefineModel mkRefine(oldShape); + resShape = mkRefine.Shape(); + ShapeHistory hist = buildHistory(mkRefine, TopAbs_FACE, resShape, oldShape); + for (std::vector::iterator jt = history.begin(); jt != history.end(); ++jt) + *jt = joinHistory(*jt, hist); + } + + this->Shape.setValue(resShape); + this->History.setValues(history); } catch (Standard_Failure) { Handle_Standard_Failure e = Standard_Failure::Caught(); diff --git a/src/Mod/Part/App/FeaturePartFuse.h b/src/Mod/Part/App/FeaturePartFuse.h index 251d7ec580..f1c83bb6f8 100644 --- a/src/Mod/Part/App/FeaturePartFuse.h +++ b/src/Mod/Part/App/FeaturePartFuse.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; @@ -54,6 +54,7 @@ public: MultiFuse(); App::PropertyLinkList Shapes; + PropertyShapeHistory History; /** @name methods override feature */ //@{ diff --git a/src/Mod/Part/App/FeaturePartSection.cpp b/src/Mod/Part/App/FeaturePartSection.cpp index 6beb1eeef3..55cd6da6ea 100644 --- a/src/Mod/Part/App/FeaturePartSection.cpp +++ b/src/Mod/Part/App/FeaturePartSection.cpp @@ -39,12 +39,8 @@ Section::Section(void) { } -TopoDS_Shape Section::runOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const +BRepAlgoAPI_BooleanOperation* Section::makeOperation(const TopoDS_Shape& base, const TopoDS_Shape& tool) const { // Let's call algorithm computing a section operation: - BRepAlgoAPI_Section mkSection(base, tool); - // Let's check if the section has been successful - if (!mkSection.IsDone()) - throw Base::Exception("Section failed"); - return mkSection.Shape(); + return new BRepAlgoAPI_Section(base, tool); } diff --git a/src/Mod/Part/App/FeaturePartSection.h b/src/Mod/Part/App/FeaturePartSection.h index 911c2a5b7d..69ec64c927 100644 --- a/src/Mod/Part/App/FeaturePartSection.h +++ b/src/Mod/Part/App/FeaturePartSection.h @@ -42,7 +42,7 @@ public: //@{ /// recalculate the Feature protected: - TopoDS_Shape runOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; + BRepAlgoAPI_BooleanOperation* makeOperation(const TopoDS_Shape&, const TopoDS_Shape&) const; //@} }; diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index c9483f37c1..ef43452df9 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -26,6 +26,10 @@ #ifndef _PreComp_ # include # include +# include +# include +# include +# include #endif @@ -128,6 +132,78 @@ TopLoc_Location Feature::getLocation() const return TopLoc_Location(trf); } +ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS) +{ + ShapeHistory history; + history.type = type; + + TopTools_IndexedMapOfShape newM, oldM; + TopExp::MapShapes(newS, type, newM); + TopExp::MapShapes(oldS, type, oldM); + + for (int i=1; i<=oldM.Extent(); i++) { + bool found = false; + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) { + found = true; + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(it.Value())) { + history.shapeMap[i-1].push_back(j-1); + break; + } + } + } + + for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) { + found = true; + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(it.Value())) { + history.shapeMap[i-1].push_back(j-1); + break; + } + } + } + + if (!found) { + if (mkShape.IsDeleted(oldM(i))) { + history.shapeMap[i-1] = std::vector(); + } + else { + for (int j=1; j<=newM.Extent(); j++) { + if (newM(j).IsPartner(oldM(i))) { + history.shapeMap[i-1].push_back(j-1); + break; + } + } + } + } + } + + return history; +} + +ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& newH) +{ + ShapeHistory join; + join.type = oldH.type; + + for (ShapeHistory::MapList::const_iterator it = oldH.shapeMap.begin(); it != oldH.shapeMap.end(); ++it) { + int old_shape_index = it->first; + if (it->second.empty()) + join.shapeMap[old_shape_index] = ShapeHistory::List(); + for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) { + ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt); + if (kt != newH.shapeMap.end()) { + ShapeHistory::List& ary = join.shapeMap[old_shape_index]; + ary.insert(ary.end(), kt->second.begin(), kt->second.end()); + } + } + } + + return join; +} + /// returns the type name of the ViewProvider const char* Feature::getViewProviderName(void) const { return "PartGui::ViewProviderPart"; diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index efa0b7a836..689736a420 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -30,6 +30,8 @@ #include #include +class BRepBuilderAPI_MakeShape; + namespace Part { @@ -63,9 +65,10 @@ public: protected: void onChanged(const App::Property* prop); - -protected: TopLoc_Location getLocation() const; + ShapeHistory buildHistory(BRepBuilderAPI_MakeShape&, TopAbs_ShapeEnum type, + const TopoDS_Shape& newS, const TopoDS_Shape& oldS); + ShapeHistory joinHistory(const ShapeHistory&, const ShapeHistory&); }; class FilletBase : public Part::Feature diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index d0cc3981c2..f3e83a809a 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -343,6 +343,72 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) // ------------------------------------------------------------------------- +TYPESYSTEM_SOURCE(Part::PropertyShapeHistory , App::PropertyLists); + +PropertyShapeHistory::PropertyShapeHistory() +{ +} + +PropertyShapeHistory::~PropertyShapeHistory() +{ +} + +void PropertyShapeHistory::setValue(const ShapeHistory& sh) +{ + aboutToSetValue(); + _lValueList.resize(1); + _lValueList[0] = sh; + hasSetValue(); +} + +void PropertyShapeHistory::setValues(const std::vector& values) +{ + aboutToSetValue(); + _lValueList = values; + hasSetValue(); +} + +PyObject *PropertyShapeHistory::getPyObject(void) +{ + return Py::new_reference_to(Py::None()); +} + +void PropertyShapeHistory::setPyObject(PyObject *value) +{ +} + +void PropertyShapeHistory::Save (Base::Writer &writer) const +{ +} + +void PropertyShapeHistory::Restore(Base::XMLReader &reader) +{ +} + +void PropertyShapeHistory::SaveDocFile (Base::Writer &writer) const +{ +} + +void PropertyShapeHistory::RestoreDocFile(Base::Reader &reader) +{ +} + +App::Property *PropertyShapeHistory::Copy(void) const +{ + PropertyShapeHistory *p= new PropertyShapeHistory(); + p->_lValueList = _lValueList; + return p; +} + +void PropertyShapeHistory::Paste(const Property &from) +{ + aboutToSetValue(); + _lValueList = dynamic_cast(from)._lValueList; + hasSetValue(); +} + +// ------------------------------------------------------------------------- + TYPESYSTEM_SOURCE(Part::PropertyFilletEdges , App::PropertyLists); PropertyFilletEdges::PropertyFilletEdges() diff --git a/src/Mod/Part/App/PropertyTopoShape.h b/src/Mod/Part/App/PropertyTopoShape.h index 3113ed4561..a75c85ccd0 100644 --- a/src/Mod/Part/App/PropertyTopoShape.h +++ b/src/Mod/Part/App/PropertyTopoShape.h @@ -21,12 +21,15 @@ ***************************************************************************/ -#ifndef PROPERTYTOPOSHAPE_H -#define PROPERTYTOPOSHAPE_H +#ifndef PART_PROPERTYTOPOSHAPE_H +#define PART_PROPERTYTOPOSHAPE_H #include "TopoShape.h" +#include #include #include +#include +#include namespace Part { @@ -95,6 +98,59 @@ private: TopoShape _Shape; }; +struct PartExport ShapeHistory { + typedef std::map > MapList; + typedef std::vector List; + + TopAbs_ShapeEnum type; + MapList shapeMap; +}; + +class PartExport PropertyShapeHistory : public App::PropertyLists +{ + TYPESYSTEM_HEADER(); + +public: + PropertyShapeHistory(); + ~PropertyShapeHistory(); + + virtual void setSize(int newSize) { + _lValueList.resize(newSize); + } + virtual int getSize(void) const { + return _lValueList.size(); + } + + /** Sets the property + */ + void setValue(const ShapeHistory&); + + void setValues (const std::vector& values); + + const std::vector &getValues(void) const { + return _lValueList; + } + + virtual PyObject *getPyObject(void); + virtual void setPyObject(PyObject *); + + virtual void Save (Base::Writer &writer) const; + virtual void Restore(Base::XMLReader &reader); + + virtual void SaveDocFile (Base::Writer &writer) const; + virtual void RestoreDocFile(Base::Reader &reader); + + virtual Property *Copy(void) const; + virtual void Paste(const Property &from); + + virtual unsigned int getMemSize (void) const { + return _lValueList.size() * sizeof(ShapeHistory); + } + +private: + std::vector _lValueList; +}; + /** A property class to store hash codes and two radii for the fillet algorithm. * @author Werner Mayer */ @@ -151,4 +207,4 @@ private: } //namespace Part -#endif // PROPERTYTOPOSHAPE_H +#endif // PART_PROPERTYTOPOSHAPE_H diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index fd90af18f5..9bf4870744 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -433,25 +433,19 @@ void TopoShape::operator = (const TopoShape& sh) } } -void TopoShape::setTransform(const Base::Matrix4D& rclTrf) +void TopoShape::convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf) { - gp_Trsf mov; - mov.SetValues(rclTrf[0][0],rclTrf[0][1],rclTrf[0][2],rclTrf[0][3], - rclTrf[1][0],rclTrf[1][1],rclTrf[1][2],rclTrf[1][3], - rclTrf[2][0],rclTrf[2][1],rclTrf[2][2],rclTrf[2][3], - 0.00001,0.00001); - TopLoc_Location loc(mov); - _Shape.Location(loc); + trsf.SetValues(mtrx[0][0],mtrx[0][1],mtrx[0][2],mtrx[0][3], + mtrx[1][0],mtrx[1][1],mtrx[1][2],mtrx[1][3], + mtrx[2][0],mtrx[2][1],mtrx[2][2],mtrx[2][3], + 0.00001,0.00001); } -Base::Matrix4D TopoShape::getTransform(void) const +void TopoShape::convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx) { - Base::Matrix4D mtrx; - gp_Trsf Trf = _Shape.Location().Transformation(); - - gp_Mat m = Trf._CSFDB_Getgp_Trsfmatrix(); - gp_XYZ p = Trf._CSFDB_Getgp_Trsfloc(); - Standard_Real scale = Trf._CSFDB_Getgp_Trsfscale(); + gp_Mat m = trsf._CSFDB_Getgp_Trsfmatrix(); + gp_XYZ p = trsf._CSFDB_Getgp_Trsfloc(); + Standard_Real scale = trsf._CSFDB_Getgp_Trsfscale(); // set Rotation matrix mtrx[0][0] = scale * m._CSFDB_Getgp_Matmatrix(0,0); @@ -470,7 +464,21 @@ Base::Matrix4D TopoShape::getTransform(void) const mtrx[0][3] = p._CSFDB_Getgp_XYZx(); mtrx[1][3] = p._CSFDB_Getgp_XYZy(); mtrx[2][3] = p._CSFDB_Getgp_XYZz(); +} +void TopoShape::setTransform(const Base::Matrix4D& rclTrf) +{ + gp_Trsf mov; + convertTogpTrsf(rclTrf, mov); + TopLoc_Location loc(mov); + _Shape.Location(loc); +} + +Base::Matrix4D TopoShape::getTransform(void) const +{ + Base::Matrix4D mtrx; + gp_Trsf Trf = _Shape.Location().Transformation(); + convertToMatrix(Trf, mtrx); return mtrx; } diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 1de76b1cad..1d16b9e073 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -73,6 +73,8 @@ public: Base::Matrix4D getTransform(void) const; /// Bound box from the CasCade shape Base::BoundBox3d getBoundBox(void)const; + static void convertTogpTrsf(const Base::Matrix4D& mtrx, gp_Trsf& trsf); + static void convertToMatrix(const gp_Trsf& trsf, Base::Matrix4D& mtrx); //@} /** @name Subelement management */ diff --git a/src/Mod/Part/App/TopoShapeEdgePy.xml b/src/Mod/Part/App/TopoShapeEdgePy.xml index 873b3fb726..40def10305 100644 --- a/src/Mod/Part/App/TopoShapeEdgePy.xml +++ b/src/Mod/Part/App/TopoShapeEdgePy.xml @@ -24,7 +24,12 @@ Vector = valueAt(pos) - Get the point at the given parameter [0|Length] if defined - + + + Float = parameterAt(Vertex) - Get the parameter at the given vertex if lying on the edge + + + Vector = normalAt(pos) - Get the normal vector at the given parameter [0|Length] if defined @@ -64,6 +69,11 @@ Discretizes the edge using a given deflection or number of points and returns a list of points + + + Splits the edge at the given parameter values and builds a wire out of it + + Set or get the tolerance of the vertex diff --git a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp index ec5bc09054..329c6bd6c6 100644 --- a/src/Mod/Part/App/TopoShapeEdgePyImp.cpp +++ b/src/Mod/Part/App/TopoShapeEdgePyImp.cpp @@ -23,10 +23,13 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include # include +# include +# include # include # include # include @@ -47,6 +50,7 @@ # include # include # include +# include # include #endif @@ -55,12 +59,14 @@ #include #include +#include #include #include #include "TopoShape.h" #include "TopoShapeFacePy.h" #include "TopoShapeVertexPy.h" +#include "TopoShapeWirePy.h" #include "TopoShapeEdgePy.h" #include "TopoShapeEdgePy.cpp" @@ -185,6 +191,35 @@ PyObject* TopoShapeEdgePy::valueAt(PyObject *args) return new Base::VectorPy(new Base::Vector3d(V.X(),V.Y(),V.Z())); } +PyObject* TopoShapeEdgePy::parameterAt(PyObject *args) +{ + PyObject* pnt; + PyObject* face=0; + if (!PyArg_ParseTuple(args, "O!|O!",&TopoShapeVertexPy::Type,&pnt, + &TopoShapeFacePy::Type,&face)) + return 0; + + try { + const TopoDS_Shape& v = static_cast(pnt)->getTopoShapePtr()->_Shape; + const TopoDS_Edge& e = TopoDS::Edge(getTopoShapePtr()->_Shape); + + if (face) { + const TopoDS_Shape& f = static_cast(face)->getTopoShapePtr()->_Shape; + Standard_Real par = BRep_Tool::Parameter(TopoDS::Vertex(v), e, TopoDS::Face(f)); + return PyFloat_FromDouble(par); + } + else { + Standard_Real par = BRep_Tool::Parameter(TopoDS::Vertex(v), e); + return PyFloat_FromDouble(par); + } + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + PyErr_SetString(PyExc_Exception, e->GetMessageString()); + return 0; + } +} + PyObject* TopoShapeEdgePy::tangentAt(PyObject *args) { double u; @@ -443,6 +478,74 @@ PyObject* TopoShapeEdgePy::discretize(PyObject *args) return 0; } +PyObject* TopoShapeEdgePy::split(PyObject *args) +{ + PyObject* float_or_list; + if (!PyArg_ParseTuple(args, "O", &float_or_list)) + return 0; + + try { + BRepAdaptor_Curve adapt(TopoDS::Edge(getTopoShapePtr()->_Shape)); + Standard_Real f = adapt.FirstParameter(); + Standard_Real l = adapt.LastParameter(); + + std::vector par; + par.push_back(f); + if (PyFloat_Check(float_or_list)) { + double val = PyFloat_AsDouble(float_or_list); + if (val == f || val == l) { + PyErr_SetString(PyExc_ValueError, "Cannot split edge at start or end point"); + return 0; + } + else if (val < f || val > l) { + PyErr_SetString(PyExc_ValueError, "Value out of parameter range"); + return 0; + } + par.push_back(val); + } + else if (PyList_Check(float_or_list)) { + Py::List list(float_or_list); + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + double val = (double)Py::Float(*it); + if (val == f || val == l) { + PyErr_SetString(PyExc_ValueError, "Cannot split edge at start or end point"); + return 0; + } + else if (val < f || val > l) { + PyErr_SetString(PyExc_ValueError, "Value out of parameter range"); + return 0; + } + par.push_back(val); + } + } + else { + PyErr_SetString(PyExc_TypeError, "Either float or list of floats expected"); + return 0; + } + + par.push_back(l); + std::sort(par.begin(), par.end()); + + BRepBuilderAPI_MakeWire mkWire; + Handle_Geom_Curve c = adapt.Curve().Curve(); + std::vector::iterator end = par.end() - 1; + for (std::vector::iterator it = par.begin(); it != end; ++it) { + BRepBuilderAPI_MakeEdge mkBuilder(c, it[0], it[1]); + mkWire.Add(mkBuilder.Edge()); + } + + return new TopoShapeWirePy(new TopoShape(mkWire.Shape())); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + PyErr_SetString(PyExc_Exception, e->GetMessageString()); + return 0; + } + + PyErr_SetString(PyExc_Exception, "Geometry is not a curve"); + return 0; +} + PyObject* TopoShapeEdgePy::setTolerance(PyObject *args) { double tol; diff --git a/src/Mod/Part/App/modelRefine.cpp b/src/Mod/Part/App/modelRefine.cpp index 1a68b8ffea..25bb9378ac 100644 --- a/src/Mod/Part/App/modelRefine.cpp +++ b/src/Mod/Part/App/modelRefine.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -547,6 +548,8 @@ bool FaceUniter::process() { if (workShell.IsNull()) return false; + modifiedShapes.clear(); + deletedShapes.clear(); typeObjects.push_back(&getPlaneObject()); typeObjects.push_back(&getCylinderObject()); //add more face types. @@ -583,6 +586,12 @@ bool FaceUniter::process() facesToRemove.reserve(facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size()); FaceVectorType temp = adjacencySplitter.getGroup(adjacentIndex); facesToRemove.insert(facesToRemove.end(), temp.begin(), temp.end()); + // the first shape will be marked as modified, i.e. replaced by newFace, all others are marked as deleted + if (!temp.empty()) + { + modifiedShapes.push_back(std::make_pair(temp.front(), newFace)); + deletedShapes.insert(deletedShapes.end(), temp.begin()+1, temp.end()); + } } } } @@ -608,6 +617,15 @@ bool FaceUniter::process() sew.Add(*sewIt); sew.Perform(); workShell = TopoDS::Shell(sew.SewedShape()); + // update the list of modifications + for (std::vector::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it) + { + if (sew.IsModified(it->second)) + { + it->second = sew.Modified(it->second); + break; + } + } } else { @@ -629,6 +647,156 @@ bool FaceUniter::process() faceFixer.Perform(); } workShell = TopoDS::Shell(edgeFuse.Shape()); + // update the list of modifications + TopTools_DataMapOfShapeShape faceMap; + edgeFuse.Faces(faceMap); + for (std::vector::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it) + { + if (faceMap.IsBound(it->second)) + { + const TopoDS_Shape& value = faceMap.Find(it->second); + if (!value.IsSame(it->second)) + it->second = value; + } + } } return true; } + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +//BRepBuilderAPI_RefineModel implement a way to log all modifications on the faces + +Part::BRepBuilderAPI_RefineModel::BRepBuilderAPI_RefineModel(const TopoDS_Shape& shape) +{ + myShape = shape; + Build(); +} + +void Part::BRepBuilderAPI_RefineModel::Build() +{ + if (myShape.IsNull()) + Standard_Failure::Raise("Cannot remove splitter from empty shape"); + + if (myShape.ShapeType() == TopAbs_SOLID) { + const TopoDS_Solid &solid = TopoDS::Solid(myShape); + BRepTools_ReShape reshape; + TopExp_Explorer it; + for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) { + const TopoDS_Shell ¤tShell = TopoDS::Shell(it.Current()); + ModelRefine::FaceUniter uniter(currentShell); + if (uniter.process()) { + if (uniter.isModified()) { + const TopoDS_Shell &newShell = uniter.getShell(); + reshape.Replace(currentShell, newShell); + LogModifications(uniter); + } + } + else { + Standard_Failure::Raise("Removing splitter failed"); + } + } + myShape = reshape.Apply(solid); + } + else if (myShape.ShapeType() == TopAbs_SHELL) { + const TopoDS_Shell& shell = TopoDS::Shell(myShape); + ModelRefine::FaceUniter uniter(shell); + if (uniter.process()) { + myShape = uniter.getShell(); + LogModifications(uniter); + } + else { + Standard_Failure::Raise("Removing splitter failed"); + } + } + else if (myShape.ShapeType() == TopAbs_COMPOUND) { + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + + TopExp_Explorer xp; + // solids + for (xp.Init(myShape, TopAbs_SOLID); xp.More(); xp.Next()) { + const TopoDS_Solid &solid = TopoDS::Solid(xp.Current()); + BRepTools_ReShape reshape; + TopExp_Explorer it; + for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) { + const TopoDS_Shell ¤tShell = TopoDS::Shell(it.Current()); + ModelRefine::FaceUniter uniter(currentShell); + if (uniter.process()) { + if (uniter.isModified()) { + const TopoDS_Shell &newShell = uniter.getShell(); + reshape.Replace(currentShell, newShell); + LogModifications(uniter); + } + } + } + builder.Add(comp, reshape.Apply(solid)); + } + // free shells + for (xp.Init(myShape, TopAbs_SHELL, TopAbs_SOLID); xp.More(); xp.Next()) { + const TopoDS_Shell& shell = TopoDS::Shell(xp.Current()); + ModelRefine::FaceUniter uniter(shell); + if (uniter.process()) { + builder.Add(comp, uniter.getShell()); + LogModifications(uniter); + } + } + // the rest + for (xp.Init(myShape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + for (xp.Init(myShape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) { + if (!xp.Current().IsNull()) + builder.Add(comp, xp.Current()); + } + + myShape = comp; + } + + Done(); +} + +void Part::BRepBuilderAPI_RefineModel::LogModifications(const ModelRefine::FaceUniter& uniter) +{ + const std::vector& modShapes = uniter.getModifiedShapes(); + for (std::vector::const_iterator it = modShapes.begin(); it != modShapes.end(); ++it) { + TopTools_ListOfShape list; + list.Append(it->second); + myModified.Bind(it->first, list); + } + const ShapeVectorType& delShapes = uniter.getDeletedShapes(); + for (ShapeVectorType::const_iterator it = delShapes.begin(); it != delShapes.end(); ++it) { + myDeleted.Append(*it); + } +} + +const TopTools_ListOfShape& Part::BRepBuilderAPI_RefineModel::Modified(const TopoDS_Shape& S) +{ + if (myModified.IsBound(S)) + return myModified.Find(S); + else + return myEmptyList; +} + +Standard_Boolean Part::BRepBuilderAPI_RefineModel::IsDeleted(const TopoDS_Shape& S) +{ + TopTools_ListIteratorOfListOfShape it; + for (it.Initialize(myDeleted); it.More(); it.Next()) + { + if (it.Value().IsSame(S)) + return Standard_True; + } + + return Standard_False; +} + diff --git a/src/Mod/Part/App/modelRefine.h b/src/Mod/Part/App/modelRefine.h index ed1267f984..32c897ff70 100644 --- a/src/Mod/Part/App/modelRefine.h +++ b/src/Mod/Part/App/modelRefine.h @@ -36,12 +36,15 @@ #include #include #include +#include namespace ModelRefine { - typedef std::vector FaceVectorType; - typedef std::vector EdgeVectorType; + typedef std::vector FaceVectorType; + typedef std::vector EdgeVectorType; + typedef std::vector ShapeVectorType; + typedef std::pair ShapePairType; void getFaceEdges(const TopoDS_Face &face, EdgeVectorType &edges); void boundaryEdges(const FaceVectorType &faces, EdgeVectorType &edgesOut); @@ -147,10 +150,16 @@ namespace ModelRefine bool process(); const TopoDS_Shell& getShell() const {return workShell;} bool isModified(){return modifiedSignal;} + const std::vector& getModifiedShapes() const + {return modifiedShapes;} + const ShapeVectorType& getDeletedShapes() const + {return deletedShapes;} private: TopoDS_Shell workShell; std::vector typeObjects; + std::vector modifiedShapes; + ShapeVectorType deletedShapes; bool modifiedSignal; }; } @@ -170,5 +179,23 @@ GeomAbs_OffsetSurface, GeomAbs_OtherSurface }; */ +namespace Part { +class BRepBuilderAPI_RefineModel : public BRepBuilderAPI_MakeShape +{ +public: + BRepBuilderAPI_RefineModel(const TopoDS_Shape&); + void Build(); + const TopTools_ListOfShape& Modified(const TopoDS_Shape& S); + Standard_Boolean IsDeleted(const TopoDS_Shape& S); + +private: + void LogModifications(const ModelRefine::FaceUniter& uniter); + +private: + TopTools_DataMapOfShapeListOfShape myModified; + TopTools_ListOfShape myEmptyList; + TopTools_ListOfShape myDeleted; +}; +} #endif // MODELREFINE_H diff --git a/src/Mod/Part/Gui/DlgSettingsGeneral.cpp b/src/Mod/Part/Gui/DlgSettingsGeneral.cpp index 61bce31f2f..a9c917a86e 100644 --- a/src/Mod/Part/Gui/DlgSettingsGeneral.cpp +++ b/src/Mod/Part/Gui/DlgSettingsGeneral.cpp @@ -69,6 +69,7 @@ void DlgSettingsGeneral::saveSettings() Interface_Static::SetCVal("write.step.unit","MM"); break; } + ui->checkBooleanRefine->onSave(); } void DlgSettingsGeneral::loadSettings() @@ -77,6 +78,7 @@ void DlgSettingsGeneral::loadSettings() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part"); int unit = hGrp->GetInt("Unit", 0); ui->comboBoxUnits->setCurrentIndex(unit); + ui->checkBooleanRefine->onRestore(); } /** diff --git a/src/Mod/Part/Gui/DlgSettingsGeneral.ui b/src/Mod/Part/Gui/DlgSettingsGeneral.ui index 4bca9519f0..4ea3f5222d 100644 --- a/src/Mod/Part/Gui/DlgSettingsGeneral.ui +++ b/src/Mod/Part/Gui/DlgSettingsGeneral.ui @@ -1,10 +1,8 @@ - - - - + + PartGui::DlgSettingsGeneral - - + + 0 0 @@ -12,73 +10,54 @@ 333 - + General - - - 9 - - - 6 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + + + + Export - - + + 9 - + 6 - - + + - + Millimeter - + Meter - + Inch - - - + + + Units for export of STEP/IGES - + - + Qt::Horizontal - + 40 20 @@ -89,9 +68,50 @@ + + + + Boolean operation + + + + + + Automatically refine model after boolean operation + + + RefineModel + + + Mod/Part/Boolean + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + - + + + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+
diff --git a/src/Mod/Part/Gui/TaskLoft.cpp b/src/Mod/Part/Gui/TaskLoft.cpp index b6d58885df..96c3417a27 100644 --- a/src/Mod/Part/Gui/TaskLoft.cpp +++ b/src/Mod/Part/Gui/TaskLoft.cpp @@ -68,10 +68,14 @@ LoftWidget::LoftWidget(QWidget* parent) Gui::Application::Instance->runPythonCode("import Part"); d->ui.setupUi(this); - connect(d->ui.treeWidgetWire, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Loft")); + + connect(d->ui.selector->availableTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); - connect(d->ui.treeWidgetLoft, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + connect(d->ui.selector->selectedTreeWidget(), SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); + findShapes(); } @@ -105,7 +109,7 @@ void LoftWidget::findShapes() child->setData(0, Qt::UserRole, name); Gui::ViewProvider* vp = activeGui->getViewProvider(*it); if (vp) child->setIcon(0, vp->getIcon()); - d->ui.treeWidgetWire->addTopLevelItem(child); + d->ui.selector->availableTreeWidget()->addTopLevelItem(child); } } } @@ -125,13 +129,13 @@ bool LoftWidget::accept() QTextStream str(&list); - int count = d->ui.treeWidgetLoft->topLevelItemCount(); + int count = d->ui.selector->selectedTreeWidget()->topLevelItemCount(); if (count < 2) { QMessageBox::critical(this, tr("Too few elements"), tr("At least two vertices, edges or wires are required.")); return false; } for (int i=0; iui.treeWidgetLoft->topLevelItem(i); + QTreeWidgetItem* child = d->ui.selector->selectedTreeWidget()->topLevelItem(i); QString name = child->data(0, Qt::UserRole).toString(); str << "App.getDocument('" << d->document.c_str() << "')." << name << ", "; } @@ -177,61 +181,13 @@ void LoftWidget::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* } } -void LoftWidget::on_addButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetWire->currentItem(); - if (item) { - int index = d->ui.treeWidgetWire->indexOfTopLevelItem(item); - item = d->ui.treeWidgetWire->takeTopLevelItem(index); - d->ui.treeWidgetWire->setCurrentItem(0); - d->ui.treeWidgetLoft->addTopLevelItem(item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } -} - -void LoftWidget::on_removeButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - item = d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->setCurrentItem(0); - d->ui.treeWidgetWire->addTopLevelItem(item); - d->ui.treeWidgetWire->setCurrentItem(item); - } -} - -void LoftWidget::on_upButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item && d->ui.treeWidgetLoft->isItemSelected(item)) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - if (index > 0) { - d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->insertTopLevelItem(index-1, item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } - } -} - -void LoftWidget::on_downButton_clicked() -{ - QTreeWidgetItem* item = d->ui.treeWidgetLoft->currentItem(); - if (item && d->ui.treeWidgetLoft->isItemSelected(item)) { - int index = d->ui.treeWidgetLoft->indexOfTopLevelItem(item); - if (index < d->ui.treeWidgetLoft->topLevelItemCount()-1) { - d->ui.treeWidgetLoft->takeTopLevelItem(index); - d->ui.treeWidgetLoft->insertTopLevelItem(index+1, item); - d->ui.treeWidgetLoft->setCurrentItem(item); - } - } -} - void LoftWidget::changeEvent(QEvent *e) { QWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) { d->ui.retranslateUi(this); + d->ui.selector->setAvailableLabel(tr("Vertex/Wire")); + d->ui.selector->setSelectedLabel(tr("Loft")); } } diff --git a/src/Mod/Part/Gui/TaskLoft.h b/src/Mod/Part/Gui/TaskLoft.h index 82ac877a51..50d30771d1 100644 --- a/src/Mod/Part/Gui/TaskLoft.h +++ b/src/Mod/Part/Gui/TaskLoft.h @@ -43,10 +43,6 @@ public: bool reject(); private Q_SLOTS: - void on_addButton_clicked(); - void on_removeButton_clicked(); - void on_upButton_clicked(); - void on_downButton_clicked(); void onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); private: diff --git a/src/Mod/Part/Gui/TaskLoft.ui b/src/Mod/Part/Gui/TaskLoft.ui index 1dd0c6159f..dd461047d9 100644 --- a/src/Mod/Part/Gui/TaskLoft.ui +++ b/src/Mod/Part/Gui/TaskLoft.ui @@ -6,7 +6,7 @@ 0 0 - 324 + 336 326
@@ -14,172 +14,8 @@ Loft - - - - - Vertex/Wire - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 33 - 58 - - - - - - - - true - - - - 30 - 30 - - - - Move right - - - <b>Move the selected item one level down.</b><p>This will also change the level of the parent item.</p> - - - - - - - :/icons/button_right.xpm:/icons/button_right.xpm - - - - - - - true - - - - 30 - 30 - - - - Move left - - - <b>Move the selected item one level up.</b><p>This will also change the level of the parent item.</p> - - - - - - - :/icons/button_left.xpm:/icons/button_left.xpm - - - true - - - false - - - - - - - true - - - - 30 - 30 - - - - Move up - - - <b>Move the selected item up.</b><p>The item will be moved within the hierarchy level.</p> - - - - - - - :/icons/button_up.xpm:/icons/button_up.xpm - - - - - - - true - - - - 30 - 30 - - - - Move down - - - <b>Move the selected item down.</b><p>The item will be moved within the hierarchy level.</p> - - - - - - - :/icons/button_down.xpm:/icons/button_down.xpm - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 33 - 57 - - - - - - - - - - - Loft - - - + + @@ -188,15 +24,35 @@ - + Ruled surface + + + + Qt::Horizontal + + + + 130 + 20 + + + +
+ + + Gui::ActionSelector + QWidget +
Gui/Widgets.h
+
+
diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 5857b2ef71..d73afe2966 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -24,9 +24,15 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include +# include +# include +# include +# include #endif #include "ViewProviderBoolean.h" +#include #include #include #include @@ -71,6 +77,72 @@ QIcon ViewProviderBoolean::getIcon(void) const return ViewProviderPart::getIcon(); } +void applyColor(const Part::ShapeHistory& hist, + const std::vector& colBase, + std::vector& colBool) +{ + std::map >::const_iterator jt; + // apply color from modified faces + for (jt = hist.shapeMap.begin(); jt != hist.shapeMap.end(); ++jt) { + std::vector::const_iterator kt; + for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { + colBool[*kt] = colBase[jt->first]; + } + } +} + +void ViewProviderBoolean::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + if (hist.size() != 2) + return; + Part::Boolean* objBool = dynamic_cast(getObject()); + Part::Feature* objBase = dynamic_cast(objBool->Base.getValue()); + Part::Feature* objTool = dynamic_cast(objBool->Tool.getValue()); + if (objBase && objTool) { + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + const TopoDS_Shape& toolShape = objTool->Shape.getValue(); + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap, toolMap, boolMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + TopExp::MapShapes(toolShape, TopAbs_FACE, toolMap); + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + Gui::ViewProvider* vpTool = Gui::Application::Instance->getViewProvider(objTool); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + std::vector colTool = static_cast(vpTool)->DiffuseColor.getValues(); + std::vector colBool; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[0], colBase, colBool); + setColor = true; + } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[0], colBase, colBool); + setColor = true; + } + if (colTool.size() == toolMap.Extent()) { + applyColor(hist[1], colTool, colBool); + setColor = true; + } + else if (!colTool.empty() && colTool[0] != this->ShapeColor.getValue()) { + colTool.resize(toolMap.Extent(), colTool[0]); + applyColor(hist[1], colTool, colBool); + setColor = true; + } + if (setColor) + this->DiffuseColor.setValues(colBool); + } + } +} PROPERTY_SOURCE(PartGui::ViewProviderMultiFuse,PartGui::ViewProviderPart) @@ -92,6 +164,51 @@ QIcon ViewProviderMultiFuse::getIcon(void) const return Gui::BitmapFactory().pixmap("Part_Fuse"); } +void ViewProviderMultiFuse::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + Part::MultiFuse* objBool = dynamic_cast(getObject()); + std::vector sources = objBool->Shapes.getValues(); + if (hist.size() != sources.size()) + return; + + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + TopTools_IndexedMapOfShape boolMap; + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + + std::vector colBool; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + int index=0; + for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { + Part::Feature* objBase = dynamic_cast(*it); + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[index], colBase, colBool); + setColor = true; + } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[index], colBase, colBool); + setColor = true; + } + } + + if (setColor) + this->DiffuseColor.setValues(colBool); + } +} + PROPERTY_SOURCE(PartGui::ViewProviderMultiCommon,PartGui::ViewProviderPart) @@ -112,3 +229,48 @@ QIcon ViewProviderMultiCommon::getIcon(void) const { return Gui::BitmapFactory().pixmap("Part_Common"); } + +void ViewProviderMultiCommon::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + Part::MultiCommon* objBool = dynamic_cast(getObject()); + std::vector sources = objBool->Shapes.getValues(); + if (hist.size() != sources.size()) + return; + + const TopoDS_Shape& boolShape = objBool->Shape.getValue(); + TopTools_IndexedMapOfShape boolMap; + TopExp::MapShapes(boolShape, TopAbs_FACE, boolMap); + + std::vector colBool; + colBool.resize(boolMap.Extent(), this->ShapeColor.getValue()); + + bool setColor=false; + int index=0; + for (std::vector::iterator it = sources.begin(); it != sources.end(); ++it, ++index) { + Part::Feature* objBase = dynamic_cast(*it); + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[index], colBase, colBool); + setColor = true; + } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[index], colBase, colBool); + setColor = true; + } + } + + if (setColor) + this->DiffuseColor.setValues(colBool); + } +} diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.h b/src/Mod/Part/Gui/ViewProviderBoolean.h index 0fb245bb4e..29c5311092 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.h +++ b/src/Mod/Part/Gui/ViewProviderBoolean.h @@ -42,6 +42,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; /// ViewProvider for the MultiFuse feature @@ -58,6 +59,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; /// ViewProvider for the MultiFuse feature @@ -74,6 +76,7 @@ public: /// grouping handling std::vector claimChildren(void) const; QIcon getIcon(void) const; + void updateData(const App::Property*); }; diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp index c5fa4d2ad1..37e02dc6a9 100644 --- a/src/Mod/PartDesign/Gui/Command.cpp +++ b/src/Mod/PartDesign/Gui/Command.cpp @@ -428,11 +428,15 @@ void CmdPartDesignFillet::activated(int iMsg) SubNames.erase(SubNames.begin()+i); } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } } - if(SubNames.size() == 0){ + if (SubNames.size() == 0) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No Fillet possilbe on seleced faces/edges")); + QObject::tr("No fillet possible on selected faces/edges")); return; } @@ -571,11 +575,15 @@ void CmdPartDesignChamfer::activated(int iMsg) SubNames.erase(SubNames.begin()+i); } + // empty name or any other sub-element + else { + SubNames.erase(SubNames.begin()+i); + } } - if(SubNames.size() == 0){ + if (SubNames.size() == 0) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("No Fillet possilbe on seleced faces/edges")); + QObject::tr("No chamfer possible on selected faces/edges")); return; } diff --git a/src/Mod/Points/App/PointsPyImp.cpp b/src/Mod/Points/App/PointsPyImp.cpp index 75df742c8e..b0b2819ac8 100644 --- a/src/Mod/Points/App/PointsPyImp.cpp +++ b/src/Mod/Points/App/PointsPyImp.cpp @@ -59,14 +59,20 @@ int PointsPy::PyInit(PyObject* args, PyObject* /*kwd*/) *getPointKernelPtr() = *(static_cast(pcObj)->getPointKernelPtr()); } else if (PyList_Check(pcObj)) { - addPoints(args); + if (!addPoints(args)) + return -1; } else if (PyTuple_Check(pcObj)) { - addPoints(args); + if (!addPoints(args)) + return -1; } else if (PyString_Check(pcObj)) { getPointKernelPtr()->load(PyString_AsString(pcObj)); } + else { + PyErr_SetString(PyExc_TypeError, "optional argument must be list, tuple or string"); + return -1; + } return 0; } diff --git a/src/Mod/Ship/Instance.py b/src/Mod/Ship/Instance.py index 54a262b1d3..96045141df 100644 --- a/src/Mod/Ship/Instance.py +++ b/src/Mod/Ship/Instance.py @@ -49,7 +49,6 @@ class Ship: # Add shapes obj.Shape = Part.makeCompound(solids) obj.Proxy = self - self.obj = obj def onChanged(self, fp, prop): """ Called when a ship property is modified diff --git a/src/Tools/plugins/widget/FreeCAD_widgets.sln b/src/Tools/plugins/widget/FreeCAD_widgets.sln deleted file mode 100644 index 98489c937f..0000000000 --- a/src/Tools/plugins/widget/FreeCAD_widgets.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FreeCAD_widgets", "FreeCAD_widgets.vcproj", "{50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Debug|Win32.ActiveCfg = Debug|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Debug|Win32.Build.0 = Debug|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Release|Win32.ActiveCfg = Release|Win32 - {50CDC58F-0FF6-3CFA-BF66-E79DA98E7DF0}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Tools/plugins/widget/customwidgets.cpp b/src/Tools/plugins/widget/customwidgets.cpp index 6a2280377e..9de8c0f823 100644 --- a/src/Tools/plugins/widget/customwidgets.cpp +++ b/src/Tools/plugins/widget/customwidgets.cpp @@ -330,6 +330,94 @@ void AccelLineEdit::keyPressEvent ( QKeyEvent * e) // ------------------------------------------------------------------------------ +ActionSelector::ActionSelector(QWidget* parent) + : QWidget(parent) +{ + addButton = new QPushButton(this); + addButton->setMinimumSize(QSize(30, 30)); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/button_right.xpm"), QSize(), QIcon::Normal, QIcon::Off); + addButton->setIcon(icon); + gridLayout = new QGridLayout(this); + gridLayout->addWidget(addButton, 1, 1, 1, 1); + + spacerItem = new QSpacerItem(33, 57, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem, 5, 1, 1, 1); + spacerItem1 = new QSpacerItem(33, 58, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem1, 0, 1, 1, 1); + + removeButton = new QPushButton(this); + removeButton->setMinimumSize(QSize(30, 30)); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/button_left.xpm"), QSize(), QIcon::Normal, QIcon::Off); + removeButton->setIcon(icon1); + removeButton->setAutoDefault(true); + removeButton->setDefault(false); + + gridLayout->addWidget(removeButton, 2, 1, 1, 1); + + upButton = new QPushButton(this); + upButton->setMinimumSize(QSize(30, 30)); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/icons/button_up.xpm"), QSize(), QIcon::Normal, QIcon::Off); + upButton->setIcon(icon3); + + gridLayout->addWidget(upButton, 3, 1, 1, 1); + + downButton = new QPushButton(this); + downButton->setMinimumSize(QSize(30, 30)); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/button_down.xpm"), QSize(), QIcon::Normal, QIcon::Off); + downButton->setIcon(icon2); + downButton->setAutoDefault(true); + + gridLayout->addWidget(downButton, 4, 1, 1, 1); + + vboxLayout = new QVBoxLayout(); + vboxLayout->setContentsMargins(0, 0, 0, 0); + labelAvailable = new QLabel(this); + vboxLayout->addWidget(labelAvailable); + + availableWidget = new QTreeWidget(this); + availableWidget->setRootIsDecorated(false); + availableWidget->setHeaderLabels(QStringList() << QString()); + availableWidget->header()->hide(); + vboxLayout->addWidget(availableWidget); + + gridLayout->addLayout(vboxLayout, 0, 0, 6, 1); + + vboxLayout1 = new QVBoxLayout(); + vboxLayout1->setContentsMargins(0, 0, 0, 0); + labelSelected = new QLabel(this); + vboxLayout1->addWidget(labelSelected); + + selectedWidget = new QTreeWidget(this); + selectedWidget->setRootIsDecorated(false); + selectedWidget->setHeaderLabels(QStringList() << QString()); + selectedWidget->header()->hide(); + vboxLayout1->addWidget(selectedWidget); + + gridLayout->addLayout(vboxLayout1, 0, 2, 6, 1); + + addButton->setText(QString()); + removeButton->setText(QString()); + upButton->setText(QString()); + downButton->setText(QString()); + + labelAvailable->setText(QApplication::translate("Gui::ActionSelector", "Available:", 0, QApplication::UnicodeUTF8)); + labelSelected->setText(QApplication::translate("Gui::ActionSelector", "Selected:", 0, QApplication::UnicodeUTF8)); + addButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Add", 0, QApplication::UnicodeUTF8)); + removeButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Remove", 0, QApplication::UnicodeUTF8)); + upButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move up", 0, QApplication::UnicodeUTF8)); + downButton->setToolTip(QApplication::translate("Gui::ActionSelector", "Move down", 0, QApplication::UnicodeUTF8)); +} + +ActionSelector::~ActionSelector() +{ +} + +// -------------------------------------------------------------------- + CommandIconView::CommandIconView ( QWidget * parent ) : QListWidget(parent) { diff --git a/src/Tools/plugins/widget/customwidgets.h b/src/Tools/plugins/widget/customwidgets.h index 2543a119ef..013f6ba4ad 100644 --- a/src/Tools/plugins/widget/customwidgets.h +++ b/src/Tools/plugins/widget/customwidgets.h @@ -37,6 +37,7 @@ #include #include #include +#include namespace Gui { @@ -176,6 +177,32 @@ protected: // ------------------------------------------------------------------------------ +class ActionSelector : public QWidget +{ + Q_OBJECT + +public: + ActionSelector(QWidget* parent=0); + ~ActionSelector(); + +private: + QGridLayout *gridLayout; + QVBoxLayout *vboxLayout; + QVBoxLayout *vboxLayout1; + QPushButton *addButton; + QPushButton *removeButton; + QPushButton *upButton; + QPushButton *downButton; + QLabel *labelAvailable; + QLabel *labelSelected; + QTreeWidget *availableWidget; + QTreeWidget *selectedWidget; + QSpacerItem *spacerItem; + QSpacerItem *spacerItem1; +}; + +// ------------------------------------------------------------------------------ + class CommandIconView : public QListWidget { Q_OBJECT diff --git a/src/Tools/plugins/widget/plugin.cpp b/src/Tools/plugins/widget/plugin.cpp index d0a2aca923..3769f37d48 100644 --- a/src/Tools/plugins/widget/plugin.cpp +++ b/src/Tools/plugins/widget/plugin.cpp @@ -340,6 +340,79 @@ public: } }; +/* XPM */ +static const char *actionselector_pixmap[]={ +"22 22 6 1", +"a c #000000", +"# c #000080", +"b c #008080", +"c c #808080", +"d c #c0c0c0", +". c #ffffff", +"......................", +"......................", +"......................", +"...#aaaaaaaaaaaaaa#...", +".baccccccccccccccccab.", +".acccddddddddddddddca.", +"#ccd................d#", +"acc.................da", +"acd.......d....ca.ac.a", +"acd......db......a...a", +"acd.dbbb.dbbbd...a...a", +"acd.ccdbddb.db...a...a", +"acd.dbbbddb..b...a...a", +"acd.bd.bddb..b...a...a", +"acd.bbbbddbbbc...a...a", +"acd..d.....dd..ca.acda", +"#cd.................d#", +".ac................da.", +".badd............dda#.", +"...#aaaaaaaaaaaaaa#...", +"......................", +"......................"}; + +class ActionSelectorPlugin : public QDesignerCustomWidgetInterface +{ + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + ActionSelectorPlugin() + { + } + QWidget *createWidget(QWidget *parent) + { + return new Gui::ActionSelector(parent); + } + QString group() const + { + return QLatin1String("Input Widgets"); + } + QIcon icon() const + { + return QIcon( QPixmap( actionselector_pixmap ) ); + } + QString includeFile() const + { + return QLatin1String("Gui/Widgets.h"); + } + QString toolTip() const + { + return QLatin1String("Action Selector"); + } + QString whatsThis() const + { + return QLatin1String("A widget to select actions."); + } + bool isContainer() const + { + return false; + } + QString name() const + { + return QLatin1String("Gui::ActionSelector"); + } +}; + /* XPM */ static const char *iconview_pixmap[]={ "22 22 10 1", @@ -1061,6 +1134,7 @@ QList CustomWidgetPlugin::customWidgets () con cw.append(new LocationWidgetPlugin); cw.append(new FileChooserPlugin); cw.append(new AccelLineEditPlugin); + cw.append(new ActionSelectorPlugin); cw.append(new CommandIconViewPlugin); cw.append(new UIntSpinBoxPlugin); cw.append(new ColorButtonPlugin);