celestia/src/celestia/qt/qtglwidget.cpp

520 lines
15 KiB
C++

/***************************************************************************
kdeglwidget.cpp - description
-------------------
begin : Tue Jul 16 2002
copyright : (C) 2002 by Christophe Teyssier
email : chris@teyssier.org
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <config.h>
#include <cassert>
#include <celengine/starbrowser.h>
#include <QCursor>
#include <QPaintDevice>
#include <QMouseEvent>
#include <QSettings>
#include <QMessageBox>
#ifndef DEBUG
# define G_DISABLE_ASSERT
#endif
#include "celengine/astro.h"
#include "celutil/filetype.h"
#include "celutil/gettext.h"
#include "celestia/celestiacore.h"
#include "celengine/simulation.h"
#include "qtglwidget.h"
#include <cmath>
#include <vector>
using namespace Qt;
const int DEFAULT_ORBIT_MASK = Body::Planet | Body::Moon | Body::Stellar;
const int DEFAULT_LABEL_MODE = 2176;
const float DEFAULT_AMBIENT_LIGHT_LEVEL = 0.1f;
const int DEFAULT_STARS_COLOR = ColorTable_Blackbody_D65;
const float DEFAULT_VISUAL_MAGNITUDE = 8.0f;
const Renderer::StarStyle DEFAULT_STAR_STYLE = Renderer::FuzzyPointStars;
const unsigned int DEFAULT_TEXTURE_RESOLUTION = medres;
CelestiaGlWidget::CelestiaGlWidget(QWidget* parent, const char* /* name */, CelestiaCore* core) :
QOpenGLWidget(parent)
{
setFocusPolicy(Qt::ClickFocus);
appCore = core;
appRenderer = appCore->getRenderer();
appSim = appCore->getSimulation();
setCursor(QCursor(Qt::CrossCursor));
currentCursor = CelestiaCore::CrossCursor;
setMouseTracking(true);
cursorVisible = true;
}
/*!
Paint the box. The actual openGL commands for drawing the box are
performed here.
*/
void CelestiaGlWidget::paintGL()
{
appCore->draw();
}
/*!
Set up the OpenGL rendering state, and define display list
*/
void CelestiaGlWidget::initializeGL()
{
using namespace celestia;
if (!gl::init(appCore->getConfig()->ignoreGLExtensions) || !gl::checkVersion(gl::GL_2_1))
{
QMessageBox::critical(0, "Celestia", _("Celestia was unable to initialize OpenGL 2.1."));
exit(1);
}
appCore->setScreenDpi(logicalDpiY() * devicePixelRatioF());
if (!appCore->initRenderer())
{
// cerr << "Failed to initialize renderer.\n";
exit(1);
}
appCore->tick();
// Read saved settings
QSettings settings;
appRenderer->setRenderFlags(settings.value("RenderFlags", static_cast<quint64>(Renderer::DefaultRenderFlags)).toULongLong());
appRenderer->setOrbitMask(settings.value("OrbitMask", DEFAULT_ORBIT_MASK).toInt());
appRenderer->setLabelMode(settings.value("LabelMode", DEFAULT_LABEL_MODE).toInt());
appRenderer->setAmbientLightLevel((float) settings.value("AmbientLightLevel", DEFAULT_AMBIENT_LIGHT_LEVEL).toDouble());
appRenderer->setStarStyle((Renderer::StarStyle) settings.value("StarStyle", DEFAULT_STAR_STYLE).toInt());
appRenderer->setResolution(settings.value("TextureResolution", DEFAULT_TEXTURE_RESOLUTION).toUInt());
if (settings.value("StarsColor", DEFAULT_STARS_COLOR).toInt() == 0)
appRenderer->setStarColorTable(GetStarColorTable(ColorTable_Enhanced));
else
appRenderer->setStarColorTable(GetStarColorTable(ColorTable_Blackbody_D65));
appCore->getSimulation()->setFaintestVisible((float) settings.value("Preferences/VisualMagnitude", DEFAULT_VISUAL_MAGNITUDE).toDouble());
appRenderer->setSolarSystemMaxDistance(appCore->getConfig()->SolarSystemMaxDistance);
appRenderer->setShadowMapSize(appCore->getConfig()->ShadowMapSize);
}
void CelestiaGlWidget::resizeGL(int w, int h)
{
qreal scale = devicePixelRatioF();
auto width = static_cast<int>(w * scale);
auto height = static_cast<int>(h * scale);
appCore->resize(width, height);
}
void CelestiaGlWidget::mouseMoveEvent(QMouseEvent* m)
{
qreal scale = devicePixelRatioF();
auto x = static_cast<int>(m->x() * scale);
auto y = static_cast<int>(m->y() * scale);
int buttons = 0;
if (m->buttons() & LeftButton)
buttons |= CelestiaCore::LeftButton;
if (m->buttons() & MiddleButton)
buttons |= CelestiaCore::MiddleButton;
if (m->buttons() & RightButton)
buttons |= CelestiaCore::RightButton;
if (m->modifiers() & ShiftModifier)
buttons |= CelestiaCore::ShiftKey;
if (m->modifiers() & ControlModifier)
buttons |= CelestiaCore::ControlKey;
#ifdef __APPLE__
// On the Mac, right dragging is be simulated with Option+left drag.
// We may want to enable this on other platforms, though it's mostly only helpful
// for users with single button mice.
if (m->modifiers() & AltModifier)
{
buttons &= ~CelestiaCore::LeftButton;
buttons |= CelestiaCore::RightButton;
}
#endif
if ((m->buttons() & (LeftButton | RightButton)) != 0)
{
if (cursorVisible)
{
// Hide the cursor.
setCursor(QCursor(Qt::BlankCursor));
cursorVisible = false;
// Save the cursor position
QPoint pt;
pt.setX(m->x());
pt.setY(m->y());
// Store a local and global location
saveLocalCursorPos = pt;
pt = mapToGlobal(pt);
saveGlobalCursorPos = pt;
}
// Calculate mouse delta from local coordinate then move it back to the saved location
appCore->mouseMove((m->x() - saveLocalCursorPos.rx()) * scale, (m->y() - saveLocalCursorPos.ry()) * scale, buttons);
QCursor::setPos(saveGlobalCursorPos);
}
else
{
appCore->mouseMove(x, y);
}
}
void CelestiaGlWidget::mousePressEvent( QMouseEvent* m )
{
qreal scale = devicePixelRatioF();
auto x = static_cast<int>(m->x() * scale);
auto y = static_cast<int>(m->y() * scale);
if (m->button() == LeftButton)
appCore->mouseButtonDown(x, y, CelestiaCore::LeftButton);
else if (m->button() == MiddleButton)
appCore->mouseButtonDown(x, y, CelestiaCore::MiddleButton);
else if (m->button() == RightButton)
appCore->mouseButtonDown(x, y, CelestiaCore::RightButton);
}
void CelestiaGlWidget::mouseReleaseEvent( QMouseEvent* m )
{
qreal scale = devicePixelRatioF();
auto x = static_cast<int>(m->x() * scale);
auto y = static_cast<int>(m->y() * scale);
if (m->button() == LeftButton)
{
if (!cursorVisible)
{
// Restore the cursor position and make it visible again.
setCursor(QCursor(Qt::CrossCursor));
cursorVisible = true;
QCursor::setPos(saveGlobalCursorPos);
}
appCore->mouseButtonUp(x, y, CelestiaCore::LeftButton);
}
else if (m->button() == MiddleButton)
{
appCore->mouseButtonUp(x, y, CelestiaCore::MiddleButton);
}
else if (m->button() == RightButton)
{
if (!cursorVisible)
{
// Restore the cursor position and make it visible again.
setCursor(QCursor(Qt::CrossCursor));
cursorVisible = true;
QCursor::setPos(saveGlobalCursorPos);
}
appCore->mouseButtonUp(x, y, CelestiaCore::RightButton);
}
}
void CelestiaGlWidget::wheelEvent( QWheelEvent* w )
{
QPoint numDegrees = w->angleDelta();
if (numDegrees.isNull() || numDegrees.y() == 0)
return;
if (numDegrees.y() > 0 )
{
appCore->mouseWheel(-1.0f, 0);
}
else
{
appCore->mouseWheel(1.0f, 0);
}
}
bool CelestiaGlWidget::handleSpecialKey(QKeyEvent* e, bool down)
{
int k = -1;
switch (e->key())
{
case Key_Up:
k = CelestiaCore::Key_Up;
break;
case Key_Down:
k = CelestiaCore::Key_Down;
break;
case Key_Left:
k = CelestiaCore::Key_Left;
break;
case Key_Right:
k = CelestiaCore::Key_Right;
break;
case Key_Home:
k = CelestiaCore::Key_Home;
break;
case Key_End:
k = CelestiaCore::Key_End;
break;
case Key_F1:
k = CelestiaCore::Key_F1;
break;
case Key_F2:
k = CelestiaCore::Key_F2;
break;
case Key_F3:
k = CelestiaCore::Key_F3;
break;
case Key_F4:
k = CelestiaCore::Key_F4;
break;
case Key_F5:
k = CelestiaCore::Key_F5;
break;
case Key_F6:
k = CelestiaCore::Key_F6;
break;
case Key_F7:
k = CelestiaCore::Key_F7;
break;
case Key_F11:
k = CelestiaCore::Key_F11;
break;
case Key_F12:
k = CelestiaCore::Key_F12;
break;
case Key_PageDown:
k = CelestiaCore::Key_PageDown;
break;
case Key_PageUp:
k = CelestiaCore::Key_PageUp;
break;
/* case Key_F10:
if (e->modifiers()& ShiftModifier)
k = CelestiaCore::Key_F10;
break;*/
case Key_0:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad0;
break;
case Key_1:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad1;
break;
case Key_2:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad2;
break;
case Key_3:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad3;
break;
case Key_4:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad4;
break;
case Key_5:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad5;
break;
case Key_6:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad6;
break;
case Key_7:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad7;
break;
case Key_8:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad8;
break;
case Key_9:
if (e->modifiers() & Qt::KeypadModifier)
k = CelestiaCore::Key_NumPad9;
break;
case Qt::Key_A:
if (e->modifiers() == NoModifier)
k = 'A';
break;
case Qt::Key_Z:
if (e->modifiers() == NoModifier)
k = 'Z';
break;
}
if (k >= 0)
{
int buttons = 0;
if (e->modifiers() & ShiftModifier)
buttons |= CelestiaCore::ShiftKey;
if (down)
appCore->keyDown(k, buttons);
else
appCore->keyUp(k);
return (k < 'A' || k > 'Z');
}
return false;
}
void CelestiaGlWidget::keyPressEvent( QKeyEvent* e )
{
int modifiers = 0;
if (e->modifiers() & ShiftModifier)
modifiers |= CelestiaCore::ShiftKey;
if (e->modifiers() & ControlModifier)
modifiers |= CelestiaCore::ControlKey;
switch (e->key())
{
case Key_Escape:
appCore->charEntered('\033');
break;
case Key_Backtab:
appCore->charEntered(CelestiaCore::Key_BackTab);
break;
default:
if (!handleSpecialKey(e, true))
{
if (!e->text().isEmpty())
{
QString input = e->text();
#ifdef __APPLE__
// Taken from the macOS project
if (input.length() == 1)
{
uint16_t c = input.at(0).unicode();
if (c == 0x7f /* NSDeleteCharacter */)
input.replace(0, 1, QChar((uint16_t)0x08) /* NSBackspaceCharacter */); // delete = backspace
else if (c == 0x19 /* NSBackTabCharacter */)
input.replace(0, 1, QChar((uint16_t)0x7f) /* NSDeleteCharacter */);
}
#endif
appCore->charEntered(input.toUtf8().data(), modifiers);
}
}
}
}
void CelestiaGlWidget::keyReleaseEvent( QKeyEvent* e )
{
handleSpecialKey(e, false);
}
void CelestiaGlWidget::setCursorShape(CelestiaCore::CursorShape shape)
{
Qt::CursorShape cursor;
if (currentCursor != shape)
{
switch(shape)
{
case CelestiaCore::ArrowCursor:
cursor = Qt::ArrowCursor;
break;
case CelestiaCore::UpArrowCursor:
cursor = Qt::UpArrowCursor;
break;
case CelestiaCore::CrossCursor:
cursor = Qt::CrossCursor;
break;
case CelestiaCore::InvertedCrossCursor:
cursor = Qt::CrossCursor;
break;
case CelestiaCore::WaitCursor:
cursor = Qt::WaitCursor;
break;
case CelestiaCore::BusyCursor:
cursor = Qt::WaitCursor;
break;
case CelestiaCore::IbeamCursor:
cursor = Qt::IBeamCursor;
break;
case CelestiaCore::SizeVerCursor:
cursor = Qt::SizeVerCursor;
break;
case CelestiaCore::SizeHorCursor:
cursor = Qt::SizeHorCursor;
break;
case CelestiaCore::SizeBDiagCursor:
cursor = Qt::SizeBDiagCursor;
break;
case CelestiaCore::SizeFDiagCursor:
cursor = Qt::SizeFDiagCursor;
break;
case CelestiaCore::SizeAllCursor:
cursor = Qt::SizeAllCursor;
break;
case CelestiaCore::SplitVCursor:
cursor = Qt::SplitVCursor;
break;
case CelestiaCore::SplitHCursor:
cursor = Qt::SplitHCursor;
break;
case CelestiaCore::PointingHandCursor:
cursor = Qt::PointingHandCursor;
break;
case CelestiaCore::ForbiddenCursor:
cursor = Qt::ForbiddenCursor;
break;
case CelestiaCore::WhatsThisCursor:
cursor = Qt::WhatsThisCursor;
break;
default:
cursor = Qt::CrossCursor;
break;
}
setCursor(QCursor(cursor));
currentCursor = shape;
}
}
CelestiaCore::CursorShape CelestiaGlWidget::getCursorShape() const
{
return currentCursor;
}
QSize CelestiaGlWidget::sizeHint() const
{
return QSize(640, 480);
}