initial commit, lordsawar source, slightly modified
[lordsawar] / src / gui / line-chart.cpp
diff --git a/src/gui/line-chart.cpp b/src/gui/line-chart.cpp
new file mode 100644 (file)
index 0000000..311f24c
--- /dev/null
@@ -0,0 +1,249 @@
+//  Copyright (C) 2007, 2008 Ben Asselstine
+//
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
+//  02110-1301, USA.
+
+#include "line-chart.h"
+#include "ucompose.hpp"
+#include <cairomm/context.h>
+
+LineChart::LineChart(std::list<std::list<unsigned int> > lines, 
+                    std::list<Gdk::Color> colours, 
+                    unsigned int max_height_value,
+                    std::string x_axis_description,
+                    std::string y_axis_description)
+{
+  d_lines = lines;
+  d_colours = colours;
+  d_max_height_value = max_height_value;
+  d_x_axis_description = x_axis_description;
+  d_y_axis_description = y_axis_description;
+  d_x_indicator = -1;
+}
+
+LineChart::~LineChart()
+{
+}
+
+bool LineChart::on_expose_event(GdkEventExpose* event)
+{
+  // This is where we draw on the window
+  Glib::RefPtr<Gdk::Window> window = get_window();
+  if(window)
+  {
+    Gtk::Allocation allocation = get_allocation();
+    const int width = allocation.get_width();
+    const int height = allocation.get_height();
+
+    // coordinates for the center of the window
+    int xc, yc;
+    xc = width / 2;
+    yc = height / 2;
+    int origin_x = 0;
+    int origin_y = height;
+    unsigned int hoffs = 30;
+    unsigned int voffs = 30;
+
+    Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
+      
+    // clip to the area indicated by the expose event so that we only redraw
+    // the portion of the window that needs to be redrawn
+    cr->rectangle(event->area.x, event->area.y,
+            event->area.width, event->area.height);
+    cr->clip();
+    cr->set_source_rgb (0.8, 0.8, 0.8);
+    cr->set_line_width(1000.0);
+    cr->move_to(0,0);
+    cr->line_to(width,height);
+    cr->stroke();
+
+    cr->set_line_width(1.0);
+    //loop through the outer list, and operate on the inner lists
+    unsigned int max_turn = 0;
+    std::list<std::list<unsigned int> >::iterator line = d_lines.begin();
+    for (; line!= d_lines.end(); line++)
+      {
+       if ((*line).size() > max_turn)
+         max_turn = (*line).size();
+      }
+
+    if (d_max_height_value == 0)
+      {
+       line = d_lines.begin();
+       for (; line!= d_lines.end(); line++)
+         {
+           std::list<unsigned int>::iterator it = (*line).begin();
+           for (; it != (*line).end(); it++)
+             {
+               if (*it > d_max_height_value)
+                 d_max_height_value = *it;
+             }
+         }
+      }
+
+    // ensure the border is big enough for the label.
+    Glib::RefPtr<Pango::Layout> layout = Glib::wrap (pango_cairo_create_layout (cr->cobj ()));
+    std::string text_font = "Sans 8";
+    Pango::FontDescription font_desc (text_font);
+    layout->set_font_description (font_desc);
+
+    layout->set_text(String::ucompose("%1", d_max_height_value));
+    int w, h;
+    layout->get_pixel_size (w, h);
+    if (w * (hoffs / 4) > hoffs)
+      hoffs = w + (hoffs / 4);
+
+    std::list<Gdk::Color>::iterator cit = d_colours.begin();
+    line = d_lines.begin();
+    for (; line!= d_lines.end(), cit != d_colours.end(); line++, cit++)
+      {
+       //okay, here's my line and it's colour,
+       double red = (double)(*cit).get_red() /65535.0;
+       double green = (double)(*cit).get_green() /65535.0;
+       double blue = (double)(*cit).get_blue() /65535.0;
+       cr->set_source_rgb(red, green, blue);
+       std::list<unsigned int>::iterator it = (*line).begin();
+       unsigned int turn = 1;
+       cr->move_to(origin_x + hoffs, origin_y - voffs);
+       for (; it != (*line).end(); it++, turn++)
+         {
+           cr->line_to((((float)turn / (float)max_turn) * 
+                        (width - (hoffs * 2))) + hoffs,
+                       height - (voffs * 2) - 
+                       (((float)*it / (float)d_max_height_value) * 
+                        (height - (voffs * 2.0))) + voffs);
+         }
+       cr->stroke();
+      }
+
+    //draw horizontal axis
+    cr->set_source_rgb(0.3, 0.3, 0.3);
+    cr->move_to(origin_x + hoffs, origin_y - voffs);
+    cr->line_to((width - (hoffs * 2)) + hoffs, origin_y - voffs);
+    cr->stroke();
+
+    //draw ticks on the horizontal axis
+    cr->set_source_rgb(0.3, 0.3, 0.3);
+    cr->move_to(origin_x + hoffs, origin_y - voffs + 1);
+    cr->line_to(origin_x + hoffs, origin_y - voffs + (voffs / 4) + 1);
+    cr->stroke();
+
+    cr->move_to((width - (hoffs * 2)) + hoffs - 1, origin_y - voffs + 1);
+    cr->line_to((width - (hoffs * 2)) + hoffs - 1, 
+               origin_y - voffs + (voffs / 4) + 1);
+    cr->stroke();
+
+    //draw vertical axis
+    cr->set_source_rgb(0.3, 0.3, 0.3);
+    cr->move_to(origin_x + hoffs, origin_y - voffs);
+    cr->line_to(origin_x + hoffs, voffs);
+    cr->stroke();
+
+    //draw ticks on the vertical axis
+    cr->move_to(origin_x + hoffs - 1, origin_y - voffs);
+    cr->line_to(origin_x + hoffs - (hoffs / 4) - 1, origin_y - voffs);
+    cr->stroke();
+    cr->move_to(origin_x + hoffs - 1, voffs + 1);
+    cr->line_to(origin_x + hoffs - (hoffs / 4) - 1, voffs + 1);
+    cr->stroke();
+
+    //draw the indicator line
+    if (d_x_indicator > -1 && (unsigned int) d_x_indicator <= max_turn)
+      {
+       //draw a line at turn x
+       cr->set_source_rgb(0.0, 0.0, 0.0);
+       cr->move_to((((float)d_x_indicator/ (float)max_turn) * (width - (hoffs * 2))) + hoffs, voffs);
+       cr->line_to((((float)d_x_indicator/ (float)max_turn) * (width - (hoffs * 2))) + hoffs, height - voffs);
+       cr->stroke();
+      }
+
+    // draw the labels on the horizontal axis
+    layout->set_font_description (font_desc);
+    layout->set_text("0");
+    layout->get_pixel_size (w, h);
+    cr->move_to(hoffs - (w / 2), origin_y - voffs + (voffs / 4));
+    cr->set_source_rgb (0.0, 0.0, 0.0);
+    cr->set_operator (Cairo::OPERATOR_ATOP);
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+    layout->set_text(String::ucompose("%1", max_turn));
+    layout->get_pixel_size (w, h);
+    cr->move_to((width - (hoffs * 2)) + hoffs - (w / 2), 
+               origin_y - voffs + (voffs / 4) + 1);
+    cr->set_source_rgb (0.0, 0.0, 0.0);
+    cr->set_operator (Cairo::OPERATOR_ATOP);
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+    layout->set_text(d_y_axis_description);
+    layout->get_pixel_size (w, h);
+    cr->move_to((width / 2 - (hoffs * 1)) + hoffs - (w / 2), 
+               origin_y - voffs + (voffs / 2) + 1);
+    cr->set_source_rgb (0.0, 0.0, 0.0);
+    cr->set_operator (Cairo::OPERATOR_ATOP);
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+    // draw the labels on the vertical axis
+    layout->set_font_description (font_desc);
+    layout->set_text("0");
+    layout->get_pixel_size (w, h);
+    cr->move_to(origin_x + hoffs - (hoffs / 4) - 1 - w, origin_y - voffs - (h/2));
+    cr->set_source_rgb (0.0, 0.0, 0.0);
+    cr->set_operator (Cairo::OPERATOR_ATOP);
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+    layout->set_text(String::ucompose("%1", d_max_height_value));
+    layout->get_pixel_size (w, h);
+    cr->move_to(origin_x + hoffs - (hoffs / 4) - 1 - w, voffs + 1 - (h/2));
+    cr->set_source_rgb (0.0, 0.0, 0.0);
+    cr->set_operator (Cairo::OPERATOR_ATOP);
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+    PangoContext *context;
+    PangoCairoFontMap *fontmap;
+    fontmap = (PangoCairoFontMap *) pango_cairo_font_map_get_default ();
+    context = pango_cairo_font_map_create_context (fontmap);
+    pango_context_set_base_gravity(context, PANGO_GRAVITY_EAST);
+    pango_context_set_gravity_hint(context, PANGO_GRAVITY_HINT_STRONG);
+    layout->context_changed();
+
+    layout->set_text(d_x_axis_description);
+    layout->get_pixel_size (w, h);
+    cr->move_to(0, height - (voffs * 2) - 
+               (0.50 * (height - (voffs * 2.0))) + voffs + (w / 2));
+    cr->rotate(-90 / (180.0 / G_PI));
+    pango_cairo_show_layout (cr->cobj (), layout->gobj ());
+
+  }
+
+  return true;
+}
+
+void LineChart::set_x_indicator(int x)
+{
+  d_x_indicator = x;
+  Glib::RefPtr<Gdk::Window> window = get_window();
+  if(window)
+    {
+      Gtk::Allocation allocation = get_allocation();
+      const int width = allocation.get_width();
+      const int height = allocation.get_height();
+      GdkEventExpose event;
+      event.area.x = 0;
+      event.area.y = 0;
+      event.area.height = height;
+      event.area.width = width;
+      on_expose_event(&event);
+    }
+}