File indexing completed on 2024-04-21 14:47:01
0001 /* 0002 SPDX-FileCopyrightText: 2008 Jason Harris <kstars@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "skycalendar.h" 0008 0009 #include "geolocation.h" 0010 #include "ksplanetbase.h" 0011 #include "kstarsdata.h" 0012 #include "dialogs/locationdialog.h" 0013 #include "skycomponents/skymapcomposite.h" 0014 0015 #include <KPlotObject> 0016 0017 #include <QPainter> 0018 #include <QPixmap> 0019 #include <QPrintDialog> 0020 #include <QPrinter> 0021 #include <QPushButton> 0022 #include <QScreen> 0023 #include <QtConcurrent> 0024 0025 SkyCalendarUI::SkyCalendarUI(QWidget *parent) : QFrame(parent) 0026 { 0027 setupUi(this); 0028 } 0029 0030 SkyCalendar::SkyCalendar(QWidget *parent) : QDialog(parent) 0031 { 0032 #ifdef Q_OS_OSX 0033 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0034 #endif 0035 0036 scUI = new SkyCalendarUI(this); 0037 0038 QVBoxLayout *mainLayout = new QVBoxLayout; 0039 0040 mainLayout->addWidget(scUI); 0041 setLayout(mainLayout); 0042 0043 geo = KStarsData::Instance()->geo(); 0044 0045 setWindowTitle(i18nc("@title:window", "Sky Calendar")); 0046 0047 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0048 mainLayout->addWidget(buttonBox); 0049 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 0050 0051 QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print...")); 0052 printB->setToolTip(i18n("Print the Sky Calendar")); 0053 buttonBox->addButton(printB, QDialogButtonBox::ActionRole); 0054 connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint())); 0055 0056 setModal(false); 0057 0058 //Adjust minimum size for small screens: 0059 if (QGuiApplication::primaryScreen()->geometry().height() <= scUI->CalendarView->height()) 0060 { 0061 scUI->CalendarView->setMinimumSize(400, 600); 0062 } 0063 0064 scUI->CalendarView->setShowGrid(false); 0065 scUI->Year->setValue(KStarsData::Instance()->lt().date().year()); 0066 0067 scUI->LocationButton->setText(geo->fullName()); 0068 0069 scUI->CalendarView->setHorizon(); 0070 0071 plotButtonText = scUI->CreateButton->text(); 0072 connect(scUI->CreateButton, &QPushButton::clicked, this, [this]() 0073 { 0074 scUI->CreateButton->setText(i18n("Please Wait") + "..."); 0075 slotFillCalendar(); 0076 }); 0077 0078 connect(scUI->LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); 0079 } 0080 0081 int SkyCalendar::year() 0082 { 0083 return scUI->Year->value(); 0084 } 0085 0086 void SkyCalendar::slotFillCalendar() 0087 { 0088 scUI->CreateButton->setEnabled(false); 0089 0090 scUI->CalendarView->resetPlot(); 0091 scUI->CalendarView->setHorizon(); 0092 0093 if (scUI->checkBox_Mercury->isChecked()) 0094 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MERCURY); 0095 if (scUI->checkBox_Venus->isChecked()) 0096 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::VENUS); 0097 if (scUI->checkBox_Mars->isChecked()) 0098 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MARS); 0099 if (scUI->checkBox_Jupiter->isChecked()) 0100 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::JUPITER); 0101 if (scUI->checkBox_Saturn->isChecked()) 0102 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::SATURN); 0103 if (scUI->checkBox_Uranus->isChecked()) 0104 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::URANUS); 0105 if (scUI->checkBox_Neptune->isChecked()) 0106 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::NEPTUNE); 0107 0108 scUI->CreateButton->setText(i18n("Plot Planetary Almanac")); 0109 scUI->CreateButton->setEnabled(true); 0110 } 0111 0112 #if 0 0113 void SkyCalendar::slotCalculating() 0114 { 0115 while (calculating) 0116 { 0117 //QMutexLocker locker(&calculationMutex); 0118 0119 if (!calculating) 0120 continue; 0121 0122 scUI->CreateButton->setText(i18n("Please Wait") + ". "); 0123 scUI->CreateButton->repaint(); 0124 QThread::msleep(200); 0125 scUI->CreateButton->setText(i18n("Please Wait") + ".. "); 0126 scUI->CreateButton->repaint(); 0127 QThread::msleep(200); 0128 0129 scUI->CreateButton->repaint(); 0130 QThread::msleep(200); 0131 } 0132 scUI->CreateButton->setText(plotButtonText); 0133 } 0134 #endif 0135 0136 // FIXME: For the time being, adjust with dirty, cluttering labels that don't align to the line 0137 /* 0138 void SkyCalendar::drawEventLabel( float x1, float y1, float x2, float y2, QString LabelText ) { 0139 QFont origFont = p->font(); 0140 p->setFont( QFont( "Bitstream Vera", 10 ) ); 0141 0142 int textFlags = Qt::AlignCenter; // TODO: See if Qt::SingleLine flag works better 0143 QFontMetricsF fm( p->font(), p->device() ); 0144 0145 QRectF LabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, LabelText ); 0146 QPointF LabelPoint = scUI->CalendarView->mapToWidget( QPointF( x, y ) ); 0147 0148 float LabelAngle = atan2( y2 - y1, x2 - x1 )/dms::DegToRad; 0149 0150 p->save(); 0151 p->translate( LabelPoint ); 0152 p->rotate( LabelAngle ); 0153 p->drawText( LabelRect, textFlags, LabelText ); 0154 p->restore(); 0155 0156 p->setFont( origFont ); 0157 } 0158 */ 0159 0160 void SkyCalendar::addPlanetEvents(int nPlanet) 0161 { 0162 KSPlanetBase *ksp = KStarsData::Instance()->skyComposite()->planet(nPlanet); 0163 QColor pColor = ksp->color(); 0164 //QVector<QPointF> vRise, vSet, vTransit; 0165 std::vector<QPointF> vRise, vSet, vTransit; 0166 0167 for (KStarsDateTime kdt(QDate(year(), 1, 1), QTime(12, 0, 0)); kdt.date().year() == year(); 0168 kdt = kdt.addDays(scUI->spinBox_Interval->value())) 0169 { 0170 float rTime, sTime, tTime; 0171 0172 //Compute rise/set/transit times. If they occur before noon, 0173 //recompute for the following day 0174 QTime tmp_rTime = ksp->riseSetTime(kdt, geo, true, true); //rise time, exact 0175 QTime tmp_sTime = ksp->riseSetTime(kdt, geo, false, true); //set time, exact 0176 QTime tmp_tTime = ksp->transitTime(kdt, geo); 0177 QTime midday(12, 0, 0); 0178 0179 // NOTE: riseSetTime should be fix now, this test is no longer necessary 0180 if (tmp_rTime == tmp_sTime) 0181 { 0182 tmp_rTime = QTime(); 0183 tmp_sTime = QTime(); 0184 } 0185 0186 if (tmp_rTime.isValid() && tmp_sTime.isValid()) 0187 { 0188 rTime = tmp_rTime.secsTo(midday) * 24.0 / 86400.0; 0189 sTime = tmp_sTime.secsTo(midday) * 24.0 / 86400.0; 0190 0191 if (tmp_rTime <= midday) 0192 rTime = 12.0 - rTime; 0193 else 0194 rTime = -12.0 - rTime; 0195 0196 if (tmp_sTime <= midday) 0197 sTime = 12.0 - sTime; 0198 else 0199 sTime = -12.0 - sTime; 0200 } 0201 else 0202 { 0203 if (ksp->transitAltitude(kdt, geo).degree() > 0) 0204 { 0205 rTime = -24.0; 0206 sTime = 24.0; 0207 } 0208 else 0209 { 0210 rTime = 24.0; 0211 sTime = -24.0; 0212 } 0213 } 0214 0215 tTime = tmp_tTime.secsTo(midday) * 24.0 / 86400.0; 0216 if (tmp_tTime <= midday) 0217 tTime = 12.0 - tTime; 0218 else 0219 tTime = -12.0 - tTime; 0220 0221 float dy = kdt.date().daysInYear() - kdt.date().dayOfYear(); 0222 vRise.push_back(QPointF(rTime, dy)); 0223 vSet.push_back(QPointF(sTime, dy)); 0224 vTransit.push_back(QPointF(tTime, dy)); 0225 } 0226 0227 //Now, find continuous segments in each QVector and add each segment 0228 //as a separate KPlotObject 0229 0230 KPlotObject *oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0231 KPlotObject *oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0232 KPlotObject *oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0233 0234 float defaultSetTime = -10.0; 0235 float defaultRiseTime = 8.0; 0236 QString label; 0237 bool initialRise = true; 0238 bool initialSet = true; 0239 bool initialTransit = true; 0240 bool extraCheckRise = true; 0241 bool extraCheckSet = true; 0242 bool extraCheckTransit = true; 0243 bool needRiseLabel = false; 0244 bool needSetLabel = false; 0245 bool needTransertLabel = false; 0246 0247 float maxRiseTime = 0.0; 0248 for (auto &item : vRise) 0249 { 0250 if (item.x() >= maxRiseTime) 0251 maxRiseTime = item.x(); 0252 } 0253 maxRiseTime = qFloor(maxRiseTime) - 1.0; 0254 0255 for (uint32_t i = 0; i < vRise.size(); ++i) 0256 { 0257 if (initialRise && (vRise.at(i).x() > defaultSetTime && vRise.at(i).x() < defaultRiseTime)) 0258 { 0259 needRiseLabel = true; 0260 initialRise = false; 0261 } 0262 else if (vRise.at(i).x() < defaultSetTime || vRise.at(i).x() > defaultRiseTime) 0263 { 0264 initialRise = true; 0265 needRiseLabel = false; 0266 } 0267 else 0268 needRiseLabel = false; 0269 0270 if (extraCheckRise && vRise.at(i).x() > defaultRiseTime && vRise.at(i).x() < maxRiseTime) 0271 { 0272 needRiseLabel = true; 0273 extraCheckRise = false; 0274 } 0275 0276 if (initialSet && (vSet.at(i).x() > defaultSetTime && vSet.at(i).x() < defaultRiseTime)) 0277 { 0278 needSetLabel = true; 0279 initialSet = false; 0280 } 0281 else if (vSet.at(i).x() < defaultSetTime || vSet.at(i).x() > defaultRiseTime) 0282 { 0283 initialSet = true; 0284 needSetLabel = false; 0285 } 0286 else 0287 needSetLabel = false; 0288 0289 if (extraCheckSet && vSet.at(i).x() > defaultRiseTime && vSet.at(i).x() < maxRiseTime) 0290 { 0291 needSetLabel = true; 0292 extraCheckSet = false; 0293 } 0294 0295 if (initialTransit && (vTransit.at(i).x() > defaultSetTime && vTransit.at(i).x() < defaultRiseTime)) 0296 { 0297 needTransertLabel = true; 0298 initialTransit = false; 0299 } 0300 else if (vTransit.at(i).x() < defaultSetTime || vTransit.at(i).x() > defaultRiseTime) 0301 { 0302 initialTransit = true; 0303 needTransertLabel = false; 0304 ; 0305 } 0306 else 0307 needTransertLabel = false; 0308 0309 if (extraCheckTransit && vTransit.at(i).x() > defaultRiseTime && vTransit.at(i).x() < maxRiseTime) 0310 { 0311 needTransertLabel = true; 0312 extraCheckTransit = false; 0313 } 0314 0315 if (vRise.at(i).x() > -23.0 && vRise.at(i).x() < 23.0) 0316 { 0317 if (i > 0 && fabs(vRise.at(i).x() - vRise.at(i - 1).x()) > 6.0) 0318 { 0319 scUI->CalendarView->addPlotObject(oRise); 0320 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0321 extraCheckRise = true; 0322 } 0323 0324 if (needRiseLabel) 0325 label = i18nc("A planet rises from the horizon", "%1 rises", ksp->name()); 0326 else 0327 label = QString(); 0328 // Add the current point to KPlotObject 0329 oRise->addPoint(vRise.at(i), label); 0330 } 0331 else 0332 { 0333 scUI->CalendarView->addPlotObject(oRise); 0334 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0335 } 0336 0337 if (vSet.at(i).x() > -23.0 && vSet.at(i).x() < 23.0) 0338 { 0339 if (i > 0 && fabs(vSet.at(i).x() - vSet.at(i - 1).x()) > 6.0) 0340 { 0341 scUI->CalendarView->addPlotObject(oSet); 0342 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0343 extraCheckSet = true; 0344 } 0345 0346 if (needSetLabel) 0347 label = i18nc("A planet sets from the horizon", "%1 sets", ksp->name()); 0348 else 0349 label = QString(); 0350 0351 oSet->addPoint(vSet.at(i), label); 0352 } 0353 else 0354 { 0355 scUI->CalendarView->addPlotObject(oSet); 0356 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0357 } 0358 0359 if (vTransit.at(i).x() > -23.0 && vTransit.at(i).x() < 23.0) 0360 { 0361 if (i > 0 && fabs(vTransit.at(i).x() - vTransit.at(i - 1).x()) > 6.0) 0362 { 0363 scUI->CalendarView->addPlotObject(oTransit); 0364 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0365 extraCheckTransit = true; 0366 } 0367 0368 if (needTransertLabel) 0369 label = i18nc("A planet transits across the meridian", "%1 transits", ksp->name()); 0370 else 0371 label = QString(); 0372 0373 oTransit->addPoint(vTransit.at(i), label); 0374 } 0375 else 0376 { 0377 scUI->CalendarView->addPlotObject(oTransit); 0378 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0); 0379 } 0380 } 0381 0382 // Add the last points 0383 scUI->CalendarView->addPlotObject(oRise); 0384 scUI->CalendarView->addPlotObject(oSet); 0385 scUI->CalendarView->addPlotObject(oTransit); 0386 } 0387 0388 void SkyCalendar::slotPrint() 0389 { 0390 QPainter p; // Our painter object 0391 QPrinter printer; // Our printer object 0392 QString str_legend; // Text legend 0393 QString str_year; // Calendar's year 0394 int text_height = 200; // Height of legend text zone in points 0395 QSize calendar_size; // Initial calendar widget size 0396 QFont calendar_font; // Initial calendar font 0397 int calendar_font_size; // Initial calendar font size 0398 0399 // Set printer resolution to 300 dpi 0400 printer.setResolution(300); 0401 0402 // Open print dialog 0403 //NOTE Changed from pointer to statically allocated object, what effect will it have? 0404 //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) ); 0405 QPrintDialog dialog(&printer, this); 0406 dialog.setWindowTitle(i18nc("@title:window", "Print sky calendar")); 0407 if (dialog.exec() == QDialog::Accepted) 0408 { 0409 // Change mouse cursor 0410 QApplication::setOverrideCursor(Qt::WaitCursor); 0411 0412 // Save calendar widget font 0413 calendar_font = scUI->CalendarView->font(); 0414 // Save calendar widget font size 0415 calendar_font_size = calendar_font.pointSize(); 0416 // Save calendar widget size 0417 calendar_size = scUI->CalendarView->size(); 0418 0419 // Set text legend 0420 str_year.setNum(year()); 0421 str_legend = i18n("Sky Calendar"); 0422 str_legend += '\n'; 0423 str_legend += geo->fullName(); 0424 str_legend += " - "; 0425 str_legend += str_year; 0426 0427 // Create a rectangle for legend text zone 0428 QRect text_rect(0, 0, printer.width(), text_height); 0429 0430 // Increase calendar widget font size so it looks good in 300 dpi 0431 calendar_font.setPointSize(calendar_font_size * 3); 0432 scUI->CalendarView->setFont(calendar_font); 0433 // Increase calendar widget size to fit the entire page 0434 scUI->CalendarView->resize(printer.width(), printer.height() - text_height); 0435 0436 // Create a pixmap and render calendar widget into it 0437 QPixmap pixmap(scUI->CalendarView->size()); 0438 scUI->CalendarView->render(&pixmap); 0439 0440 // Begin painting on printer 0441 p.begin(&printer); 0442 // Draw legend 0443 p.drawText(text_rect, Qt::AlignLeft, str_legend); 0444 // Draw calendar 0445 p.drawPixmap(0, text_height, pixmap); 0446 // Ending painting 0447 p.end(); 0448 0449 // Restore calendar widget font size 0450 calendar_font.setPointSize(calendar_font_size); 0451 scUI->CalendarView->setFont(calendar_font); 0452 // Restore calendar widget size 0453 scUI->CalendarView->resize(calendar_size); 0454 0455 // Restore mouse cursor 0456 QApplication::restoreOverrideCursor(); 0457 } 0458 //delete dialog; 0459 } 0460 0461 void SkyCalendar::slotLocation() 0462 { 0463 QPointer<LocationDialog> ld = new LocationDialog(this); 0464 if (ld->exec() == QDialog::Accepted) 0465 { 0466 GeoLocation *newGeo = ld->selectedCity(); 0467 if (newGeo) 0468 { 0469 geo = newGeo; 0470 scUI->LocationButton->setText(geo->fullName()); 0471 } 0472 } 0473 delete ld; 0474 0475 scUI->CalendarView->setHorizon(); 0476 slotFillCalendar(); 0477 } 0478 0479 GeoLocation *SkyCalendar::get_geo() 0480 { 0481 return geo; 0482 }