1 /****************************************************************************
2  **
3  ** Copyright (C) 2016 The Qt Company Ltd.
4  ** Contact: https://www.qt.io/licensing/
5  **
6  ** This file is part of the examples of the Qt Toolkit.
7  **
8  ** $QT_BEGIN_LICENSE:BSD$
9  ** Commercial License Usage
10  ** Licensees holding valid commercial Qt licenses may use this file in
11  ** accordance with the commercial license agreement provided with the
12  ** Software or, alternatively, in accordance with the terms contained in
13  ** a written agreement between you and The Qt Company. For licensing terms
14  ** and conditions see https://www.qt.io/terms-conditions. For further
15  ** information use the contact form at https://www.qt.io/contact-us.
16  **
17  ** BSD License Usage
18  ** Alternatively, you may use this file under the terms of the BSD license
19  ** as follows:
20  **
21  ** "Redistribution and use in source and binary forms, with or without
22  ** modification, are permitted provided that the following conditions are
23  ** met:
24  **   * Redistributions of source code must retain the above copyright
25  **     notice, this list of conditions and the following disclaimer.
26  **   * Redistributions in binary form must reproduce the above copyright
27  **     notice, this list of conditions and the following disclaimer in
28  **     the documentation and/or other materials provided with the
29  **     distribution.
30  **   * Neither the name of The Qt Company Ltd nor the names of its
31  **     contributors may be used to endorse or promote products derived
32  **     from this software without specific prior written permission.
33  **
34  **
35  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46  **
47  ** $QT_END_LICENSE$
48  **
49  ****************************************************************************/
50 
51 #include "glwidget.h"
52 #include <QPainter>
53 #include <QPaintEngine>
54 #include <QOpenGLShaderProgram>
55 #include <QOpenGLTexture>
56 #include <QRandomGenerator>
57 #include <QCoreApplication>
58 #include <qmath.h>
59 
60 #include "mainwindow.h"
61 #include "bubble.h"
62 
63 const int bubbleNum = 8;
64 
65 #ifndef GL_SRGB8_ALPHA8
66 #define GL_SRGB8_ALPHA8 0x8C43
67 #endif
68 
GLWidget(MainWindow * mw,bool button,const QColor & background)69 GLWidget::GLWidget(MainWindow *mw, bool button, const QColor &background)
70     : m_mainWindow(mw),
71       m_hasButton(button),
72       m_background(background)
73 {
74     setMinimumSize(300, 250);
75     if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
76         setTextureFormat(GL_SRGB8_ALPHA8);
77 }
78 
~GLWidget()79 GLWidget::~GLWidget()
80 {
81     qDeleteAll(m_bubbles);
82 
83     // And now release all OpenGL resources.
84     makeCurrent();
85     delete m_texture;
86     delete m_program1;
87     delete m_program2;
88     delete m_vshader1;
89     delete m_fshader1;
90     delete m_vshader2;
91     delete m_fshader2;
92     m_vbo1.destroy();
93     m_vbo2.destroy();
94     doneCurrent();
95 }
96 
setScaling(int scale)97 void GLWidget::setScaling(int scale)
98 {
99     if (scale > 30)
100         m_fScale = 1 + qreal(scale - 30) / 30 * 0.25;
101     else if (scale < 30)
102         m_fScale =  1 - (qreal(30 - scale) / 30 * 0.25);
103     else
104         m_fScale = 1;
105 }
106 
setLogo()107 void GLWidget::setLogo()
108 {
109     m_qtLogo = true;
110 }
111 
setTexture()112 void GLWidget::setTexture()
113 {
114     m_qtLogo = false;
115 }
116 
setShowBubbles(bool bubbles)117 void GLWidget::setShowBubbles(bool bubbles)
118 {
119     m_showBubbles = bubbles;
120 }
121 
paintQtLogo()122 void GLWidget::paintQtLogo()
123 {
124     m_program1->enableAttributeArray(m_vertexAttr1);
125     m_program1->enableAttributeArray(m_normalAttr1);
126 
127     m_vbo1.bind();
128     // The data in the buffer is placed like this:
129     // vertex1.x, vertex1.y, vertex1.z, normal1.x, normal1.y, normal1.z, vertex2.x, ...
130     m_program1->setAttributeBuffer(m_vertexAttr1, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));
131     m_program1->setAttributeBuffer(m_normalAttr1, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
132     m_vbo1.release();
133 
134     glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
135 
136     m_program1->disableAttributeArray(m_normalAttr1);
137     m_program1->disableAttributeArray(m_vertexAttr1);
138 }
139 
paintTexturedCube()140 void GLWidget::paintTexturedCube()
141 {
142     m_texture->bind();
143 
144     if (!m_vbo2.isCreated()) {
145         static GLfloat afVertices[] = {
146             -0.5, 0.5, 0.5, 0.5,-0.5,0.5,-0.5,-0.5,0.5,
147             0.5, -0.5, 0.5, -0.5,0.5,0.5,0.5,0.5,0.5,
148             -0.5, -0.5, -0.5, 0.5,-0.5,-0.5,-0.5,0.5,-0.5,
149             0.5, 0.5, -0.5, -0.5,0.5,-0.5,0.5,-0.5,-0.5,
150 
151             0.5, -0.5, -0.5, 0.5,-0.5,0.5,0.5,0.5,-0.5,
152             0.5, 0.5, 0.5, 0.5,0.5,-0.5,0.5,-0.5,0.5,
153             -0.5, 0.5, -0.5, -0.5,-0.5,0.5,-0.5,-0.5,-0.5,
154             -0.5, -0.5, 0.5, -0.5,0.5,-0.5,-0.5,0.5,0.5,
155 
156             0.5, 0.5,  -0.5, -0.5, 0.5,  0.5,  -0.5,  0.5,  -0.5,
157             -0.5,  0.5,  0.5,  0.5,  0.5,  -0.5, 0.5, 0.5,  0.5,
158             -0.5,  -0.5, -0.5, -0.5, -0.5, 0.5,  0.5, -0.5, -0.5,
159             0.5, -0.5, 0.5,  0.5,  -0.5, -0.5, -0.5,  -0.5, 0.5
160         };
161 
162         static GLfloat afTexCoord[] = {
163             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
164             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
165             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
166             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
167 
168             1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f,
169             0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f,
170             0.0f,0.0f, 1.0f,1.0f, 1.0f,0.0f,
171             1.0f,1.0f, 0.0f,0.0f, 0.0f,1.0f,
172 
173             0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f,
174             1.0f,0.0f, 0.0f,1.0f, 0.0f,0.0f,
175             1.0f,0.0f, 1.0f,1.0f, 0.0f,0.0f,
176             0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f
177         };
178 
179         GLfloat afNormals[] = {
180 
181             0,0,-1, 0,0,-1, 0,0,-1,
182             0,0,-1, 0,0,-1, 0,0,-1,
183             0,0,1, 0,0,1, 0,0,1,
184             0,0,1, 0,0,1, 0,0,1,
185 
186             -1,0,0, -1,0,0, -1,0,0,
187             -1,0,0, -1,0,0, -1,0,0,
188             1,0,0, 1,0,0, 1,0,0,
189             1,0,0, 1,0,0, 1,0,0,
190 
191             0,-1,0, 0,-1,0, 0,-1,0,
192             0,-1,0, 0,-1,0, 0,-1,0,
193             0,1,0, 0,1,0, 0,1,0,
194             0,1,0, 0,1,0, 0,1,0
195         };
196 
197         m_vbo2.create();
198         m_vbo2.bind();
199         m_vbo2.allocate(36 * 8 * sizeof(GLfloat));
200         m_vbo2.write(0, afVertices, sizeof(afVertices));
201         m_vbo2.write(sizeof(afVertices), afTexCoord, sizeof(afTexCoord));
202         m_vbo2.write(sizeof(afVertices) + sizeof(afTexCoord), afNormals, sizeof(afNormals));
203         m_vbo2.release();
204     }
205 
206     m_program2->setUniformValue(m_textureUniform2, 0); // use texture unit 0
207 
208     m_program2->enableAttributeArray(m_vertexAttr2);
209     m_program2->enableAttributeArray(m_normalAttr2);
210     m_program2->enableAttributeArray(m_texCoordAttr2);
211 
212     m_vbo2.bind();
213     // In the buffer we first have 36 vertices (3 floats for each), then 36 texture
214     // coordinates (2 floats for each), then 36 normals (3 floats for each).
215     m_program2->setAttributeBuffer(m_vertexAttr2, GL_FLOAT, 0, 3);
216     m_program2->setAttributeBuffer(m_texCoordAttr2, GL_FLOAT, 36 * 3 * sizeof(GLfloat), 2);
217     m_program2->setAttributeBuffer(m_normalAttr2, GL_FLOAT, 36 * 5 * sizeof(GLfloat), 3);
218     m_vbo2.release();
219 
220     glDrawArrays(GL_TRIANGLES, 0, 36);
221 
222     m_program2->disableAttributeArray(m_vertexAttr2);
223     m_program2->disableAttributeArray(m_normalAttr2);
224     m_program2->disableAttributeArray(m_texCoordAttr2);
225 }
226 
initializeGL()227 void GLWidget::initializeGL()
228 {
229     initializeOpenGLFunctions();
230 
231     m_texture = new QOpenGLTexture(QImage(":/qt.png"));
232 
233     m_vshader1 = new QOpenGLShader(QOpenGLShader::Vertex);
234     const char *vsrc1 =
235         "attribute highp vec4 vertex;\n"
236         "attribute mediump vec3 normal;\n"
237         "uniform mediump mat4 matrix;\n"
238         "varying mediump vec4 color;\n"
239         "void main(void)\n"
240         "{\n"
241         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
242         "    float angle = max(dot(normal, toLight), 0.0);\n"
243         "    vec3 col = vec3(0.40, 1.0, 0.0);\n"
244         "    color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n"
245         "    color = clamp(color, 0.0, 1.0);\n"
246         "    gl_Position = matrix * vertex;\n"
247         "}\n";
248     m_vshader1->compileSourceCode(vsrc1);
249 
250     m_fshader1 = new QOpenGLShader(QOpenGLShader::Fragment);
251     const char *fsrc1 =
252         "varying mediump vec4 color;\n"
253         "void main(void)\n"
254         "{\n"
255         "    gl_FragColor = color;\n"
256         "}\n";
257     m_fshader1->compileSourceCode(fsrc1);
258 
259     m_program1 = new QOpenGLShaderProgram;
260     m_program1->addShader(m_vshader1);
261     m_program1->addShader(m_fshader1);
262     m_program1->link();
263 
264     m_vertexAttr1 = m_program1->attributeLocation("vertex");
265     m_normalAttr1 = m_program1->attributeLocation("normal");
266     m_matrixUniform1 = m_program1->uniformLocation("matrix");
267 
268     m_vshader2 = new QOpenGLShader(QOpenGLShader::Vertex);
269     const char *vsrc2 =
270         "attribute highp vec4 vertex;\n"
271         "attribute highp vec4 texCoord;\n"
272         "attribute mediump vec3 normal;\n"
273         "uniform mediump mat4 matrix;\n"
274         "varying highp vec4 texc;\n"
275         "varying mediump float angle;\n"
276         "void main(void)\n"
277         "{\n"
278         "    vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n"
279         "    angle = max(dot(normal, toLight), 0.0);\n"
280         "    gl_Position = matrix * vertex;\n"
281         "    texc = texCoord;\n"
282         "}\n";
283     m_vshader2->compileSourceCode(vsrc2);
284 
285     m_fshader2 = new QOpenGLShader(QOpenGLShader::Fragment);
286     const char *fsrc2 =
287         "varying highp vec4 texc;\n"
288         "uniform sampler2D tex;\n"
289         "varying mediump float angle;\n"
290         "void main(void)\n"
291         "{\n"
292         "    highp vec3 color = texture2D(tex, texc.st).rgb;\n"
293         "    color = color * 0.2 + color * 0.8 * angle;\n"
294         "    gl_FragColor = vec4(clamp(color, 0.0, 1.0), 1.0);\n"
295         "}\n";
296     m_fshader2->compileSourceCode(fsrc2);
297 
298     m_program2 = new QOpenGLShaderProgram;
299     m_program2->addShader(m_vshader2);
300     m_program2->addShader(m_fshader2);
301     m_program2->link();
302 
303     m_vertexAttr2 = m_program2->attributeLocation("vertex");
304     m_normalAttr2 = m_program2->attributeLocation("normal");
305     m_texCoordAttr2 = m_program2->attributeLocation("texCoord");
306     m_matrixUniform2 = m_program2->uniformLocation("matrix");
307     m_textureUniform2 = m_program2->uniformLocation("tex");
308 
309     m_fAngle = 0;
310     m_fScale = 1;
311 
312     createGeometry();
313 
314     // Use a vertex buffer object. Client-side pointers are old-school and should be avoided.
315     m_vbo1.create();
316     m_vbo1.bind();
317     // For the cube all the data belonging to the texture coordinates and
318     // normals is placed separately, after the vertices. Here, for the Qt logo,
319     // let's do something different and potentially more efficient: create a
320     // properly interleaved data set.
321     const int vertexCount = m_vertices.count();
322     QVector<GLfloat> buf;
323     buf.resize(vertexCount * 3 * 2);
324     GLfloat *p = buf.data();
325     for (int i = 0; i < vertexCount; ++i) {
326         *p++ = m_vertices[i].x();
327         *p++ = m_vertices[i].y();
328         *p++ = m_vertices[i].z();
329         *p++ = m_normals[i].x();
330         *p++ = m_normals[i].y();
331         *p++ = m_normals[i].z();
332     }
333     m_vbo1.allocate(buf.constData(), buf.count() * sizeof(GLfloat));
334     m_vbo1.release();
335 
336     createBubbles(bubbleNum - m_bubbles.count());
337 }
338 
paintGL()339 void GLWidget::paintGL()
340 {
341     createBubbles(bubbleNum - m_bubbles.count());
342 
343     QPainter painter;
344     painter.begin(this);
345 
346     painter.beginNativePainting();
347 
348     glClearColor(m_background.redF(), m_background.greenF(), m_background.blueF(), m_transparent ? 0.0f : 1.0f);
349     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
350 
351     glFrontFace(GL_CW);
352     glCullFace(GL_FRONT);
353     glEnable(GL_CULL_FACE);
354     glEnable(GL_DEPTH_TEST);
355 
356     QMatrix4x4 modelview;
357     modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f);
358     modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f);
359     modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f);
360     modelview.scale(m_fScale);
361     modelview.translate(0.0f, -0.2f, 0.0f);
362 
363     if (m_qtLogo) {
364         m_program1->bind();
365         m_program1->setUniformValue(m_matrixUniform1, modelview);
366         paintQtLogo();
367         m_program1->release();
368     } else {
369         m_program2->bind();
370         m_program2->setUniformValue(m_matrixUniform2, modelview);
371         paintTexturedCube();
372         m_program2->release();
373     }
374 
375     glDisable(GL_DEPTH_TEST);
376     glDisable(GL_CULL_FACE);
377 
378     painter.endNativePainting();
379 
380     if (m_showBubbles) {
381         for (Bubble *bubble : qAsConst(m_bubbles))
382             bubble->drawBubble(&painter);
383     }
384 
385     if (const int elapsed = m_time.elapsed()) {
386         QString framesPerSecond;
387         framesPerSecond.setNum(m_frames /(elapsed / 1000.0), 'f', 2);
388         painter.setPen(m_transparent ? Qt::black : Qt::white);
389         painter.drawText(20, 40, framesPerSecond + " paintGL calls / s");
390     }
391 
392     painter.end();
393 
394     for (Bubble *bubble : qAsConst(m_bubbles))
395         bubble->move(rect());
396 
397     if (!(m_frames % 100)) {
398         m_time.start();
399         m_frames = 0;
400     }
401     m_fAngle += 1.0f;
402     ++m_frames;
403 
404     // When requested, follow the ideal way to animate: Rely on
405     // blocking swap and just schedule updates continuously.
406     if (!m_mainWindow->timerEnabled())
407         update();
408 }
409 
createBubbles(int number)410 void GLWidget::createBubbles(int number)
411 {
412     for (int i = 0; i < number; ++i) {
413         QPointF position(width()*(0.1 + QRandomGenerator::global()->bounded(0.8)),
414                          height()*(0.1 + QRandomGenerator::global()->bounded(0.8)));
415         qreal radius = qMin(width(), height())*(0.0175 + QRandomGenerator::global()->bounded(0.0875));
416         QPointF velocity(width()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)),
417                          height()*0.0175*(-0.5 + QRandomGenerator::global()->bounded(1.0)));
418 
419         m_bubbles.append(new Bubble(position, radius, velocity));
420     }
421 }
422 
createGeometry()423 void GLWidget::createGeometry()
424 {
425     m_vertices.clear();
426     m_normals.clear();
427 
428     qreal x1 = +0.06f;
429     qreal y1 = -0.14f;
430     qreal x2 = +0.14f;
431     qreal y2 = -0.06f;
432     qreal x3 = +0.08f;
433     qreal y3 = +0.00f;
434     qreal x4 = +0.30f;
435     qreal y4 = +0.22f;
436 
437     quad(x1, y1, x2, y2, y2, x2, y1, x1);
438     quad(x3, y3, x4, y4, y4, x4, y3, x3);
439 
440     extrude(x1, y1, x2, y2);
441     extrude(x2, y2, y2, x2);
442     extrude(y2, x2, y1, x1);
443     extrude(y1, x1, x1, y1);
444     extrude(x3, y3, x4, y4);
445     extrude(x4, y4, y4, x4);
446     extrude(y4, x4, y3, x3);
447 
448     const int NumSectors = 100;
449     const qreal sectorAngle = 2 * qreal(M_PI) / NumSectors;
450 
451     for (int i = 0; i < NumSectors; ++i) {
452         qreal angle = i * sectorAngle;
453         qreal x5 = 0.30 * sin(angle);
454         qreal y5 = 0.30 * cos(angle);
455         qreal x6 = 0.20 * sin(angle);
456         qreal y6 = 0.20 * cos(angle);
457 
458         angle += sectorAngle;
459         qreal x7 = 0.20 * sin(angle);
460         qreal y7 = 0.20 * cos(angle);
461         qreal x8 = 0.30 * sin(angle);
462         qreal y8 = 0.30 * cos(angle);
463 
464         quad(x5, y5, x6, y6, x7, y7, x8, y8);
465 
466         extrude(x6, y6, x7, y7);
467         extrude(x8, y8, x5, y5);
468     }
469 
470     for (int i = 0;i < m_vertices.size();i++)
471         m_vertices[i] *= 2.0f;
472 }
473 
quad(qreal x1,qreal y1,qreal x2,qreal y2,qreal x3,qreal y3,qreal x4,qreal y4)474 void GLWidget::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4)
475 {
476     m_vertices << QVector3D(x1, y1, -0.05f);
477     m_vertices << QVector3D(x2, y2, -0.05f);
478     m_vertices << QVector3D(x4, y4, -0.05f);
479 
480     m_vertices << QVector3D(x3, y3, -0.05f);
481     m_vertices << QVector3D(x4, y4, -0.05f);
482     m_vertices << QVector3D(x2, y2, -0.05f);
483 
484     QVector3D n = QVector3D::normal
485         (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f));
486 
487     m_normals << n;
488     m_normals << n;
489     m_normals << n;
490 
491     m_normals << n;
492     m_normals << n;
493     m_normals << n;
494 
495     m_vertices << QVector3D(x4, y4, 0.05f);
496     m_vertices << QVector3D(x2, y2, 0.05f);
497     m_vertices << QVector3D(x1, y1, 0.05f);
498 
499     m_vertices << QVector3D(x2, y2, 0.05f);
500     m_vertices << QVector3D(x4, y4, 0.05f);
501     m_vertices << QVector3D(x3, y3, 0.05f);
502 
503     n = QVector3D::normal
504         (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f));
505 
506     m_normals << n;
507     m_normals << n;
508     m_normals << n;
509 
510     m_normals << n;
511     m_normals << n;
512     m_normals << n;
513 }
514 
extrude(qreal x1,qreal y1,qreal x2,qreal y2)515 void GLWidget::extrude(qreal x1, qreal y1, qreal x2, qreal y2)
516 {
517     m_vertices << QVector3D(x1, y1, +0.05f);
518     m_vertices << QVector3D(x2, y2, +0.05f);
519     m_vertices << QVector3D(x1, y1, -0.05f);
520 
521     m_vertices << QVector3D(x2, y2, -0.05f);
522     m_vertices << QVector3D(x1, y1, -0.05f);
523     m_vertices << QVector3D(x2, y2, +0.05f);
524 
525     QVector3D n = QVector3D::normal
526         (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f));
527 
528     m_normals << n;
529     m_normals << n;
530     m_normals << n;
531 
532     m_normals << n;
533     m_normals << n;
534     m_normals << n;
535 }
536 
setTransparent(bool transparent)537 void GLWidget::setTransparent(bool transparent)
538 {
539     setAttribute(Qt::WA_AlwaysStackOnTop, transparent);
540     m_transparent = transparent;
541     // Call update() on the top-level window after toggling AlwayStackOnTop to make sure
542     // the entire backingstore is updated accordingly.
543     window()->update();
544 }
545 
resizeGL(int,int)546 void GLWidget::resizeGL(int, int)
547 {
548     if (m_hasButton) {
549         if (!m_btn) {
550             m_btn = new QPushButton("A widget on top.\nPress for more widgets.", this);
551             connect(m_btn, &QPushButton::clicked, this, &GLWidget::handleButtonPress);
552         }
553         m_btn->move(20, 80);
554     }
555 }
556 
handleButtonPress()557 void GLWidget::handleButtonPress()
558 {
559     m_mainWindow->addNew();
560 }
561