/* * Stellarium * Copyright (C) 2008 Fabien Chereau * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "StelApp.hpp" #include "StelCore.hpp" #include "StelProjector.hpp" #include "StelNavigator.hpp" #include "StelGuiItems.hpp" #include "StelLocaleMgr.hpp" #include "StelLocation.hpp" #include "StelMovementMgr.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include StelButton::StelButton(QGraphicsItem* parent, const QPixmap& apixOn, const QPixmap& apixOff, const QPixmap& apixHover, QAction* aaction, bool noBackground) : QGraphicsPixmapItem(apixOff, parent), pixOn(apixOn), pixOff(apixOff), pixHover(apixHover), checked(ButtonStateOff), action(aaction), noBckground(noBackground), isTristate_(false), opacity(1.), hoverOpacity(0.) { Q_ASSERT(!pixOn.isNull()); Q_ASSERT(!pixOff.isNull()); redMode = StelApp::getInstance().getVisionModeNight(); pixOnRed = StelButton::makeRed(pixOn); pixOffRed = StelButton::makeRed(pixOff); if (!pixHover.isNull()) pixHoverRed = StelButton::makeRed(pixHover); if (!pixBackground.isNull()) pixBackgroundRed = StelButton::makeRed(pixBackground); setShapeMode(QGraphicsPixmapItem::BoundingRectShape); setAcceptsHoverEvents(true); timeLine = new QTimeLine(250, this); timeLine->setCurveShape(QTimeLine::EaseOutCurve); connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal))); if (action!=NULL) { QObject::connect(action, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool))); if (action->isCheckable()) { setChecked(action->isChecked()); QObject::connect(this, SIGNAL(toggled(bool)), action, SLOT(setChecked(bool))); } else { QObject::connect(this, SIGNAL(triggered()), action, SLOT(trigger())); } } } StelButton::StelButton(QGraphicsItem* parent, const QPixmap& apixOn, const QPixmap& apixOff, const QPixmap& apixNoChange, const QPixmap& apixHover, QAction* aaction, bool noBackground, bool isTristate) : QGraphicsPixmapItem(apixOff, parent), pixOn(apixOn), pixOff(apixOff), pixNoChange(apixNoChange), pixHover(apixHover), checked(ButtonStateOff), action(aaction), noBckground(noBackground), isTristate_(isTristate), opacity(1.), hoverOpacity(0.) { Q_ASSERT(!pixOn.isNull()); Q_ASSERT(!pixOff.isNull()); redMode = StelApp::getInstance().getVisionModeNight(); pixOnRed = StelButton::makeRed(pixOn); pixOffRed = StelButton::makeRed(pixOff); if (isTristate_) { Q_ASSERT(!pixNoChange.isNull()); pixNoChangeRed = StelButton::makeRed(pixNoChange); } if (!pixHover.isNull()) pixHoverRed = StelButton::makeRed(pixHover); if (!pixBackground.isNull()) pixBackgroundRed = StelButton::makeRed(pixBackground); setShapeMode(QGraphicsPixmapItem::BoundingRectShape); setAcceptsHoverEvents(true); timeLine = new QTimeLine(250, this); timeLine->setCurveShape(QTimeLine::EaseOutCurve); connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animValueChanged(qreal))); if (action!=NULL) { QObject::connect(action, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool))); if (action->isCheckable()) { setChecked(action->isChecked()); QObject::connect(this, SIGNAL(toggled(bool)), action, SLOT(setChecked(bool))); } else { QObject::connect(this, SIGNAL(triggered()), action, SLOT(trigger())); } } } int StelButton::toggleChecked(int checked) { if (!isTristate_) checked = !!!checked; else { if (++checked > ButtonStateNoChange) checked = ButtonStateOff; } return checked; } void StelButton::mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsItem::mousePressEvent(event); event->accept(); setChecked(toggleChecked(checked)); emit(toggled(checked)); emit(triggered()); } void StelButton::hoverEnterEvent(QGraphicsSceneHoverEvent*) { timeLine->setDirection(QTimeLine::Forward); if (timeLine->state()!=QTimeLine::Running) timeLine->start(); emit(hoverChanged(true)); } void StelButton::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { timeLine->setDirection(QTimeLine::Backward); if (timeLine->state()!=QTimeLine::Running) timeLine->start(); emit(hoverChanged(false)); } void StelButton::mouseReleaseEvent(QGraphicsSceneMouseEvent*) { if (action!=NULL && !action->isCheckable()) setChecked(toggleChecked(checked)); } void StelButton::updateIcon() { if (opacity<0.) opacity=0; QPixmap pix(pixOn.size()); pix.fill(QColor(0,0,0,0)); QPainter painter(&pix); painter.setOpacity(opacity); if (!pixBackground.isNull() && noBckground==false) painter.drawPixmap(0,0, redMode ? pixBackgroundRed : pixBackground); painter.drawPixmap(0, 0, (isTristate_ && checked == ButtonStateNoChange) ? (redMode ? pixNoChangeRed : pixNoChange) : (checked == ButtonStateOn) ? (redMode ? pixOnRed : pixOn) : /* (checked == ButtonStateOff) ? */ (redMode ? pixOffRed : pixOff)); if (hoverOpacity>0) { painter.setOpacity(hoverOpacity*opacity); painter.drawPixmap(0,0, redMode ? pixHoverRed : pixHover); } setPixmap(pix); } void StelButton::animValueChanged(qreal value) { hoverOpacity = value; updateIcon(); } void StelButton::setChecked(int b) { checked=b; updateIcon(); } QPixmap StelButton::makeRed(const QPixmap& p) { QImage im = p.toImage().convertToFormat(QImage::Format_ARGB32); Q_ASSERT(im.format()==QImage::Format_ARGB32); QRgb* bits = (QRgb*)im.bits(); const QRgb* stop = bits+im.width()*im.height(); do { *bits = qRgba(qRed(*bits), (int)(0.2*qGreen(*bits)), (int)(0.2*qBlue(*bits)), qAlpha(*bits)); ++bits; } while (bits!=stop); return QPixmap::fromImage(im); } LeftStelBar::LeftStelBar(QGraphicsItem* parent) : QGraphicsItem(parent) { // Create the help label helpLabel = new QGraphicsSimpleTextItem("", this); helpLabel->setBrush(QBrush(QColor::fromRgbF(1,1,1,1))); } LeftStelBar::~LeftStelBar() { } void LeftStelBar::addButton(StelButton* button) { double posY = 0; if (QGraphicsItem::children().size()!=0) { const QRectF& r = childrenBoundingRect(); posY += r.bottom()-1; } button->setParentItem(this); button->prepareGeometryChange(); // could possibly be removed when qt 4.6 become stable button->setPos(0., round(posY+10.5)); connect(button, SIGNAL(hoverChanged(bool)), this, SLOT(buttonHoverChanged(bool))); } void LeftStelBar::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { } QRectF LeftStelBar::boundingRect() const { return childrenBoundingRect(); } QRectF LeftStelBar::boundingRectNoHelpLabel() const { // Re-use original Qt code, just remove the help label QRectF childRect; foreach (QGraphicsItem *child, QGraphicsItem::children()) { if (child==helpLabel) continue; QPointF childPos = child->pos(); QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y()); childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); } return childRect; } // Update the help label when a button is hovered void LeftStelBar::buttonHoverChanged(bool b) { StelButton* button = qobject_cast(sender()); Q_ASSERT(button); if (b==true) { if (button->action) { QString tip(button->action->toolTip()); QString shortcut(button->action->shortcut().toString()); if (!shortcut.isEmpty()) { if (shortcut == "Space") shortcut = q_("Space"); tip += " [" + shortcut + "]"; } helpLabel->setText(tip); helpLabel->setPos(round(boundingRectNoHelpLabel().width()+15.5),round(button->pos().y()+button->pixmap().size().height()/2-8)); } } else { helpLabel->setText(""); } } // Set the pen for all the sub elements void LeftStelBar::setColor(const QColor& c) { helpLabel->setBrush(c); } // Activate red mode for the buttons, i.e. will reduce the non red color component of the icon void LeftStelBar::setRedMode(bool b) { foreach (QGraphicsItem *child, QGraphicsItem::children()) { StelButton* bt = qgraphicsitem_cast(child); if (bt==0) continue; bt->setRedMode(b); } } BottomStelBar::BottomStelBar(QGraphicsItem* parent, const QPixmap& pixLeft, const QPixmap& pixRight, const QPixmap& pixMiddle, const QPixmap& pixSingle) : QGraphicsItem(parent), pixBackgroundLeft(pixLeft), pixBackgroundRight(pixRight), pixBackgroundMiddle(pixMiddle), pixBackgroundSingle(pixSingle) { // The text is dummy just for testing datetime = new QGraphicsSimpleTextItem("2008-02-06 17:33", this); location = new QGraphicsSimpleTextItem("Munich, Earth, 500m", this); fov = new QGraphicsSimpleTextItem("FOV 43.45", this); fps = new QGraphicsSimpleTextItem("43.2 FPS", this); // Create the help label helpLabel = new QGraphicsSimpleTextItem("", this); helpLabel->setBrush(QBrush(QColor::fromRgbF(1,1,1,1))); QColor color = QColor::fromRgbF(1,1,1,1); setColor(color); datetime->font().setPixelSize(12); location->font().setPixelSize(12); fov->font().setPixelSize(12); fps->font().setPixelSize(12); flagShowTime = true; flagShowLocation = true; } BottomStelBar::~BottomStelBar() { // Remove currently hidden buttons which are not delete by a parent element for (QMap::iterator iter=buttonGroups.begin();iter!=buttonGroups.end();++iter) { foreach (StelButton* b, iter.value().elems) { if (b->parentItem()==0) { delete b; b=NULL; } } } } void BottomStelBar::addButton(StelButton* button, const QString& groupName, const QString& beforeActionName) { QList& g = buttonGroups[groupName].elems; bool done=false; for (int i=0;iaction && g[i]->action->objectName()==beforeActionName) { g.insert(i, button); done=true; break; } } if (done==false) g.append(button); button->setVisible(true); button->setParentItem(this); updateButtonsGroups(); connect(button, SIGNAL(hoverChanged(bool)), this, SLOT(buttonHoverChanged(bool))); } StelButton* BottomStelBar::hideButton(const QString& actionName) { QString gName; StelButton* bToRemove=NULL; for (QMap::iterator iter=buttonGroups.begin();iter!=buttonGroups.end();++iter) { int i=0; foreach (StelButton* b, iter.value().elems) { if (b->action && b->action->objectName()==actionName) { gName=iter.key(); bToRemove = b; iter.value().elems.removeAt(i); break; } ++i; } } if (bToRemove==NULL) return NULL; if (buttonGroups[gName].elems.size()==0) { buttonGroups.remove(gName); } // Cannot really delete because some part of the GUI depend on the presence of some buttons // so just make invisible bToRemove->setParentItem(NULL); bToRemove->setVisible(false); updateButtonsGroups(); return bToRemove; } // Set the margin at the left and right of a button group in pixels void BottomStelBar::setGroupMargin(const QString& groupName, int left, int right) { if (!buttonGroups.contains(groupName)) return; buttonGroups[groupName].leftMargin=left; buttonGroups[groupName].rightMargin=right; updateButtonsGroups(); } //! Change the background of a group void BottomStelBar::setGroupBackground(const QString& groupName, const QPixmap& pixLeft, const QPixmap& pixRight, const QPixmap& pixMiddle, const QPixmap& pixSingle){ if (!buttonGroups.contains(groupName)) return; buttonGroups[groupName].pixBackgroundLeft = new QPixmap(pixLeft); buttonGroups[groupName].pixBackgroundRight = new QPixmap(pixRight); buttonGroups[groupName].pixBackgroundMiddle = new QPixmap(pixMiddle); buttonGroups[groupName].pixBackgroundSingle = new QPixmap(pixSingle); updateButtonsGroups(); } QRectF BottomStelBar::getButtonsBoundingRect() const { // Re-use original Qt code, just remove the help label QRectF childRect; bool hasBtn = false; foreach (QGraphicsItem *child, QGraphicsItem::children()) { if (qgraphicsitem_cast(child)==0) continue; hasBtn = true; QPointF childPos = child->pos(); QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y()); childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); } if (hasBtn) return QRectF(0, 0, childRect.width()-1, childRect.height()-1); else return QRectF(); } void BottomStelBar::updateButtonsGroups() { double x=0; double y = datetime->boundingRect().height()+3; for (QMap::iterator iter=buttonGroups.begin();iter!=buttonGroups.end();++iter) { if (iter.value().elems.empty()) continue; x+=iter.value().leftMargin; int n=0; foreach (StelButton* b, iter.value().elems) { // We check if the group has its own background if not the case // We apply a default background. if (n==0) { if (iter.value().elems.size()==1) { if (iter.value().pixBackgroundSingle == NULL) b->pixBackground = pixBackgroundSingle; else b->pixBackground = *(iter.value().pixBackgroundSingle); } else { if (iter.value().pixBackgroundLeft == NULL) b->pixBackground = pixBackgroundLeft; else b->pixBackground = *(iter.value().pixBackgroundLeft); } } else if (n==iter.value().elems.size()-1) { if (iter.value().elems.size()!=1) { if (iter.value().pixBackgroundSingle == NULL) b->pixBackground = pixBackgroundSingle; else b->pixBackground = *(iter.value().pixBackgroundSingle); } if (iter.value().pixBackgroundRight == NULL) b->pixBackground = pixBackgroundRight; else b->pixBackground = *(iter.value().pixBackgroundRight); } else { if (iter.value().pixBackgroundMiddle == NULL) b->pixBackground = pixBackgroundMiddle; else b->pixBackground = *(iter.value().pixBackgroundMiddle); } // Update the button pixmap b->animValueChanged(0.); b->setPos(x, y); x+=b->pixOn.width(); ++n; } x+=iter.value().rightMargin; } updateText(true); } // Make sure to avoid any change if not necessary to avoid triggering useless redraw void BottomStelBar::updateText(bool updatePos) { StelCore* core = StelApp::getInstance().getCore(); double jd = core->getNavigator()->getJDay(); QString newDate = flagShowTime ? StelApp::getInstance().getLocaleMgr().getPrintableDateLocal(jd) +" " +StelApp::getInstance().getLocaleMgr().getPrintableTimeLocal(jd) : " "; if (datetime->text()!=newDate) { updatePos = true; datetime->setText(newDate); } QString newLocation = flagShowLocation ? q_(core->getNavigator()->getCurrentLocation().planetName) +", " +core->getNavigator()->getCurrentLocation().name + ", " // xgettext:no-c-format +q_("%1m").arg(core->getNavigator()->getCurrentLocation().altitude) : " "; if (location->text()!=newLocation) { updatePos = true; location->setText(newLocation); } QString str; QTextStream wos(&str); wos << "FOV " << qSetRealNumberPrecision(3) << core->getMovementMgr()->getCurrentFov() << QChar(0x00B0); if (fov->text()!=str) { updatePos = true; fov->setText(str); } str=""; QTextStream wos2(&str); wos2 << qSetRealNumberPrecision(3) << StelApp::getInstance().getFps() << " FPS"; if (fps->text()!=str) { updatePos = true; fps->setText(str); } if (updatePos) { QRectF rectCh = getButtonsBoundingRect(); location->setPos(0, 0); datetime->setPos(rectCh.right()-datetime->boundingRect().width()-5,0); fov->setPos(datetime->x()-230, 0); fps->setPos(datetime->x()-140, 0); } } void BottomStelBar::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { updateText(); } QRectF BottomStelBar::boundingRect() const { if (QGraphicsItem::children().size()==0) return QRectF(); const QRectF& r = childrenBoundingRect(); return QRectF(0, 0, r.width()-1, r.height()-1); } QRectF BottomStelBar::boundingRectNoHelpLabel() const { // Re-use original Qt code, just remove the help label QRectF childRect; foreach (QGraphicsItem *child, QGraphicsItem::children()) { if (child==helpLabel) continue; QPointF childPos = child->pos(); QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y()); childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); } return childRect; } // Set the pen for all the sub elements void BottomStelBar::setColor(const QColor& c) { datetime->setBrush(c); location->setBrush(c); fov->setBrush(c); fps->setBrush(c); helpLabel->setBrush(c); } // Activate red mode for the buttons, i.e. will reduce the non red color component of the icon void BottomStelBar::setRedMode(bool b) { foreach (QGraphicsItem *child, QGraphicsItem::children()) { StelButton* bt = qgraphicsitem_cast(child); if (bt==0) continue; bt->setRedMode(b); } } // Update the help label when a button is hovered void BottomStelBar::buttonHoverChanged(bool b) { StelButton* button = qobject_cast(sender()); Q_ASSERT(button); if (b==true) { if (button->action) { QString tip(button->action->toolTip()); QString shortcut(button->action->shortcut().toString()); if (!shortcut.isEmpty()) { if (shortcut == "Space") shortcut = q_("Space"); tip += " [" + shortcut + "]"; } helpLabel->setText(tip); //helpLabel->setPos(button->pos().x()+button->pixmap().size().width()/2,-27); helpLabel->setPos(20,-27); } } else { helpLabel->setText(""); } } StelBarsPath::StelBarsPath(QGraphicsItem* parent) : QGraphicsPathItem(parent) { roundSize = 6; QPen aPen(QColor::fromRgbF(0.7,0.7,0.7,0.5)); aPen.setWidthF(1.); setBrush(QBrush(QColor::fromRgbF(0.22, 0.22, 0.23, 0.2))); setPen(aPen); } void StelBarsPath::updatePath(BottomStelBar* bot, LeftStelBar* lef) { QPainterPath newPath; QPointF p = lef->pos(); QRectF r = lef->boundingRectNoHelpLabel(); QPointF p2 = bot->pos(); QRectF r2 = bot->boundingRectNoHelpLabel(); newPath.moveTo(p.x()-roundSize, p.y()-roundSize); newPath.lineTo(p.x()+r.width(),p.y()-roundSize); newPath.arcTo(p.x()+r.width()-roundSize, p.y()-roundSize, 2.*roundSize, 2.*roundSize, 90, -90); newPath.lineTo(p.x()+r.width()+roundSize, p2.y()-roundSize); newPath.lineTo(p2.x()+r2.width(),p2.y()-roundSize); newPath.arcTo(p2.x()+r2.width()-roundSize, p2.y()-roundSize, 2.*roundSize, 2.*roundSize, 90, -90); newPath.lineTo(p2.x()+r2.width()+roundSize, p2.y()+r2.height()+roundSize); newPath.lineTo(p.x()-roundSize, p2.y()+r2.height()+roundSize); setPath(newPath); } void StelBarsPath::setBackgroundOpacity(double opacity) { setBrush(QBrush(QColor::fromRgbF(0.22, 0.22, 0.23, opacity))); } StelProgressBarMgr::StelProgressBarMgr(QGraphicsItem*) { setLayout(new QGraphicsLinearLayout(Qt::Vertical)); } /* QRectF StelProgressBarMgr::boundingRect() const { if (QGraphicsItem::children().size()==0) return QRectF(); const QRectF& r = childrenBoundingRect(); return QRectF(0, 0, r.width()-1, r.height()-1); }*/ QProgressBar* StelProgressBarMgr::addProgressBar() { QProgressBar* pb = new QProgressBar(); pb->setFixedHeight(15); pb->setFixedWidth(250); pb->setTextVisible(true); pb->setValue(66); QGraphicsProxyWidget* pbProxy = new QGraphicsProxyWidget(); pbProxy->setWidget(pb); pbProxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache); pbProxy->setZValue(150); static_cast(layout())->addItem(pbProxy); return pb; } CornerButtons::CornerButtons(QGraphicsItem*) : lastOpacity(10) { } void CornerButtons::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { // Do nothing. Just paint the child widgets } QRectF CornerButtons::boundingRect() const { if (QGraphicsItem::children().size()==0) return QRectF(); const QRectF& r = childrenBoundingRect(); return QRectF(0, 0, r.width()-1, r.height()-1); } void CornerButtons::setOpacity(double opacity) { if (opacity<=0. && lastOpacity<=0.) return; lastOpacity = opacity; if (QGraphicsItem::children().size()==0) return; foreach (QGraphicsItem *child, QGraphicsItem::children()) { StelButton* sb = qgraphicsitem_cast(child); Q_ASSERT(sb!=NULL); sb->setOpacity(opacity); } }