1/***************************************************************************
2 moonphasecalendarwidget.cpp - K Desktop Planetarium
3 -------------------
4 begin : Sat Jun 26 2010
5 copyright : (C) 2010 by Akarsh Simha
6 email : akarshsimha@gmail.com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "moonphasecalendarwidget.h"
19
20#include "skyobjects/ksmoon.h"
21#include "skyobjects/kssun.h"
22#include "skyobjects/ksplanet.h"
23#include "ksnumbers.h"
24#include "kstarsdatetime.h"
25#include "ksutils.h"
26#include "texturemanager.h"
27
28#include <kcolorscheme.h>
29#include <kglobal.h>
30#include <kglobalsettings.h>
31#include <kdebug.h>
32#include <kcalendarsystem.h>
33
34#include <QActionEvent>
35#include <QPainter>
36#include <QStyle>
37#include <QtGui/QStyleOptionViewItem>
38
39#include <cmath>
40
41MoonPhaseCalendar::MoonPhaseCalendar( KSMoon &moon, KSSun &sun, QWidget *parent ) :
42 KDateTable(parent),
43 m_Moon(moon), m_Sun(sun)
44{
45 // Populate moon images from disk into the hash
46 numDayColumns = calendar()->daysInWeek( QDate::currentDate() );
47 numWeekRows = 7;
48 imagesLoaded = false;
49 // TODO: Set geometry.
50}
51
52MoonPhaseCalendar::~MoonPhaseCalendar() {
53}
54
55QSize MoonPhaseCalendar::sizeHint() const {
56 const int suggestedMoonImageSize = 50;
57 return QSize( qRound( ( suggestedMoonImageSize + 2 ) * numDayColumns ),
58 ( qRound( suggestedMoonImageSize + 4 + 12 ) * numWeekRows ) ); // FIXME: Using hard-coded fontsize
59}
60
61void MoonPhaseCalendar::loadImages() {
62 computeMoonImageSize();
63 kDebug() << "Loading moon images. MoonImageSize = " << MoonImageSize;
64 for( int i = 0; i < 36; ++i ) {
65 QString imName = QString().sprintf("moon%02d", i);
66 m_Images[i] =
67 QPixmap::fromImage( TextureManager::getImage( imName ) ).
68 scaled( MoonImageSize, MoonImageSize, Qt::KeepAspectRatio, Qt::SmoothTransformation );
69 }
70 imagesLoaded = true;
71}
72
73void MoonPhaseCalendar::computeMoonImageSize() {
74 cellWidth = width() / ( double ) numDayColumns;
75 cellHeight = height() / ( double ) numWeekRows;
76 kDebug() << cellWidth << cellHeight;
77 MoonImageSize = ( (cellWidth > cellHeight - 12) ? cellHeight - 12 : cellWidth ) - 2; // FIXME: Using hard-coded fontsize
78}
79
80void MoonPhaseCalendar::setGeometry( int, int, int, int ) {
81 imagesLoaded = false;
82}
83
84void MoonPhaseCalendar::setGeometry( const QRect &r ) {
85 setGeometry( r.x(), r.y(), r.width(), r.height() ); // FIXME: +1 / -1 pixel compensation. Not required at the moment.
86}
87
88
89void MoonPhaseCalendar::paintEvent( QPaintEvent *e )
90{
91 QPainter p( this );
92 if( !imagesLoaded )
93 loadImages();
94 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::View);
95 const QRect &rectToUpdate = e->rect();
96 int leftCol = ( int )std::floor( rectToUpdate.left() / cellWidth );
97 int topRow = ( int )std::floor( rectToUpdate.top() / cellHeight );
98 int rightCol = ( int )std::ceil( rectToUpdate.right() / cellWidth );
99 int bottomRow = ( int )std::ceil( rectToUpdate.bottom() / cellHeight );
100 bottomRow = qMin( bottomRow, numWeekRows - 1 );
101 rightCol = qMin( rightCol, numDayColumns - 1 );
102 p.translate( leftCol * cellWidth, topRow * cellHeight );
103 for ( int i = leftCol; i <= rightCol; ++i ) {
104 for ( int j = topRow; j <= bottomRow; ++j ) {
105 this->paintCell( &p, j, i, colorScheme );
106 p.translate( 0, cellHeight );
107 }
108 p.translate( cellWidth, 0 );
109 p.translate( 0, -cellHeight * ( bottomRow - topRow + 1 ) );
110 }
111 p.end();
112}
113
114
115void MoonPhaseCalendar::paintCell( QPainter *painter, int row, int col, const KColorScheme &colorScheme )
116{
117 double w = cellWidth - 1;
118 double h = cellHeight - 1;
119 QRectF cell = QRectF( 0, 0, w, h );
120 QString cellText;
121 QPen pen;
122 QColor cellBackgroundColor, cellTextColor;
123 QFont cellFont = KGlobalSettings::generalFont();
124 bool workingDay = false;
125 int cellWeekDay, pos;
126
127 //Calculate the position of the cell in the grid
128 pos = numDayColumns * ( row - 1 ) + col;
129
130 //Calculate what day of the week the cell is
131 cellWeekDay = col + calendar()->weekStartDay();
132 if ( cellWeekDay > numDayColumns ) {
133 cellWeekDay -= numDayColumns;
134 }
135
136 //See if cell day is normally a working day
137 if ( KGlobal::locale()->workingWeekStartDay() <= KGlobal::locale()->workingWeekEndDay() ) {
138 workingDay = cellWeekDay >= KGlobal::locale()->workingWeekStartDay()
139 && cellWeekDay <= KGlobal::locale()->workingWeekEndDay();
140 } else {
141 workingDay = cellWeekDay >= KGlobal::locale()->workingWeekStartDay()
142 || cellWeekDay <= KGlobal::locale()->workingWeekEndDay();
143 }
144
145 if( row == 0 ) {
146
147 //We are drawing a header cell
148
149 //If not a normal working day, then use "do not work today" color
150 if ( workingDay ) {
151 cellTextColor = palette().color(QPalette::WindowText);
152 } else {
153 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::Window);
154 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color();
155 }
156 cellBackgroundColor = palette().color(QPalette::Window);
157
158 //Set the text to the short day name and bold it
159 cellFont.setBold( true );
160 cellText = calendar()->weekDayName( cellWeekDay, KCalendarSystem::ShortDayName );
161
162 } else {
163
164 //We are drawing a day cell
165
166 //Calculate the date the cell represents
167 QDate cellDate = dateFromPos( pos );
168
169 bool validDay = calendar()->isValid( cellDate );
170
171 // Draw the day number in the cell, if the date is not valid then we don't want to show it
172 if ( validDay ) {
173 cellText = calendar()->dayString( cellDate, KCalendarSystem::ShortFormat );
174 } else {
175 cellText = "";
176 }
177
178 if( ! validDay || calendar()->month( cellDate ) != calendar()->month( date() ) ) {
179 // we are either
180 // ° painting an invalid day
181 // ° painting a day of the previous month or
182 // ° painting a day of the following month or
183 cellBackgroundColor = palette().color(backgroundRole());
184 cellTextColor = colorScheme.foreground(KColorScheme::InactiveText).color();
185 } else {
186 //Paint a day of the current month
187
188 // Background Colour priorities will be (high-to-low):
189 // * Selected Day Background Colour
190 // * Customized Day Background Colour
191 // * Normal Day Background Colour
192
193 // Background Shape priorities will be (high-to-low):
194 // * Customized Day Shape
195 // * Normal Day Shape
196
197 // Text Colour priorities will be (high-to-low):
198 // * Customized Day Colour
199 // * Day of Pray Colour (Red letter)
200 // * Selected Day Colour
201 // * Normal Day Colour
202
203 //Determine various characteristics of the cell date
204 bool selectedDay = ( cellDate == date() );
205 bool currentDay = ( cellDate == QDate::currentDate() );
206 bool dayOfPray = ( calendar()->dayOfWeek( cellDate ) == KGlobal::locale()->weekDayOfPray() );
207
208 //Default values for a normal cell
209 cellBackgroundColor = palette().color( backgroundRole() );
210 cellTextColor = palette().color( foregroundRole() );
211
212 // If we are drawing the current date, then draw it bold and active
213 if ( currentDay ) {
214 cellFont.setBold( true );
215 cellTextColor = colorScheme.foreground(KColorScheme::ActiveText).color();
216 }
217
218 // if we are drawing the day cell currently selected in the table
219 if ( selectedDay ) {
220 // set the background to highlighted
221 cellBackgroundColor = palette().color( QPalette::Highlight );
222 cellTextColor = palette().color( QPalette::HighlightedText );
223 }
224
225 //If the cell day is the day of religious observance, then always color text red unless Custom overrides
226 if ( dayOfPray ) {
227 KColorScheme colorScheme(palette().currentColorGroup(),
228 selectedDay ? KColorScheme::Selection : KColorScheme::View);
229 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color();
230 }
231
232 }
233 }
234
235 //Draw the background
236 if (row == 0) {
237 painter->setPen( cellBackgroundColor );
238 painter->setBrush( cellBackgroundColor );
239 painter->drawRect( cell );
240 } else if (cellBackgroundColor != palette().color(backgroundRole())) {
241 QStyleOptionViewItemV4 opt;
242 opt.initFrom(this);
243 opt.rect = cell.toRect();
244 if (cellBackgroundColor != palette().color(backgroundRole())) {
245 opt.palette.setBrush(QPalette::Highlight, cellBackgroundColor);
246 opt.state |= QStyle::State_Selected;
247 }
248 if (false && opt.state & QStyle::State_Enabled) {
249 opt.state |= QStyle::State_MouseOver;
250 } else {
251 opt.state &= ~QStyle::State_MouseOver;
252 }
253 opt.showDecorationSelected = true;
254 opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
255 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, this);
256 }
257
258 if( row != 0 ) {
259 // Paint the moon phase
260 QDate cellDate = dateFromPos( pos );
261 if( calendar()->isValid( cellDate ) ) {
262 int iPhase = computeMoonPhase( KStarsDateTime( cellDate, QTime(0, 0, 0) ) );
263 QRect drawRect = cell.toRect();
264 painter->drawPixmap( ( drawRect.width() - MoonImageSize )/2, 12 + (( drawRect.height() - 12 ) - MoonImageSize)/2, m_Images[ iPhase ] ); // FIXME: Using hard coded fon
265// + painter
266 // painter->drawPixmap( ( drawRect.width() - MoonImageSize )/2,
267 // 12 + (( drawRect.height() - 12 ) - MoonImageSize)/2,
268 // m_Images[ iPhase ] );
269 // FIXME: Using hard coded fontsize
270 // kDebug() << "Drew moon image " << iPhase;
271 }
272 }
273
274 //Draw the text
275 painter->setPen( cellTextColor );
276 painter->setFont( cellFont );
277 painter->drawText( cell, (row == 0) ? Qt::AlignCenter : (Qt::AlignTop | Qt::AlignHCenter), cellText, &cell );
278
279 //Draw the base line
280 if (row == 0) {
281 painter->setPen( palette().color(foregroundRole()) );
282 painter->drawLine( QPointF( 0, h ), QPointF( w, h ) );
283 }
284
285 // If the day cell we just drew is bigger than the current max cell sizes,
286 // then adjust the max to the current cell
287
288 /*
289 if ( cell.width() > d->maxCell.width() ) d->maxCell.setWidth( cell.width() );
290 if ( cell.height() > d->maxCell.height() ) d->maxCell.setHeight( cell.height() );
291 */
292}
293
294unsigned short MoonPhaseCalendar::computeMoonPhase( const KStarsDateTime &date ) {
295
296 KSNumbers num( date.djd() );
297 KSPlanet earth( I18N_NOOP( "Earth" ), QString(), QColor( "white" ), 12756.28 /*diameter in km*/ );
298 earth.findPosition( &num );
299
300 m_Sun.findPosition( &num, 0, 0, &earth ); // Find position is overkill for this purpose. Wonder if it is worth making findGeocentricPosition public instead of protected.
301 m_Moon.findGeocentricPosition( &num, &earth );
302
303 m_Moon.findPhase( &m_Sun );
304
305 return m_Moon.getIPhase();
306
307}
308