summaryrefslogtreecommitdiffstats
path: root/grapher/GraphWidget.cxx
diff options
context:
space:
mode:
authorTim Moore <timoore@redhat.com>2009-04-20 08:13:38 +0200
committerTim Moore <timoore@redhat.com>2009-04-20 08:13:38 +0200
commita6e65750e80380d42ea238f3c688c3499de16d74 (patch)
treef5ef0b9df733e04d405b969d2fc6290d5ddce773 /grapher/GraphWidget.cxx
parent2d45e339f3287cf0b4805ea91b3aa9f17b6d4752 (diff)
downloadsystemtap-steved-a6e65750e80380d42ea238f3c688c3499de16d74.tar.gz
systemtap-steved-a6e65750e80380d42ea238f3c688c3499de16d74.tar.xz
systemtap-steved-a6e65750e80380d42ea238f3c688c3499de16d74.zip
Move grapher to subdirectory
Diffstat (limited to 'grapher/GraphWidget.cxx')
-rw-r--r--grapher/GraphWidget.cxx323
1 files changed, 323 insertions, 0 deletions
diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx
new file mode 100644
index 00000000..5186c471
--- /dev/null
+++ b/grapher/GraphWidget.cxx
@@ -0,0 +1,323 @@
+#include <algorithm>
+#include <ctime>
+#include <math.h>
+#include <sstream>
+#include <iomanip>
+#include <cairomm/context.h>
+#include "GraphWidget.hxx"
+#include "CairoWidget.hxx"
+
+namespace systemtap
+{
+ GraphWidget::GraphWidget()
+ : _left(0.0), _right(1.0), _top(1.0), _bottom(0.0), _lineWidth(10),
+ _autoScaling(true), _autoScrolling(true), _zoomFactor(1.0),
+ _trackingDrag(false), _playButton(new CairoPlayButton)
+ {
+ add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK
+ | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK);
+ Glib::signal_timeout()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_timeout), 1000);
+ signal_expose_event()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_expose_event), false);
+ signal_button_press_event()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_button_press_event),
+ false);
+ signal_button_release_event()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_button_release_event),
+ false);
+ signal_motion_notify_event()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_motion_notify_event),
+ false);
+ signal_scroll_event()
+ .connect(sigc::mem_fun(*this, &GraphWidget::on_scroll_event), false);
+ }
+
+ void GraphWidget::getExtents(double& left, double& right, double& top,
+ double& bottom) const
+ {
+ left = _left;
+ right = _right;
+ top = _top;
+ bottom = _bottom;
+ }
+
+ void GraphWidget::setExtents(double left, double right, double top,
+ double bottom)
+ {
+ _left = left;
+ _right = right;
+ _top = top;
+ _bottom = bottom;
+
+ }
+ GraphWidget::~GraphWidget()
+ {
+ }
+
+ void GraphWidget::addGraphData(std::tr1::shared_ptr<GraphData> data)
+ {
+ _datasets.push_back(data);
+ }
+
+ bool GraphWidget::on_expose_event(GdkEventExpose* event)
+ {
+ // This is where we draw on the window
+ Glib::RefPtr<Gdk::Window> window = get_window();
+ if(!window)
+ return true;
+
+ Gtk::Allocation allocation = get_allocation();
+
+ const int graphWidth = allocation.get_width();
+ const int graphHeight = allocation.get_height();
+ const int width = graphWidth - 20;
+ const int height = graphHeight - 20;
+
+ Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
+ if(event && !_autoScaling)
+ {
+ // 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();
+ }
+ if (_autoScaling)
+ {
+ // line separation
+ int linesPossible = width / (_lineWidth + 2);
+ // Find latest time.
+ double latestTime = 0;
+ for (DatasetList::iterator ditr = _datasets.begin(),
+ de = _datasets.end();
+ ditr != de;
+ ++ditr)
+ {
+ if (!(*ditr)->data.empty())
+ {
+ double lastDataTime = (*ditr)->data.back().first;
+ if (lastDataTime > latestTime)
+ latestTime = lastDataTime;
+ }
+ }
+ double minDiff = 0.0;
+ double maxTotal = 0.0;
+ for (DatasetList::iterator ditr = _datasets.begin(),
+ de = _datasets.end();
+ ditr != de;
+ ++ditr)
+ {
+ GraphData::List& gdata = (*ditr)->data;
+ if (gdata.size() <= 1)
+ continue;
+ double totalDiff = 0.0;
+ for (GraphData::List::reverse_iterator ritr = gdata.rbegin(),
+ re = gdata.rend();
+ ritr + 1 != gdata.rend();
+ ritr++)
+ {
+ double timeDiff = ritr->first - (ritr + 1)->first;
+ if (timeDiff < minDiff || (timeDiff != 0 && minDiff == 0))
+ minDiff = timeDiff;
+ if (minDiff != 0
+ && (totalDiff + timeDiff) / minDiff > linesPossible)
+ break;
+ totalDiff += timeDiff;
+ }
+ if (totalDiff > maxTotal)
+ maxTotal = totalDiff;
+ }
+ // Now we have a global scale.
+ _right = latestTime;
+ if (maxTotal != 0)
+ _left = latestTime - maxTotal;
+ else
+ _left = _right - 1.0;
+ }
+ cr->save();
+ double horizScale = _zoomFactor * width / ( _right - _left);
+ cr->translate(20.0, 0.0);
+ cr->set_line_width(_lineWidth);
+ cr->save();
+ cr->set_source_rgba(0.0, 0.0, 0.0, 1.0);
+ cr->paint();
+ cr->restore();
+
+ for (DatasetList::iterator itr = _datasets.begin(), e = _datasets.end();
+ itr != e;
+ ++itr)
+ {
+ cr->save();
+ cr->translate(0.0, height);
+ cr->scale(1.0, -1.0);
+ GraphData::List::iterator lower
+ = std::lower_bound((*itr)->data.begin(), (*itr)->data.end(), _left,
+ GraphData::Compare());
+ GraphData::List::iterator upper
+ = std::upper_bound((*itr)->data.begin(), (*itr)->data.end(), _right,
+ GraphData::Compare());
+ for (GraphData::List::iterator ditr = lower, de = upper;
+ ditr != de;
+ ++ditr)
+ {
+ cr->set_source_rgba((*itr)->color[0], (*itr)->color[1],
+ (*itr)->color[2], 1.0);
+ if ((*itr)->style == GraphData::BAR)
+ {
+ cr->move_to((ditr->first - _left) * horizScale, 0);
+ cr->line_to((ditr->first - _left) * horizScale,
+ ditr->second * height / (*itr)->scale);
+ cr->stroke();
+ }
+ else
+ {
+ cr->arc((ditr->first - _left) * horizScale,
+ ditr->second * height / (*itr)->scale,
+ _lineWidth / 2.0, 0.0, M_PI * 2.0);
+ cr->fill();
+ }
+ }
+ cr->restore();
+ }
+ cr->restore();
+ cr->save();
+ cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL,
+ Cairo::FONT_WEIGHT_BOLD);
+ cr->set_font_size(14.0);
+ cr->set_source_rgba(1.0, 1.0, 1.0, .8);
+
+ if (!_title.empty())
+ {
+ cr->move_to(20.0, 20.0);
+ cr->show_text(_title);
+ }
+ if (!_xAxisText.empty())
+ {
+ cr->move_to(10.0, graphHeight - 5);
+ cr->show_text(_xAxisText);
+ }
+ if (!_yAxisText.empty())
+ {
+ cr->save();
+ cr->translate(10.0, height - 10.0);
+ cr->rotate(-M_PI / 2.0);
+ cr->move_to(10.0, 0.0);
+ cr->show_text(_yAxisText);
+ cr->restore();
+ }
+ cr->restore();
+ // Draw axes
+ double diff = _right - _left;
+ double majorUnit = pow(10.0, floor(log(diff) / log(10.0)));
+ double startTime = floor(_left / majorUnit) * majorUnit;
+ cr->save();
+ cr->set_source_rgba(1.0, 1.0, 1.0, .9);
+ cr->set_line_cap(Cairo::LINE_CAP_BUTT);
+ cr->set_line_width(_lineWidth);
+ cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL,
+ Cairo::FONT_WEIGHT_NORMAL);
+ cr->set_font_size(10.0);
+ cr->move_to(20.0, 0.0);
+ cr->line_to(20.0, height);
+ cr->move_to(20.0, height);
+ cr->line_to(graphWidth, height);
+ cr->stroke();
+ std::vector<double> dash(1);
+ dash[0] = height / 10;
+ cr->set_dash(dash, 0);
+ for (double tickVal = startTime; tickVal < _right; tickVal += majorUnit)
+ {
+ cr->move_to((tickVal - _left) * horizScale + 20.0, graphHeight - 5);
+ std::ostringstream stream;
+ stream << std::fixed << std::setprecision(0) << tickVal;
+ cr->show_text(stream.str());
+ cr->move_to((tickVal - _left) * horizScale + 20.0, 0.0);
+ cr->line_to((tickVal - _left) * horizScale + 20.0, height);
+ cr->stroke();
+ }
+ cr->stroke();
+ cr->restore();
+
+ if (!_autoScrolling)
+ {
+ _playButton->setVisible(true);
+ _playButton->setOrigin(width / 2 - 25, .875 * height - 50);
+ _playButton->draw(cr);
+ }
+
+ return true;
+ }
+
+ bool GraphWidget::on_button_press_event(GdkEventButton* event)
+ {
+ if (!_autoScrolling && _playButton->containsPoint(event->x, event->y))
+ {
+ _autoScaling = true;
+ _autoScrolling = true;
+ queue_draw();
+ }
+ else
+ {
+ _trackingDrag = true;
+ _autoScaling = false;
+ _autoScrolling = false;
+ _dragOriginX = event->x;
+ _dragOriginY = event->y;
+ _dragOrigLeft = _left;
+ _dragOrigRight = _right;
+ }
+ return true;
+ }
+
+ bool GraphWidget::on_button_release_event(GdkEventButton* event)
+ {
+ _trackingDrag = false;
+ return true;
+ }
+
+ bool GraphWidget::on_motion_notify_event(GdkEventMotion* event)
+ {
+ Glib::RefPtr<Gdk::Window> win = get_window();
+ if(!win)
+ return true;
+ double x;
+ double y;
+ // XXX Hint
+ if (event->is_hint)
+ {
+ }
+ else
+ {
+ x = event->x;
+ y = event->y;
+ }
+ if (_trackingDrag)
+ {
+ Gtk::Allocation allocation = get_allocation();
+ const int width = allocation.get_width();
+ double motion = (x - _dragOriginX) / (double) width;
+ double increment = motion * (_dragOrigLeft - _dragOrigRight);
+ _left = _dragOrigLeft + increment;
+ _right = _dragOrigRight + increment;
+ queue_draw();
+ }
+ return true;
+ }
+
+ bool GraphWidget::on_scroll_event(GdkEventScroll* event)
+ {
+ if (event->direction == GDK_SCROLL_UP)
+ _zoomFactor += .1;
+ else if (event->direction == GDK_SCROLL_DOWN)
+ _zoomFactor -= .1;
+ queue_draw();
+ return true;
+ }
+
+ bool GraphWidget::on_timeout()
+ {
+ queue_draw();
+ return true;
+ }
+}