File indexing completed on 2025-02-16 06:41:07
0001 /* 0002 SPDX-FileCopyrightText: 2010 Akarsh Simha <akarshsimha@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "moonphasecalendarwidget.h" 0008 0009 #include "ksnumbers.h" 0010 #include "kstarsdatetime.h" 0011 #include "ksutils.h" 0012 #include "texturemanager.h" 0013 #include "skyobjects/ksmoon.h" 0014 #include "skyobjects/ksplanet.h" 0015 #include "skyobjects/kssun.h" 0016 0017 #include <kcalendarsystem.h> 0018 #include <kcolorscheme.h> 0019 #include <kglobal.h> 0020 #include <kglobalsettings.h> 0021 #include <KLocale> 0022 0023 #include <QActionEvent> 0024 #include <QDebug> 0025 #include <QFontDatabase> 0026 #include <QPainter> 0027 #include <QStyle> 0028 #include <QtGui/QStyleOptionViewItem> 0029 0030 #include <cmath> 0031 0032 MoonPhaseCalendar::MoonPhaseCalendar(KSMoon &moon, KSSun &sun, QWidget *parent) 0033 : KDateTable(parent), m_Moon(moon), m_Sun(sun) 0034 { 0035 // Populate moon images from disk into the hash 0036 numDayColumns = calendar()->daysInWeek(QDate::currentDate()); 0037 numWeekRows = 7; 0038 imagesLoaded = false; 0039 // TODO: Set geometry. 0040 } 0041 0042 MoonPhaseCalendar::~MoonPhaseCalendar() 0043 { 0044 } 0045 0046 QSize MoonPhaseCalendar::sizeHint() const 0047 { 0048 const int suggestedMoonImageSize = 50; 0049 return QSize(qRound((suggestedMoonImageSize + 2) * numDayColumns), 0050 (qRound(suggestedMoonImageSize + 4 + 12) * numWeekRows)); // FIXME: Using hard-coded fontsize 0051 } 0052 0053 void MoonPhaseCalendar::loadImages() 0054 { 0055 computeMoonImageSize(); 0056 qDebug() << "Loading moon images. MoonImageSize = " << MoonImageSize; 0057 for (int i = 0; i < 36; ++i) 0058 { 0059 QString imName = QString().sprintf("moon%02d", i); 0060 m_Images[i] = QPixmap::fromImage(TextureManager::getImage(imName)) 0061 .scaled(MoonImageSize, MoonImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0062 } 0063 imagesLoaded = true; 0064 } 0065 0066 void MoonPhaseCalendar::computeMoonImageSize() 0067 { 0068 cellWidth = width() / (double)numDayColumns; 0069 cellHeight = height() / (double)numWeekRows; 0070 qDebug() << cellWidth << cellHeight; 0071 MoonImageSize = 0072 ((cellWidth > cellHeight - 12) ? cellHeight - 12 : cellWidth) - 2; // FIXME: Using hard-coded fontsize 0073 } 0074 0075 void MoonPhaseCalendar::setGeometry(int, int, int, int) 0076 { 0077 imagesLoaded = false; 0078 } 0079 0080 void MoonPhaseCalendar::setGeometry(const QRect &r) 0081 { 0082 setGeometry(r.x(), r.y(), r.width(), r.height()); // FIXME: +1 / -1 pixel compensation. Not required at the moment. 0083 } 0084 0085 void MoonPhaseCalendar::paintEvent(QPaintEvent *e) 0086 { 0087 QPainter p(this); 0088 if (!imagesLoaded) 0089 loadImages(); 0090 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::View); 0091 const QRect &rectToUpdate = e->rect(); 0092 int leftCol = (int)std::floor(rectToUpdate.left() / cellWidth); 0093 int topRow = (int)std::floor(rectToUpdate.top() / cellHeight); 0094 int rightCol = (int)std::ceil(rectToUpdate.right() / cellWidth); 0095 int bottomRow = (int)std::ceil(rectToUpdate.bottom() / cellHeight); 0096 bottomRow = qMin(bottomRow, numWeekRows - 1); 0097 rightCol = qMin(rightCol, numDayColumns - 1); 0098 p.translate(leftCol * cellWidth, topRow * cellHeight); 0099 for (int i = leftCol; i <= rightCol; ++i) 0100 { 0101 for (int j = topRow; j <= bottomRow; ++j) 0102 { 0103 this->paintCell(&p, j, i, colorScheme); 0104 p.translate(0, cellHeight); 0105 } 0106 p.translate(cellWidth, 0); 0107 p.translate(0, -cellHeight * (bottomRow - topRow + 1)); 0108 } 0109 p.end(); 0110 } 0111 0112 void MoonPhaseCalendar::paintCell(QPainter *painter, int row, int col, const KColorScheme &colorScheme) 0113 { 0114 double w = cellWidth - 1; 0115 double h = cellHeight - 1; 0116 QRectF cell = QRectF(0, 0, w, h); 0117 QString cellText; 0118 QPen pen; 0119 QColor cellBackgroundColor, cellTextColor; 0120 QFont cellFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 0121 bool workingDay = false; 0122 int cellWeekDay, pos; 0123 0124 //Calculate the position of the cell in the grid 0125 pos = numDayColumns * (row - 1) + col; 0126 0127 //Calculate what day of the week the cell is 0128 cellWeekDay = col + calendar()->weekStartDay(); 0129 if (cellWeekDay > numDayColumns) 0130 { 0131 cellWeekDay -= numDayColumns; 0132 } 0133 0134 //See if cell day is normally a working day 0135 if (KLocale::global()->workingWeekStartDay() <= KLocale::global()->workingWeekEndDay()) 0136 { 0137 workingDay = cellWeekDay >= KLocale::global()->workingWeekStartDay() && 0138 cellWeekDay <= KLocale::global()->workingWeekEndDay(); 0139 } 0140 else 0141 { 0142 workingDay = cellWeekDay >= KLocale::global()->workingWeekStartDay() || 0143 cellWeekDay <= KLocale::global()->workingWeekEndDay(); 0144 } 0145 0146 if (row == 0) 0147 { 0148 //We are drawing a header cell 0149 0150 //If not a normal working day, then use "do not work today" color 0151 if (workingDay) 0152 { 0153 cellTextColor = palette().color(QPalette::WindowText); 0154 } 0155 else 0156 { 0157 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::Window); 0158 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color(); 0159 } 0160 cellBackgroundColor = palette().color(QPalette::Window); 0161 0162 //Set the text to the short day name and bold it 0163 cellFont.setBold(true); 0164 cellText = calendar()->weekDayName(cellWeekDay, KCalendarSystem::ShortDayName); 0165 } 0166 else 0167 { 0168 //We are drawing a day cell 0169 0170 //Calculate the date the cell represents 0171 QDate cellDate = dateFromPos(pos); 0172 0173 bool validDay = calendar()->isValid(cellDate); 0174 0175 // Draw the day number in the cell, if the date is not valid then we don't want to show it 0176 if (validDay) 0177 { 0178 cellText = calendar()->dayString(cellDate, KCalendarSystem::ShortFormat); 0179 } 0180 else 0181 { 0182 cellText = ""; 0183 } 0184 0185 if (!validDay || calendar()->month(cellDate) != calendar()->month(date())) 0186 { 0187 // we are either 0188 // ° painting an invalid day 0189 // ° painting a day of the previous month or 0190 // ° painting a day of the following month or 0191 cellBackgroundColor = palette().color(backgroundRole()); 0192 cellTextColor = colorScheme.foreground(KColorScheme::InactiveText).color(); 0193 } 0194 else 0195 { 0196 //Paint a day of the current month 0197 0198 // Background Colour priorities will be (high-to-low): 0199 // * Selected Day Background Colour 0200 // * Customized Day Background Colour 0201 // * Normal Day Background Colour 0202 0203 // Background Shape priorities will be (high-to-low): 0204 // * Customized Day Shape 0205 // * Normal Day Shape 0206 0207 // Text Colour priorities will be (high-to-low): 0208 // * Customized Day Colour 0209 // * Day of Pray Colour (Red letter) 0210 // * Selected Day Colour 0211 // * Normal Day Colour 0212 0213 //Determine various characteristics of the cell date 0214 bool selectedDay = (cellDate == date()); 0215 bool currentDay = (cellDate == QDate::currentDate()); 0216 bool dayOfPray = (calendar()->dayOfWeek(cellDate) == KLocale::global()->weekDayOfPray()); 0217 0218 //Default values for a normal cell 0219 cellBackgroundColor = palette().color(backgroundRole()); 0220 cellTextColor = palette().color(foregroundRole()); 0221 0222 // If we are drawing the current date, then draw it bold and active 0223 if (currentDay) 0224 { 0225 cellFont.setBold(true); 0226 cellTextColor = colorScheme.foreground(KColorScheme::ActiveText).color(); 0227 } 0228 0229 // if we are drawing the day cell currently selected in the table 0230 if (selectedDay) 0231 { 0232 // set the background to highlighted 0233 cellBackgroundColor = palette().color(QPalette::Highlight); 0234 cellTextColor = palette().color(QPalette::HighlightedText); 0235 } 0236 0237 //If the cell day is the day of religious observance, then always color text red unless Custom overrides 0238 if (dayOfPray) 0239 { 0240 KColorScheme colorScheme(palette().currentColorGroup(), 0241 selectedDay ? KColorScheme::Selection : KColorScheme::View); 0242 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color(); 0243 } 0244 } 0245 } 0246 0247 //Draw the background 0248 if (row == 0) 0249 { 0250 painter->setPen(cellBackgroundColor); 0251 painter->setBrush(cellBackgroundColor); 0252 painter->drawRect(cell); 0253 } 0254 else if (cellBackgroundColor != palette().color(backgroundRole())) 0255 { 0256 QStyleOptionViewItemV4 opt; 0257 opt.initFrom(this); 0258 opt.rect = cell.toRect(); 0259 if (cellBackgroundColor != palette().color(backgroundRole())) 0260 { 0261 opt.palette.setBrush(QPalette::Highlight, cellBackgroundColor); 0262 opt.state |= QStyle::State_Selected; 0263 } 0264 if (false && opt.state & QStyle::State_Enabled) 0265 { 0266 opt.state |= QStyle::State_MouseOver; 0267 } 0268 else 0269 { 0270 opt.state &= ~QStyle::State_MouseOver; 0271 } 0272 opt.showDecorationSelected = true; 0273 opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne; 0274 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, this); 0275 } 0276 0277 if (row != 0) 0278 { 0279 // Paint the moon phase 0280 QDate cellDate = dateFromPos(pos); 0281 if (calendar()->isValid(cellDate)) 0282 { 0283 int iPhase = computeMoonPhase(KStarsDateTime(cellDate, QTime(0, 0, 0))); 0284 QRect drawRect = cell.toRect(); 0285 painter->drawPixmap((drawRect.width() - MoonImageSize) / 2, 0286 12 + ((drawRect.height() - 12) - MoonImageSize) / 2, 0287 m_Images[iPhase]); // FIXME: Using hard coded fon 0288 // + painter 0289 // painter->drawPixmap( ( drawRect.width() - MoonImageSize )/2, 0290 // 12 + (( drawRect.height() - 12 ) - MoonImageSize)/2, 0291 // m_Images[ iPhase ] ); 0292 // FIXME: Using hard coded fontsize 0293 // qDebug() << "Drew moon image " << iPhase; 0294 } 0295 } 0296 0297 //Draw the text 0298 painter->setPen(cellTextColor); 0299 painter->setFont(cellFont); 0300 painter->drawText(cell, (row == 0) ? Qt::AlignCenter : (Qt::AlignTop | Qt::AlignHCenter), cellText, &cell); 0301 0302 //Draw the base line 0303 if (row == 0) 0304 { 0305 painter->setPen(palette().color(foregroundRole())); 0306 painter->drawLine(QPointF(0, h), QPointF(w, h)); 0307 } 0308 0309 // If the day cell we just drew is bigger than the current max cell sizes, 0310 // then adjust the max to the current cell 0311 0312 /* 0313 if ( cell.width() > d->maxCell.width() ) d->maxCell.setWidth( cell.width() ); 0314 if ( cell.height() > d->maxCell.height() ) d->maxCell.setHeight( cell.height() ); 0315 */ 0316 } 0317 0318 unsigned short MoonPhaseCalendar::computeMoonPhase(const KStarsDateTime &date) 0319 { 0320 KSNumbers num(date.djd()); 0321 KSPlanet earth(I18N_NOOP("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); 0322 earth.findPosition(&num); 0323 0324 m_Sun.findPosition( 0325 &num, 0, 0, 0326 &earth); // Find position is overkill for this purpose. Wonder if it is worth making findGeocentricPosition public instead of protected. 0327 m_Moon.findGeocentricPosition(&num, &earth); 0328 0329 m_Moon.findPhase(&m_Sun); 0330 0331 return m_Moon.getIPhase(); 0332 }