329 lines
12 KiB
C++
329 lines
12 KiB
C++
/**************************************************************************\
|
|
* Copyright (c) Kongsberg Oil & Gas Technologies AS
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
\**************************************************************************/
|
|
|
|
#include "QuarterWidgetP.h"
|
|
#include <Quarter/QuarterWidget.h>
|
|
#include <Quarter/eventhandlers/EventFilter.h>
|
|
|
|
#include <QtGui/QApplication>
|
|
#include <QtGui/QCursor>
|
|
#include <QtGui/QMenu>
|
|
#include <QtCore/QMap>
|
|
|
|
#include <Inventor/nodes/SoCamera.h>
|
|
#include <Inventor/nodes/SoNode.h>
|
|
#include <Inventor/actions/SoSearchAction.h>
|
|
#include <Inventor/elements/SoGLCacheContextElement.h>
|
|
#include <Inventor/lists/SbList.h>
|
|
#include <Inventor/SoEventManager.h>
|
|
#include <Inventor/scxml/SoScXMLStateMachine.h>
|
|
#include <Inventor/misc/SoContextHandler.h>
|
|
#include <Inventor/C/glue/gl.h>
|
|
|
|
#include "NativeEvent.h"
|
|
#include "ContextMenu.h"
|
|
#include "QuarterP.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
using namespace SIM::Coin3D::Quarter;
|
|
|
|
class QuarterWidgetP_cachecontext {
|
|
public:
|
|
uint32_t id;
|
|
SbList <const QGLWidget *> widgetlist;
|
|
};
|
|
|
|
static SbList <QuarterWidgetP_cachecontext *> * cachecontext_list = NULL;
|
|
|
|
QuarterWidgetP::QuarterWidgetP(QuarterWidget * masterptr, const QGLWidget * sharewidget)
|
|
: master(masterptr),
|
|
scene(NULL),
|
|
eventfilter(NULL),
|
|
interactionmode(NULL),
|
|
sorendermanager(NULL),
|
|
soeventmanager(NULL),
|
|
initialsorendermanager(false),
|
|
initialsoeventmanager(false),
|
|
headlight(NULL),
|
|
cachecontext(NULL),
|
|
contextmenuenabled(true),
|
|
autoredrawenabled(true),
|
|
interactionmodeenabled(false),
|
|
clearzbuffer(true),
|
|
clearwindow(true),
|
|
addactions(true),
|
|
contextmenu(NULL)
|
|
{
|
|
this->cachecontext = findCacheContext(masterptr, sharewidget);
|
|
|
|
// FIXME: Centralize this as only one custom event filter can be
|
|
// added to an application. (20101019 handegar)
|
|
#ifdef HAVE_SPACENAV_LIB // fixes #0001970
|
|
qApp->setEventFilter(QuarterWidgetP::nativeEventFilter);
|
|
#endif
|
|
}
|
|
|
|
QuarterWidgetP::~QuarterWidgetP()
|
|
{
|
|
QGLWidget* glMaster = static_cast<QGLWidget*>(this->master->viewport());
|
|
removeFromCacheContext(this->cachecontext, glMaster);
|
|
if (this->contextmenu) {
|
|
delete this->contextmenu;
|
|
}
|
|
}
|
|
|
|
SoCamera *
|
|
QuarterWidgetP::searchForCamera(SoNode * root)
|
|
{
|
|
SoSearchAction sa;
|
|
sa.setInterest(SoSearchAction::FIRST);
|
|
sa.setType(SoCamera::getClassTypeId());
|
|
sa.apply(root);
|
|
|
|
if (sa.getPath()) {
|
|
SoNode * node = sa.getPath()->getTail();
|
|
if (node && node->isOfType(SoCamera::getClassTypeId())) {
|
|
return (SoCamera *) node;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t
|
|
QuarterWidgetP::getCacheContextId(void) const
|
|
{
|
|
return this->cachecontext->id;
|
|
}
|
|
|
|
QuarterWidgetP_cachecontext *
|
|
QuarterWidgetP::findCacheContext(QuarterWidget * widget, const QGLWidget * sharewidget)
|
|
{
|
|
if (cachecontext_list == NULL) {
|
|
// FIXME: static memory leak
|
|
cachecontext_list = new SbList <QuarterWidgetP_cachecontext*>;
|
|
}
|
|
for (int i = 0; i < cachecontext_list->getLength(); i++) {
|
|
QuarterWidgetP_cachecontext * cachecontext = (*cachecontext_list)[i];
|
|
|
|
for (int j = 0; j < cachecontext->widgetlist.getLength(); j++) {
|
|
if (cachecontext->widgetlist[j] == sharewidget) {
|
|
cachecontext->widgetlist.append(static_cast<const QGLWidget*>(widget->viewport()));
|
|
return cachecontext;
|
|
}
|
|
}
|
|
}
|
|
QuarterWidgetP_cachecontext * cachecontext = new QuarterWidgetP_cachecontext;
|
|
cachecontext->id = SoGLCacheContextElement::getUniqueCacheContext();
|
|
cachecontext->widgetlist.append(static_cast<const QGLWidget*>(widget->viewport()));
|
|
cachecontext_list->append(cachecontext);
|
|
|
|
return cachecontext;
|
|
}
|
|
|
|
void
|
|
QuarterWidgetP::removeFromCacheContext(QuarterWidgetP_cachecontext * context, const QGLWidget * widget)
|
|
{
|
|
context->widgetlist.removeItem((const QGLWidget*) widget);
|
|
|
|
if (context->widgetlist.getLength() == 0) { // last context in this share group?
|
|
assert(cachecontext_list);
|
|
|
|
for (int i = 0; i < cachecontext_list->getLength(); i++) {
|
|
if ((*cachecontext_list)[i] == context) {
|
|
// set the context while calling destructingContext() (might trigger OpenGL calls)
|
|
const_cast<QGLWidget*> (widget)->makeCurrent();
|
|
// fetch the cc_glglue context instance as a workaround for a bug fixed in Coin r12818
|
|
(void) cc_glglue_instance(context->id);
|
|
cachecontext_list->removeFast(i);
|
|
SoContextHandler::destructingContext(context->id);
|
|
const_cast<QGLWidget*> (widget)->doneCurrent();
|
|
delete context;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void
|
|
QuarterWidgetP::rendercb(void * userdata, SoRenderManager *)
|
|
{
|
|
QuarterWidget * thisp = static_cast<QuarterWidget *>(userdata);
|
|
|
|
if (thisp->pimpl->autoredrawenabled) {
|
|
thisp->redraw();
|
|
}
|
|
}
|
|
|
|
void
|
|
QuarterWidgetP::prerendercb(void * userdata, SoRenderManager * manager)
|
|
{
|
|
QuarterWidgetP * thisp = static_cast<QuarterWidgetP *>(userdata);
|
|
SoEventManager * evman = thisp->soeventmanager;
|
|
assert(evman);
|
|
for (int c = 0; c < evman->getNumSoScXMLStateMachines(); ++c) {
|
|
SoScXMLStateMachine * statemachine = evman->getSoScXMLStateMachine(c);
|
|
statemachine->preGLRender();
|
|
}
|
|
}
|
|
|
|
void
|
|
QuarterWidgetP::postrendercb(void * userdata, SoRenderManager * manager)
|
|
{
|
|
QuarterWidgetP * thisp = static_cast<QuarterWidgetP *>(userdata);
|
|
SoEventManager * evman = thisp->soeventmanager;
|
|
assert(evman);
|
|
for (int c = 0; c < evman->getNumSoScXMLStateMachines(); ++c) {
|
|
SoScXMLStateMachine * statemachine = evman->getSoScXMLStateMachine(c);
|
|
statemachine->postGLRender();
|
|
}
|
|
}
|
|
|
|
void
|
|
QuarterWidgetP::statechangecb(void * userdata, ScXMLStateMachine * statemachine, const char * stateid, SbBool enter, SbBool)
|
|
{
|
|
static const SbName contextmenurequest("contextmenurequest");
|
|
QuarterWidgetP * thisp = static_cast<QuarterWidgetP *>(userdata);
|
|
assert(thisp && thisp->master);
|
|
if (enter) {
|
|
SbName state(stateid);
|
|
if (thisp->contextmenuenabled && state == contextmenurequest) {
|
|
thisp->contextMenu()->exec(thisp->eventfilter->globalMousePosition());
|
|
}
|
|
if (QuarterP::statecursormap->contains(state)) {
|
|
QCursor cursor = QuarterP::statecursormap->value(state);
|
|
thisp->master->setCursor(cursor);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ADD_ACTION(enum, text, group, parent, list) \
|
|
do { \
|
|
QAction * action = new QAction(text, parent); \
|
|
action->setCheckable(true); \
|
|
action->setData(enum); \
|
|
action->setObjectName(text); \
|
|
action->setActionGroup(group); \
|
|
list.append(action); \
|
|
} while (0)
|
|
|
|
|
|
QList<QAction *>
|
|
QuarterWidgetP::transparencyTypeActions(void) const
|
|
{
|
|
if (this->transparencytypeactions.isEmpty()) {
|
|
this->transparencytypegroup = new QActionGroup(this->master);
|
|
ADD_ACTION(QuarterWidget::NONE, "none", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SCREEN_DOOR, "screen door", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::ADD, "add", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::DELAYED_ADD, "delayed add", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SORTED_OBJECT_ADD, "sorted object add", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::BLEND, "blend", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::DELAYED_BLEND, "delayed blend", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SORTED_OBJECT_BLEND, "sorted object blend", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SORTED_OBJECT_SORTED_TRIANGLE_ADD, "sorted object sorted triangle add", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SORTED_OBJECT_SORTED_TRIANGLE_BLEND, "sorted object sorted triangle blend", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
ADD_ACTION(QuarterWidget::SORTED_LAYERS_BLEND, "sorted layers blend", transparencytypegroup, this->master, this->transparencytypeactions);
|
|
}
|
|
return this->transparencytypeactions;
|
|
}
|
|
|
|
QList<QAction *>
|
|
QuarterWidgetP::stereoModeActions(void) const
|
|
{
|
|
if (this->stereomodeactions.isEmpty()) {
|
|
this->stereomodegroup = new QActionGroup(this->master);
|
|
ADD_ACTION(QuarterWidget::MONO, "mono", stereomodegroup, this->master, stereomodeactions);
|
|
ADD_ACTION(QuarterWidget::ANAGLYPH, "anaglyph", stereomodegroup, this->master, stereomodeactions);
|
|
ADD_ACTION(QuarterWidget::QUAD_BUFFER, "quad buffer", stereomodegroup, this->master, stereomodeactions);
|
|
ADD_ACTION(QuarterWidget::INTERLEAVED_ROWS, "interleaved rows", stereomodegroup, this->master, stereomodeactions);
|
|
ADD_ACTION(QuarterWidget::INTERLEAVED_COLUMNS, "interleaved columns", stereomodegroup, this->master, stereomodeactions);
|
|
}
|
|
return this->stereomodeactions;
|
|
}
|
|
|
|
QList<QAction *>
|
|
QuarterWidgetP::renderModeActions(void) const
|
|
{
|
|
if (this->rendermodeactions.isEmpty()) {
|
|
this->rendermodegroup = new QActionGroup(this->master);
|
|
ADD_ACTION(QuarterWidget::AS_IS, "as is", rendermodegroup, this->master, rendermodeactions);
|
|
ADD_ACTION(QuarterWidget::WIREFRAME, "wireframe", rendermodegroup, this->master, rendermodeactions);
|
|
ADD_ACTION(QuarterWidget::WIREFRAME_OVERLAY, "wireframe overlay", rendermodegroup, this->master, rendermodeactions);
|
|
ADD_ACTION(QuarterWidget::POINTS, "points", rendermodegroup, this->master, rendermodeactions);
|
|
ADD_ACTION(QuarterWidget::HIDDEN_LINE, "hidden line", rendermodegroup, this->master, rendermodeactions);
|
|
ADD_ACTION(QuarterWidget::BOUNDING_BOX, "bounding box", rendermodegroup, this->master, rendermodeactions);
|
|
}
|
|
return this->rendermodeactions;
|
|
}
|
|
|
|
#undef ADD_ACTION
|
|
|
|
QMenu *
|
|
QuarterWidgetP::contextMenu(void)
|
|
{
|
|
if (!this->contextmenu) {
|
|
this->contextmenu = new ContextMenu(this->master);
|
|
}
|
|
|
|
return this->contextmenu->getMenu();
|
|
}
|
|
|
|
|
|
bool
|
|
QuarterWidgetP::nativeEventFilter(void * message, long * result)
|
|
{
|
|
#ifdef HAVE_SPACENAV_LIB
|
|
XEvent * event = (XEvent *) message;
|
|
if (event->type == ClientMessage) {
|
|
// FIXME: I dont really like this, but the original XEvent will
|
|
// die before reaching the destination within the Qt system. To
|
|
// avoid this, we'll have to make a copy. We should try to find a
|
|
// workaround for this. (20101020 handegar)
|
|
|
|
// The copy will automatically be deleted when the NativeEvent dies.
|
|
XEvent * copy = (XEvent *) malloc(sizeof(XEvent));
|
|
memcpy(copy, event, sizeof(XEvent));
|
|
NativeEvent * ne = new NativeEvent(copy);
|
|
|
|
qApp->postEvent(QApplication::focusWidget(), ne);
|
|
return true;
|
|
}
|
|
#endif // HAVE_SPACENAV_LIB
|
|
|
|
return false;
|
|
}
|
|
|