/*========================================================================= Library: CTK Copyright (c) Kitware Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.commontk.org/LICENSE Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =========================================================================*/ // Qt includes #include #include #include #include #include // CTK includes #include "ctkVTKConnection.h" // VTK includes #include #include #include //----------------------------------------------------------------------------- QString convertPointerToString(void* pointer) { QString pointerAsString; QTextStream(&pointerAsString) << pointer; return pointerAsString; } //----------------------------------------------------------------------------- class ctkVTKConnectionPrivate { Q_DECLARE_PUBLIC(ctkVTKConnection); protected: ctkVTKConnection* const q_ptr; public: enum { ARG_UNKNOWN = 0, ARG_VTKOBJECT_AND_VTKOBJECT, ARG_VTKOBJECT_VOID_ULONG_VOID }; typedef ctkVTKConnectionPrivate Self; ctkVTKConnectionPrivate(ctkVTKConnection& object); ~ctkVTKConnectionPrivate(); void connect(); void disconnect(); /// /// VTK Callback static void DoCallback(vtkObject* vtk_obj, unsigned long event, void* client_data, void* call_data); /// /// Called by 'DoCallback' to emit signal void execute(vtkObject* vtk_obj, unsigned long vtk_event, void* client_data, void* call_data); vtkSmartPointer Callback; vtkObject* VTKObject; const QObject* QtObject; unsigned long VTKEvent; QString QtSlot; float Priority; int SlotType; bool Connected; bool Blocked; QString Id; bool ObserveDeletion; }; //----------------------------------------------------------------------------- // ctkVTKConnectionPrivate methods //----------------------------------------------------------------------------- ctkVTKConnectionPrivate::ctkVTKConnectionPrivate(ctkVTKConnection& object) :q_ptr(&object) { this->Callback = vtkSmartPointer::New(); this->Callback->SetCallback(ctkVTKConnectionPrivate::DoCallback); this->Callback->SetClientData(this); this->VTKObject = 0; this->QtObject = 0; this->VTKEvent = vtkCommand::NoEvent; this->Priority = 0.0; this->SlotType = ARG_UNKNOWN; this->Connected = false; this->Blocked = false; this->Id = convertPointerToString(this); this->ObserveDeletion = false; } //----------------------------------------------------------------------------- ctkVTKConnectionPrivate::~ctkVTKConnectionPrivate() { } //----------------------------------------------------------------------------- void ctkVTKConnectionPrivate::connect() { Q_Q(ctkVTKConnection); if (this->Connected) { qDebug() << "ctkVTKConnection already connected."; return; } switch (this->SlotType) { case ctkVTKConnectionPrivate::ARG_VTKOBJECT_AND_VTKOBJECT: QObject::connect(q, SIGNAL(emitExecute(vtkObject*, vtkObject*)), this->QtObject, this->QtSlot.toLatin1().data(), Qt::AutoConnection); break; case ctkVTKConnectionPrivate::ARG_VTKOBJECT_VOID_ULONG_VOID: QObject::connect(q, SIGNAL(emitExecute(vtkObject*, void*, unsigned long, void*)), this->QtObject, this->QtSlot.toLatin1().data(), Qt::AutoConnection); break; default: Q_ASSERT(false); qCritical() << "Failed to connect - " << "The slot (" << this->QtSlot << ") owned by " << "QObject(" << this->QtObject->objectName() << ")" << " seems to have a wrong signature."; break; } // Make a connection between this and the vtk object this->VTKObject->AddObserver(this->VTKEvent, this->Callback, this->Priority); // If necessary, observe vtk DeleteEvent if(this->ObserveDeletion) { // don't observe it twice if (this->VTKEvent != vtkCommand::DeleteEvent) { this->VTKObject->AddObserver(vtkCommand::DeleteEvent, this->Callback); } // Remove itself from its parent when vtkObject is deleted QObject::connect(this->QtObject, SIGNAL(destroyed(QObject*)), q, SLOT(qobjectDeleted())); } this->Connected = true; } //----------------------------------------------------------------------------- void ctkVTKConnectionPrivate::disconnect() { Q_Q(ctkVTKConnection); if (!this->Connected) { return; } if (this->QtObject) { switch (this->SlotType) { case ctkVTKConnectionPrivate::ARG_VTKOBJECT_AND_VTKOBJECT: QObject::disconnect(q, SIGNAL(emitExecute(vtkObject*, vtkObject*)), this->QtObject,this->QtSlot.toLatin1().data()); break; case ctkVTKConnectionPrivate::ARG_VTKOBJECT_VOID_ULONG_VOID: QObject::disconnect(q, SIGNAL(emitExecute(vtkObject*, void*, unsigned long, void*)), this->QtObject, this->QtSlot.toLatin1().data()); break; default: Q_ASSERT(false); qCritical() << "Failed to disconnect - " << "The slot (" << this->QtSlot << ") owned by " << "QObject(" << this->QtObject->objectName() << ")" << " seems to have a wrong signature."; break; } } if (this->VTKObject) { this->VTKObject->RemoveObserver(this->Callback); } if (this->ObserveDeletion && this->QtObject) { //this->VTKObject->AddObserver(vtkCommand::DeleteEvent, this->Callback); has already been removed QObject::disconnect(this->QtObject, SIGNAL(destroyed(QObject*)), q, SIGNAL(isBroke())); } this->Connected = false; } //----------------------------------------------------------------------------- // ctkVTKConnection methods //----------------------------------------------------------------------------- ctkVTKConnection::ctkVTKConnection(QObject* _parent): Superclass(_parent) , d_ptr(new ctkVTKConnectionPrivate(*this)) { } //----------------------------------------------------------------------------- ctkVTKConnection::~ctkVTKConnection() { Q_D(ctkVTKConnection); if (d->ObserveDeletion) { d->disconnect(); } } //----------------------------------------------------------------------------- QString ctkVTKConnection::id()const { Q_D(const ctkVTKConnection); return d->Id; } //----------------------------------------------------------------------------- QObject* ctkVTKConnection::object()const { Q_D(const ctkVTKConnection); return const_cast(d->QtObject); } //----------------------------------------------------------------------------- QDebug operator<<(QDebug dbg, const ctkVTKConnection& connection) { const ctkVTKConnectionPrivate* d = connection.d_func(); dbg.nospace() << "ctkVTKConnection:" << &connection << endl << "Id:" << d->Id << endl << " VTKObject:" << d->VTKObject->GetClassName() << "(" << d->VTKObject << ")" << endl << " QtObject:" << d->QtObject << endl << " VTKEvent:" << d->VTKEvent << endl << " QtSlot:" << d->QtSlot << endl << " SlotType:" << d->SlotType << endl << " Priority:" << d->Priority << endl << " Connected:" << d->Connected << endl << " Blocked:" << d->Blocked; return dbg.space(); } //----------------------------------------------------------------------------- QString ctkVTKConnection::shortDescription() { Q_D(ctkVTKConnection); return ctkVTKConnection::shortDescription(d->VTKObject, d->VTKEvent, d->QtObject, d->QtSlot); } //----------------------------------------------------------------------------- QString ctkVTKConnection::shortDescription(vtkObject* vtk_obj, unsigned long vtk_event, const QObject* qt_obj, QString qt_slot) { QString ret; QTextStream ts( &ret ); ts << (vtk_obj ? vtk_obj->GetClassName() : "NULL") << " " << vtk_event << " " << qt_obj << " " << qt_slot; return ret; } //----------------------------------------------------------------------------- bool ctkVTKConnection::isValid(vtkObject* vtk_obj, unsigned long vtk_event, const QObject* qt_obj, QString qt_slot) { Q_UNUSED(vtk_event); if (!vtk_obj) { return false; } if (!qt_obj) { return false; } if (qt_slot.isEmpty()) { return false; } return true; } //----------------------------------------------------------------------------- void ctkVTKConnection::setup(vtkObject* vtk_obj, unsigned long vtk_event, const QObject* qt_obj, QString qt_slot, float priority) { Q_D(ctkVTKConnection); if (!ctkVTKConnection::isValid(vtk_obj, vtk_event, qt_obj, qt_slot)) { return; } d->VTKObject = vtk_obj; d->QtObject = qt_obj; d->VTKEvent = vtk_event; d->QtSlot = qt_slot; d->Priority = priority; if (qt_slot.contains(QRegExp(QString("\\( ?vtkObject ?\\* ?, ?vtkObject ?\\* ?\\)")))) { d->SlotType = ctkVTKConnectionPrivate::ARG_VTKOBJECT_AND_VTKOBJECT; } else { d->SlotType = ctkVTKConnectionPrivate::ARG_VTKOBJECT_VOID_ULONG_VOID; } d->connect(); } //----------------------------------------------------------------------------- void ctkVTKConnection::setBlocked(bool block) { Q_D(ctkVTKConnection); d->Blocked = block; } //----------------------------------------------------------------------------- bool ctkVTKConnection::isBlocked()const { Q_D(const ctkVTKConnection); return d->Blocked; } //----------------------------------------------------------------------------- bool ctkVTKConnection::isEqual(vtkObject* vtk_obj, unsigned long vtk_event, const QObject* qt_obj, QString qt_slot)const { Q_D(const ctkVTKConnection); if (vtk_obj && d->VTKObject != vtk_obj) { return false; } if (vtk_event != vtkCommand::NoEvent && d->VTKEvent != vtk_event) { return false; } if (qt_obj && d->QtObject != qt_obj) { return false; } if (!qt_slot.isEmpty() && (QString(d->QtSlot).remove(' ').compare( QString(qt_slot).remove(' ')) != 0)) { return false; } return true; } //----------------------------------------------------------------------------- void ctkVTKConnectionPrivate::DoCallback(vtkObject* vtk_obj, unsigned long event, void* client_data, void* call_data) { ctkVTKConnectionPrivate* conn = reinterpret_cast(client_data); Q_ASSERT(conn); conn->execute(vtk_obj, event, client_data, call_data); } //----------------------------------------------------------------------------- // callback from VTK to emit signal void ctkVTKConnectionPrivate::execute(vtkObject* vtk_obj, unsigned long vtk_event, void* client_data, void* call_data) { Q_Q(ctkVTKConnection); Q_ASSERT(this->Connected); if (this->Blocked) { return; } QPointer connection(q); if(!this->ObserveDeletion || vtk_event != vtkCommand::DeleteEvent || this->VTKEvent == vtkCommand::DeleteEvent) { vtkObject* callDataAsVtkObject = 0; switch (this->SlotType) { case ctkVTKConnectionPrivate::ARG_VTKOBJECT_AND_VTKOBJECT: if (this->VTKEvent == vtk_event) { callDataAsVtkObject = reinterpret_cast( call_data ); if (!callDataAsVtkObject) { qCritical() << "The VTKEvent(" << this->VTKEvent<< ") triggered by vtkObject(" << this->VTKObject->GetClassName() << ") " << "doesn't return data of type vtkObject." << endl << "The slot (" << this->QtSlot << ") owned by " << "QObject(" << this->QtObject->objectName() << ")" << " may be incorrect."; } emit q->emitExecute(vtk_obj, callDataAsVtkObject); } break; case ctkVTKConnectionPrivate::ARG_VTKOBJECT_VOID_ULONG_VOID: emit q->emitExecute(vtk_obj, call_data, vtk_event, client_data); break; default: // Should never reach qCritical() << "Unknown SlotType:" << this->SlotType; return; break; } } if (!connection.isNull() && vtk_event == vtkCommand::DeleteEvent) { q->vtkObjectDeleted(); } } void ctkVTKConnection::observeDeletion(bool enable) { Q_D(ctkVTKConnection); d->ObserveDeletion = enable; } bool ctkVTKConnection::deletionObserved()const { Q_D(const ctkVTKConnection); return d->ObserveDeletion; } void ctkVTKConnection::vtkObjectDeleted() { Q_D(ctkVTKConnection); d->VTKObject = 0; d->disconnect(); emit isBroke(); } void ctkVTKConnection::qobjectDeleted() { Q_D(ctkVTKConnection); d->QtObject = 0; d->disconnect(); emit isBroke(); }