2
// Copyright (C) 2009 GNOME Do
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public License
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
using System.Collections.Generic;
25
using Docky.CairoHelper;
29
namespace WeatherDocklet
32
/// A painter that displays information about weather forecasts.
34
public class WeatherPainter : PagingDockPainter
37
/// The color to draw most text in.
39
static readonly Cairo.Color colorTitle = new Cairo.Color (0.627, 0.627, 0.627, 1);
42
/// The color to draw high temperatures in.
44
static readonly Cairo.Color colorHigh = new Cairo.Color (0.945, 0.431, 0.431, 1);
47
/// The color to draw low temperatures in.
49
static readonly Cairo.Color colorLow = new Cairo.Color (0.427, 0.714, 0.945, 1);
52
/// Creates a new weather painter object.
54
public WeatherPainter () : base (3)
58
public override int MinimumHeight {
64
public override int MinimumWidth {
66
return 2 * BUTTON_SIZE + 2 * WeatherController.Weather.ForecastDays * Math.Max (MinimumHeight, Allocation.Height);
70
#region IDockPainter implementation
72
protected override void DrawPageOnSurface (int page, DockySurface surface)
78
DrawCurrentCondition (surface.Context);
82
DrawForecast (surface.Context);
86
DrawTempGraph (surface.Context);
94
/// Paints an overview of the forecast including high/low temps and a condition icon.
97
/// A <see cref="Cairo.Context"/> to do the painting.
99
void DrawForecast (Cairo.Context cr)
101
int xOffset = BUTTON_SIZE;
103
Pango.Layout layout = DockServices.Drawing.ThemedPangoLayout ();
104
Pango.Rectangle inkRect, logicalRect;
106
layout.FontDescription = new Gtk.Style().FontDescription;
107
layout.FontDescription.Weight = Pango.Weight.Bold;
108
layout.Ellipsize = Pango.EllipsizeMode.None;
109
layout.Width = Pango.Units.FromPixels (Allocation.Height);
111
for (int day = 0; day < WeatherController.Weather.ForecastDays; day++)
113
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 5));
115
cr.Color = colorTitle;
116
layout.SetText (string.Format ("{0}", WeatherForecast.DayShortName (WeatherController.Weather.Forecasts [day].dow)));
117
layout.GetPixelExtents (out inkRect, out logicalRect);
118
cr.MoveTo (xOffset + (Allocation.Height - inkRect.Width) / 2, 0);
119
Pango.CairoHelper.LayoutPath (cr, layout);
122
cr.Color = colorHigh;
123
layout.SetText (string.Format ("{0}{1}", WeatherController.Weather.Forecasts [day].high, WeatherUnits.TempUnit));
124
layout.GetPixelExtents (out inkRect, out logicalRect);
125
cr.MoveTo (xOffset + (Allocation.Height - inkRect.Width) / 2, Allocation.Height / 2 - logicalRect.Height / 2);
126
Pango.CairoHelper.LayoutPath (cr, layout);
130
layout.SetText (string.Format ("{0}{1}", WeatherController.Weather.Forecasts [day].low, WeatherUnits.TempUnit));
131
layout.GetPixelExtents (out inkRect, out logicalRect);
132
cr.MoveTo (xOffset + (Allocation.Height - inkRect.Width) / 2, Allocation.Height - logicalRect.Height);
133
Pango.CairoHelper.LayoutPath (cr, layout);
136
using (Gdk.Pixbuf pbuf = DockServices.Drawing.LoadIcon (WeatherController.Weather.Forecasts [day].image, Allocation.Height - 5))
138
Gdk.CairoHelper.SetSourcePixbuf (cr, pbuf, xOffset + Allocation.Height + 2, 5);
139
cr.PaintWithAlpha (WeatherController.Weather.Forecasts [day].chanceOf ? .6 : 1);
142
if (WeatherController.Weather.Forecasts [day].chanceOf)
144
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 2));
146
layout.SetText ("?");
148
layout.GetPixelExtents (out inkRect, out logicalRect);
149
cr.MoveTo (xOffset + Allocation.Height + (Allocation.Height - inkRect.Width) / 2, Allocation.Height / 2 - logicalRect.Height / 2);
152
cr.Color = new Cairo.Color (0, 0, 0, 0.3);
153
Pango.CairoHelper.LayoutPath (cr, layout);
154
cr.StrokePreserve ();
156
cr.Color = new Cairo.Color (1, 1, 1, .6);
160
xOffset += 2 * (Allocation.Height);
165
/// Paints the forecast temperatures as a chart.
167
/// <param name="cr">
168
/// A <see cref="Cairo.Context"/> to do the painting.
170
void DrawTempGraph (Cairo.Context cr)
172
int max = -1000, min = 1000;
174
for (int day = 0; day < WeatherController.Weather.ForecastDays; day++)
176
if (WeatherController.Weather.Forecasts [day].high > max)
177
max = WeatherController.Weather.Forecasts [day].high;
178
if (WeatherController.Weather.Forecasts [day].low > max)
179
max = WeatherController.Weather.Forecasts [day].low;
180
if (WeatherController.Weather.Forecasts [day].high < min)
181
min = WeatherController.Weather.Forecasts [day].high;
182
if (WeatherController.Weather.Forecasts [day].low < min)
183
min = WeatherController.Weather.Forecasts [day].low;
189
Pango.Layout layout = DockServices.Drawing.ThemedPangoLayout ();
190
Pango.Rectangle inkRect, logicalRect;
192
layout.FontDescription = new Gtk.Style().FontDescription;
193
layout.FontDescription.Weight = Pango.Weight.Bold;
194
layout.Ellipsize = Pango.EllipsizeMode.None;
195
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 5));
198
layout.Width = Pango.Units.FromPixels (Allocation.Height);
199
cr.Color = colorHigh;
200
layout.SetText (string.Format ("{0}{1}", max, WeatherUnits.TempUnit));
201
layout.GetPixelExtents (out inkRect, out logicalRect);
202
cr.MoveTo (Allocation.Width - Allocation.Height + (Allocation.Height - inkRect.Width) / 2 - BUTTON_SIZE, Allocation.Height / 6 - logicalRect.Height / 2);
203
Pango.CairoHelper.LayoutPath (cr, layout);
207
layout.SetText (string.Format ("{0}{1}", min, WeatherUnits.TempUnit));
208
layout.GetPixelExtents (out inkRect, out logicalRect);
209
cr.MoveTo (Allocation.Width - Allocation.Height + (Allocation.Height - inkRect.Width) / 2 - BUTTON_SIZE, Allocation.Height * 6 / 9 - logicalRect.Height / 2);
210
Pango.CairoHelper.LayoutPath (cr, layout);
214
layout.Width = Pango.Units.FromPixels (2 * Allocation.Height);
216
cr.Color = colorTitle;
217
for (int day = 0; day < WeatherController.Weather.ForecastDays; day++)
219
layout.SetText (WeatherForecast.DayShortName (WeatherController.Weather.Forecasts [day].dow));
220
layout.GetPixelExtents (out inkRect, out logicalRect);
221
cr.MoveTo (BUTTON_SIZE + day * Allocation.Height * 2 + (Allocation.Height - inkRect.Width) / 2, Allocation.Height * 8 / 9 - logicalRect.Height / 2);
222
Pango.CairoHelper.LayoutPath (cr, layout);
228
cr.Color = new Cairo.Color (0.627, 0.627, 0.627, .8);
230
cr.LineCap = LineCap.Round;
233
for (int line = 0; line < lines - 1; line++) {
234
cr.MoveTo (BUTTON_SIZE + Allocation.Height / 4, 4.5 + Allocation.Height * line / lines);
235
cr.LineTo (BUTTON_SIZE + (2 * WeatherController.Weather.ForecastDays - 1) * Allocation.Height - Allocation.Height / 4, 4.5 + Allocation.Height * line / lines);
238
for (int line = 0; ; line++) {
239
double x = BUTTON_SIZE + Allocation.Height / 2 + line * 2 * Allocation.Height - 0.5;
240
if (x >= BUTTON_SIZE + (2 * WeatherController.Weather.ForecastDays - 1) * Allocation.Height - Allocation.Height / 4)
243
cr.LineTo (x, 4.5 + Allocation.Height * (lines - 2) / lines);
249
double height = ((double) Allocation.Height * 2 / 3 - 5) / (max - min);
252
cr.Color = colorHigh;
253
cr.MoveTo (BUTTON_SIZE + Allocation.Height / 2, 5 + height * (max - WeatherController.Weather.Forecasts [0].high));
254
for (int day = 1; day < WeatherController.Weather.ForecastDays; day++)
255
cr.LineTo (BUTTON_SIZE + day * Allocation.Height * 2 + Allocation.Height / 2, 5 + height * (max - WeatherController.Weather.Forecasts [day].high));
260
cr.MoveTo (BUTTON_SIZE + Allocation.Height / 2, 5 + height * (max - WeatherController.Weather.Forecasts [0].low));
261
for (int day = 1; day < WeatherController.Weather.ForecastDays; day++)
262
cr.LineTo (BUTTON_SIZE + day * Allocation.Height * 2 + Allocation.Height / 2, 5 + height * (max - WeatherController.Weather.Forecasts [day].low));
266
for (int day = 0; day < WeatherController.Weather.ForecastDays; day++)
267
DrawDataPoint (cr, Allocation.Height, height, max, day, WeatherController.Weather.Forecasts [day].high);
270
for (int day = 0; day < WeatherController.Weather.ForecastDays; day++)
271
DrawDataPoint (cr, Allocation.Height, height, max, day, WeatherController.Weather.Forecasts [day].low);
274
void DrawDataPoint (Cairo.Context cr, int cellWidth, double height, int max, int day, int temp)
276
cr.Color = new Cairo.Color (0, 0, 0, 0.4);
277
cr.Arc (BUTTON_SIZE + day * cellWidth * 2 + cellWidth / 2 + 2, 7 + height * (max - temp), 3, 0, 2 * Math.PI);
280
cr.Color = colorTitle;
281
cr.Arc (BUTTON_SIZE + day * cellWidth * 2 + cellWidth / 2, 5 + height * (max - temp), 3, 0, 2 * Math.PI);
286
/// Paints the current condition.
288
/// <param name="cr">
289
/// A <see cref="Cairo.Context"/> to do the painting.
291
void DrawCurrentCondition (Cairo.Context cr)
293
Pango.Layout layout = DockServices.Drawing.ThemedPangoLayout ();
294
Pango.Rectangle inkRect, logicalRect;
296
layout.FontDescription = new Gtk.Style().FontDescription;
297
layout.FontDescription.Weight = Pango.Weight.Bold;
298
layout.Ellipsize = Pango.EllipsizeMode.None;
299
layout.Width = Pango.Units.FromPixels ((int) ((Allocation.Width - 4 * BUTTON_SIZE) / 4.5));
301
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 3));
303
cr.Color = new Cairo.Color (1, 1, 1, 1);
305
layout.SetText (WeatherController.Weather.City);
306
layout.GetPixelExtents (out inkRect, out logicalRect);
307
cr.MoveTo (2 * BUTTON_SIZE, Allocation.Height / 3.5 - logicalRect.Height / 2);
308
Pango.CairoHelper.LayoutPath (cr, layout);
311
DrawCondition (cr, WeatherController.Weather.SupportsFeelsLike ? 1 : 2, 2, Catalog.GetString ("Humidity"), WeatherController.Weather.Humidity);
313
DrawCondition (cr, 2, 1, Catalog.GetString ("Temp"), WeatherController.Weather.Temp + WeatherUnits.TempUnit);
314
if (WeatherController.Weather.SupportsFeelsLike)
315
DrawCondition (cr, 2, 2, Catalog.GetString ("Feels Like"), WeatherController.Weather.FeelsLike + WeatherUnits.TempUnit);
317
DrawCondition (cr, 3, 1, Catalog.GetString ("Wind"), WeatherController.Weather.Wind + " " + WeatherUnits.WindUnit);
318
DrawCondition (cr, 3, 2, Catalog.GetString ("Direction"), WeatherController.Weather.WindDirection);
320
DrawCondition (cr, 4, 1, Catalog.GetString ("Sunrise"), WeatherController.Weather.SunRise.ToShortTimeString ());
321
DrawCondition (cr, 4, 2, Catalog.GetString ("Sunset"), WeatherController.Weather.SunSet.ToShortTimeString ());
324
void DrawCondition (Cairo.Context cr, int column, int row, string label, string val)
326
int xWidth = (Allocation.Width - 4 * BUTTON_SIZE) / 8;
328
Pango.Layout layout = DockServices.Drawing.ThemedPangoLayout ();
329
Pango.Rectangle inkRect, logicalRect;
331
layout.FontDescription = new Gtk.Style().FontDescription;
332
layout.FontDescription.Weight = Pango.Weight.Bold;
333
layout.Ellipsize = Pango.EllipsizeMode.None;
334
layout.Width = Pango.Units.FromPixels (xWidth);
336
if (WeatherController.Weather.ForecastDays < 6)
337
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 6.5));
339
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels ((int) (Allocation.Height / 4.5));
341
int xOffset = 2 * BUTTON_SIZE + (column - 1) * 2 * xWidth;
342
int yOffset = row == 1 ? Allocation.Height : (int) (Allocation.Height * 2.5);
343
yOffset = (int) (yOffset / 3.5);
345
cr.Color = new Cairo.Color (1, 1, 1, 0.9);
346
layout.SetText (label);
347
layout.GetPixelExtents (out inkRect, out logicalRect);
348
cr.MoveTo (xOffset + (xWidth - inkRect.Width) / 2, yOffset - logicalRect.Height / 2);
349
Pango.CairoHelper.LayoutPath (cr, layout);
352
cr.Color = new Cairo.Color (1, 1, 1, 0.7);
353
layout.SetText (val);
354
layout.GetPixelExtents (out inkRect, out logicalRect);
355
cr.MoveTo (xOffset + xWidth + (xWidth - inkRect.Width) / 2, yOffset - logicalRect.Height / 2);
356
Pango.CairoHelper.LayoutPath (cr, layout);
361
/// Called when new weather data arrives, to purge the buffers and redraw.
363
public void WeatherChanged ()