1 /*
2 * Stellarium
3 * Copyright (C) 2008 Fabien Chereau
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "StelApp.hpp"
21 #include "StelCore.hpp"
22 #include "StelProjector.hpp"
23
24 #include "StelUtils.hpp"
25 #include "SolarSystem.hpp"
26 #include "StelGuiItems.hpp"
27 #include "StelGui.hpp"
28 #include "StelLocaleMgr.hpp"
29 #include "StelLocation.hpp"
30 #include "StelMainView.hpp"
31 #include "StelMovementMgr.hpp"
32 #include "StelModuleMgr.hpp"
33 #include "StelActionMgr.hpp"
34 #include "StelProgressController.hpp"
35 #include "StelPropertyMgr.hpp"
36 #include "StelObserver.hpp"
37 #include "SkyGui.hpp"
38 #include "EphemWrapper.hpp"
39
40 #include <QPainter>
41 #include <QGraphicsScene>
42 #include <QGraphicsView>
43 #include <QGraphicsLineItem>
44 #include <QRectF>
45 #include <QDebug>
46 #include <QGraphicsSceneMouseEvent>
47 #include <QGraphicsTextItem>
48 #include <QTimeLine>
49 #include <QMouseEvent>
50 #include <QPixmapCache>
51 #include <QProgressBar>
52 #include <QGraphicsWidget>
53 #include <QGraphicsProxyWidget>
54 #include <QGraphicsLinearLayout>
55 #include <QSettings>
56 #include <QGuiApplication>
57
58 // Inspired by text-use-opengl-buffer branch: work around font problems in GUI buttons.
59 // May be useful in other broken OpenGL font situations. RasPi necessity as of 2016-03-26. Mesa 13 (2016-11) has finally fixed this on RasPi(VC4).
getTextPixmap(const QString & str,QFont font)60 QPixmap getTextPixmap(const QString& str, QFont font)
61 {
62 // Render the text str into a QPixmap.
63 QRect strRect = QFontMetrics(font).boundingRect(str);
64 int w = strRect.width()+1+static_cast<int>(0.02f*strRect.width());
65 int h = strRect.height();
66
67 QPixmap strPixmap(w, h);
68 strPixmap.fill(Qt::transparent);
69 QPainter painter(&strPixmap);
70 font.setStyleStrategy(QFont::NoAntialias); // else: font problems on RasPi20160326
71 painter.setFont(font);
72 //painter.setRenderHints(QPainter::TextAntialiasing);
73 painter.setPen(Qt::white);
74 painter.drawText(-strRect.x(), -strRect.y(), str);
75 return strPixmap;
76 }
77
initCtor(const QPixmap & apixOn,const QPixmap & apixOff,const QPixmap & apixNoChange,const QPixmap & apixHover,StelAction * anAction,StelAction * otherAction,bool noBackground,bool isTristate)78 void StelButton::initCtor(const QPixmap& apixOn,
79 const QPixmap& apixOff,
80 const QPixmap& apixNoChange,
81 const QPixmap& apixHover,
82 StelAction* anAction,
83 StelAction* otherAction,
84 bool noBackground,
85 bool isTristate)
86 {
87 pixOn = apixOn;
88 pixOff = apixOff;
89 pixHover = apixHover;
90 pixNoChange = apixNoChange;
91 noBckground = noBackground;
92 isTristate_ = isTristate;
93 opacity = 1.;
94 hoverOpacity = 0.;
95 action = anAction;
96 secondAction = otherAction;
97 checked = false;
98 flagChangeFocus = false;
99
100 //Q_ASSERT(!pixOn.isNull());
101 ///Q_ASSERT(!pixOff.isNull());
102
103 if (isTristate_)
104 {
105 Q_ASSERT(!pixNoChange.isNull());
106 }
107
108 setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
109 setAcceptHoverEvents(true);
110 timeLine = new QTimeLine(250, this);
111 timeLine->setCurveShape(QTimeLine::EaseOutCurve);
112 connect(timeLine, SIGNAL(valueChanged(qreal)),
113 this, SLOT(animValueChanged(qreal)));
114 connect(&StelMainView::getInstance(), SIGNAL(updateIconsRequested()), this, SLOT(updateIcon())); // Not sure if this is ever called?
115 StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
116 connect(gui, SIGNAL(flagUseButtonsBackgroundChanged(bool)), this, SLOT(updateIcon()));
117
118 if (action!=Q_NULLPTR)
119 {
120 if (action->isCheckable())
121 {
122 setChecked(action->isChecked());
123 connect(action, SIGNAL(toggled(bool)), this, SLOT(setChecked(bool)));
124 connect(this, SIGNAL(toggled(bool)), action, SLOT(setChecked(bool)));
125 }
126 else
127 {
128 QObject::connect(this, SIGNAL(triggered()), action, SLOT(trigger()));
129 }
130 }
131 if (secondAction!=Q_NULLPTR)
132 {
133 QObject::connect(this, SIGNAL(triggeredRight()), secondAction, SLOT(trigger()));
134 }
135 else {
136 setAcceptedMouseButtons(Qt::LeftButton);
137 }
138 }
139
StelButton(QGraphicsItem * parent,const QPixmap & pixOn,const QPixmap & pixOff,const QPixmap & pixHover,StelAction * action,bool noBackground,StelAction * otherAction)140 StelButton::StelButton(QGraphicsItem* parent,
141 const QPixmap& pixOn,
142 const QPixmap& pixOff,
143 const QPixmap& pixHover,
144 StelAction *action,
145 bool noBackground,
146 StelAction *otherAction) :
147 QGraphicsPixmapItem(pixOff, parent)
148 {
149 initCtor(pixOn, pixOff, QPixmap(), pixHover, action, otherAction, noBackground, false);
150 }
151
StelButton(QGraphicsItem * parent,const QPixmap & pixOn,const QPixmap & pixOff,const QPixmap & pixNoChange,const QPixmap & pixHover,const QString & actionId,bool noBackground,bool isTristate)152 StelButton::StelButton(QGraphicsItem* parent,
153 const QPixmap& pixOn,
154 const QPixmap& pixOff,
155 const QPixmap& pixNoChange,
156 const QPixmap& pixHover,
157 const QString& actionId,
158 bool noBackground,
159 bool isTristate) :
160 QGraphicsPixmapItem(pixOff, parent)
161 {
162 StelAction *action = StelApp::getInstance().getStelActionManager()->findAction(actionId);
163 initCtor(pixOn, pixOff, pixNoChange, pixHover, action, Q_NULLPTR, noBackground, isTristate);
164 }
165
StelButton(QGraphicsItem * parent,const QPixmap & pixOn,const QPixmap & pixOff,const QPixmap & pixHover,const QString & actionId,bool noBackground,const QString & otherActionId)166 StelButton::StelButton(QGraphicsItem* parent,
167 const QPixmap& pixOn,
168 const QPixmap& pixOff,
169 const QPixmap& pixHover,
170 const QString& actionId,
171 bool noBackground,
172 const QString &otherActionId)
173 :QGraphicsPixmapItem(pixOff, parent)
174 {
175 StelAction *action = StelApp::getInstance().getStelActionManager()->findAction(actionId);
176 StelAction *otherAction=Q_NULLPTR;
177 if (otherActionId.length()>0)
178 otherAction = StelApp::getInstance().getStelActionManager()->findAction(otherActionId);
179
180 initCtor(pixOn, pixOff, QPixmap(), pixHover, action, otherAction, noBackground, false);
181 }
182
183
toggleChecked(int checked)184 int StelButton::toggleChecked(int checked)
185 {
186 if (!isTristate_)
187 checked = !!!checked;
188 else
189 {
190 if (++checked > ButtonStateNoChange)
191 checked = ButtonStateOff;
192 }
193 return checked;
194 }
195
hoverEnterEvent(QGraphicsSceneHoverEvent *)196 void StelButton::hoverEnterEvent(QGraphicsSceneHoverEvent*)
197 {
198 timeLine->setDirection(QTimeLine::Forward);
199 if (timeLine->state()!=QTimeLine::Running)
200 timeLine->start();
201
202 emit(hoverChanged(true));
203 }
204
hoverLeaveEvent(QGraphicsSceneHoverEvent *)205 void StelButton::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
206 {
207 timeLine->setDirection(QTimeLine::Backward);
208 if (timeLine->state()!=QTimeLine::Running)
209 timeLine->start();
210 emit(hoverChanged(false));
211 }
212
mousePressEvent(QGraphicsSceneMouseEvent * event)213 void StelButton::mousePressEvent(QGraphicsSceneMouseEvent* event)
214 {
215 if (event->button()==Qt::LeftButton)
216 {
217 QGraphicsItem::mousePressEvent(event);
218 event->accept();
219 setChecked(toggleChecked(checked));
220 if (!triggerOnRelease)
221 {
222 emit(toggled(checked));
223 emit(triggered());
224 }
225 }
226 else if (event->button()==Qt::RightButton)
227 {
228 QGraphicsItem::mousePressEvent(event);
229 event->accept();
230 //setChecked(toggleChecked(checked));
231 if (!triggerOnRelease)
232 {
233 //emit(toggled(checked));
234 emit(triggeredRight());
235 }
236 }
237 }
238
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)239 void StelButton::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
240 {
241 if (event->button()==Qt::LeftButton)
242 {
243 if (action!=Q_NULLPTR && !action->isCheckable())
244 setChecked(toggleChecked(checked));
245
246 if (flagChangeFocus) // true if button is on bottom bar
247 StelMainView::getInstance().focusSky(); // Change the focus after clicking on button
248 if (triggerOnRelease)
249 {
250 emit(toggled(checked));
251 emit(triggered());
252 }
253 }
254 else if (event->button()==Qt::RightButton)
255 {
256 //if (flagChangeFocus) // true if button is on bottom bar
257 // StelMainView::getInstance().focusSky(); // Change the focus after clicking on button
258 if (triggerOnRelease)
259 {
260 //emit(toggled(checked));
261 emit(triggeredRight());
262 }
263 }
264 }
265
updateIcon()266 void StelButton::updateIcon()
267 {
268 if (opacity < 0.)
269 opacity = 0;
270 QPixmap pix(pixOn.size());
271 pix.fill(QColor(0,0,0,0));
272 QPainter painter(&pix);
273 painter.setOpacity(opacity);
274 if (!pixBackground.isNull() && noBckground==false && StelApp::getInstance().getStelPropertyManager()->getStelPropertyValue("StelGui.flagUseButtonsBackground").toBool())
275 painter.drawPixmap(0, 0, pixBackground);
276
277 painter.drawPixmap(0, 0,
278 (isTristate_ && checked == ButtonStateNoChange) ? (pixNoChange) :
279 (checked == ButtonStateOn) ? (pixOn) :
280 /* (checked == ButtonStateOff) ? */ (pixOff));
281
282 if (hoverOpacity > 0)
283 {
284 painter.setOpacity(hoverOpacity * opacity);
285 painter.drawPixmap(0, 0, pixHover);
286 }
287 setPixmap(pix);
288 }
289
animValueChanged(qreal value)290 void StelButton::animValueChanged(qreal value)
291 {
292 hoverOpacity = value;
293 updateIcon();
294 }
295
setChecked(int b)296 void StelButton::setChecked(int b)
297 {
298 checked=b;
299 updateIcon();
300 }
301
setBackgroundPixmap(const QPixmap & newBackground)302 void StelButton::setBackgroundPixmap(const QPixmap &newBackground)
303 {
304 pixBackground = newBackground;
305 updateIcon();
306 }
307
LeftStelBar(QGraphicsItem * parent)308 LeftStelBar::LeftStelBar(QGraphicsItem* parent)
309 : QGraphicsItem(parent)
310 , hideTimeLine(Q_NULLPTR)
311 , helpLabelPixmap(Q_NULLPTR)
312 {
313 // Create the help label
314 helpLabel = new QGraphicsSimpleTextItem("", this);
315 helpLabel->setBrush(QBrush(QColor::fromRgbF(1,1,1,1)));
316 if (qApp->property("text_texture")==true) // CLI option -t given?
317 helpLabelPixmap=new QGraphicsPixmapItem(this);
318 }
319
~LeftStelBar()320 LeftStelBar::~LeftStelBar()
321 {
322 if (helpLabelPixmap) { delete helpLabelPixmap; helpLabelPixmap=Q_NULLPTR; }
323 }
324
addButton(StelButton * button)325 void LeftStelBar::addButton(StelButton* button)
326 {
327 double posY = 0;
328 if (QGraphicsItem::childItems().size()!=0)
329 {
330 const QRectF& r = childrenBoundingRect();
331 posY += r.bottom()-1;
332 }
333 button->setParentItem(this);
334 button->setFocusOnSky(false);
335 button->prepareGeometryChange(); // could possibly be removed when qt 4.6 become stable
336 button->setPos(0., qRound(posY+10.5));
337
338 connect(button, SIGNAL(hoverChanged(bool)), this, SLOT(buttonHoverChanged(bool)));
339 }
340
paint(QPainter *,const QStyleOptionGraphicsItem *,QWidget *)341 void LeftStelBar::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
342 {
343 }
344
boundingRect() const345 QRectF LeftStelBar::boundingRect() const
346 {
347 return childrenBoundingRect();
348 }
349
boundingRectNoHelpLabel() const350 QRectF LeftStelBar::boundingRectNoHelpLabel() const
351 {
352 // Re-use original Qt code, just remove the help label
353 QRectF childRect;
354 for (auto* child : QGraphicsItem::childItems())
355 {
356 if ((child==helpLabel) || (child==helpLabelPixmap))
357 continue;
358 QPointF childPos = child->pos();
359 QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y());
360 childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
361 }
362 return childRect;
363 }
364
365
366 // Update the help label when a button is hovered
buttonHoverChanged(bool b)367 void LeftStelBar::buttonHoverChanged(bool b)
368 {
369 StelButton* button = qobject_cast<StelButton*>(sender());
370 Q_ASSERT(button);
371 if (b==true)
372 {
373 if (button->action)
374 {
375 QString tip(button->action->getText());
376 QString shortcut(button->action->getShortcut().toString(QKeySequence::NativeText));
377 if (!shortcut.isEmpty())
378 {
379 //XXX: this should be unnecessary since we used NativeText.
380 if (shortcut == "Space")
381 shortcut = q_("Space");
382 tip += " [" + shortcut + "]";
383 }
384 helpLabel->setText(tip);
385 helpLabel->setPos(qRound(boundingRectNoHelpLabel().width()+15.5),qRound(button->pos().y()+button->pixmap().size().height()/2-8));
386 if (qApp->property("text_texture")==true)
387 {
388 helpLabel->setVisible(false);
389 helpLabelPixmap->setPixmap(getTextPixmap(tip, helpLabel->font()));
390 helpLabelPixmap->setPos(helpLabel->pos());
391 helpLabelPixmap->setVisible(true);
392 }
393 }
394 }
395 else
396 {
397 helpLabel->setText("");
398 if (qApp->property("text_texture")==true)
399 helpLabelPixmap->setVisible(false);
400 }
401 // Update the screen as soon as possible.
402 StelMainView::getInstance().thereWasAnEvent();
403 }
404
405 // Set the pen for all the sub elements
setColor(const QColor & c)406 void LeftStelBar::setColor(const QColor& c)
407 {
408 helpLabel->setBrush(c);
409 }
410
BottomStelBar(QGraphicsItem * parent,const QPixmap & pixLeft,const QPixmap & pixRight,const QPixmap & pixMiddle,const QPixmap & pixSingle)411 BottomStelBar::BottomStelBar(QGraphicsItem* parent,
412 const QPixmap& pixLeft,
413 const QPixmap& pixRight,
414 const QPixmap& pixMiddle,
415 const QPixmap& pixSingle) :
416 QGraphicsItem(parent),
417 locationPixmap(Q_NULLPTR),
418 datetimePixmap(Q_NULLPTR),
419 fovPixmap(Q_NULLPTR),
420 fpsPixmap(Q_NULLPTR),
421 pixBackgroundLeft(pixLeft),
422 pixBackgroundRight(pixRight),
423 pixBackgroundMiddle(pixMiddle),
424 pixBackgroundSingle(pixSingle),
425 helpLabelPixmap(Q_NULLPTR)
426 {
427 // The text is dummy just for testing
428 datetime = new QGraphicsSimpleTextItem("2008-02-06 17:33", this);
429 location = new QGraphicsSimpleTextItem("Munich, Earth, 500m", this);
430 fov = new QGraphicsSimpleTextItem("FOV 43.45", this);
431 fps = new QGraphicsSimpleTextItem("43.2 FPS", this);
432 if (qApp->property("text_texture")==true) // CLI option -t given?
433 {
434 datetimePixmap=new QGraphicsPixmapItem(this);
435 locationPixmap=new QGraphicsPixmapItem(this);
436 fovPixmap=new QGraphicsPixmapItem(this);
437 fpsPixmap=new QGraphicsPixmapItem(this);
438 helpLabelPixmap=new QGraphicsPixmapItem(this);
439 }
440
441 // Create the help label
442 helpLabel = new QGraphicsSimpleTextItem("", this);
443 helpLabel->setBrush(QBrush(QColor::fromRgbF(1,1,1,1)));
444
445 setColor(QColor::fromRgbF(1,1,1,1));
446
447 setFontSizeFromApp(StelApp::getInstance().getScreenFontSize());
448 connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), this, SLOT(setFontSizeFromApp(int)));
449 connect(&StelApp::getInstance(), SIGNAL(fontChanged(QFont)), this, SLOT(setFont(QFont)));
450
451 QSettings* confSettings = StelApp::getInstance().getSettings();
452 setFlagShowTime(confSettings->value("gui/flag_show_datetime", true).toBool());
453 setFlagShowLocation(confSettings->value("gui/flag_show_location", true).toBool());
454 setFlagShowFov(confSettings->value("gui/flag_show_fov", true).toBool());
455 setFlagShowFps(confSettings->value("gui/flag_show_fps", true).toBool());
456 setFlagTimeJd(confSettings->value("gui/flag_time_jd", false).toBool());
457 setFlagFovDms(confSettings->value("gui/flag_fov_dms", false).toBool());
458 setFlagShowTz(confSettings->value("gui/flag_show_tz", true).toBool());
459 }
460
461 //! connect from StelApp to resize fonts on the fly.
setFontSizeFromApp(int size)462 void BottomStelBar::setFontSizeFromApp(int size)
463 {
464 // Font size was developed based on base font size 13, i.e. 12
465 int screenFontSize = size-1;
466 QFont font=QGuiApplication::font();
467 font.setPixelSize(screenFontSize);
468 datetime->setFont(font);
469 location->setFont(font);
470 fov->setFont(font);
471 fps->setFont(font);
472 StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
473 if (gui)
474 {
475 // to avoid crash
476 SkyGui* skyGui=gui->getSkyGui();
477 if (skyGui)
478 skyGui->updateBarsPos();
479 }
480 }
481
482 //! connect from StelApp to resize fonts on the fly.
setFont(QFont font)483 void BottomStelBar::setFont(QFont font)
484 {
485 font.setPixelSize(StelApp::getInstance().getScreenFontSize()-1);
486 datetime->setFont(font);
487 location->setFont(font);
488 fov->setFont(font);
489 fps->setFont(font);
490 StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
491 if (gui)
492 {
493 // to avoid crash
494 SkyGui* skyGui=gui->getSkyGui();
495 if (skyGui)
496 skyGui->updateBarsPos();
497 }
498 }
499
~BottomStelBar()500 BottomStelBar::~BottomStelBar()
501 {
502 // Remove currently hidden buttons which are not deleted by a parent element
503 for (auto& group : buttonGroups)
504 {
505 for (auto* b : group.elems)
506 {
507 if (b->parentItem()==Q_NULLPTR)
508 {
509 delete b;
510 b=Q_NULLPTR;
511 }
512 }
513 }
514 if (datetimePixmap) { delete datetimePixmap; datetimePixmap=Q_NULLPTR; }
515 if (locationPixmap) { delete locationPixmap; locationPixmap=Q_NULLPTR; }
516 if (fovPixmap) { delete fovPixmap; fovPixmap=Q_NULLPTR; }
517 if (fpsPixmap) { delete fpsPixmap; fpsPixmap=Q_NULLPTR; }
518 if (helpLabelPixmap) { delete helpLabelPixmap; helpLabelPixmap=Q_NULLPTR; }
519 }
520
addButton(StelButton * button,const QString & groupName,const QString & beforeActionName)521 void BottomStelBar::addButton(StelButton* button, const QString& groupName, const QString& beforeActionName)
522 {
523 QList<StelButton*>& g = buttonGroups[groupName].elems;
524 bool done = false;
525 for (int i=0; i<g.size(); ++i)
526 {
527 if (g[i]->action && g[i]->action->objectName()==beforeActionName)
528 {
529 g.insert(i, button);
530 done = true;
531 break;
532 }
533 }
534 if (done == false)
535 g.append(button);
536
537 button->setVisible(true);
538 button->setParentItem(this);
539 button->setFocusOnSky(true);
540 updateButtonsGroups();
541
542 connect(button, SIGNAL(hoverChanged(bool)), this, SLOT(buttonHoverChanged(bool)));
543 emit sizeChanged();
544 }
545
hideButton(const QString & actionName)546 StelButton* BottomStelBar::hideButton(const QString& actionName)
547 {
548 QString gName;
549 StelButton* bToRemove = Q_NULLPTR;
550 for (auto iter = buttonGroups.begin(); iter != buttonGroups.end(); ++iter)
551 {
552 int i=0;
553 for (auto* b : iter.value().elems)
554 {
555 if (b->action && b->action->objectName()==actionName)
556 {
557 gName = iter.key();
558 bToRemove = b;
559 iter.value().elems.removeAt(i);
560 break;
561 }
562 ++i;
563 }
564 }
565 if (bToRemove == Q_NULLPTR)
566 return Q_NULLPTR;
567 if (buttonGroups[gName].elems.size() == 0)
568 {
569 buttonGroups.remove(gName);
570 }
571 // Cannot really delete because some part of the GUI depend on the presence of some buttons
572 // so just make invisible
573 bToRemove->setParentItem(Q_NULLPTR);
574 bToRemove->setVisible(false);
575 updateButtonsGroups();
576 emit sizeChanged();
577 return bToRemove;
578 }
579
580 // Set the margin at the left and right of a button group in pixels
setGroupMargin(const QString & groupName,int left,int right)581 void BottomStelBar::setGroupMargin(const QString& groupName, int left, int right)
582 {
583 if (!buttonGroups.contains(groupName))
584 return;
585 buttonGroups[groupName].leftMargin = left;
586 buttonGroups[groupName].rightMargin = right;
587 updateButtonsGroups();
588 }
589
590 //! Change the background of a group
setGroupBackground(const QString & groupName,const QPixmap & pixLeft,const QPixmap & pixRight,const QPixmap & pixMiddle,const QPixmap & pixSingle)591 void BottomStelBar::setGroupBackground(const QString& groupName,
592 const QPixmap& pixLeft,
593 const QPixmap& pixRight,
594 const QPixmap& pixMiddle,
595 const QPixmap& pixSingle)
596 {
597 if (!buttonGroups.contains(groupName))
598 return;
599
600 buttonGroups[groupName].pixBackgroundLeft = new QPixmap(pixLeft);
601 buttonGroups[groupName].pixBackgroundRight = new QPixmap(pixRight);
602 buttonGroups[groupName].pixBackgroundMiddle = new QPixmap(pixMiddle);
603 buttonGroups[groupName].pixBackgroundSingle = new QPixmap(pixSingle);
604 updateButtonsGroups();
605 }
606
getButtonsBoundingRect() const607 QRectF BottomStelBar::getButtonsBoundingRect() const
608 {
609 // Re-use original Qt code, just remove the help label
610 QRectF childRect;
611 bool hasBtn = false;
612 for (auto* child : QGraphicsItem::childItems())
613 {
614 if (qgraphicsitem_cast<StelButton*>(child)==Q_NULLPTR)
615 continue;
616 hasBtn = true;
617 QPointF childPos = child->pos();
618 QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y());
619 childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
620 }
621
622 if (hasBtn)
623 return QRectF(0, 0, childRect.width()-1, childRect.height()-1);
624 else
625 return QRectF();
626 }
627
updateButtonsGroups()628 void BottomStelBar::updateButtonsGroups()
629 {
630 double x = 0;
631 double y = datetime->boundingRect().height() + 3;
632 for (auto& group : buttonGroups)
633 {
634 QList<StelButton*>& buttons = group.elems;
635 if (buttons.empty())
636 continue;
637 x += group.leftMargin;
638 int n = 0;
639 for (auto* b : buttons)
640 {
641 // We check if the group has its own background if not the case
642 // We apply a default background.
643 if (n == 0)
644 {
645 if (buttons.size() == 1)
646 {
647 if (group.pixBackgroundSingle == Q_NULLPTR)
648 b->setBackgroundPixmap(pixBackgroundSingle);
649 else
650 b->setBackgroundPixmap(*group.pixBackgroundSingle);
651 }
652 else
653 {
654 if (group.pixBackgroundLeft == Q_NULLPTR)
655 b->setBackgroundPixmap(pixBackgroundLeft);
656 else
657 b->setBackgroundPixmap(*group.pixBackgroundLeft);
658 }
659 }
660 else if (n == buttons.size()-1)
661 {
662 if (buttons.size() != 1)
663 {
664 if (group.pixBackgroundSingle == Q_NULLPTR)
665 b->setBackgroundPixmap(pixBackgroundSingle);
666 else
667 b->setBackgroundPixmap(*group.pixBackgroundSingle);
668 }
669 if (group.pixBackgroundRight == Q_NULLPTR)
670 b->setBackgroundPixmap(pixBackgroundRight);
671 else
672 b->setBackgroundPixmap(*group.pixBackgroundRight);
673 }
674 else
675 {
676 if (group.pixBackgroundMiddle == Q_NULLPTR)
677 b->setBackgroundPixmap(pixBackgroundMiddle);
678 else
679 b->setBackgroundPixmap(*group.pixBackgroundMiddle);
680 }
681 // Update the button pixmap
682 b->animValueChanged(0.);
683 b->setPos(x, y);
684 x += b->getButtonPixmapWidth();
685 ++n;
686 }
687 x+=group.rightMargin;
688 }
689 updateText(true);
690 }
691
692 // create text elements and tooltips in bottom toolbar.
693 // Make sure to avoid any change if not necessary to avoid triggering useless redraw
updateText(bool updatePos)694 void BottomStelBar::updateText(bool updatePos)
695 {
696 StelCore* core = StelApp::getInstance().getCore();
697 const double jd = core->getJD();
698 const double deltaT = core->getDeltaT();
699 const double sigma = StelUtils::getDeltaTStandardError(jd);
700 QString sigmaInfo = "";
701 QString validRangeMarker = "";
702 core->getCurrentDeltaTAlgorithmValidRangeDescription(jd, &validRangeMarker);
703
704 const StelLocaleMgr& locmgr = StelApp::getInstance().getLocaleMgr();
705 QString tz = locmgr.getPrintableTimeZoneLocal(jd);
706 QString newDateInfo = " ";
707 if (getFlagShowTime())
708 {
709 if (getFlagShowTz())
710 newDateInfo = QString("%1 %2 %3").arg(locmgr.getPrintableDateLocal(jd), locmgr.getPrintableTimeLocal(jd), tz);
711 else
712 newDateInfo = QString("%1 %2").arg(locmgr.getPrintableDateLocal(jd), locmgr.getPrintableTimeLocal(jd));
713 }
714 QString newDateAppx = QString("JD %1").arg(jd, 0, 'f', 5); // up to seconds
715 if (getFlagTimeJd())
716 {
717 newDateAppx = newDateInfo;
718 newDateInfo = QString("JD %1").arg(jd, 0, 'f', 5); // up to seconds
719 }
720
721 QString planetName = core->getCurrentLocation().planetName;
722 QString planetNameI18n;
723 if (planetName=="SpaceShip") // Avoid crash
724 {
725 const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
726 planetNameI18n = trans.qtranslate(planetName, "special celestial body"); // added context
727 }
728 else
729 planetNameI18n = GETSTELMODULE(SolarSystem)->searchByEnglishName(planetName)->getNameI18n();
730
731 QString tzName = core->getCurrentTimeZone();
732 if (tzName.contains("system_default") || (tzName.isEmpty() && planetName=="Earth"))
733 tzName = q_("System default");
734
735 QString currTZ = QString("%1: %2").arg(q_("Time zone"), tzName);
736
737 if (tzName.contains("LMST") || tzName.contains("auto") || (planetName=="Earth" && jd<=StelCore::TZ_ERA_BEGINNING && !core->getUseCustomTimeZone()) )
738 currTZ = q_("Local Mean Solar Time");
739
740 if (tzName.contains("LTST"))
741 currTZ = q_("Local True Solar Time");
742
743 // TRANSLATORS: unit of measurement: minutes per second
744 QString timeRateMU = qc_("min/s", "unit of measurement");
745 double timeRate = qAbs(core->getTimeRate()/StelCore::JD_SECOND);
746 double timeSpeed = timeRate/60.;
747
748 if (timeSpeed>=60.)
749 {
750 timeSpeed /= 60.;
751 // TRANSLATORS: unit of measurement: hours per second
752 timeRateMU = qc_("hr/s", "unit of measurement");
753 }
754 if (timeSpeed>=24.)
755 {
756 timeSpeed /= 24.;
757 // TRANSLATORS: unit of measurement: days per second
758 timeRateMU = qc_("d/s", "unit of measurement");
759 }
760 if (timeSpeed>=365.25)
761 {
762 timeSpeed /= 365.25;
763 // TRANSLATORS: unit of measurement: years per second
764 timeRateMU = qc_("yr/s", "unit of measurement");
765 }
766 QString timeRateInfo = QString("%1: x%2").arg(q_("Simulation speed"), QString::number(timeRate, 'f', 0));
767 if (timeRate>60.)
768 timeRateInfo = QString("%1: x%2 (%3 %4)").arg(q_("Simulation speed"), QString::number(timeRate, 'f', 0), QString::number(timeSpeed, 'f', 2), timeRateMU);
769
770 if (datetime->text()!=newDateInfo)
771 {
772 updatePos = true;
773 datetime->setText(newDateInfo);
774 }
775
776 if (core->getCurrentDeltaTAlgorithm()!=StelCore::WithoutCorrection)
777 {
778 if (sigma>0)
779 sigmaInfo = QString("; %1(%2T) = %3s").arg(QChar(0x03c3)).arg(QChar(0x0394)).arg(sigma, 3, 'f', 1);
780
781 QString deltaTInfo = "";
782 if (qAbs(deltaT)>60.)
783 deltaTInfo = QString("%1 (%2s)%3").arg(StelUtils::hoursToHmsStr(deltaT/3600.)).arg(deltaT, 5, 'f', 2).arg(validRangeMarker);
784 else
785 deltaTInfo = QString("%1s%2").arg(deltaT, 3, 'f', 3).arg(validRangeMarker);
786
787 // the corrective ndot to be displayed could be set according to the currently used DeltaT algorithm.
788 //float ndot=core->getDeltaTnDot();
789 // or just to the used ephemeris. This has to be read as "Selected DeltaT formula used, but with the ephemeris's nDot applied it corrects DeltaT to..."
790 const double ndot=( (EphemWrapper::use_de430(jd) || EphemWrapper::use_de431(jd) || EphemWrapper::use_de440(jd) || EphemWrapper::use_de441(jd)) ? -25.8 : -23.8946 );
791
792 datetime->setToolTip(QString("<p style='white-space:pre'>%1T = %2 [n%8 @ %3\"/cy%4%5]<br>%6<br>%7<br>%9</p>").arg(QChar(0x0394), deltaTInfo, QString::number(ndot, 'f', 4), QChar(0x00B2), sigmaInfo, newDateAppx, currTZ, QChar(0x2032), timeRateInfo));
793 }
794 else
795 datetime->setToolTip(QString("<p style='white-space:pre'>%1<br>%2<br>%3</p>").arg(newDateAppx, currTZ, timeRateInfo));
796
797 if (qApp->property("text_texture")==true) // CLI option -t given?
798 {
799 datetime->setVisible(false); // hide normal thingy.
800 datetimePixmap->setPixmap(getTextPixmap(newDateInfo, datetime->font()));
801 }
802
803 // build location tooltip
804 QString newLocation = "";
805 if (getFlagShowLocation())
806 {
807 const StelLocation* loc = &core->getCurrentLocation();
808 if(loc->name.isEmpty())
809 newLocation = planetNameI18n +", "+StelUtils::decDegToDmsStr(loc->latitude)+", "+StelUtils::decDegToDmsStr(loc->longitude);
810 else
811 {
812 if (loc->name.contains("->")) // a spaceship
813 newLocation = QString("%1 [%2 %3]").arg(planetNameI18n, q_("flight"), loc->name);
814 else
815 {
816 //TRANSLATORS: Unit of measure for distance - meter
817 newLocation = planetNameI18n +", "+q_(loc->name) + ", "+ QString("%1 %2").arg(loc->altitude).arg(qc_("m", "distance"));
818 }
819 }
820 }
821 // TODO: When topocentric switch is toggled, this must be redrawn!
822 if (location->text()!=newLocation)
823 {
824 updatePos = true;
825 location->setText(newLocation);
826 double lat = static_cast<double>(core->getCurrentLocation().latitude);
827 double lon = static_cast<double>(core->getCurrentLocation().longitude);
828 QString latStr, lonStr, pm;
829 if (lat >= 0)
830 pm = "N";
831 else
832 {
833 pm = "S";
834 lat *= -1;
835 }
836 latStr = QString("%1%2%3").arg(pm).arg(lat).arg(QChar(0x00B0));
837 if (lon >= 0)
838 pm = "E";
839 else
840 {
841 pm = "W";
842 lon *= -1;
843 }
844 lonStr = QString("%1%2%3").arg(pm).arg(lon).arg(QChar(0x00B0));
845 QString rho, weather;
846 if (core->getUseTopocentricCoordinates())
847 rho = QString("%1 %2 %3").arg(q_("planetocentric distance")).arg(core->getCurrentObserver()->getDistanceFromCenter() * AU).arg(qc_("km", "distance"));
848 else
849 rho = q_("planetocentric observer");
850
851 if (newLocation.contains("->")) // a spaceship
852 location->setToolTip(QString());
853 else
854 {
855 if (core->getCurrentPlanet()->hasAtmosphere())
856 {
857 const StelPropertyMgr* propMgr=StelApp::getInstance().getStelPropertyManager();
858 weather = QString("%1: %2 %3; %4: %5 %6C").arg(q_("Atmospheric pressure"), QString::number(propMgr->getStelPropertyValue("StelSkyDrawer.atmospherePressure").toFloat(), 'f', 2), qc_("mbar", "pressure unit"), q_("temperature"), QString::number(propMgr->getStelPropertyValue("StelSkyDrawer.atmosphereTemperature").toFloat(), 'f', 1), QChar(0x00B0));
859 location->setToolTip(QString("<p style='white-space:pre'>%1 %2; %3<br>%4</p>").arg(latStr, lonStr, rho, weather));
860 }
861 else
862 location->setToolTip(QString("%1 %2; %3").arg(latStr, lonStr, rho));
863 }
864
865 if (qApp->property("text_texture")==true) // CLI option -t given?
866 {
867 locationPixmap->setPixmap(getTextPixmap(newLocation, location->font()));
868 location->setVisible(false);
869 }
870 }
871
872 QString str;
873
874 // build fov tooltip
875 QTextStream wos(&str);
876 // TRANSLATORS: Field of view. Please use abbreviation.
877 QString fovstr = QString("%1 ").arg(qc_("FOV", "abbreviation"));
878 if (getFlagFovDms())
879 {
880 wos << fovstr << StelUtils::decDegToDmsStr(core->getMovementMgr()->getCurrentFov());
881 }
882 else
883 {
884 wos << fovstr << qSetRealNumberPrecision(3) << core->getMovementMgr()->getCurrentFov() << QChar(0x00B0);
885 }
886
887 if (fov->text()!=str)
888 {
889 updatePos = true;
890 if (getFlagShowFov())
891 {
892 fov->setText(str);
893 fov->setToolTip(q_("Field of view"));
894 if (qApp->property("text_texture")==true) // CLI option -t given?
895 {
896 fovPixmap->setPixmap(getTextPixmap(str, fov->font()));
897 fov->setVisible(false);
898 }
899 }
900 else
901 {
902 fov->setText("");
903 fov->setToolTip("");
904 }
905 }
906
907 str="";
908
909 // build fps tooltip
910 QTextStream wos2(&str);
911 // TRANSLATORS: Frames per second. Please use abbreviation.
912 QString fpsstr = QString(" %1").arg(qc_("FPS", "abbreviation"));
913 wos2 << qSetRealNumberPrecision(3) << StelApp::getInstance().getFps() << fpsstr;
914 if (fps->text()!=str)
915 {
916 updatePos = true;
917 if (getFlagShowFps())
918 {
919 fps->setText(str);
920 fps->setToolTip(q_("Frames per second"));
921 if (qApp->property("text_texture")==true) // CLI option -t given?
922 {
923 fpsPixmap->setPixmap(getTextPixmap(str, fps->font()));
924 fps->setVisible(false);
925 }
926 }
927 else
928 {
929 fps->setText("");
930 fps->setToolTip("");
931 }
932 }
933
934 if (updatePos)
935 {
936 QFontMetrics fpsMetrics(fps->font());
937 int fpsShift = fpsMetrics.boundingRect(fpsstr).width() + 50;
938
939 QFontMetrics fovMetrics(fov->font());
940 int fovShift = fpsShift + fovMetrics.boundingRect(fovstr).width() + 80;
941 if (getFlagFovDms())
942 fovShift += 25;
943
944 QRectF rectCh = getButtonsBoundingRect();
945 location->setPos(0, 0);
946 int dtp = static_cast<int>(rectCh.right()-datetime->boundingRect().width())-5;
947 if ((dtp%2) == 1) dtp--; // make even pixel
948 datetime->setPos(dtp,0);
949 fov->setPos(datetime->x()-fovShift, 0);
950 fps->setPos(datetime->x()-fpsShift, 0);
951 if (qApp->property("text_texture")==true) // CLI option -t given?
952 {
953 locationPixmap->setPos(0,0);
954 int dtp = static_cast<int>(rectCh.right()-datetimePixmap->boundingRect().width())-5;
955 if ((dtp%2) == 1) dtp--; // make even pixel
956 datetimePixmap->setPos(dtp,0);
957 fovPixmap->setPos(datetime->x()-fovShift, 0);
958 fpsPixmap->setPos(datetime->x()-fpsShift, 0);
959 }
960 }
961 }
962
paint(QPainter *,const QStyleOptionGraphicsItem *,QWidget *)963 void BottomStelBar::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
964 {
965 updateText();
966 }
967
boundingRect() const968 QRectF BottomStelBar::boundingRect() const
969 {
970 if (QGraphicsItem::childItems().size()==0)
971 return QRectF();
972 const QRectF& r = childrenBoundingRect();
973 return QRectF(0, 0, r.width()-1, r.height()-1);
974 }
975
boundingRectNoHelpLabel() const976 QRectF BottomStelBar::boundingRectNoHelpLabel() const
977 {
978 // Re-use original Qt code, just remove the help label
979 QRectF childRect;
980 for (auto* child : QGraphicsItem::childItems())
981 {
982 if ((child==helpLabel) || (child==helpLabelPixmap))
983 continue;
984 QPointF childPos = child->pos();
985 QTransform matrix = child->transform() * QTransform().translate(childPos.x(), childPos.y());
986 childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect());
987 }
988 return childRect;
989 }
990
991 // Set the pen for all the sub elements
setColor(const QColor & c)992 void BottomStelBar::setColor(const QColor& c)
993 {
994 datetime->setBrush(c);
995 location->setBrush(c);
996 fov->setBrush(c);
997 fps->setBrush(c);
998 helpLabel->setBrush(c);
999 }
1000
1001 // Update the help label when a button is hovered
buttonHoverChanged(bool b)1002 void BottomStelBar::buttonHoverChanged(bool b)
1003 {
1004 StelButton* button = qobject_cast<StelButton*>(sender());
1005 Q_ASSERT(button);
1006 if (b==true)
1007 {
1008 StelAction* action = button->action;
1009 if (action)
1010 {
1011 QString tip(action->getText());
1012 QString shortcut(action->getShortcut().toString(QKeySequence::NativeText));
1013 if (!shortcut.isEmpty())
1014 {
1015 //XXX: this should be unnecessary since we used NativeText.
1016 if (shortcut == "Space")
1017 shortcut = q_("Space");
1018 tip += " [" + shortcut + "]";
1019 }
1020 helpLabel->setText(tip);
1021 //helpLabel->setPos(button->pos().x()+button->pixmap().size().width()/2,-27);
1022 helpLabel->setPos(20,-27);
1023 if (qApp->property("text_texture")==true)
1024 {
1025 helpLabel->setVisible(false);
1026 helpLabelPixmap->setPixmap(getTextPixmap(tip, helpLabel->font()));
1027 helpLabelPixmap->setPos(helpLabel->pos());
1028 helpLabelPixmap->setVisible(true);
1029 }
1030 }
1031 }
1032 else
1033 {
1034 helpLabel->setText("");
1035 if (qApp->property("text_texture")==true)
1036 helpLabelPixmap->setVisible(false);
1037 }
1038 // Update the screen as soon as possible.
1039 StelMainView::getInstance().thereWasAnEvent();
1040 }
1041
StelBarsPath(QGraphicsItem * parent)1042 StelBarsPath::StelBarsPath(QGraphicsItem* parent) : QGraphicsPathItem(parent), roundSize(6)
1043 {
1044 QPen aPen(QColor::fromRgbF(0.7,0.7,0.7,0.5));
1045 aPen.setWidthF(1.);
1046 setBrush(QBrush(QColor::fromRgbF(0.22, 0.22, 0.23, 0.2)));
1047 setPen(aPen);
1048 }
1049
updatePath(BottomStelBar * bot,LeftStelBar * lef)1050 void StelBarsPath::updatePath(BottomStelBar* bot, LeftStelBar* lef)
1051 {
1052 QPainterPath newPath;
1053 QPointF p = lef->pos();
1054 QRectF r = lef->boundingRectNoHelpLabel();
1055 QPointF p2 = bot->pos();
1056 QRectF r2 = bot->boundingRectNoHelpLabel();
1057
1058 newPath.moveTo(p.x()-roundSize, p.y()-roundSize);
1059 newPath.lineTo(p.x()+r.width(),p.y()-roundSize);
1060 newPath.arcTo(p.x()+r.width()-roundSize, p.y()-roundSize, 2.*roundSize, 2.*roundSize, 90, -90);
1061 newPath.lineTo(p.x()+r.width()+roundSize, p2.y()-roundSize);
1062 newPath.lineTo(p2.x()+r2.width(),p2.y()-roundSize);
1063 newPath.arcTo(p2.x()+r2.width()-roundSize, p2.y()-roundSize, 2.*roundSize, 2.*roundSize, 90, -90);
1064 newPath.lineTo(p2.x()+r2.width()+roundSize, p2.y()+r2.height()+roundSize);
1065 newPath.lineTo(p.x()-roundSize, p2.y()+r2.height()+roundSize);
1066 setPath(newPath);
1067 }
1068
setBackgroundOpacity(double opacity)1069 void StelBarsPath::setBackgroundOpacity(double opacity)
1070 {
1071 setBrush(QBrush(QColor::fromRgbF(0.22, 0.22, 0.23, opacity)));
1072 }
1073
StelProgressBarMgr(QGraphicsItem * parent)1074 StelProgressBarMgr::StelProgressBarMgr(QGraphicsItem* parent):
1075 QGraphicsWidget(parent)
1076 {
1077 setLayout(new QGraphicsLinearLayout(Qt::Vertical));
1078 }
1079 /*
1080 QRectF StelProgressBarMgr::boundingRect() const
1081 {
1082 if (QGraphicsItem::children().size()==0)
1083 return QRectF();
1084 const QRectF& r = childrenBoundingRect();
1085 return QRectF(0, 0, r.width()-1, r.height()-1);
1086 }*/
1087
addProgressBar(const StelProgressController * p)1088 void StelProgressBarMgr::addProgressBar(const StelProgressController* p)
1089 {
1090 StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
1091 QProgressBar* pb = new QProgressBar();
1092 pb->setFixedHeight(25);
1093 pb->setFixedWidth(200);
1094 pb->setTextVisible(true);
1095 pb->setValue(p->getValue());
1096 pb->setMinimum(p->getMin());
1097 pb->setMaximum(p->getMax());
1098 pb->setFormat(p->getFormat());
1099 if (gui!=Q_NULLPTR)
1100 pb->setStyleSheet(gui->getStelStyle().qtStyleSheet);
1101 QGraphicsProxyWidget* pbProxy = new QGraphicsProxyWidget();
1102 pbProxy->setWidget(pb);
1103 pbProxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
1104 pbProxy->setZValue(150);
1105 static_cast<QGraphicsLinearLayout*>(layout())->addItem(pbProxy);
1106 allBars.insert(p, pb);
1107 pb->setVisible(true);
1108
1109 connect(p, SIGNAL(changed()), this, SLOT(oneBarChanged()));
1110 }
1111
removeProgressBar(const StelProgressController * p)1112 void StelProgressBarMgr::removeProgressBar(const StelProgressController *p)
1113 {
1114 QProgressBar* pb = allBars[p];
1115 pb->deleteLater();
1116 allBars.remove(p);
1117 }
1118
oneBarChanged()1119 void StelProgressBarMgr::oneBarChanged()
1120 {
1121 const StelProgressController *p = static_cast<StelProgressController*>(QObject::sender());
1122 QProgressBar* pb = allBars[p];
1123 pb->setValue(p->getValue());
1124 pb->setMinimum(p->getMin());
1125 pb->setMaximum(p->getMax());
1126 pb->setFormat(p->getFormat());
1127 }
1128
CornerButtons(QGraphicsItem * parent)1129 CornerButtons::CornerButtons(QGraphicsItem* parent) :
1130 QGraphicsItem(parent),
1131 lastOpacity(10)
1132 {
1133 }
1134
paint(QPainter *,const QStyleOptionGraphicsItem *,QWidget *)1135 void CornerButtons::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
1136 {
1137 // Do nothing. Just paint the child widgets
1138 }
1139
boundingRect() const1140 QRectF CornerButtons::boundingRect() const
1141 {
1142 if (QGraphicsItem::childItems().size()==0)
1143 return QRectF();
1144 const QRectF& r = childrenBoundingRect();
1145 return QRectF(0, 0, r.width()-1, r.height()-1);
1146 }
1147
setOpacity(double opacity)1148 void CornerButtons::setOpacity(double opacity)
1149 {
1150 if (opacity<=0. && lastOpacity<=0.)
1151 return;
1152 lastOpacity = opacity;
1153 if (QGraphicsItem::childItems().size()==0)
1154 return;
1155 for (auto* child : QGraphicsItem::childItems())
1156 {
1157 StelButton* sb = qgraphicsitem_cast<StelButton*>(child);
1158 Q_ASSERT(sb!=Q_NULLPTR);
1159 sb->setOpacity(opacity);
1160 }
1161 }
1162