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 | |
41 | MoonPhaseCalendar::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 | |
52 | MoonPhaseCalendar::~MoonPhaseCalendar() { |
53 | } |
54 | |
55 | QSize 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 | |
61 | void 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 | |
73 | void 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 | |
80 | void MoonPhaseCalendar::setGeometry( int, int, int, int ) { |
81 | imagesLoaded = false; |
82 | } |
83 | |
84 | void 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 | |
89 | void 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 | |
115 | void 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 | |
294 | unsigned 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 | |