From d293d53136fa7e6e7eda510847145edf4d3d7f55 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 1 Dec 2009 12:26:59 +0100 Subject: Add hover text to the graph. When the graph display is paused, leaving the mouse stationary over the graph will display the data point under the pointer. * grapher/CairoWidget.hxx (CairoTextBox): new class (CairoWidget, CairoPlayButton): refector some play button-specific things from CairoWidget to CairoPlayButton. * grapher/CairoWidget.cxx (CairoTextBox::draw): new function. * grapher/GraphWidget.hxx (GraphWidget): new members for supporting hover text. * grapher/GraphWidget.cxx (on_motion_notify_event): Set up hover text box. (establishHoverTimeout, onHoverTimeout, getGraphUnderPoint): new functions. --- grapher/GraphWidget.cxx | 108 +++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 38 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index cee1a6d2..9a203d87 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -1,8 +1,10 @@ #include #include +#include #include #include +#include #include #include @@ -19,7 +21,8 @@ namespace systemtap GraphWidget::GraphWidget() - : _trackingDrag(false), _width(600), _height(200) + : _trackingDrag(false), _width(600), _height(200), _mouseX(0.0), + _mouseY(0.0) { add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK); @@ -104,16 +107,6 @@ namespace systemtap return true; Cairo::RefPtr cr = window->create_cairo_context(); -#if 0 - 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(); - } -#endif cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); @@ -126,28 +119,21 @@ namespace systemtap (*g)->draw(cr); cr->restore(); } + if (_hoverText && _hoverText->isVisible()) + _hoverText->draw(cr); return true; } bool GraphWidget::on_button_press_event(GdkEventButton* event) { - for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) + shared_ptr g = getGraphUnderPoint(event->x, event->y); + if (g) { - if (event->x >= (*g)->_graphX - && event->x < (*g)->_graphX + (*g)->_graphWidth - && event->y >= (*g)->_graphY - && event->y < (*g)->_graphY + (*g)->_graphHeight) + _activeGraph = g; + if (event->button == 3) { - _activeGraph = *g; - if (event->button == 3) - { - _dataDialog->show(); - return true; - } - else - { - break; - } + _dataDialog->show(); + return true; } } if (!_activeGraph) @@ -171,6 +157,7 @@ namespace systemtap _dragOriginY = event->y; _dragOrigLeft = _activeGraph->_left; _dragOrigRight = _activeGraph->_right; + establishHoverTimeout(); } return true; } @@ -191,27 +178,25 @@ namespace systemtap Glib::RefPtr win = get_window(); if(!win) return true; - double x = 0.0; - double y = 0.0; - // XXX Hint - if (event->is_hint) - { - } - else - { - x = event->x; - y = event->y; - } + _mouseX = event->x; + _mouseY = event->y; if (_trackingDrag && _activeGraph) { Gtk::Allocation allocation = get_allocation(); const int width = allocation.get_width(); - double motion = (x - _dragOriginX) / (double) width; + double motion = (_mouseX - _dragOriginX) / (double) width; double increment = motion * (_dragOrigLeft - _dragOrigRight); _activeGraph->_left = _dragOrigLeft + increment; _activeGraph->_right = _dragOrigRight + increment; queue_draw(); } + if (_hoverText && _hoverText->isVisible()) + { + _hoverText->setVisible(false); + queue_draw(); + } + establishHoverTimeout(); + return true; } @@ -282,4 +267,51 @@ namespace systemtap row[_dataColumns._graphData] = *itr; } } + + bool GraphWidget::onHoverTimeout() + { + shared_ptr g = getGraphUnderPoint(_mouseX, _mouseY); + if (g && !g->_autoScrolling) + { + if (!_hoverText) + _hoverText = shared_ptr(new CairoTextBox()); + _hoverText->setOrigin(_mouseX + 5, _mouseY - 5); + int64_t t = g->getTimeAtPoint(_mouseX); + Graph::DatasetList& dataSets = g->getDatasets(); + if (!dataSets.empty()) + { + shared_ptr gdbase = dataSets[0]; + GraphDataBase::TimeList::iterator itime + = std::lower_bound(gdbase->times.begin(), gdbase->times.end(), t); + if (itime != gdbase->times.end()) { + size_t index = distance(gdbase->times.begin(), itime); + _hoverText->contents = gdbase->elementAsString(index); + _hoverText->setVisible(true); + queue_draw(); + } + } + } + return false; + } + + shared_ptr GraphWidget::getGraphUnderPoint(double x, double y) + { + for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) + { + if (x >= (*g)->_graphX + && x < (*g)->_graphX + (*g)->_graphWidth + && y >= (*g)->_graphY + && y < (*g)->_graphY + (*g)->_graphHeight) + return *g; + } + return shared_ptr(); + } + + void GraphWidget::establishHoverTimeout() + { + if (_hover_timeout_connection.connected()) + _hover_timeout_connection.disconnect(); + _hover_timeout_connection = Glib::signal_timeout() + .connect(sigc::mem_fun(*this, &GraphWidget::onHoverTimeout), 1000); + } } -- cgit From c10fce7d6aaa57a4f94f9d7aeea906597456f7ce Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 2 Dec 2009 19:27:07 +0100 Subject: Make the hover text conform to data displayed. Start of code to be more selective about the association between the hover text and the underling graph. Also, show the data set name in hover text. * grapher/GraphStyle.hxx (dataIndexAtPoint): New virtual function. * grapher/GraphStyle.cxx (dataIndexAtPoint): Implementation for bar graphs * grapher/GraphWidget.cxx (onHoverTimeout): Use dataIndexAtPoint. --- grapher/GraphWidget.cxx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 9a203d87..9067988a 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -276,20 +276,23 @@ namespace systemtap if (!_hoverText) _hoverText = shared_ptr(new CairoTextBox()); _hoverText->setOrigin(_mouseX + 5, _mouseY - 5); - int64_t t = g->getTimeAtPoint(_mouseX); Graph::DatasetList& dataSets = g->getDatasets(); - if (!dataSets.empty()) - { - shared_ptr gdbase = dataSets[0]; - GraphDataBase::TimeList::iterator itime - = std::lower_bound(gdbase->times.begin(), gdbase->times.end(), t); - if (itime != gdbase->times.end()) { - size_t index = distance(gdbase->times.begin(), itime); - _hoverText->contents = gdbase->elementAsString(index); - _hoverText->setVisible(true); - queue_draw(); + for (Graph::DatasetList::reverse_iterator ritr = dataSets.rbegin(), + end = dataSets.rend(); + ritr != end; + ++ritr) + { + ssize_t index + = (*ritr)->style->dataIndexAtPoint(_mouseX, _mouseY, *ritr, g); + if (index >= 0) + { + _hoverText->contents = (*ritr)->name + + ": " + (*ritr)->elementAsString(index); + _hoverText->setVisible(true); + queue_draw(); + break; } - } + } } return false; } -- cgit