File indexing completed on 2024-07-07 07:09:17

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 }