initial commit, lordsawar source, slightly modified
[lordsawar] / src / gui / line-chart.cpp
1 //  Copyright (C) 2007, 2008 Ben Asselstine
2 //
3 //  This program is free software; you can redistribute it and/or modify
4 //  it under the terms of the GNU General Public License as published by
5 //  the Free Software Foundation; either version 3 of the License, or
6 //  (at your option) any later version.
7 //
8 //  This program is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //  GNU Library General Public License for more details.
12 //
13 //  You should have received a copy of the GNU General Public License
14 //  along with this program; if not, write to the Free Software
15 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
16 //  02110-1301, USA.
17
18 #include "line-chart.h"
19 #include "ucompose.hpp"
20 #include <cairomm/context.h>
21
22 LineChart::LineChart(std::list<std::list<unsigned int> > lines, 
23                      std::list<Gdk::Color> colours, 
24                      unsigned int max_height_value,
25                      std::string x_axis_description,
26                      std::string y_axis_description)
27 {
28   d_lines = lines;
29   d_colours = colours;
30   d_max_height_value = max_height_value;
31   d_x_axis_description = x_axis_description;
32   d_y_axis_description = y_axis_description;
33   d_x_indicator = -1;
34 }
35
36 LineChart::~LineChart()
37 {
38 }
39
40 bool LineChart::on_expose_event(GdkEventExpose* event)
41 {
42   // This is where we draw on the window
43   Glib::RefPtr<Gdk::Window> window = get_window();
44   if(window)
45   {
46     Gtk::Allocation allocation = get_allocation();
47     const int width = allocation.get_width();
48     const int height = allocation.get_height();
49
50     // coordinates for the center of the window
51     int xc, yc;
52     xc = width / 2;
53     yc = height / 2;
54     int origin_x = 0;
55     int origin_y = height;
56     unsigned int hoffs = 30;
57     unsigned int voffs = 30;
58
59     Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
60       
61     // clip to the area indicated by the expose event so that we only redraw
62     // the portion of the window that needs to be redrawn
63     cr->rectangle(event->area.x, event->area.y,
64             event->area.width, event->area.height);
65     cr->clip();
66     cr->set_source_rgb (0.8, 0.8, 0.8);
67     cr->set_line_width(1000.0);
68     cr->move_to(0,0);
69     cr->line_to(width,height);
70     cr->stroke();
71
72     cr->set_line_width(1.0);
73     //loop through the outer list, and operate on the inner lists
74     unsigned int max_turn = 0;
75     std::list<std::list<unsigned int> >::iterator line = d_lines.begin();
76     for (; line!= d_lines.end(); line++)
77       {
78         if ((*line).size() > max_turn)
79           max_turn = (*line).size();
80       }
81
82     if (d_max_height_value == 0)
83       {
84         line = d_lines.begin();
85         for (; line!= d_lines.end(); line++)
86           {
87             std::list<unsigned int>::iterator it = (*line).begin();
88             for (; it != (*line).end(); it++)
89               {
90                 if (*it > d_max_height_value)
91                   d_max_height_value = *it;
92               }
93           }
94       }
95
96     // ensure the border is big enough for the label.
97     Glib::RefPtr<Pango::Layout> layout = Glib::wrap (pango_cairo_create_layout (cr->cobj ()));
98     std::string text_font = "Sans 8";
99     Pango::FontDescription font_desc (text_font);
100     layout->set_font_description (font_desc);
101
102     layout->set_text(String::ucompose("%1", d_max_height_value));
103     int w, h;
104     layout->get_pixel_size (w, h);
105     if (w * (hoffs / 4) > hoffs)
106       hoffs = w + (hoffs / 4);
107
108     std::list<Gdk::Color>::iterator cit = d_colours.begin();
109     line = d_lines.begin();
110     for (; line!= d_lines.end(), cit != d_colours.end(); line++, cit++)
111       {
112         //okay, here's my line and it's colour,
113         double red = (double)(*cit).get_red() /65535.0;
114         double green = (double)(*cit).get_green() /65535.0;
115         double blue = (double)(*cit).get_blue() /65535.0;
116         cr->set_source_rgb(red, green, blue);
117         std::list<unsigned int>::iterator it = (*line).begin();
118         unsigned int turn = 1;
119         cr->move_to(origin_x + hoffs, origin_y - voffs);
120         for (; it != (*line).end(); it++, turn++)
121           {
122             cr->line_to((((float)turn / (float)max_turn) * 
123                          (width - (hoffs * 2))) + hoffs,
124                         height - (voffs * 2) - 
125                         (((float)*it / (float)d_max_height_value) * 
126                          (height - (voffs * 2.0))) + voffs);
127           }
128         cr->stroke();
129       }
130
131     //draw horizontal axis
132     cr->set_source_rgb(0.3, 0.3, 0.3);
133     cr->move_to(origin_x + hoffs, origin_y - voffs);
134     cr->line_to((width - (hoffs * 2)) + hoffs, origin_y - voffs);
135     cr->stroke();
136
137     //draw ticks on the horizontal axis
138     cr->set_source_rgb(0.3, 0.3, 0.3);
139     cr->move_to(origin_x + hoffs, origin_y - voffs + 1);
140     cr->line_to(origin_x + hoffs, origin_y - voffs + (voffs / 4) + 1);
141     cr->stroke();
142
143     cr->move_to((width - (hoffs * 2)) + hoffs - 1, origin_y - voffs + 1);
144     cr->line_to((width - (hoffs * 2)) + hoffs - 1, 
145                 origin_y - voffs + (voffs / 4) + 1);
146     cr->stroke();
147
148     //draw vertical axis
149     cr->set_source_rgb(0.3, 0.3, 0.3);
150     cr->move_to(origin_x + hoffs, origin_y - voffs);
151     cr->line_to(origin_x + hoffs, voffs);
152     cr->stroke();
153
154     //draw ticks on the vertical axis
155     cr->move_to(origin_x + hoffs - 1, origin_y - voffs);
156     cr->line_to(origin_x + hoffs - (hoffs / 4) - 1, origin_y - voffs);
157     cr->stroke();
158     cr->move_to(origin_x + hoffs - 1, voffs + 1);
159     cr->line_to(origin_x + hoffs - (hoffs / 4) - 1, voffs + 1);
160     cr->stroke();
161
162     //draw the indicator line
163     if (d_x_indicator > -1 && (unsigned int) d_x_indicator <= max_turn)
164       {
165         //draw a line at turn x
166         cr->set_source_rgb(0.0, 0.0, 0.0);
167         cr->move_to((((float)d_x_indicator/ (float)max_turn) * (width - (hoffs * 2))) + hoffs, voffs);
168         cr->line_to((((float)d_x_indicator/ (float)max_turn) * (width - (hoffs * 2))) + hoffs, height - voffs);
169         cr->stroke();
170       }
171
172     // draw the labels on the horizontal axis
173     layout->set_font_description (font_desc);
174     layout->set_text("0");
175     layout->get_pixel_size (w, h);
176     cr->move_to(hoffs - (w / 2), origin_y - voffs + (voffs / 4));
177     cr->set_source_rgb (0.0, 0.0, 0.0);
178     cr->set_operator (Cairo::OPERATOR_ATOP);
179     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
180
181     layout->set_text(String::ucompose("%1", max_turn));
182     layout->get_pixel_size (w, h);
183     cr->move_to((width - (hoffs * 2)) + hoffs - (w / 2), 
184                 origin_y - voffs + (voffs / 4) + 1);
185     cr->set_source_rgb (0.0, 0.0, 0.0);
186     cr->set_operator (Cairo::OPERATOR_ATOP);
187     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
188
189     layout->set_text(d_y_axis_description);
190     layout->get_pixel_size (w, h);
191     cr->move_to((width / 2 - (hoffs * 1)) + hoffs - (w / 2), 
192                 origin_y - voffs + (voffs / 2) + 1);
193     cr->set_source_rgb (0.0, 0.0, 0.0);
194     cr->set_operator (Cairo::OPERATOR_ATOP);
195     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
196
197     // draw the labels on the vertical axis
198     layout->set_font_description (font_desc);
199     layout->set_text("0");
200     layout->get_pixel_size (w, h);
201     cr->move_to(origin_x + hoffs - (hoffs / 4) - 1 - w, origin_y - voffs - (h/2));
202     cr->set_source_rgb (0.0, 0.0, 0.0);
203     cr->set_operator (Cairo::OPERATOR_ATOP);
204     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
205
206     layout->set_text(String::ucompose("%1", d_max_height_value));
207     layout->get_pixel_size (w, h);
208     cr->move_to(origin_x + hoffs - (hoffs / 4) - 1 - w, voffs + 1 - (h/2));
209     cr->set_source_rgb (0.0, 0.0, 0.0);
210     cr->set_operator (Cairo::OPERATOR_ATOP);
211     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
212
213     PangoContext *context;
214     PangoCairoFontMap *fontmap;
215     fontmap = (PangoCairoFontMap *) pango_cairo_font_map_get_default ();
216     context = pango_cairo_font_map_create_context (fontmap);
217     pango_context_set_base_gravity(context, PANGO_GRAVITY_EAST);
218     pango_context_set_gravity_hint(context, PANGO_GRAVITY_HINT_STRONG);
219     layout->context_changed();
220
221     layout->set_text(d_x_axis_description);
222     layout->get_pixel_size (w, h);
223     cr->move_to(0, height - (voffs * 2) - 
224                 (0.50 * (height - (voffs * 2.0))) + voffs + (w / 2));
225     cr->rotate(-90 / (180.0 / G_PI));
226     pango_cairo_show_layout (cr->cobj (), layout->gobj ());
227
228   }
229
230   return true;
231 }
232
233 void LineChart::set_x_indicator(int x)
234 {
235   d_x_indicator = x;
236   Glib::RefPtr<Gdk::Window> window = get_window();
237   if(window)
238     {
239       Gtk::Allocation allocation = get_allocation();
240       const int width = allocation.get_width();
241       const int height = allocation.get_height();
242       GdkEventExpose event;
243       event.area.x = 0;
244       event.area.y = 0;
245       event.area.height = height;
246       event.area.width = width;
247       on_expose_event(&event);
248     }
249 }