1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 // Class documentation in common/SoGuiComponentCommon.cpp.in.
34
35 // *************************************************************************
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif // HAVE_CONFIG_H
40 #include <qt-config.h>
41
42 #include <qbitmap.h>
43 #include <qwidget.h>
44 #include <qmainwindow.h>
45 #include <qapplication.h>
46 #include <qmetaobject.h>
47 #include <qcursor.h>
48 #include <qevent.h>
49
50 #include <Inventor/errors/SoDebugError.h>
51
52 #include <Inventor/Qt/SoQtComponentP.h>
53 #include <Inventor/Qt/moc_SoQtComponentP.icc>
54
55 #include <Inventor/Qt/SoQt.h>
56 #include <Inventor/Qt/SoQtComponent.h>
57 #include <Inventor/Qt/SoQtGLWidget.h>
58 #include <Inventor/Qt/SoQtRenderArea.h>
59 #include <Inventor/Qt/viewers/SoQtViewer.h>
60 #include <Inventor/Qt/viewers/SoQtFullViewer.h>
61 #include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
62 #include <Inventor/Qt/viewers/SoQtPlaneViewer.h>
63 #include <Inventor/Qt/viewers/SoQtConstrainedViewer.h>
64 #include <Inventor/Qt/viewers/SoQtFlyViewer.h>
65 #include <Inventor/Qt/SoAny.h>
66
67 #include <soqtdefs.h>
68
69 // debug
70 #define SOQTCOMP_RESIZE_DEBUG 0
71
72 static const char nullstring[] = "(null)";
73
74 #define PRIVATE(obj) (obj)
75 #define PUBLIC(obj) ((obj)->pub)
76
77 // *************************************************************************
78
79 #ifndef DOXYGEN_SKIP_THIS // Skip internal class SoQtComponentP.
80
81 // The private data and code for the SoQtComponent.
82
83 SbDict * SoQtComponentP::cursordict = NULL;
84
SoQtComponentP(SoQtComponent * o)85 SoQtComponentP::SoQtComponentP(SoQtComponent * o)
86 : SoGuiComponentP(o), classname(""), widgetname("")
87 {
88 }
89
~SoQtComponentP()90 SoQtComponentP::~SoQtComponentP()
91 {
92 }
93
94 void
fatalerrorHandler(void * userdata)95 SoQtComponentP::fatalerrorHandler(void * userdata)
96 {
97 SoQtComponentP * that = (SoQtComponentP *)userdata;
98 that->cleanupQtReferences();
99 }
100
101 void
cleanupQtReferences(void)102 SoQtComponentP::cleanupQtReferences(void)
103 {
104 // Kill the forwarding of messages to the eventFilter() method, as
105 // that can lead to all kinds of problems if a fatal-error triggers
106 // during construction of the component (like for instance in the
107 // case where no valid OpenGL canvas can be set up for the
108 // SoQtGLWidget).
109 this->parent->removeEventFilter(this);
110 }
111
112 #if COIN_MAJOR_VERSION >= 3
delete_dict_value(SbDict::Key key,void * value)113 static void delete_dict_value(SbDict::Key key, void * value)
114 {
115 delete (QCursor *)value;
116 }
117 #else // COIN_MAJOR_VERSION >= 3
delete_dict_value(unsigned long key,void * value)118 static void delete_dict_value(unsigned long key, void * value)
119 {
120 delete (QCursor *)value;
121 }
122 #endif // COIN_MAJOR_VERSION < 3
123 void
atexit_cleanup()124 SoQtComponentP::atexit_cleanup()
125 {
126 if (SoQtComponentP::cursordict) {
127 SoQtComponentP::cursordict->applyToAll(delete_dict_value);
128 delete SoQtComponentP::cursordict;
129 SoQtComponentP::cursordict = NULL;
130 }
131 }
132
133 // Converts from the common generic cursor format to a QCursor
134 // instance.
135 QCursor *
getNativeCursor(const SoQtCursor::CustomCursor * cc)136 SoQtComponentP::getNativeCursor(const SoQtCursor::CustomCursor * cc)
137 {
138 if (SoQtComponentP::cursordict == NULL) { // first call, initialize
139 SoQtComponentP::cursordict = new SbDict;
140 SoAny::atexit((SoAny::atexit_f*)SoQtComponentP::atexit_cleanup, 0);
141 }
142
143 void * qc;
144 SbBool b = SoQtComponentP::cursordict->find((uintptr_t)cc, qc);
145 if (b) { return (QCursor *)qc; }
146
147 #define MAXBITMAPWIDTH 32
148 #define MAXBITMAPHEIGHT 32
149 #define MAXBITMAPBYTES (((MAXBITMAPWIDTH + 7) / 8) * MAXBITMAPHEIGHT)
150
151 uchar cursorbitmap[MAXBITMAPBYTES];
152 uchar cursormask[MAXBITMAPBYTES];
153 (void)memset(cursorbitmap, 0x00, MAXBITMAPBYTES);
154 (void)memset(cursormask, 0x00, MAXBITMAPBYTES);
155
156 if ( !(cc->dim[0] <= MAXBITMAPWIDTH) )
157 printf("cursor bitmap width too large: %d\n", cc->dim[0]);
158 if ( !(cc->dim[1] <= MAXBITMAPHEIGHT) )
159 printf("cursor bitmap height too large: %d\n", cc->dim[1]);
160 assert(cc->dim[0] <= MAXBITMAPWIDTH && "internal bitmap too large");
161 assert(cc->dim[1] <= MAXBITMAPHEIGHT && "internal bitmap too large");
162
163 const int BYTEWIDTH = (cc->dim[0] + 7) / 8;
164 for (int h=0; h < cc->dim[1]; h++) {
165 for (int w=0; w < BYTEWIDTH; w++) {
166 const int cursorpos = h * ((MAXBITMAPWIDTH + 7) / 8) + w;
167 const int nativepos = h * BYTEWIDTH + w;
168 cursorbitmap[cursorpos] = cc->bitmap[nativepos];
169 cursormask[cursorpos] = cc->mask[nativepos];
170 }
171 }
172
173 // Always 32x32 because that's what is recommended in the Qt
174 // documentation for QCursor. At least WinNT 4 will give us
175 // "interesting" bugs for other cursor sizes.
176 #if QT_VERSION >= 0x040000
177 QBitmap bitmap = QBitmap::fromData(QSize(32, 32), cursorbitmap, QImage::Format_MonoLSB);
178 QBitmap mask = QBitmap::fromData(QSize(32, 32), cursormask, QImage::Format_MonoLSB);
179 #else
180 QBitmap bitmap(32, 32, cursorbitmap, true);
181 QBitmap mask(32, 32, cursormask, true);
182 #endif
183
184 // Sanity checks.
185 assert(bitmap.size().width() > 0 && bitmap.size().height() > 0);
186 assert(bitmap.size() == mask.size());
187 assert(bitmap.depth() == 1);
188 assert(mask.depth() == 1);
189
190 QCursor * c = new QCursor(bitmap, mask, cc->hotspot[0], cc->hotspot[1]);
191 SoQtComponentP::cursordict->enter((uintptr_t)cc, c);
192 return c;
193 }
194
195 // SLOT for when the user clicks/selects window decoration close.
196 void
widgetClosed(void)197 SoQtComponentP::widgetClosed(void)
198 {
199 if (this->closeCB) { this->closeCB(this->closeCBdata, PUBLIC(this)); }
200 }
201
202 // Helps us detect changes in size (base widget and parent widget)
203 // and visibility status.
204 bool
eventFilter(QObject * obj,QEvent * e)205 SoQtComponentP::eventFilter(QObject * obj, QEvent * e)
206 {
207 #if 0 // debug
208 static const char eventnaming[][50] = {
209 "None", // 0
210 "Timer",
211 "MouseButtonPress",
212 "MouseButtonRelease",
213 "MouseButtonDblClick",
214 "MouseMove",
215 "KeyPress",
216 "KeyRelease",
217 "FocusIn",
218 "FocusOut",
219 "Enter",
220 "Leave",
221 "Paint",
222 "Move",
223 "Resize",
224 "Create",
225 "Destroy",
226 "Show",
227 "Hide",
228 "Close",
229 "Quit", // 20
230 "*error*", "*error*", "*error*", "*error*", "*error*",
231 "*error*", "*error*", "*error*", "*error*",
232 "Accel", // 30
233 "Wheel",
234 "AccelAvailable", // 32
235 "*error*", "*error*", "*error*", "*error*",
236 "*error*", "*error*", "*error*",
237 "Clipboard", // 40
238 "*error*", "*error*", "*error*", "*error*", "*error*",
239 "*error*", "*error*", "*error*", "*error*",
240 "SockAct", // 50
241 "AccelOverride", "*error*", "*error*", "*error*", "*error*",
242 "*error*", "*error*", "*error*", "*error*",
243 "DragEnter", // 60
244 "DragMove",
245 "DragLeave",
246 "Drop",
247 "DragResponse", // 64
248 "*error*", "*error*", "*error*", "*error*", "*error*",
249 "ChildInserted", // 70
250 "ChildRemoved",
251 "LayoutHint", // 72
252 "*error*", "*error*", "*error*", "*error*", "*error*",
253 "*error*", "*error*",
254 "ActivateControl", // 80
255 "DeactivateControl"
256 };
257
258 SoDebugError::postInfo("SoQtComponent::eventFilter", "%p: %s",
259 obj, eventnaming[e->type()]);
260 #endif // debug
261
262 // The parent widget could receive resize events while we have still
263 // not completed the set up of our own widget, so we need to check
264 // for a NULL pointer.
265 //
266 // (One way to reproduce such a case: set up two SoQtExaminerViewer
267 // instances in an MDI workspace, first one, which is then
268 // maximized, then the second one instantiated will cause a crash.)
269 if (this->widget == NULL) { return false; }
270
271 if (e->type() == QEvent::Resize) {
272 QResizeEvent * r = (QResizeEvent *)e;
273
274 if (obj == (QObject *)this->parent) {
275 #if SOQTCOMP_RESIZE_DEBUG // debug
276 SoDebugError::postInfo("SoQtComponent::eventFilter",
277 "resize on parent (%p) to %p: (%d, %d)",
278 this->parent, this->widget,
279 r->size().width(), r->size().height());
280 #endif // debug
281 this->widget->resize(r->size());
282 this->storesize.setValue(r->size().width(), r->size().height());
283 PUBLIC(this)->sizeChanged(this->storesize);
284 }
285 else if (obj == (QObject *)this->widget) {
286 this->storesize.setValue(r->size().width(), r->size().height());
287 PUBLIC(this)->sizeChanged(this->storesize);
288 }
289 }
290 // Detect visibility changes.
291 else if (obj == this->widget &&
292 (e->type() == QEvent::Show || e->type() == QEvent::Hide)) {
293 if (this->visibilitychangeCBs) {
294 for (int i=0; i < this->visibilitychangeCBs->getLength()/2; i++) {
295 SoQtComponentVisibilityCB * cb =
296 (SoQtComponentVisibilityCB *)(*(this->visibilitychangeCBs))[i*2+0];
297 void * userdata = (*(this->visibilitychangeCBs))[i*2+1];
298 cb(userdata, e->type() == QEvent::Show ? true : false);
299 }
300 }
301 }
302
303 // It would seem more sensible that we should trigger on
304 // QEvent::Create than on QEvent::Show for the afterRealizeHook()
305 // method, but the QEvent::Create type is not yet used in Qt (as of
306 // version 2.2.2 at least) -- it has just been reserved for future
307 // releases.
308 if (e->type() == QEvent::Show && !this->realized) {
309 this->realized = true;
310 PUBLIC(this)->afterRealizeHook();
311 }
312
313 return false;
314 }
315
316 #endif // DOXYGEN_SKIP_THIS
317
318 #undef PUBLIC
319 #undef PRIVATE
320
321 #define PRIVATE(obj) ((obj)->pimpl)
322 #define PUBLIC(obj) ((obj)->pub)
323
324 // *************************************************************************
325
326 SOQT_OBJECT_ABSTRACT_SOURCE(SoQtComponent);
327
328 // documented in common/SoGuiComponentCommon.cpp.in.
329 void
initClasses(void)330 SoQtComponent::initClasses(void)
331 {
332 SoQtComponent::initClass();
333 SoQtGLWidget::initClass();
334 SoQtRenderArea::initClass();
335 SoQtViewer::initClass();
336 SoQtFullViewer::initClass();
337 SoQtExaminerViewer::initClass();
338 SoQtPlaneViewer::initClass();
339 SoQtConstrainedViewer::initClass();
340 #if 0 // TMP DISABLED: walkviewer not properly implemented yet. 20020624 mortene.
341 SoQtWalkViewer::initClass();
342 #endif // TMP DISABLED
343 SoQtFlyViewer::initClass();
344 }
345
346 // *************************************************************************
347
348 // documented in common/SoGuiComponentCommon.cpp.in.
SoQtComponent(QWidget * const parent,const char * const name,const SbBool embed)349 SoQtComponent::SoQtComponent(QWidget * const parent,
350 const char * const name,
351 const SbBool embed)
352 {
353 PRIVATE(this) = new SoQtComponentP(this);
354
355 PRIVATE(this)->realized = false;
356 PRIVATE(this)->shelled = false;
357 PRIVATE(this)->widget = NULL;
358 PRIVATE(this)->parent = parent;
359 PRIVATE(this)->closeCB = NULL;
360 PRIVATE(this)->closeCBdata = NULL;
361 PRIVATE(this)->visibilitychangeCBs = NULL;
362 PRIVATE(this)->fullscreen = false;
363
364 this->setClassName("SoQtComponent");
365
366 PRIVATE(this)->storesize.setValue(-1, -1);
367 #ifdef Q_OS_MAC
368 PRIVATE(this)->windowsize.setValue(-1, -1);
369 #endif
370
371 SoAny::si()->addInternalFatalErrorHandler(SoQtComponentP::fatalerrorHandler,
372 PRIVATE(this));
373
374 PRIVATE(this)->widgetname = (name ? name :
375 this->getDefaultWidgetName());
376
377 if (!parent || !embed) {
378 #if QT_VERSION >= 0x040000
379 PRIVATE(this)->parent = new QMainWindow();
380 PRIVATE(this)->parent->setObjectName(PRIVATE(this)->widgetname);
381 #else
382 PRIVATE(this)->parent = (QWidget *) new QMainWindow(NULL, name);
383 #endif
384 PRIVATE(this)->embedded = false;
385 PRIVATE(this)->shelled = true;
386 }
387 else {
388 PRIVATE(this)->parent = parent;
389 PRIVATE(this)->embedded = true;
390 }
391
392 PRIVATE(this)->parent->installEventFilter(PRIVATE(this));
393 }
394
395 // documented in common/SoGuiComponentCommon.cpp.in.
~SoQtComponent()396 SoQtComponent::~SoQtComponent()
397 {
398 if (PRIVATE(this)->widget) {
399 this->unregisterWidget(PRIVATE(this)->widget);
400 }
401
402 delete PRIVATE(this)->visibilitychangeCBs;
403
404 // Had to disable this code, as it caused seg-faults. FIXME: does
405 // that mean there's a leak now? 20020524 mortene.
406 #if 0
407 // If we've got a toplevel widget on our hands it won't
408 // automatically be deallocated (there's no real parent widget).
409 if (PRIVATE(this)->widget && !PRIVATE(this)->widget->parentWidget()) delete PRIVATE(this)->widget;
410 #endif
411
412 delete PRIVATE(this);
413 }
414
415 // FIXME: the visibility callback handling could be done in common
416 // code. 20020624 mortene.
417
418 // documented in common/SoGuiComponentCommon.cpp.in.
419 void
addVisibilityChangeCallback(SoQtComponentVisibilityCB * const func,void * const user)420 SoQtComponent::addVisibilityChangeCallback(SoQtComponentVisibilityCB * const func,
421 void * const user)
422 {
423 if (! PRIVATE(this)->visibilitychangeCBs)
424 PRIVATE(this)->visibilitychangeCBs = new SbPList;
425
426 PRIVATE(this)->visibilitychangeCBs->append((void *) func);
427 PRIVATE(this)->visibilitychangeCBs->append(user);
428 }
429
430 // documented in common/SoGuiComponentCommon.cpp.in.
431 void
removeVisibilityChangeCallback(SoQtComponentVisibilityCB * const func,void * const data)432 SoQtComponent::removeVisibilityChangeCallback(SoQtComponentVisibilityCB * const func,
433 void * const data)
434 {
435 #if SOQT_DEBUG
436 if (! PRIVATE(this)->visibilitychangeCBs) {
437 SoDebugError::postWarning("SoQtComponent::removeVisibilityChangeCallback",
438 "empty callback list");
439 return;
440 }
441 #endif // SOQT_DEBUG
442
443 int idx = PRIVATE(this)->visibilitychangeCBs->find((void *) func);
444 if (idx != -1) {
445 PRIVATE(this)->visibilitychangeCBs->remove(idx);
446 PRIVATE(this)->visibilitychangeCBs->remove(idx);
447 }
448
449 #if SOQT_DEBUG
450 if (idx == -1) {
451 SoDebugError::postWarning("SoQtComponent::removeVisibilityChangeCallback",
452 "tried to remove non-existant callback");
453 return;
454 }
455 #endif // SOQT_DEBUG
456 }
457
458 // documented in common/SoGuiComponentCommon.cpp.in.
459 void
setClassName(const char * const name)460 SoQtComponent::setClassName(const char * const name)
461 {
462 PRIVATE(this)->classname = name;
463 }
464
465 // *************************************************************************
466
467 // documented in common/SoGuiComponentCommon.cpp.in.
468 void
setBaseWidget(QWidget * widget)469 SoQtComponent::setBaseWidget(QWidget * widget)
470 {
471 QString iconText = this->getDefaultIconTitle();
472 QString widgetName = PRIVATE(this)->widgetname;
473
474 assert(widget);
475
476 if (PRIVATE(this)->widget) {
477 #if QT_VERSION >= 0x040000
478 iconText = (PRIVATE(this)->widget->windowIconText().isEmpty() ?
479 iconText :
480 PRIVATE(this)->widget->windowIconText());
481 widgetName = (PRIVATE(this)->widget->objectName().isEmpty() ?
482 widgetName :
483 PRIVATE(this)->widget->objectName());
484 #else
485 iconText = (PRIVATE(this)->widget->iconText().isEmpty() ?
486 iconText :
487 PRIVATE(this)->widget->iconText());
488 widgetName = (PRIVATE(this)->widget->name() == "" ?
489 widgetName :
490 QString(PRIVATE(this)->widget->name()));
491 #endif
492
493 PRIVATE(this)->widget->removeEventFilter(PRIVATE(this));
494 this->unregisterWidget(PRIVATE(this)->widget);
495 }
496
497 PRIVATE(this)->widget = widget;
498 this->registerWidget(PRIVATE(this)->widget);
499
500 #if 0 // debug
501 SoDebugError::postInfo("SoQtComponent::setBaseWidget",
502 "widget: %p, parent: %p", w, PRIVATE(this)->parent);
503 #endif // debug
504
505 if (!PRIVATE(this)->parent || PRIVATE(this)->parent->isTopLevel()) {
506 #if QT_VERSION >= 0x040000
507 if (PRIVATE(this)->widget->windowTitle() == "") {
508 #else
509 if (PRIVATE(this)->widget->caption() == "") {
510 #endif
511 this->setTitle(this->getDefaultTitle());
512 }
513
514 #if QT_VERSION >= 0x040000
515 SoQt::getShellWidget(this->getWidget())->setWindowIconText(iconText);
516 #else
517 SoQt::getShellWidget(this->getWidget())->setIconText(iconText);
518 #endif
519 }
520 #if QT_VERSION >= 0x040000
521 PRIVATE(this)->widget->setObjectName(widgetName);
522 #else
523 PRIVATE(this)->widget->setName(widgetName);
524 #endif
525 // Need this to auto-detect resize events.
526 PRIVATE(this)->widget->installEventFilter(PRIVATE(this));
527
528 QObject::connect(PRIVATE(this)->widget, SIGNAL(destroyed()),
529 PRIVATE(this), SLOT(widgetClosed()));
530 }
531
532 // *************************************************************************
533
534 /*!
535 This function \e must be called by subclasses after the component's
536 widget has been otherwise initialized.
537 */
538
539 /*
540 void
541 SoQtComponent::subclassInitialized(
542 void)
543 {
544 #if SOQT_DEBUG
545 if(!PRIVATE(this)->widget) {
546 SoDebugError::postWarning("SoQtComponent::subclassInitialized",
547 "Called while no QWidget has been set.");
548 return;
549 }
550 #endif // SOQT_DEBUG
551 }
552 */
553
554 // *************************************************************************
555
556 // documented in common/SoGuiComponentCommon.cpp.in.
557 void
558 SoQtComponent::show(void)
559 {
560 if(SOQT_DEBUG && !PRIVATE(this)->widget) { // debug
561 SoDebugError::postWarning("SoQtComponent::show",
562 "Called while no QWidget has been set.");
563 return;
564 }
565
566 if (SOQTCOMP_RESIZE_DEBUG) { // debug
567 SoDebugError::postInfo("SoQtComponent::show-1",
568 "resizing %p: (%d, %d)",
569 PRIVATE(this)->widget,
570 PRIVATE(this)->storesize[0],
571 PRIVATE(this)->storesize[1]);
572 }
573
574 if (PRIVATE(this)->shelled) {
575 PRIVATE(this)->parent->resize(PRIVATE(this)->storesize[0],
576 PRIVATE(this)->storesize[1]);
577 }
578 else {
579 PRIVATE(this)->widget->resize(PRIVATE(this)->storesize[0],
580 PRIVATE(this)->storesize[1]);
581 }
582
583 if (SOQTCOMP_RESIZE_DEBUG) { // debug
584 SoDebugError::postInfo("SoQtComponent::show-2",
585 "resized %p: (%d, %d)",
586 PRIVATE(this)->widget,
587 PRIVATE(this)->widget->size().width(),
588 PRIVATE(this)->widget->size().height());
589 }
590
591 PRIVATE(this)->widget->topLevelWidget()->show();
592
593 if (SOQTCOMP_RESIZE_DEBUG) { // debug
594 SoDebugError::postInfo("SoQtComponent::show-3",
595 "showed %p: (%d, %d)",
596 PRIVATE(this)->widget,
597 PRIVATE(this)->widget->size().width(),
598 PRIVATE(this)->widget->size().height());
599 }
600
601 PRIVATE(this)->widget->raise();
602
603 // Workaround for a bug Qt version 3.3.0 -3.3.3 on Mac OS X.
604 // Without this, a standalone examinerviewer gets displayed as a
605 // completely blank white window. The problem seems to be related to
606 // a paint event "getting lost" somewhere (it's registered in
607 // SoQtComponent but never makes it to SoQtGLWidget.) Note that
608 // this workaround is a weird thing in itself: calling raise() twice
609 // should not make a difference, since raise() puts the widget to
610 // the top of the stack so that "the widget will be visually in
611 // front of any overlapping sibling widgets".
612 #if (defined Q_OS_MAC && QT_VERSION >= 0x030300 && QT_VERSION < 0x030304)
613 PRIVATE(this)->widget->raise();
614 #endif // Q_OS_MAC
615
616 if (SOQTCOMP_RESIZE_DEBUG) { // debug
617 SoDebugError::postInfo("SoQtComponent::show-4",
618 "raised %p: (%d, %d)",
619 PRIVATE(this)->widget,
620 PRIVATE(this)->widget->size().width(),
621 PRIVATE(this)->widget->size().height());
622 }
623
624 this->sizeChanged(PRIVATE(this)->storesize);
625 }
626
627 // documented in common/SoGuiComponentCommon.cpp.in.
628 void
629 SoQtComponent::hide(void)
630 {
631 #if SOQT_DEBUG
632 if(!PRIVATE(this)->widget) {
633 SoDebugError::postWarning("SoQtComponent::hide",
634 "Called while no QWidget has been set.");
635 return;
636 }
637 #endif // SOQT_DEBUG
638
639 if (PRIVATE(this)->widget) { PRIVATE(this)->widget->topLevelWidget()->hide(); }
640 }
641
642 // *************************************************************************
643
644 // documented in common/SoGuiComponentCommon.cpp.in.
645 SbBool
646 SoQtComponent::isVisible(void)
647 {
648 if (! PRIVATE(this)->widget) { return false; }
649 return PRIVATE(this)->widget->isVisible();
650 }
651
652 // documented in common/SoGuiComponentCommon.cpp.in.
653 QWidget *
654 SoQtComponent::getWidget(void) const
655 {
656 return PRIVATE(this)->widget;
657 }
658
659 // documented in common/SoGuiComponentCommon.cpp.in.
660 QWidget *
661 SoQtComponent::getBaseWidget(void) const
662 {
663 return PRIVATE(this)->widget;
664 }
665
666 // documented in common/SoGuiComponentCommon.cpp.in.
667 SbBool
668 SoQtComponent::isTopLevelShell(void) const
669 {
670 #if SOQT_DEBUG && 0
671 if (! PRIVATE(this)->widget) {
672 SoDebugError::postWarning("SoQtComponent::isTopLevelShell",
673 "Called while no QWidget has been set.");
674 return false;
675 }
676 #endif // SOQT_DEBUG
677 return PRIVATE(this)->embedded ? false : true;
678 }
679
680 // documented in common/SoGuiComponentCommon.cpp.in.
681 QWidget *
682 SoQtComponent::getParentWidget(void) const
683 {
684 return PRIVATE(this)->parent;
685 }
686
687 // documented in common/SoGuiComponentCommon.cpp.in.
688 void
689 SoQtComponent::setTitle(const char * const title)
690 {
691 if (this->getWidget()) {
692 QWidget * toplevel = this->getWidget();
693 while (!toplevel->isTopLevel() ) {
694 toplevel = toplevel->parentWidget();
695 }
696 if (toplevel) {
697 #if QT_VERSION >= 0x040000
698 toplevel->setWindowTitle(title);
699 #else
700 toplevel->setCaption(title);
701 #endif
702 }
703 }
704 }
705
706 // documented in common/SoGuiComponentCommon.cpp.in.
707 const char *
708 SoQtComponent::getTitle(void) const
709 {
710 const char * result = "";
711
712 if (this->getWidget()) {
713 QWidget * toplevel = this->getWidget();
714 while (!toplevel->isTopLevel() ) {
715 toplevel = toplevel->parentWidget();
716 }
717 if (toplevel) {
718 #if QT_VERSION >= 0x040000
719 result = toplevel->windowTitle().toUtf8().constData();
720 #else
721 // Qt3 featured an implicit operator const char * () const
722 result = toplevel->caption();
723 #endif
724 }
725 }
726
727 return result;
728 }
729
730 // documented in common/SoGuiComponentCommon.cpp.in.
731 void
732 SoQtComponent::setIconTitle(const char * const title)
733 {
734 QWidget * w = this->getWidget();
735 if (w && this->isTopLevelShell()) {
736 #if QT_VERSION >= 0x040000
737 SoQt::getShellWidget(w)->setWindowIconText(title);
738 #else
739 SoQt::getShellWidget(w)->setIconText(title);
740 #endif
741 }
742 }
743
744 // documented in common/SoGuiComponentCommon.cpp.in.
745 const char *
746 SoQtComponent::getIconTitle(void) const
747 {
748 const char * result = nullstring;
749
750 QWidget * w = this->getWidget();
751 if (w && this->isTopLevelShell()) {
752 #if QT_VERSION >= 0x040000
753 QString iconText = SoQt::getShellWidget(w)->windowIconText();
754 #else
755 QString iconText = SoQt::getShellWidget(w)->iconText();
756 #endif
757
758 if (!iconText.isEmpty()) {
759 #if QT_VERSION >= 0x040000
760 result = iconText.toUtf8().constData();
761 #else
762 // Qt3 featured and implicit operator const char * () const
763 result = iconText;
764 #endif
765 }
766 }
767
768 return result;
769 }
770
771 // documented in common/SoGuiComponentCommon.cpp.in.
772 const char *
773 SoQtComponent::getWidgetName(void) const
774 {
775 const char * result = nullstring;
776
777 if (!PRIVATE(this)->widgetname.isEmpty()) {
778 #if QT_VERSION >= 0x040000
779 result = PRIVATE(this)->widgetname.toUtf8().constData();
780 #else
781 // Qt3 featured an implicit operator const char * () const
782 result = PRIVATE(this)->widgetname;
783 #endif
784 }
785
786 return result;
787 }
788
789 // documented in common/SoGuiComponentCommon.cpp.in.
790 const char *
791 SoQtComponent::getClassName(void) const
792 {
793 const char * result = nullstring;
794
795 if (!PRIVATE(this)->classname.isEmpty()) {
796 #if QT_VERSION >= 0x040000
797 result = PRIVATE(this)->classname.toUtf8().constData();
798 #else
799 // Qt3 featured and implicit operator const char * () const
800 result = PRIVATE(this)->classname;
801 #endif
802 }
803
804 return result;
805 }
806
807 // *************************************************************************
808
809 // FIXME: the resize handling in SoQtComponent and derived classes is
810 // really horrible and has provided us with heaps of "interesting"
811 // bugs. Should clean it up properly.
812 //
813 // One important part of the clean-up is to make a decent set of test
814 // cases to check with (and to later use for testing for
815 // regressions). The tests should at least cover these usage contexts
816 // for SoQt component classes:
817 //
818 //
819 // * an SoQt-component viewer embedded in other Qt-widgets
820 // * a top-level (ie "free window") viewer
821 //
822 // * an embedded SoQtRenderArea
823 // * a top-level SoQtRenderArea
824 //
825 // * viewers with and without sidebar- or bottom-decorations
826 //
827 // * changing size for all the above cases by user interaction with
828 // window manager decorations
829 //
830 // * changing size programmatically with SoQtComponent::setSize(),
831 // before and after component realization
832 //
833 // And all the above should be tested with both Qt v2 and Qt v3, on
834 // both a UNIX/X11 system and an MSWindows system (Qt is supposed to
835 // behave exactly the same on both platforms, but that's not always
836 // the case).
837 //
838 // 20021024 mortene.
839
840 // documented in common/SoGuiComponentCommon.cpp.in.
841 void
842 SoQtComponent::setSize(const SbVec2s size)
843 {
844 #if SOQT_DEBUG
845 if((size[0] <= 0) || (size[1] <= 0)) {
846 SoDebugError::postWarning("SoQtComponent::setSize",
847 "Invalid size setting: <%d, %d>.",
848 size[0], size[1]);
849 return;
850 }
851 #endif // SOQT_DEBUG
852
853 #if SOQTCOMP_RESIZE_DEBUG // debug
854 SoDebugError::postInfo("SoQtComponent::setSize",
855 "resize %p: (%d, %d)",
856 PRIVATE(this)->widget,
857 size[0], size[1]);
858 #endif // debug
859 const SbBool yetbuilt = (this->getWidget() != NULL);
860 if (yetbuilt) {
861 QWidget * shell = this->getShellWidget();
862 if (shell) { shell->resize(size[0], size[1]); }
863 }
864 PRIVATE(this)->storesize = size;
865 this->sizeChanged(size);
866 }
867
868 // documented in common/SoGuiComponentCommon.cpp.in.
869 SbVec2s
870 SoQtComponent::getSize(void) const
871 {
872 return PRIVATE(this)->storesize;
873 }
874
875 // documented in common/SoGuiComponentCommon.cpp.in.
876 void
877 SoQtComponent::sizeChanged(const SbVec2s & size)
878 {
879 // The default implementation does nothing.
880 }
881
882 // *************************************************************************
883
884 // documented in common/SoGuiComponentCommon.cpp.in.
885 void
886 SoQtComponent::setWindowCloseCallback(SoQtComponentCB * const func,
887 void * const data)
888 {
889 PRIVATE(this)->closeCB = func;
890 PRIVATE(this)->closeCBdata = data;
891 }
892
893 // *************************************************************************
894
895 // Documented in common/SoGuiComponentCommon.cpp.in.
896 void
897 SoQtComponent::afterRealizeHook(void)
898 {
899 }
900
901 // documented in common/SoGuiComponentCommon.cpp.in.
902 SbBool
903 SoQtComponent::setFullScreen(const SbBool onoff)
904 {
905 if (onoff == PRIVATE(this)->fullscreen) { return true; }
906
907 // FIXME: hmm.. this looks suspicious. Shouldn't we just return
908 // FALSE if the (base)widget is not a shellwidget? 20010817 mortene.
909 QWidget * w = this->getShellWidget();
910 if (w == NULL) w = this->getParentWidget();
911 if (w == NULL) w = this->getWidget();
912 if (!w) { return false; }
913
914 // FIXME: note that the compile-time binding technique against
915 // QWidget::showFullScreen() doesn't work very well with the idea
916 // that we'll compile a distribution version of SoQt against the
917 // lowest common denominator of Qt versions we support (ie v2.0.0),
918 // as that means SoQtComponent::setFullScreen() will never work as
919 // expected in the pre-compiled distro version we make. 20010608 mortene.
920
921 // prefer setWindowState() to showFullScreen() since
922 // setWindowState() will preserve other window flags/states.
923 #if HAVE_QWIDGET_SETWINDOWSTATE
924 if (onoff) {
925 #ifdef Q_OS_MAC
926 // Qt/Mac does not remember the window size when going fullscreen,
927 // so when going back to windowed mode, the window will be 1x1 pixels
928 // small -> we have to store the window size ourselves...
929 // FIXME: We should really figure out why this happens, and fix the
930 // problem, but I guess having a workaround is better than just having
931 // the window "disappear"... kyrah 20041118
932 PRIVATE(this)->windowsize[0] = w->size().width();
933 PRIVATE(this)->windowsize[1] = w->size().height();
934 #endif
935 w->setWindowState(w->windowState() | Qt::WindowFullScreen);
936 #ifdef Q_OS_MAC
937 // Explicit show needed for Mac OS X, otherwise the window "vanishes"
938 w->show();
939 #endif
940 } else {
941 w->setWindowState(w->windowState() & ~Qt::WindowFullScreen);
942 #ifdef Q_OS_MAC
943 w->resize(QSize(PRIVATE(this)->windowsize[0], PRIVATE(this)->windowsize[1]));
944 w->show();
945 #endif
946 }
947 #elif HAVE_QWIDGET_SHOWFULLSCREEN
948 if (onoff) w->showFullScreen();
949 else w->showNormal();
950 #else // HAVE_QWIDGET_SHOWFULLSCREEN
951 SoDebugError::postWarning("SoQtComponent::setFullScreen",
952 "SoQt was compiled against version %s of Qt, "
953 "which doesn't have the "
954 "QWidget::showFullScreen() method",
955 QT_VERSION_STR);
956 return false;
957 #endif // !HAVE_QWIDGET_SHOWFULLSCREEN && !HAVE_QWIDGET_SETWINDOWSTATE
958 PRIVATE(this)->fullscreen = onoff;
959 return true;
960 }
961
962 // documented in common/SoGuiComponentCommon.cpp.in.
963 SbBool
964 SoQtComponent::isFullScreen(void) const
965 {
966 return PRIVATE(this)->fullscreen;
967 }
968
969 // documented in common/SoGuiComponentCommon.cpp.in.
970 void
971 SoQtComponent::setComponentCursor(const SoQtCursor & cursor)
972 {
973 SoQtComponent::setWidgetCursor(this->getWidget(), cursor);
974 }
975
976 // documented in common/SoGuiComponentCommon.cpp.in.
977 void
978 SoQtComponent::setWidgetCursor(QWidget * w, const SoQtCursor & cursor)
979 {
980 // FIXME: as this function is called all the time when the cursor is
981 // grabbed by the window under X11, we should really compare with
982 // the previous cursor before doing anything, to avoid spending
983 // unnecessary clockcycles during animation. 20011203 mortene.
984
985 if (cursor.getShape() == SoQtCursor::CUSTOM_BITMAP) {
986
987 // Custom cursors do not work correctly in Qt/Mac versions 3.1.0
988 // and 3.1.1 - the displayed graphics look totally wrong.
989 //
990 // The bug has been confirmed fixed in Qt 3.1.2, but instead they
991 // introduced another bug: when you click on the widget, the
992 // cursor disappears. The Trolls have acknowledged that this is a
993 // bug, and that it will be fixed in 3.1.3.
994 #if defined Q_OS_MAC && ((QT_VERSION == 0x030100) || (QT_VERSION == 0x030101) || (QT_VERSION == 0x030102))
995 w->setCursor(QCursor(Qt::arrowCursor));
996 // spit out a warning that this is a Qt/Mac bug, not an SoQt problem
997 static SbBool warningdisplayed = false;
998 if (!warningdisplayed) {
999 const char * env = SoAny::si()->getenv("SOQT_NO_QTMAC_BUG_WARNINGS");
1000 if (!env || !atoi(env))
1001 SoDebugError::postWarning("SoQtComponent::setWidgetCursor",
1002 "\nThis version of Qt/Mac contains a bug "
1003 "that makes it impossible to use custom\n"
1004 "cursors. Warnings about Qt/Mac bugs "
1005 "can be turned off permanently by setting\n"
1006 "the environment variable "
1007 "SOQT_NO_QTMAC_BUG_WARNINGS=1.\n");
1008 warningdisplayed = true;
1009 }
1010 #else
1011 const SoQtCursor::CustomCursor * cc = &cursor.getCustomCursor();
1012 w->setCursor(*SoQtComponentP::getNativeCursor(cc));
1013 #endif
1014 }
1015 else {
1016 switch (cursor.getShape()) {
1017 case SoQtCursor::DEFAULT:
1018 w->setCursor(QCursor(Qt::ArrowCursor));
1019 break;
1020
1021 case SoQtCursor::BUSY:
1022 w->setCursor(QCursor(Qt::WaitCursor));
1023 break;
1024
1025 case SoQtCursor::CROSSHAIR:
1026 w->setCursor(QCursor(Qt::CrossCursor));
1027 break;
1028
1029 case SoQtCursor::UPARROW:
1030 w->setCursor(QCursor(Qt::UpArrowCursor));
1031 break;
1032
1033 default:
1034 assert(false && "unsupported cursor shape type");
1035 break;
1036 }
1037 }
1038
1039 // QWidget::setCursor() doesn't have an immediate effect with Qt/Win
1040 // if the cursor is already positioned over the widget -- and won't
1041 // change until the cursor position is moved. This is at least true
1042 // for Qt version 2.1.1.
1043 //
1044 // The code below is a simple work-around for this problem -- we
1045 // just offset the cursor position by a single pixel. There might be
1046 // a better way to get around the problem, but this seems easy
1047 // enough.
1048 // mortene
1049
1050 // FIXME: had to disable this as it didn't work under X11 (we get
1051 // continuous calls to this function, which means the mousecursor is
1052 // quickly pushed off the screen..). 20011203 mortene.
1053 #if 0
1054 QPoint p = w->cursor().pos();
1055 p.setX(p.x() + 1);
1056 w->cursor().setPos(p);
1057 #endif // disabled
1058 }
1059
1060 // *************************************************************************
1061
1062 #undef PRIVATE
1063 #undef PUBLIC
1064