Salome HOME
544917c70b3ca2a16b02a1ce0fb7628db7caa72e
[modules/gui.git] / src / GLViewer / GLViewer_ViewPort.cxx
1 // Copyright (C) 2007-2023  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  Author : OPEN CASCADE
24 // File:      GLViewer_ViewPort.cxx
25 // Created:   November, 2004
26 //#include <GLViewerAfx.h>
27 //
28 #include "GLViewer_ViewPort.h"
29
30 #include "SUIT_ResourceMgr.h"
31 #include "SUIT_Session.h"
32
33 #include <QRect>
34 #include <QCursor>
35 #include <QPainter>
36 #include <QMultiHash>
37 //#include <QMenu>
38 #include <QColorDialog>
39 #include <QColormap>
40 #include <QMouseEvent>
41 #include <QCoreApplication>
42
43 #include <stdlib.h>
44
45 #if !defined WIN32 && !defined __APPLE__
46 #include <QX11Info>
47 #include <GL/glx.h>
48 #include <X11/Xlib.h>
49 #include <X11/Xutil.h>
50 #include <X11/Xatom.h>
51 #include <X11/Xmu/StdCmap.h>
52 #undef QT_CLEAN_NAMESPACE
53 #include <Xw_Window.hxx>
54
55 /*!
56   \struct CMapEntry
57   Map of indexed colors
58   For internal purposes
59 */
60 struct CMapEntry
61 {
62     CMapEntry();
63     ~CMapEntry();
64     Colormap          cmap;
65     bool              alloc;
66     XStandardColormap scmap;
67 };
68
69 /*!
70   Constructor
71 */
72 CMapEntry::CMapEntry()
73 {
74     cmap = 0;
75     alloc = false;
76     scmap.colormap = 0;
77 }
78
79 /*!
80   Destructor
81 */
82 CMapEntry::~CMapEntry()
83 {
84     if ( alloc )
85         XFreeColormap( QX11Info::display(), cmap );
86 }
87
88 static QMultiHash<int,CMapEntry> *cmap_dict = 0;
89 static bool mesa_gl = false;
90
91 static void cleanup_cmaps()
92 {
93     if ( !cmap_dict )
94         return;
95     //while (!cmap_dict->isEmpty())
96     //  cmap_dict->erase(cmap_dict->begin());
97     cmap_dict->clear();
98     delete cmap_dict;
99     cmap_dict = 0;
100 }
101
102 static Colormap choose_cmap( Display *dpy, XVisualInfo *vi )
103 {
104     if ( !cmap_dict )
105     {
106         cmap_dict = new QMultiHash<int,CMapEntry>;
107         const char *v = glXQueryServerString( dpy, vi->screen, GLX_VERSION );
108         mesa_gl = strstr( v,"Mesa" ) != 0;
109         qAddPostRoutine( cleanup_cmaps );
110     }
111
112     QHash<int,CMapEntry>::iterator itH = cmap_dict->find( (long)vi->visualid );
113     if ( itH != cmap_dict->end() )  // found colormap for visual
114         return itH.value().cmap;
115
116     CMapEntry x;
117
118     XStandardColormap *c;
119     int n, i;
120
121 #ifdef DEBUG
122     cout << "Choosing cmap for vID = " << vi->visualid << endl;
123 #endif
124     if ( vi->visualid == XVisualIDFromVisual( XDefaultVisual( QX11Info::display(), -1 ) ) )
125     {
126 #ifdef DEBUG
127         cout << "Using XDefaultColormap" << endl;
128 #endif
129         return XDefaultColormap( QX11Info::display(), -1 );
130     }
131     if ( mesa_gl )
132     {
133         Atom hp_cmaps = XInternAtom( dpy, "_HP_RGB_SMOOTH_MAP_LIST", true );
134         if ( hp_cmaps && vi->visual->c_class == TrueColor && vi->depth == 8 )
135         {
136             if ( XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen ), &c, &n, hp_cmaps ) )
137             {
138                 i = 0;
139                 while ( i < n && x.cmap == 0 )
140                 {
141                     if ( c[i].visualid == vi->visual->visualid )
142                     {
143                         x.cmap = c[i].colormap;
144                         x.scmap = c[i];
145                     }
146                     i++;
147                 }
148                 XFree( (char*)c );
149             }
150         }
151     }
152 #if !defined( _OS_SOLARIS_ )
153     if ( !x.cmap )
154     {
155         if ( XmuLookupStandardColormap( dpy, vi->screen, vi->visualid, vi->depth, XA_RGB_DEFAULT_MAP, false, true ) )
156         {
157             if ( XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen ), &c, &n, XA_RGB_DEFAULT_MAP ) )
158             {
159                 i = 0;
160                 while ( i < n && x.cmap == 0 )
161                 {
162                     if ( c[i].visualid == vi->visualid )
163                     {
164                         x.cmap = c[i].colormap;
165                         x.scmap = c[i];
166                     }
167                     i++;
168                 }
169                 XFree( (char *)c );
170             }
171         }
172     }
173 #endif
174     if ( !x.cmap )
175     {
176         // no shared cmap found
177         x.cmap = XCreateColormap( dpy, RootWindow( dpy, vi->screen ), vi->visual, AllocNone );
178         x.alloc = true;
179     }
180
181     cmap_dict->insert( (long)vi->visualid, x ); // associate cmap with visualid
182     return x.cmap;
183 }
184 #endif
185
186 int GLViewer_ViewPort::nCounter = 0;
187 QCursor* GLViewer_ViewPort::defCursor = 0;
188 QCursor* GLViewer_ViewPort::panglCursor = 0;
189 QCursor* GLViewer_ViewPort::handCursor = 0;
190 QCursor* GLViewer_ViewPort::panCursor = 0;
191 QCursor* GLViewer_ViewPort::zoomCursor = 0;
192 QCursor* GLViewer_ViewPort::rotCursor = 0;
193 QCursor* GLViewer_ViewPort::sketchCursor = 0;
194
195 /*!
196     Creates the necessary viewport cursors. [ static ]
197 */
198 void GLViewer_ViewPort::createCursors ()
199 {
200     defCursor   = new QCursor( Qt::ArrowCursor );
201     panglCursor = new QCursor( Qt::CrossCursor );
202     handCursor  = new QCursor( Qt::PointingHandCursor );
203     panCursor   = new QCursor( Qt::SizeAllCursor );
204
205     SUIT_ResourceMgr* rmgr = SUIT_Session::session()->resourceMgr();
206     zoomCursor   = new QCursor( rmgr->loadPixmap( "GLViewer", tr( "ICON_GL_CURSOR_ZOOM" ) ) );
207     rotCursor    = new QCursor( rmgr->loadPixmap( "GLViewer", tr( "ICON_GL_CURSOR_ROTATE" ) ) );
208     sketchCursor = new QCursor( rmgr->loadPixmap( "GLViewer", tr( "ICON_GL_CURSOR_SKETCH" ) ) );
209 }
210
211 /*!
212     Destroys the viewport cursors. [ static ]
213 */
214 void GLViewer_ViewPort::destroyCursors()
215 {
216     delete defCursor;   defCursor   = 0;
217     delete panglCursor; panglCursor = 0;
218     delete handCursor;  handCursor  = 0;
219     delete panCursor;   panCursor   = 0;
220     delete zoomCursor;  zoomCursor  = 0;
221     delete rotCursor;   rotCursor   = 0;
222     delete sketchCursor; sketchCursor = 0;
223 }
224
225 /*!
226     Sets new default cursor. [ static ]
227 */
228 void GLViewer_ViewPort::setDefaultCursor( const QCursor& newCursor )
229 {
230     if ( !defCursor )
231         defCursor = new QCursor();
232     *defCursor = newCursor;
233 }
234
235 /*!
236     Sets new cursor for drawing rectangle in the viewport. [ static ]
237 */
238 void GLViewer_ViewPort::setHandCursor( const QCursor& newCursor )
239 {
240     if ( !handCursor )
241         handCursor = new QCursor();
242     *handCursor = newCursor;
243 }
244
245 /*!
246     Sets new cursor for panning. [ static ]
247 */
248 void GLViewer_ViewPort::setPanCursor( const QCursor& newCursor )
249 {
250     if ( !panCursor )
251         panCursor = new QCursor();
252     *panCursor = newCursor;
253 }
254
255 /*!
256     Sets new cursor for global panning. [ static ]
257 */
258 void GLViewer_ViewPort::setPanglCursor( const QCursor& newCursor )
259 {
260     if ( !panglCursor )
261         panglCursor = new QCursor();
262     *panglCursor = newCursor;
263 }
264
265 /*!
266     Sets new cursor for zooming. [ static ]
267 */
268 void GLViewer_ViewPort::setZoomCursor( const QCursor& newCursor )
269 {
270     if ( !zoomCursor )
271         zoomCursor = new QCursor();
272     *zoomCursor = newCursor;
273 }
274
275 /*!
276     Sets new cursor for rotating. [ static ]
277 */
278 void GLViewer_ViewPort::setRotCursor( const QCursor& newCursor )
279 {
280     if ( !rotCursor )
281         rotCursor = new QCursor();
282     *rotCursor = newCursor;
283 }
284
285 /*!
286     Sets new cursor for rotating. [ static ]
287 */
288 void GLViewer_ViewPort::setSketchCursor( const QCursor& newCursor )
289 {
290     if ( !rotCursor )
291         sketchCursor = new QCursor();
292     *sketchCursor = newCursor;
293 }
294
295 /*!
296     Constructor
297 */
298 GLViewer_ViewPort::GLViewer_ViewPort( QWidget* parent )
299 : QWidget( parent, 0 )
300 {
301     initialize();
302 }
303
304 /*!
305     Destructor
306 */
307 GLViewer_ViewPort::~GLViewer_ViewPort()
308 {
309     cleanup();
310 }
311
312 /*!
313     Initializes viewport. [ private ]
314 */
315 void GLViewer_ViewPort::initialize()
316 {
317     if ( nCounter++ == 0 )
318         createCursors();
319
320     //myPopupActions.setAutoDelete( true );
321     myPaintersRedrawing = false;
322     myEnableSketching = false;
323     myEnableTransform = true;
324
325     setMouseTracking( true );
326     setBackgroundRole( QPalette::NoRole );//NoBackground );
327
328     setFocusPolicy( Qt::StrongFocus );
329 }
330
331 /*!
332     Cleans up the viewport. [ private ]
333 */
334 void GLViewer_ViewPort::cleanup()
335 {
336     if ( --nCounter == 0 )
337         destroyCursors();
338 }
339
340 /*!
341     Selects visual ID for OpenGL window ( X11 specific ). [ protected ]
342 */
343 void GLViewer_ViewPort::selectVisualId( ViewType type )
344 {
345 #if !defined WIN32 && !defined __APPLE__
346     XVisualInfo* pVisualInfo;
347     if ( QX11Info::display() )
348     {
349         /* Initialization with the default VisualID */
350         //Visual *v = DefaultVisual( x11Display(), DefaultScreen( x11Display() ) );
351         // int visualID = XVisualIDFromVisual( v ); unused
352
353         /*  Here we use the settings from Optimizer_ViewInfo::TxglCreateWindow() */
354         int visualAttr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 1, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
355                              GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, None };
356
357         pVisualInfo = ::glXChooseVisual( QX11Info::display(), DefaultScreen( QX11Info::display() ), visualAttr );
358
359         if ( isVisible() )
360             hide();
361
362         XSetWindowAttributes a;
363
364         a.colormap = choose_cmap( QX11Info::display(), pVisualInfo );      /* find best colormap */
365         a.background_pixel = QColormap::instance().pixel( backgroundColor() );
366         a.border_pixel = QColormap::instance().pixel( Qt::black );
367         Window p = RootWindow( QX11Info::display(), DefaultScreen( QX11Info::display() ) );
368         if ( parentWidget() )
369             p = parentWidget()->winId();
370
371         Window w;
372         if ( type == Type2D )  // creating simple X window for 2d
373         {
374             unsigned long xbackground =
375                 BlackPixel( QX11Info::display(), DefaultScreen( QX11Info::display() ) );
376             unsigned long xforeground =
377                 WhitePixel( QX11Info::display(), DefaultScreen( QX11Info::display() ) );
378
379             w = XCreateSimpleWindow ( QX11Info::display(), p, x(), y(), width(),
380                                       height(), 0, xforeground, xbackground );
381         }
382         else if ( type == Type3D )
383         {
384             w = XCreateWindow( QX11Info::display(), p,  x(), y(), width(), height(),
385                                0, pVisualInfo->depth, InputOutput, pVisualInfo->visual,
386                                CWBackPixel | CWBorderPixel | CWColormap, &a );
387         }
388         else
389             return;
390
391         Window *cmw;
392         Window *cmwret;
393         int count;
394         if ( XGetWMColormapWindows( QX11Info::display(), topLevelWidget()->winId(), &cmwret, &count ) )
395         {
396             cmw = new Window[count+1];
397             memcpy( (char*)cmw, (char*)cmwret, sizeof(Window) * count );
398             XFree( (char*)cmwret );
399             int i;
400
401             for ( i = 0; i < count; i++ )
402             {
403                 if ( cmw[i] == winId() ) /* replace old window */
404                 {
405                     cmw[i] = w;
406                     break;
407                 }
408             }
409
410             if ( i >= count )            /* append new window */
411                 cmw[count++] = w;
412         }
413         else
414         {
415             count = 1;
416             cmw = new Window[count];
417             cmw[0] = w;
418         }
419
420         /* Creating new window (with good VisualID) for this widget */
421         create(w);
422         XSetWMColormapWindows( QX11Info::display(), topLevelWidget()->winId(), cmw, count );
423         delete[] cmw;
424
425         if ( isVisible() )
426             show();
427
428         if ( pVisualInfo )
429         {
430             XFree( (char *)pVisualInfo );
431         }
432         XFlush( QX11Info::display() );
433     }
434 #endif
435 }
436
437 /*!
438     Sets the background 'color'. [ virtual ]
439 */
440 void GLViewer_ViewPort::setBackgroundColor( const QColor& color )
441 {
442     QPalette pal = palette();
443     pal.setColor( QPalette::Background, color );
444     setPalette( pal );
445     repaint();
446 }
447
448 /*!
449     Returns the background color. [ virtual ]
450 */
451 QColor GLViewer_ViewPort::backgroundColor() const
452 {
453     return palette().color( QPalette::Active, QPalette::Background );
454 }
455
456 /*!
457     Returns 'true' if sketching is enabled in  this viewport. [ public ]
458 */
459 bool GLViewer_ViewPort::isSketchingEnabled() const
460 {
461     return myEnableSketching;
462 }
463
464 /*!
465     Enables / disables sketching  [ public ]
466 */
467 void GLViewer_ViewPort::setSketchingEnabled( bool enable )
468 {
469     myEnableSketching = enable;
470 }
471
472 /*!
473     Returns 'true' if transformations ( rotation, zoom etc. )
474     are enabled in this viewport. [ public ]
475 */
476 bool GLViewer_ViewPort::isTransformEnabled() const
477 {
478     return myEnableTransform;
479 }
480
481 /*!
482     Enables / disables transformations. [ public ]
483 */
484 void GLViewer_ViewPort::setTransformEnabled( bool enable )
485 {
486     myEnableTransform = enable;
487 }
488
489 /*!
490     Emits 'mouseEvent' signal. [ virtual protected ]
491 */
492 void GLViewer_ViewPort::mousePressEvent( QMouseEvent *e )
493 {
494     emit vpMouseEvent( e );
495 }
496
497 /*!
498     Emits 'mouseEvent' signal. [ virtual protected ]
499 */
500 void GLViewer_ViewPort::mouseMoveEvent( QMouseEvent* e )
501 {
502     emit vpMouseEvent( e );
503 }
504
505 /*!
506     Emits 'mouseEvent' signal. [ virtual protected ]
507 */
508 void GLViewer_ViewPort::mouseReleaseEvent( QMouseEvent *e )
509 {
510     emit vpMouseEvent( e );
511
512     /* show popup menu */
513     if ( e->button() == Qt::RightButton )
514     {
515         //QPopupMenu* popup = createPopup();
516         //if ( popup && popup->count() )
517         //    popup->exec( QCursor::pos() );
518         //destroyPopup( /*popup*/ );
519     }
520 }
521
522 /*!
523     Emits 'mouseEvent' signal. [ virtual protected ]
524 */
525 void GLViewer_ViewPort::mouseDoubleClickEvent( QMouseEvent *e )
526 {
527     emit vpMouseEvent( e );
528 }
529
530 /*!
531     Emits 'keyEvent' signal. [ virtual protected ]
532 */
533 void GLViewer_ViewPort::keyPressEvent( QKeyEvent *e )
534 {
535     emit vpKeyEvent( e );
536 }
537
538 /*!
539     Emits 'keyEvent' signal. [ virtual protected ]
540 */
541 void GLViewer_ViewPort::keyReleaseEvent( QKeyEvent *e )
542 {
543     emit vpKeyEvent( e );
544 }
545
546 /*!
547     Emits 'mouseEvent' signal. [ virtual protected ]
548 */
549 void GLViewer_ViewPort::wheelEvent( QWheelEvent *e )
550 {
551     emit vpWheelEvent( e );
552 }
553
554 /*!
555     Repaints the viewport. [ virtual protected ]
556 */
557 void GLViewer_ViewPort::paintEvent( QPaintEvent* )
558 {
559     if ( myPaintersRedrawing )
560     {
561         QPainter p( this );
562         emit vpDrawExternal( &p );
563         myPaintersRedrawing = false;
564     }
565 }
566
567 /*!
568     Forces to redraw the viewport by an external painter. [ public ]
569 */
570 void GLViewer_ViewPort::redrawPainters()
571 {
572     myPaintersRedrawing = true;
573     repaint();
574 }
575
576 /*!
577     Updates this view. Does nothing by default. [ virtual public ]
578 */
579 void GLViewer_ViewPort::onUpdate()
580 {
581 }
582
583 /*!
584     Sets the background color with color selection dialog. [ virtual protected slot ]
585 */
586 void GLViewer_ViewPort::onChangeBgColor()
587 {
588     QColor selColor = QColorDialog::getColor ( backgroundColor(), this );
589     if ( selColor.isValid() )
590         setBackgroundColor( selColor );
591 }
592
593 /*!
594   Custom context menu event handler
595 */
596 void GLViewer_ViewPort::contextMenuEvent( QContextMenuEvent* e )
597 {
598   //if ( e->reason() != QContextMenuEvent::Mouse )
599     emit contextMenuRequested( e );
600 }