pv/prop/property.cpp
pv/prop/string.cpp
pv/toolbars/mainbar.cpp
- pv/view/analogsignal.cpp
- pv/view/cursor.cpp
- pv/view/cursorpair.cpp
- pv/view/flag.cpp
- pv/view/header.cpp
- pv/view/marginwidget.cpp
- pv/view/logicsignal.cpp
- pv/view/rowitem.cpp
- pv/view/ruler.cpp
- pv/view/signal.cpp
- pv/view/signalscalehandle.cpp
- pv/view/timeitem.cpp
- pv/view/timemarker.cpp
- pv/view/trace.cpp
- pv/view/tracegroup.cpp
- pv/view/tracepalette.cpp
- pv/view/tracetreeitem.cpp
- pv/view/tracetreeitemowner.cpp
- pv/view/triggermarker.cpp
- pv/view/view.cpp
- pv/view/viewitem.cpp
- pv/view/viewitemowner.cpp
- pv/view/viewitempaintparams.cpp
- pv/view/viewport.cpp
- pv/view/viewwidget.cpp
+ pv/views/trace/analogsignal.cpp
+ pv/views/trace/cursor.cpp
+ pv/views/trace/cursorpair.cpp
+ pv/views/trace/flag.cpp
+ pv/views/trace/header.cpp
+ pv/views/trace/marginwidget.cpp
+ pv/views/trace/logicsignal.cpp
+ pv/views/trace/rowitem.cpp
+ pv/views/trace/ruler.cpp
+ pv/views/trace/signal.cpp
+ pv/views/trace/signalscalehandle.cpp
+ pv/views/trace/timeitem.cpp
+ pv/views/trace/timemarker.cpp
+ pv/views/trace/trace.cpp
+ pv/views/trace/tracegroup.cpp
+ pv/views/trace/tracepalette.cpp
+ pv/views/trace/tracetreeitem.cpp
+ pv/views/trace/tracetreeitemowner.cpp
+ pv/views/trace/triggermarker.cpp
+ pv/views/trace/view.cpp
+ pv/views/trace/viewitem.cpp
+ pv/views/trace/viewitemowner.cpp
+ pv/views/trace/viewitempaintparams.cpp
+ pv/views/trace/viewport.cpp
+ pv/views/trace/viewwidget.cpp
pv/views/viewbase.cpp
pv/views/trace/standardbar.cpp
pv/widgets/colourbutton.cpp
pv/prop/property.hpp
pv/prop/string.hpp
pv/toolbars/mainbar.hpp
- pv/view/analogsignal.hpp
- pv/view/cursor.hpp
- pv/view/flag.hpp
- pv/view/header.hpp
- pv/view/logicsignal.hpp
- pv/view/marginwidget.hpp
- pv/view/rowitem.hpp
- pv/view/ruler.hpp
- pv/view/signal.hpp
- pv/view/signalscalehandle.hpp
- pv/view/timeitem.hpp
- pv/view/timemarker.hpp
- pv/view/trace.hpp
- pv/view/tracegroup.hpp
- pv/view/tracetreeitem.hpp
- pv/view/triggermarker.hpp
- pv/view/view.hpp
- pv/view/viewitem.hpp
- pv/view/viewport.hpp
- pv/view/viewwidget.hpp
+ pv/views/trace/analogsignal.hpp
+ pv/views/trace/cursor.hpp
+ pv/views/trace/flag.hpp
+ pv/views/trace/header.hpp
+ pv/views/trace/logicsignal.hpp
+ pv/views/trace/marginwidget.hpp
+ pv/views/trace/rowitem.hpp
+ pv/views/trace/ruler.hpp
+ pv/views/trace/signal.hpp
+ pv/views/trace/signalscalehandle.hpp
+ pv/views/trace/timeitem.hpp
+ pv/views/trace/timemarker.hpp
+ pv/views/trace/trace.hpp
+ pv/views/trace/tracegroup.hpp
+ pv/views/trace/tracetreeitem.hpp
+ pv/views/trace/triggermarker.hpp
+ pv/views/trace/view.hpp
+ pv/views/trace/viewitem.hpp
+ pv/views/trace/viewport.hpp
+ pv/views/trace/viewwidget.hpp
pv/views/viewbase.hpp
pv/views/trace/standardbar.hpp
pv/widgets/colourbutton.hpp
pv/data/decode/decoder.cpp
pv/data/decode/row.cpp
pv/data/decode/rowdata.cpp
- pv/view/decodetrace.cpp
+ pv/views/trace/decodetrace.cpp
pv/widgets/decodergroupbox.cpp
pv/widgets/decodermenu.cpp
)
list(APPEND pulseview_HEADERS
pv/data/decoderstack.hpp
- pv/view/decodetrace.hpp
+ pv/views/trace/decodetrace.hpp
pv/widgets/decodergroupbox.hpp
pv/widgets/decodermenu.hpp
)
#include <pv/data/logic.hpp>
#include <pv/data/logicsegment.hpp>
#include <pv/session.hpp>
-#include <pv/view/logicsignal.hpp>
+#include <pv/views/trace/logicsignal.hpp>
using std::lock_guard;
using std::mutex;
#include "globalsettings.hpp"
#include "toolbars/mainbar.hpp"
#include "util.hpp"
-#include "view/view.hpp"
+#include "views/trace/view.hpp"
#include "views/trace/standardbar.hpp"
#include <libsigrokcxx/libsigrokcxx.hpp>
#include <pv/data/signalbase.hpp>
#include <pv/devices/device.hpp>
#include <pv/session.hpp>
-#include <pv/view/signal.hpp>
+#include <pv/views/trace/signal.hpp>
#include <libsigrokcxx/libsigrokcxx.hpp>
#include "toolbars/mainbar.hpp"
-#include "view/analogsignal.hpp"
-#include "view/decodetrace.hpp"
-#include "view/logicsignal.hpp"
-#include "view/signal.hpp"
-#include "view/view.hpp"
+#include "views/trace/analogsignal.hpp"
+#include "views/trace/decodetrace.hpp"
+#include "views/trace/logicsignal.hpp"
+#include "views/trace/signal.hpp"
+#include "views/trace/view.hpp"
#include <libsigrokcxx/libsigrokcxx.hpp>
#include <pv/popups/channels.hpp>
#include <pv/popups/deviceoptions.hpp>
#include <pv/util.hpp>
-#include <pv/view/view.hpp>
+#include <pv/views/trace/view.hpp>
#include <pv/widgets/exportmenu.hpp>
#include <pv/widgets/importmenu.hpp>
#ifdef ENABLE_DECODE
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <cassert>
-#include <cmath>
-#include <cstdlib>
-#include <limits>
-#include <vector>
-
-#include <QApplication>
-#include <QCheckBox>
-#include <QComboBox>
-#include <QFormLayout>
-#include <QGridLayout>
-#include <QLabel>
-#include <QString>
-
-#include "analogsignal.hpp"
-#include "pv/data/analog.hpp"
-#include "pv/data/analogsegment.hpp"
-#include "pv/data/logic.hpp"
-#include "pv/data/logicsegment.hpp"
-#include "pv/data/signalbase.hpp"
-#include "pv/globalsettings.hpp"
-#include "pv/view/logicsignal.hpp"
-#include "pv/view/view.hpp"
-
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
-using std::deque;
-using std::div;
-using std::div_t;
-using std::max;
-using std::make_pair;
-using std::min;
-using std::numeric_limits;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor AnalogSignal::SignalColours[4] = {
- QColor(0xC4, 0xA0, 0x00), // Yellow
- QColor(0x87, 0x20, 0x7A), // Magenta
- QColor(0x20, 0x4A, 0x87), // Blue
- QColor(0x4E, 0x9A, 0x06) // Green
-};
-
-const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40 * 256 / 100);
-const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20 * 256 / 100);
-
-const QColor AnalogSignal::SamplingPointColour(0x77, 0x77, 0x77);
-
-const int64_t AnalogSignal::TracePaintBlockSize = 1024 * 1024; // 4 MiB (due to float)
-const float AnalogSignal::EnvelopeThreshold = 64.0f;
-
-const int AnalogSignal::MaximumVDivs = 10;
-const int AnalogSignal::MinScaleIndex = -6;
-const int AnalogSignal::MaxScaleIndex = 7;
-
-const int AnalogSignal::InfoTextMarginRight = 20;
-const int AnalogSignal::InfoTextMarginBottom = 5;
-
-AnalogSignal::AnalogSignal(
- pv::Session &session,
- shared_ptr<data::SignalBase> base) :
- Signal(session, base),
- scale_index_(4), // 20 per div
- scale_index_drag_offset_(0),
- div_height_(3 * QFontMetrics(QApplication::font()).height()),
- pos_vdivs_(1),
- neg_vdivs_(1),
- resolution_(0),
- conversion_type_(data::SignalBase::NoConversion),
- display_type_(DisplayBoth),
- autoranging_(true)
-{
- pv::data::Analog* analog_data =
- dynamic_cast<pv::data::Analog*>(data().get());
-
- connect(analog_data, SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
- this, SLOT(on_samples_added()));
-
- base_->set_colour(SignalColours[base_->index() % countof(SignalColours)]);
- update_scale();
-}
-
-shared_ptr<pv::data::SignalData> AnalogSignal::data() const
-{
- return base_->analog_data();
-}
-
-void AnalogSignal::save_settings(QSettings &settings) const
-{
- settings.setValue("pos_vdivs", pos_vdivs_);
- settings.setValue("neg_vdivs", neg_vdivs_);
- settings.setValue("scale_index", scale_index_);
- settings.setValue("conversion_type", conversion_type_);
- settings.setValue("display_type", display_type_);
- settings.setValue("autoranging", autoranging_);
-}
-
-void AnalogSignal::restore_settings(QSettings &settings)
-{
- if (settings.contains("pos_vdivs"))
- pos_vdivs_ = settings.value("pos_vdivs").toInt();
-
- if (settings.contains("neg_vdivs"))
- neg_vdivs_ = settings.value("neg_vdivs").toInt();
-
- if (settings.contains("scale_index")) {
- scale_index_ = settings.value("scale_index").toInt();
- update_scale();
- }
-
- if (settings.contains("conversion_type")) {
- conversion_type_ = (data::SignalBase::ConversionType)(settings.value("conversion_type").toInt());
- update_conversion_type();
- }
-
- if (settings.contains("display_type"))
- display_type_ = (DisplayType)(settings.value("display_type").toInt());
-
- if (settings.contains("autoranging"))
- autoranging_ = settings.value("autoranging").toBool();
-}
-
-pair<int, int> AnalogSignal::v_extents() const
-{
- const int ph = pos_vdivs_ * div_height_;
- const int nh = neg_vdivs_ * div_height_;
- return make_pair(-ph, nh);
-}
-
-int AnalogSignal::scale_handle_offset() const
-{
- const int h = (pos_vdivs_ + neg_vdivs_) * div_height_;
-
- return ((scale_index_drag_offset_ - scale_index_) * h / 4) - h / 2;
-}
-
-void AnalogSignal::scale_handle_dragged(int offset)
-{
- const int h = (pos_vdivs_ + neg_vdivs_) * div_height_;
-
- scale_index_ = scale_index_drag_offset_ - (offset + h / 2) / (h / 4);
-
- update_scale();
-}
-
-void AnalogSignal::scale_handle_drag_release()
-{
- scale_index_drag_offset_ = scale_index_;
- update_scale();
-}
-
-void AnalogSignal::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- if (base_->enabled()) {
- Trace::paint_back(p, pp);
- paint_axis(p, pp, get_visual_y());
- }
-}
-
-void AnalogSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
-{
- assert(base_->analog_data());
- assert(owner_);
-
- const int y = get_visual_y();
-
- if (!base_->enabled())
- return;
-
- if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) {
- paint_grid(p, y, pp.left(), pp.right());
-
- const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
- base_->analog_data()->analog_segments();
- if (segments.empty())
- return;
-
- const shared_ptr<pv::data::AnalogSegment> &segment =
- segments.front();
-
- const double pixels_offset = pp.pixels_offset();
- const double samplerate = max(1.0, segment->samplerate());
- const pv::util::Timestamp& start_time = segment->start_time();
- const int64_t last_sample = segment->get_sample_count() - 1;
- const double samples_per_pixel = samplerate * pp.scale();
- const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
- const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
-
- const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
- (int64_t)0), last_sample);
- const int64_t end_sample = min(max((ceil(end) + 1).convert_to<int64_t>(),
- (int64_t)0), last_sample);
-
- if (samples_per_pixel < EnvelopeThreshold)
- paint_trace(p, segment, y, pp.left(),
- start_sample, end_sample,
- pixels_offset, samples_per_pixel);
- else
- paint_envelope(p, segment, y, pp.left(),
- start_sample, end_sample,
- pixels_offset, samples_per_pixel);
- }
-
- if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth)) {
- if (((conversion_type_ == data::SignalBase::A2LConversionByTreshold) ||
- (conversion_type_ == data::SignalBase::A2LConversionBySchmittTrigger))) {
-
- paint_logic_mid(p, pp);
- }
- }
-}
-
-void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- if (!enabled())
- return;
-
- if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) {
- const int y = get_visual_y();
-
- // Show the info section on the right side of the trace
- const QString infotext = QString("%1 V/div").arg(resolution_);
-
- p.setPen(base_->colour());
- p.setFont(QApplication::font());
-
- const QRectF bounding_rect = QRectF(pp.left(),
- y + v_extents().first,
- pp.width() - InfoTextMarginRight,
- v_extents().second - v_extents().first - InfoTextMarginBottom);
-
- p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext);
- }
-}
-
-void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right)
-{
- p.setRenderHint(QPainter::Antialiasing, false);
-
- GlobalSettings settings;
- const bool show_analog_minor_grid =
- settings.value(GlobalSettings::Key_View_ShowAnalogMinorGrid).toBool();
-
- if (pos_vdivs_ > 0) {
- p.setPen(QPen(GridMajorColor, 1, Qt::DashLine));
- for (int i = 1; i <= pos_vdivs_; i++) {
- const float dy = i * div_height_;
- p.drawLine(QLineF(left, y - dy, right, y - dy));
- }
- }
-
- if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
- p.setPen(QPen(GridMinorColor, 1, Qt::DashLine));
- for (int i = 0; i < pos_vdivs_; i++) {
- const float dy = i * div_height_;
- const float dy25 = dy + (0.25 * div_height_);
- const float dy50 = dy + (0.50 * div_height_);
- const float dy75 = dy + (0.75 * div_height_);
- p.drawLine(QLineF(left, y - dy25, right, y - dy25));
- p.drawLine(QLineF(left, y - dy50, right, y - dy50));
- p.drawLine(QLineF(left, y - dy75, right, y - dy75));
- }
- }
-
- if (neg_vdivs_ > 0) {
- p.setPen(QPen(GridMajorColor, 1, Qt::DashLine));
- for (int i = 1; i <= neg_vdivs_; i++) {
- const float dy = i * div_height_;
- p.drawLine(QLineF(left, y + dy, right, y + dy));
- }
- }
-
- if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
- p.setPen(QPen(GridMinorColor, 1, Qt::DashLine));
- for (int i = 0; i < neg_vdivs_; i++) {
- const float dy = i * div_height_;
- const float dy25 = dy + (0.25 * div_height_);
- const float dy50 = dy + (0.50 * div_height_);
- const float dy75 = dy + (0.75 * div_height_);
- p.drawLine(QLineF(left, y + dy25, right, y + dy25));
- p.drawLine(QLineF(left, y + dy50, right, y + dy50));
- p.drawLine(QLineF(left, y + dy75, right, y + dy75));
- }
- }
-
- p.setRenderHint(QPainter::Antialiasing, true);
-}
-
-void AnalogSignal::paint_trace(QPainter &p,
- const shared_ptr<pv::data::AnalogSegment> &segment,
- int y, int left, const int64_t start, const int64_t end,
- const double pixels_offset, const double samples_per_pixel)
-{
- if (end <= start)
- return;
-
- // Calculate and paint the sampling points if enabled and useful
- GlobalSettings settings;
- const bool show_sampling_points =
- settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
- (samples_per_pixel < 0.25);
-
- p.setPen(base_->colour());
-
- const int64_t points_count = end - start;
-
- QPointF *points = new QPointF[points_count];
- QPointF *point = points;
-
- QRectF *sampling_points = nullptr;
- if (show_sampling_points)
- sampling_points = new QRectF[points_count];
- QRectF *sampling_point = sampling_points;
-
- int64_t sample_count = min(points_count, TracePaintBlockSize);
- int64_t block_sample = 0;
- const float *sample_block = segment->get_samples(start, start + sample_count);
-
- const int w = 2;
- for (int64_t sample = start; sample != end; sample++, block_sample++) {
-
- if (block_sample == TracePaintBlockSize) {
- block_sample = 0;
- delete[] sample_block;
- sample_count = min(points_count - sample, TracePaintBlockSize);
- sample_block = segment->get_samples(sample, sample + sample_count);
- }
-
- const float x = (sample / samples_per_pixel -
- pixels_offset) + left;
-
- *point++ = QPointF(x, y - sample_block[block_sample] * scale_);
-
- if (show_sampling_points)
- *sampling_point++ =
- QRectF(x - (w / 2), y - sample_block[block_sample] * scale_ - (w / 2), w, w);
- }
- delete[] sample_block;
-
- p.drawPolyline(points, points_count);
-
- if (show_sampling_points) {
- p.setPen(SamplingPointColour);
- p.drawRects(sampling_points, points_count);
- delete[] sampling_points;
- }
-
- delete[] points;
-}
-
-void AnalogSignal::paint_envelope(QPainter &p,
- const shared_ptr<pv::data::AnalogSegment> &segment,
- int y, int left, const int64_t start, const int64_t end,
- const double pixels_offset, const double samples_per_pixel)
-{
- using pv::data::AnalogSegment;
-
- AnalogSegment::EnvelopeSection e;
- segment->get_envelope_section(e, start, end, samples_per_pixel);
-
- if (e.length < 2)
- return;
-
- p.setPen(QPen(Qt::NoPen));
- p.setBrush(base_->colour());
-
- QRectF *const rects = new QRectF[e.length];
- QRectF *rect = rects;
-
- for (uint64_t sample = 0; sample < e.length - 1; sample++) {
- const float x = ((e.scale * sample + e.start) /
- samples_per_pixel - pixels_offset) + left;
- const AnalogSegment::EnvelopeSample *const s =
- e.samples + sample;
-
- // We overlap this sample with the next so that vertical
- // gaps do not appear during steep rising or falling edges
- const float b = y - max(s->max, (s + 1)->min) * scale_;
- const float t = y - min(s->min, (s + 1)->max) * scale_;
-
- float h = b - t;
- if (h >= 0.0f && h <= 1.0f)
- h = 1.0f;
- if (h <= 0.0f && h >= -1.0f)
- h = -1.0f;
-
- *rect++ = QRectF(x, t, 1.0f, h);
- }
-
- p.drawRects(rects, e.length);
-
- delete[] rects;
- delete[] e.samples;
-}
-
-void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp)
-{
- QLineF *line;
-
- vector< pair<int64_t, bool> > edges;
-
- assert(base_);
-
- const int y = get_visual_y();
-
- if (!base_->enabled() || !base_->logic_data())
- return;
-
- const int signal_margin =
- QFontMetrics(QApplication::font()).height() / 2;
-
- const int ph = min(pos_vdivs_, 1) * div_height_;
- const int nh = min(neg_vdivs_, 1) * div_height_;
- const float high_offset = y - ph + signal_margin + 0.5f;
- const float low_offset = y + nh - signal_margin - 0.5f;
-
- const deque< shared_ptr<pv::data::LogicSegment> > &segments =
- base_->logic_data()->logic_segments();
-
- if (segments.empty())
- return;
-
- const shared_ptr<pv::data::LogicSegment> &segment =
- segments.front();
-
- double samplerate = segment->samplerate();
-
- // Show sample rate as 1Hz when it is unknown
- if (samplerate == 0.0)
- samplerate = 1.0;
-
- const double pixels_offset = pp.pixels_offset();
- const pv::util::Timestamp& start_time = segment->start_time();
- const int64_t last_sample = segment->get_sample_count() - 1;
- const double samples_per_pixel = samplerate * pp.scale();
- const double pixels_per_sample = 1 / samples_per_pixel;
- const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
- const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
-
- const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
- (int64_t)0), last_sample);
- const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
- (int64_t)0), last_sample);
-
- segment->get_subsampled_edges(edges, start_sample, end_sample,
- samples_per_pixel / LogicSignal::Oversampling, 0);
- assert(edges.size() >= 2);
-
- // Check whether we need to paint the sampling points
- GlobalSettings settings;
- const bool show_sampling_points =
- settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
- (samples_per_pixel < 0.25);
-
- vector<QRectF> sampling_points;
- float sampling_point_x = 0.0f;
- int64_t sampling_point_sample = start_sample;
- const int w = 2;
-
- if (show_sampling_points) {
- sampling_points.reserve(end_sample - start_sample + 1);
- sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left();
- }
-
- // Paint the edges
- const unsigned int edge_count = edges.size() - 2;
- QLineF *const edge_lines = new QLineF[edge_count];
- line = edge_lines;
-
- for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
- const float x = ((*i).first / samples_per_pixel -
- pixels_offset) + pp.left();
- *line++ = QLineF(x, high_offset, x, low_offset);
-
- if (show_sampling_points)
- while (sampling_point_sample < (*i).first) {
- const float y = (*i).second ? low_offset : high_offset;
- sampling_points.emplace_back(
- QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
- sampling_point_sample++;
- sampling_point_x += pixels_per_sample;
- };
- }
-
- // Calculate the sample points from the last edge to the end of the trace
- if (show_sampling_points)
- while ((uint64_t)sampling_point_sample <= end_sample) {
- // Signal changed after the last edge, so the level is inverted
- const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
- sampling_points.emplace_back(
- QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
- sampling_point_sample++;
- sampling_point_x += pixels_per_sample;
- };
-
- p.setPen(LogicSignal::EdgeColour);
- p.drawLines(edge_lines, edge_count);
- delete[] edge_lines;
-
- // Paint the caps
- const unsigned int max_cap_line_count = edges.size();
- QLineF *const cap_lines = new QLineF[max_cap_line_count];
-
- p.setPen(LogicSignal::HighColour);
- paint_logic_caps(p, cap_lines, edges, true, samples_per_pixel,
- pixels_offset, pp.left(), high_offset);
- p.setPen(LogicSignal::LowColour);
- paint_logic_caps(p, cap_lines, edges, false, samples_per_pixel,
- pixels_offset, pp.left(), low_offset);
-
- delete[] cap_lines;
-
- // Paint the sampling points
- if (show_sampling_points) {
- p.setPen(SamplingPointColour);
- p.drawRects(sampling_points.data(), sampling_points.size());
- }
-}
-
-void AnalogSignal::paint_logic_caps(QPainter &p, QLineF *const lines,
- vector< pair<int64_t, bool> > &edges, bool level,
- double samples_per_pixel, double pixels_offset, float x_offset,
- float y_offset)
-{
- QLineF *line = lines;
-
- for (auto i = edges.begin(); i != (edges.end() - 1); i++)
- if ((*i).second == level) {
- *line++ = QLineF(
- ((*i).first / samples_per_pixel -
- pixels_offset) + x_offset, y_offset,
- ((*(i+1)).first / samples_per_pixel -
- pixels_offset) + x_offset, y_offset);
- }
-
- p.drawLines(lines, line - lines);
-}
-
-float AnalogSignal::get_resolution(int scale_index)
-{
- const float seq[] = {1.0f, 2.0f, 5.0f};
-
- const int offset = numeric_limits<int>::max() / (2 * countof(seq));
- const div_t d = div((int)(scale_index + countof(seq) * offset),
- countof(seq));
-
- return powf(10.0f, d.quot - offset) * seq[d.rem];
-}
-
-void AnalogSignal::update_scale()
-{
- resolution_ = get_resolution(scale_index_);
- scale_ = div_height_ / resolution_;
-}
-
-void AnalogSignal::update_conversion_type()
-{
- base_->set_conversion_type(conversion_type_);
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
-{
- const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
- base_->analog_data()->analog_segments();
-
- if (segments.empty())
- return;
-
- static double prev_min = 0, prev_max = 0;
- double min = 0, max = 0;
-
- for (shared_ptr<pv::data::AnalogSegment> segment : segments) {
- pair<double, double> mm = segment->get_min_max();
- min = std::min(min, mm.first);
- max = std::max(max, mm.second);
- }
-
- if ((min == prev_min) && (max == prev_max) && !force_update)
- return;
-
- prev_min = min;
- prev_max = max;
-
- // If we're allowed to alter the div assignment...
- if (!keep_divs) {
- // Use all divs for the positive range if there are no negative values
- if ((min == 0) && (neg_vdivs_ > 0)) {
- pos_vdivs_ += neg_vdivs_;
- neg_vdivs_ = 0;
- }
-
- // Split up the divs if there are negative values but no negative divs
- if ((min < 0) && (neg_vdivs_ == 0)) {
- neg_vdivs_ = pos_vdivs_ / 2;
- pos_vdivs_ -= neg_vdivs_;
- }
- }
-
- // If there is still no positive div when we need it, add one
- // (this can happen when pos_vdivs==neg_vdivs==0)
- if ((max > 0) && (pos_vdivs_ == 0)) {
- pos_vdivs_ = 1;
- owner_->extents_changed(false, true);
- }
-
- // If there is still no negative div when we need it, add one
- // (this can happen when pos_vdivs was 0 or 1 when trying to split)
- if ((min < 0) && (neg_vdivs_ == 0)) {
- neg_vdivs_ = 1;
- owner_->extents_changed(false, true);
- }
-
- double min_value_per_div;
- if ((pos_vdivs_ > 0) && (neg_vdivs_ > 0))
- min_value_per_div = std::max(max / pos_vdivs_, -min / neg_vdivs_);
- else if (pos_vdivs_ > 0)
- min_value_per_div = max / pos_vdivs_;
- else
- min_value_per_div = -min / neg_vdivs_;
-
- // Find first scale value that is bigger than the value we need
- for (int i = MinScaleIndex; i < MaxScaleIndex; i++)
- if (get_resolution(i) > min_value_per_div) {
- scale_index_ = i;
- break;
- }
-
- update_scale();
-}
-
-void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
-{
- // Add the standard options
- Signal::populate_popup_form(parent, form);
-
- QFormLayout *const layout = new QFormLayout;
-
- // Add the number of vdivs
- pvdiv_sb_ = new QSpinBox(parent);
- pvdiv_sb_->setRange(0, MaximumVDivs);
- pvdiv_sb_->setValue(pos_vdivs_);
- connect(pvdiv_sb_, SIGNAL(valueChanged(int)),
- this, SLOT(on_pos_vdivs_changed(int)));
- layout->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
-
- nvdiv_sb_ = new QSpinBox(parent);
- nvdiv_sb_->setRange(0, MaximumVDivs);
- nvdiv_sb_->setValue(neg_vdivs_);
- connect(nvdiv_sb_, SIGNAL(valueChanged(int)),
- this, SLOT(on_neg_vdivs_changed(int)));
- layout->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
-
- // Add the vertical resolution
- resolution_cb_ = new QComboBox(parent);
-
- for (int i = MinScaleIndex; i < MaxScaleIndex; i++) {
- const QString label = QString("%1").arg(get_resolution(i));
- resolution_cb_->insertItem(0, label, QVariant(i));
- }
-
- int cur_idx = resolution_cb_->findData(QVariant(scale_index_));
- resolution_cb_->setCurrentIndex(cur_idx);
-
- connect(resolution_cb_, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_resolution_changed(int)));
-
- QGridLayout *const vdiv_layout = new QGridLayout;
- QLabel *const vdiv_unit = new QLabel(tr("V/div"));
- vdiv_layout->addWidget(resolution_cb_, 0, 0);
- vdiv_layout->addWidget(vdiv_unit, 0, 1);
-
- layout->addRow(tr("Vertical resolution"), vdiv_layout);
-
- // Add the autoranging checkbox
- QCheckBox* autoranging_cb = new QCheckBox();
- autoranging_cb->setCheckState(autoranging_ ? Qt::Checked : Qt::Unchecked);
-
- connect(autoranging_cb, SIGNAL(stateChanged(int)),
- this, SLOT(on_autoranging_changed(int)));
-
- layout->addRow(tr("Autoranging"), autoranging_cb);
-
- // Add the conversion type dropdown
- conversion_cb_ = new QComboBox();
-
- conversion_cb_->addItem("none", data::SignalBase::NoConversion);
- conversion_cb_->addItem("to logic via threshold", data::SignalBase::A2LConversionByTreshold);
- conversion_cb_->addItem("to logic via schmitt-trigger", data::SignalBase::A2LConversionBySchmittTrigger);
-
- cur_idx = conversion_cb_->findData(QVariant(conversion_type_));
- conversion_cb_->setCurrentIndex(cur_idx);
-
-// layout->addRow(tr("Conversion"), conversion_cb_);
-
- connect(conversion_cb_, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_conversion_changed(int)));
-
- // Add the display type dropdown
- display_type_cb_ = new QComboBox();
-
- display_type_cb_->addItem(tr("Analog"), DisplayAnalog);
- display_type_cb_->addItem(tr("Converted"), DisplayConverted);
- display_type_cb_->addItem(tr("Both"), DisplayBoth);
-
- cur_idx = display_type_cb_->findData(QVariant(display_type_));
- display_type_cb_->setCurrentIndex(cur_idx);
-
-// layout->addRow(tr("Traces to show:"), display_type_cb_);
-
- connect(display_type_cb_, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_display_type_changed(int)));
-
- form->addRow(layout);
-}
-
-void AnalogSignal::on_samples_added()
-{
- perform_autoranging(false, false);
-}
-
-void AnalogSignal::on_pos_vdivs_changed(int vdivs)
-{
- if (vdivs == pos_vdivs_)
- return;
-
- pos_vdivs_ = vdivs;
-
- // There has to be at least one div, positive or negative
- if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) {
- pos_vdivs_ = 1;
- if (pvdiv_sb_)
- pvdiv_sb_->setValue(pos_vdivs_);
- }
-
- if (autoranging_) {
- perform_autoranging(true, true);
-
- // It could be that a positive or negative div was added, so update
- if (pvdiv_sb_) {
- pvdiv_sb_->setValue(pos_vdivs_);
- nvdiv_sb_->setValue(neg_vdivs_);
- }
- }
-
- if (owner_) {
- // Call order is important, otherwise the lazy event handler won't work
- owner_->extents_changed(false, true);
- owner_->row_item_appearance_changed(false, true);
- }
-}
-
-void AnalogSignal::on_neg_vdivs_changed(int vdivs)
-{
- if (vdivs == neg_vdivs_)
- return;
-
- neg_vdivs_ = vdivs;
-
- // There has to be at least one div, positive or negative
- if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) {
- pos_vdivs_ = 1;
- if (pvdiv_sb_)
- pvdiv_sb_->setValue(pos_vdivs_);
- }
-
- if (autoranging_) {
- perform_autoranging(true, true);
-
- // It could be that a positive or negative div was added, so update
- if (pvdiv_sb_) {
- pvdiv_sb_->setValue(pos_vdivs_);
- nvdiv_sb_->setValue(neg_vdivs_);
- }
- }
-
- if (owner_) {
- // Call order is important, otherwise the lazy event handler won't work
- owner_->extents_changed(false, true);
- owner_->row_item_appearance_changed(false, true);
- }
-}
-
-void AnalogSignal::on_resolution_changed(int index)
-{
- scale_index_ = resolution_cb_->itemData(index).toInt();
- update_scale();
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-void AnalogSignal::on_autoranging_changed(int state)
-{
- autoranging_ = (state == Qt::Checked);
-
- if (autoranging_)
- perform_autoranging(false, true);
-
- if (owner_) {
- // Call order is important, otherwise the lazy event handler won't work
- owner_->extents_changed(false, true);
- owner_->row_item_appearance_changed(false, true);
- }
-}
-
-void AnalogSignal::on_conversion_changed(int index)
-{
- data::SignalBase::ConversionType old_conv_type = conversion_type_;
-
- conversion_type_ = (data::SignalBase::ConversionType)(conversion_cb_->itemData(index).toInt());
-
- if (conversion_type_ != old_conv_type) {
- base_->set_conversion_type(conversion_type_);
- update_conversion_type();
- }
-}
-
-void AnalogSignal::on_display_type_changed(int index)
-{
- display_type_ = (DisplayType)(display_type_cb_->itemData(index).toInt());
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
-
-#include "signal.hpp"
-
-#include <memory>
-
-#include <QComboBox>
-#include <QSpinBox>
-
-using std::pair;
-using std::shared_ptr;
-
-namespace pv {
-
-namespace data {
-class Analog;
-class AnalogSegment;
-class SignalBase;
-}
-
-namespace views {
-namespace TraceView {
-
-class AnalogSignal : public Signal
-{
- Q_OBJECT
-
-private:
- static const QColor SignalColours[4];
- static const QColor GridMajorColor, GridMinorColor;
- static const QColor SamplingPointColour;
-
- static const int64_t TracePaintBlockSize;
- static const float EnvelopeThreshold;
-
- static const int MaximumVDivs;
- static const int MaxScaleIndex, MinScaleIndex;
- static const int InfoTextMarginRight, InfoTextMarginBottom;
-
- enum DisplayType {
- DisplayAnalog = 0,
- DisplayConverted = 1,
- DisplayBoth = 2
- };
-
-public:
- AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
-
- virtual ~AnalogSignal() = default;
-
- shared_ptr<pv::data::SignalData> data() const;
-
- virtual void save_settings(QSettings &settings) const;
-
- virtual void restore_settings(QSettings &settings);
-
- /**
- * Computes the vertical extents of the contents of this row item.
- * @return A pair containing the minimum and maximum y-values.
- */
- pair<int, int> v_extents() const;
-
- /**
- * Returns the offset to show the drag handle.
- */
- int scale_handle_offset() const;
-
- /**
- * Handles the scale handle being dragged to an offset.
- * @param offset the offset the scale handle was dragged to.
- */
- void scale_handle_dragged(int offset);
-
- /**
- * @copydoc pv::view::Signal::signal_scale_handle_drag_release()
- */
- void scale_handle_drag_release();
-
- /**
- * Paints the background layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with..
- */
- void paint_back(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the mid-layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with..
- */
- void paint_mid(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the foreground layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_fore(QPainter &p, ViewItemPaintParams &pp);
-
-private:
- void paint_grid(QPainter &p, int y, int left, int right);
-
- void paint_trace(QPainter &p,
- const shared_ptr<pv::data::AnalogSegment> &segment,
- int y, int left, const int64_t start, const int64_t end,
- const double pixels_offset, const double samples_per_pixel);
-
- void paint_envelope(QPainter &p,
- const shared_ptr<pv::data::AnalogSegment> &segment,
- int y, int left, const int64_t start, const int64_t end,
- const double pixels_offset, const double samples_per_pixel);
-
- void paint_logic_mid(QPainter &p, ViewItemPaintParams &pp);
-
- void paint_logic_caps(QPainter &p, QLineF *const lines,
- vector< pair<int64_t, bool> > &edges,
- bool level, double samples_per_pixel, double pixels_offset,
- float x_offset, float y_offset);
-
- /**
- * Computes the scale factor from the scale index and vdiv settings.
- */
- float get_resolution(int scale_index);
-
- void update_scale();
-
- void update_conversion_type();
-
- void perform_autoranging(bool keep_divs, bool force_update);
-
-protected:
- void populate_popup_form(QWidget *parent, QFormLayout *form);
-
-private Q_SLOTS:
- void on_samples_added();
-
- void on_pos_vdivs_changed(int vdivs);
- void on_neg_vdivs_changed(int vdivs);
-
- void on_resolution_changed(int index);
-
- void on_autoranging_changed(int state);
-
- void on_conversion_changed(int index);
-
- void on_display_type_changed(int index);
-
-private:
- QComboBox *resolution_cb_, *conversion_cb_, *display_type_cb_;
- QSpinBox *pvdiv_sb_, *nvdiv_sb_;
-
- float scale_;
- int scale_index_;
- int scale_index_drag_offset_;
-
- int div_height_;
- int pos_vdivs_, neg_vdivs_; // divs per positive/negative side
- float resolution_; // e.g. 10 for 10 V/div
-
- data::SignalBase::ConversionType conversion_type_;
- DisplayType display_type_;
- bool autoranging_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "cursor.hpp"
-
-#include "pv/util.hpp"
-#include "ruler.hpp"
-#include "view.hpp"
-
-#include <QApplication>
-#include <QBrush>
-#include <QPainter>
-#include <QPointF>
-#include <QRect>
-#include <QRectF>
-
-#include <cassert>
-#include <cstdio>
-#include <limits>
-
-using std::abs; // Force usage of std::abs() instead of C's abs().
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor Cursor::FillColour(52, 101, 164);
-
-Cursor::Cursor(View &view, double time) :
- TimeMarker(view, FillColour, time)
-{
-}
-
-bool Cursor::enabled() const
-{
- return view_.cursors_shown();
-}
-
-QString Cursor::get_text() const
-{
- const shared_ptr<Cursor> other = get_other_cursor();
- const pv::util::Timestamp& diff = abs(time_ - other->time_);
-
- return Ruler::format_time_with_distance(
- diff, time_, view_.tick_prefix(), view_.time_unit(), view_.tick_precision());
-}
-
-QRectF Cursor::label_rect(const QRectF &rect) const
-{
- const shared_ptr<Cursor> other(get_other_cursor());
- assert(other);
-
- const float x = get_x();
-
- QFontMetrics m(QApplication::font());
- QSize text_size = m.boundingRect(get_text()).size();
-
- const QSizeF label_size(
- text_size.width() + LabelPadding.width() * 2,
- text_size.height() + LabelPadding.height() * 2);
- const float top = rect.height() - label_size.height() -
- TimeMarker::ArrowSize - 0.5f;
- const float height = label_size.height();
-
- const pv::util::Timestamp& other_time = other->time();
-
- if (time_ > other_time ||
- (abs(time_ - other_time).is_zero() && this > other.get()))
- return QRectF(x, top, label_size.width(), height);
- else
- return QRectF(x - label_size.width(), top, label_size.width(), height);
-}
-
-shared_ptr<Cursor> Cursor::get_other_cursor() const
-{
- const shared_ptr<CursorPair> cursors(view_.cursors());
- assert(cursors);
- return (cursors->first().get() == this) ?
- cursors->second() : cursors->first();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
-
-#include "timemarker.hpp"
-
-#include <memory>
-
-#include <QSizeF>
-
-using std::shared_ptr;
-
-class QPainter;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class Cursor : public TimeMarker
-{
- Q_OBJECT
-
-public:
- static const QColor FillColour;
-
-public:
- /**
- * Constructor.
- * @param view A reference to the view that owns this cursor pair.
- * @param time The time to set the flag to.
- */
- Cursor(View &view, double time);
-
-public:
- /**
- * Returns true if the item is visible and enabled.
- */
- bool enabled() const;
-
- /**
- * Gets the text to show in the marker.
- */
- QString get_text() const;
-
- /**
- * Gets the marker label rectangle.
- * @param rect The rectangle of the ruler client area.
- * @return Returns the label rectangle.
- */
- QRectF label_rect(const QRectF &rect) const;
-
-private:
- shared_ptr<Cursor> get_other_cursor() const;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "cursorpair.hpp"
-
-#include "pv/util.hpp"
-#include "ruler.hpp"
-#include "view.hpp"
-
-#include <algorithm>
-#include <cassert>
-
-using std::max;
-using std::make_pair;
-using std::min;
-using std::shared_ptr;
-using std::pair;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const int CursorPair::DeltaPadding = 8;
-const QColor CursorPair::ViewportFillColour(220, 231, 243);
-
-CursorPair::CursorPair(View &view) :
- TimeItem(view),
- first_(new Cursor(view, 0.0)),
- second_(new Cursor(view, 1.0))
-{
-}
-
-bool CursorPair::enabled() const
-{
- return view_.cursors_shown();
-}
-
-shared_ptr<Cursor> CursorPair::first() const
-{
- return first_;
-}
-
-shared_ptr<Cursor> CursorPair::second() const
-{
- return second_;
-}
-
-void CursorPair::set_time(const pv::util::Timestamp& time)
-{
- const pv::util::Timestamp delta = second_->time() - first_->time();
- first_->set_time(time);
- second_->set_time(time + delta);
-}
-
-float CursorPair::get_x() const
-{
- return (first_->get_x() + second_->get_x()) / 2.0f;
-}
-
-QPoint CursorPair::point(const QRect &rect) const
-{
- return first_->point(rect);
-}
-
-pv::widgets::Popup* CursorPair::create_popup(QWidget *parent)
-{
- (void)parent;
- return nullptr;
-}
-
-QRectF CursorPair::label_rect(const QRectF &rect) const
-{
- const QSizeF label_size(text_size_ + LabelPadding * 2);
- const pair<float, float> offsets(get_cursor_offsets());
- const pair<float, float> normal_offsets(
- (offsets.first < offsets.second) ? offsets :
- make_pair(offsets.second, offsets.first));
-
- const float height = label_size.height();
- const float left = max(normal_offsets.first + DeltaPadding, -height);
- const float right = min(normal_offsets.second - DeltaPadding,
- (float)rect.width() + height);
-
- return QRectF(left, rect.height() - label_size.height() -
- TimeMarker::ArrowSize - 0.5f,
- right - left, height);
-}
-
-void CursorPair::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
- assert(first_);
- assert(second_);
-
- if (!enabled())
- return;
-
- const QColor text_colour =
- ViewItem::select_text_colour(Cursor::FillColour);
-
- p.setPen(text_colour);
- compute_text_size(p);
- QRectF delta_rect(label_rect(rect));
-
- const int radius = delta_rect.height() / 2;
- const QRectF text_rect(delta_rect.intersected(
- rect).adjusted(radius, 0, -radius, 0));
- if (text_rect.width() >= text_size_.width()) {
- const int highlight_radius = delta_rect.height() / 2 - 2;
-
- if (selected()) {
- p.setBrush(Qt::transparent);
- p.setPen(highlight_pen());
- p.drawRoundedRect(delta_rect, radius, radius);
- }
-
- p.setBrush(hover ? Cursor::FillColour.lighter() :
- Cursor::FillColour);
- p.setPen(Cursor::FillColour.darker());
- p.drawRoundedRect(delta_rect, radius, radius);
-
- delta_rect.adjust(1, 1, -1, -1);
- p.setPen(Cursor::FillColour.lighter());
- p.drawRoundedRect(delta_rect, highlight_radius, highlight_radius);
-
- p.setPen(text_colour);
- p.drawText(text_rect, Qt::AlignCenter | Qt::AlignVCenter,
- format_string());
- }
-}
-
-void CursorPair::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- if (!enabled())
- return;
-
- p.setPen(Qt::NoPen);
- p.setBrush(QBrush(ViewportFillColour));
-
- const pair<float, float> offsets(get_cursor_offsets());
- const int l = (int)max(min(
- offsets.first, offsets.second), 0.0f);
- const int r = (int)min(max(
- offsets.first, offsets.second), (float)pp.width());
-
- p.drawRect(l, pp.top(), r - l, pp.height());
-}
-
-QString CursorPair::format_string()
-{
- const pv::util::SIPrefix prefix = view_.tick_prefix();
- const pv::util::Timestamp diff = abs(second_->time() - first_->time());
-
- const QString s1 = Ruler::format_time_with_distance(
- diff, diff, prefix, view_.time_unit(), view_.tick_precision(), false);
- const QString s2 = util::format_time_si(
- 1 / diff, pv::util::SIPrefix::unspecified, 4, "Hz", false);
-
- return QString("%1 / %2").arg(s1).arg(s2);
-}
-
-void CursorPair::compute_text_size(QPainter &p)
-{
- assert(first_);
- assert(second_);
-
- text_size_ = p.boundingRect(QRectF(), 0, format_string()).size();
-}
-
-pair<float, float> CursorPair::get_cursor_offsets() const
-{
- assert(first_);
- assert(second_);
-
- return pair<float, float>(first_->get_x(), second_->get_x());
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
-
-#include "cursor.hpp"
-
-#include <memory>
-
-#include <QPainter>
-
-using std::pair;
-using std::shared_ptr;
-
-class QPainter;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class CursorPair : public TimeItem
-{
-private:
- static const int DeltaPadding;
- static const QColor ViewportFillColour;
-
-public:
- /**
- * Constructor.
- * @param view A reference to the view that owns this cursor pair.
- */
- CursorPair(View &view);
-
-public:
- /**
- * Returns true if the item is visible and enabled.
- */
- bool enabled() const override;
-
- /**
- * Returns a pointer to the first cursor.
- */
- shared_ptr<Cursor> first() const;
-
- /**
- * Returns a pointer to the second cursor.
- */
- shared_ptr<Cursor> second() const;
-
- /**
- * Sets the time of the marker.
- */
- void set_time(const pv::util::Timestamp& time) override;
-
- float get_x() const override;
-
- QPoint point(const QRect &rect) const override;
-
- pv::widgets::Popup* create_popup(QWidget *parent) override;
-
-public:
- QRectF label_rect(const QRectF &rect) const override;
-
- /**
- * Paints the marker's label to the ruler.
- * @param p The painter to draw with.
- * @param rect The rectangle of the ruler client area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- void paint_label(QPainter &p, const QRect &rect, bool hover) override;
-
- /**
- * Paints the background layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_back(QPainter &p, ViewItemPaintParams &pp) override;
-
- /**
- * Constructs the string to display.
- */
- QString format_string();
-
- void compute_text_size(QPainter &p);
-
- pair<float, float> get_cursor_offsets() const;
-
-private:
- shared_ptr<Cursor> first_, second_;
-
- QSizeF text_size_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-extern "C" {
-#include <libsigrokdecode/libsigrokdecode.h>
-}
-
-#include <mutex>
-
-#include <extdef.h>
-
-#include <tuple>
-
-#include <boost/functional/hash.hpp>
-
-#include <QAction>
-#include <QApplication>
-#include <QComboBox>
-#include <QFormLayout>
-#include <QLabel>
-#include <QMenu>
-#include <QPushButton>
-#include <QToolTip>
-
-#include "decodetrace.hpp"
-
-#include <pv/globalsettings.hpp>
-#include <pv/data/decode/annotation.hpp>
-#include <pv/data/decode/decoder.hpp>
-#include <pv/data/decoderstack.hpp>
-#include <pv/data/logic.hpp>
-#include <pv/data/logicsegment.hpp>
-#include <pv/session.hpp>
-#include <pv/strnatcmp.hpp>
-#include <pv/view/view.hpp>
-#include <pv/view/viewport.hpp>
-#include <pv/widgets/decodergroupbox.hpp>
-#include <pv/widgets/decodermenu.hpp>
-
-using std::all_of;
-using std::list;
-using std::make_pair;
-using std::max;
-using std::make_pair;
-using std::map;
-using std::min;
-using std::out_of_range;
-using std::pair;
-using std::shared_ptr;
-using std::make_shared;
-using std::tie;
-using std::unordered_set;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor DecodeTrace::DecodeColours[4] = {
- QColor(0xEF, 0x29, 0x29), // Red
- QColor(0xFC, 0xE9, 0x4F), // Yellow
- QColor(0x8A, 0xE2, 0x34), // Green
- QColor(0x72, 0x9F, 0xCF) // Blue
-};
-
-const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
-const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
-
-const int DecodeTrace::ArrowSize = 4;
-const double DecodeTrace::EndCapWidth = 5;
-const int DecodeTrace::RowTitleMargin = 10;
-const int DecodeTrace::DrawPadding = 100;
-
-const QColor DecodeTrace::Colours[16] = {
- QColor(0xEF, 0x29, 0x29),
- QColor(0xF6, 0x6A, 0x32),
- QColor(0xFC, 0xAE, 0x3E),
- QColor(0xFB, 0xCA, 0x47),
- QColor(0xFC, 0xE9, 0x4F),
- QColor(0xCD, 0xF0, 0x40),
- QColor(0x8A, 0xE2, 0x34),
- QColor(0x4E, 0xDC, 0x44),
- QColor(0x55, 0xD7, 0x95),
- QColor(0x64, 0xD1, 0xD2),
- QColor(0x72, 0x9F, 0xCF),
- QColor(0xD4, 0x76, 0xC4),
- QColor(0x9D, 0x79, 0xB9),
- QColor(0xAD, 0x7F, 0xA8),
- QColor(0xC2, 0x62, 0x9B),
- QColor(0xD7, 0x47, 0x6F)
-};
-
-const QColor DecodeTrace::OutlineColours[16] = {
- QColor(0x77, 0x14, 0x14),
- QColor(0x7B, 0x35, 0x19),
- QColor(0x7E, 0x57, 0x1F),
- QColor(0x7D, 0x65, 0x23),
- QColor(0x7E, 0x74, 0x27),
- QColor(0x66, 0x78, 0x20),
- QColor(0x45, 0x71, 0x1A),
- QColor(0x27, 0x6E, 0x22),
- QColor(0x2A, 0x6B, 0x4A),
- QColor(0x32, 0x68, 0x69),
- QColor(0x39, 0x4F, 0x67),
- QColor(0x6A, 0x3B, 0x62),
- QColor(0x4E, 0x3C, 0x5C),
- QColor(0x56, 0x3F, 0x54),
- QColor(0x61, 0x31, 0x4D),
- QColor(0x6B, 0x23, 0x37)
-};
-
-DecodeTrace::DecodeTrace(pv::Session &session,
- shared_ptr<data::SignalBase> signalbase, int index) :
- Trace(signalbase),
- session_(session),
- row_height_(0),
- max_visible_rows_(0),
- delete_mapper_(this),
- show_hide_mapper_(this)
-{
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- // Determine shortest string we want to see displayed in full
- QFontMetrics m(QApplication::font());
- min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
-
- base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
- base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
-
- connect(decoder_stack.get(), SIGNAL(new_decode_data()),
- this, SLOT(on_new_decode_data()));
- connect(&delete_mapper_, SIGNAL(mapped(int)),
- this, SLOT(on_delete_decoder(int)));
- connect(&show_hide_mapper_, SIGNAL(mapped(int)),
- this, SLOT(on_show_hide_decoder(int)));
-}
-
-bool DecodeTrace::enabled() const
-{
- return true;
-}
-
-shared_ptr<data::SignalBase> DecodeTrace::base() const
-{
- return base_;
-}
-
-pair<int, int> DecodeTrace::v_extents() const
-{
- const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
-
- // Make an empty decode trace appear symmetrical
- const int row_count = max(1, max_visible_rows_);
-
- return make_pair(-row_height, row_height * row_count);
-}
-
-void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- Trace::paint_back(p, pp);
- paint_axis(p, pp, get_visual_y());
-}
-
-void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
-{
- using namespace pv::data::decode;
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- const int text_height = ViewItemPaintParams::text_height();
- row_height_ = (text_height * 6) / 4;
- const int annotation_height = (text_height * 5) / 4;
-
- assert(decoder_stack);
- const QString err = decoder_stack->error_message();
- if (!err.isEmpty()) {
- draw_unresolved_period(
- p, annotation_height, pp.left(), pp.right());
- draw_error(p, err, pp);
- return;
- }
-
- // Set default pen to allow for text width calculation
- p.setPen(Qt::black);
-
- // Iterate through the rows
- int y = get_visual_y();
- pair<uint64_t, uint64_t> sample_range = get_sample_range(
- pp.left(), pp.right());
-
- const vector<Row> rows(decoder_stack->get_visible_rows());
-
- visible_rows_.clear();
- for (const Row& row : rows) {
- // Cache the row title widths
- int row_title_width;
- try {
- row_title_width = row_title_widths_.at(row);
- } catch (out_of_range) {
- const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
- RowTitleMargin;
- row_title_widths_[row] = w;
- row_title_width = w;
- }
-
- // Determine the row's color
- size_t base_colour = 0x13579BDF;
- boost::hash_combine(base_colour, this);
- boost::hash_combine(base_colour, row.decoder());
- boost::hash_combine(base_colour, row.row());
- base_colour >>= 16;
-
- vector<Annotation> annotations;
- decoder_stack->get_annotation_subset(annotations, row,
- sample_range.first, sample_range.second);
- if (!annotations.empty()) {
- draw_annotations(annotations, p, annotation_height, pp, y,
- base_colour, row_title_width);
-
- y += row_height_;
-
- visible_rows_.push_back(row);
- }
- }
-
- // Draw the hatching
- draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
-
- if ((int)visible_rows_.size() > max_visible_rows_)
- owner_->extents_changed(false, true);
-
- // Update the maximum row count if needed
- max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
-}
-
-void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- using namespace pv::data::decode;
-
- assert(row_height_);
-
- for (size_t i = 0; i < visible_rows_.size(); i++) {
- const int y = i * row_height_ + get_visual_y();
-
- p.setPen(QPen(Qt::NoPen));
- p.setBrush(QApplication::palette().brush(QPalette::WindowText));
-
- if (i != 0) {
- const QPointF points[] = {
- QPointF(pp.left(), y - ArrowSize),
- QPointF(pp.left() + ArrowSize, y),
- QPointF(pp.left(), y + ArrowSize)
- };
- p.drawPolygon(points, countof(points));
- }
-
- const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
- pp.right() - pp.left(), row_height_);
- const QString h(visible_rows_[i].title());
- const int f = Qt::AlignLeft | Qt::AlignVCenter |
- Qt::TextDontClip;
-
- // Draw the outline
- p.setPen(QApplication::palette().color(QPalette::Base));
- for (int dx = -1; dx <= 1; dx++)
- for (int dy = -1; dy <= 1; dy++)
- if (dx != 0 && dy != 0)
- p.drawText(r.translated(dx, dy), f, h);
-
- // Draw the text
- p.setPen(QApplication::palette().color(QPalette::WindowText));
- p.drawText(r, f, h);
- }
-}
-
-void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
-{
- using pv::data::decode::Decoder;
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(form);
- assert(parent);
- assert(decoder_stack);
-
- // Add the standard options
- Trace::populate_popup_form(parent, form);
-
- // Add the decoder options
- bindings_.clear();
- channel_selectors_.clear();
- decoder_forms_.clear();
-
- const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
-
- if (stack.empty()) {
- QLabel *const l = new QLabel(
- tr("<p><i>No decoders in the stack</i></p>"));
- l->setAlignment(Qt::AlignCenter);
- form->addRow(l);
- } else {
- auto iter = stack.cbegin();
- for (int i = 0; i < (int)stack.size(); i++, iter++) {
- shared_ptr<Decoder> dec(*iter);
- create_decoder_form(i, dec, parent, form);
- }
-
- form->addRow(new QLabel(
- tr("<i>* Required channels</i>"), parent));
- }
-
- // Add stacking button
- pv::widgets::DecoderMenu *const decoder_menu =
- new pv::widgets::DecoderMenu(parent);
- connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
- this, SLOT(on_stack_decoder(srd_decoder*)));
-
- QPushButton *const stack_button =
- new QPushButton(tr("Stack Decoder"), parent);
- stack_button->setMenu(decoder_menu);
- stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
-
- QHBoxLayout *stack_button_box = new QHBoxLayout;
- stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
- form->addRow(stack_button_box);
-}
-
-QMenu* DecodeTrace::create_context_menu(QWidget *parent)
-{
- QMenu *const menu = Trace::create_context_menu(parent);
-
- menu->addSeparator();
-
- QAction *const del = new QAction(tr("Delete"), this);
- del->setShortcuts(QKeySequence::Delete);
- connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
- menu->addAction(del);
-
- return menu;
-}
-
-void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
- QPainter &p, int h, const ViewItemPaintParams &pp, int y,
- size_t base_colour, int row_title_width)
-{
- using namespace pv::data::decode;
-
- vector<Annotation> a_block;
- int p_end = INT_MIN;
-
- double samples_per_pixel, pixels_offset;
- tie(pixels_offset, samples_per_pixel) =
- get_pixels_offset_samples_per_pixel();
-
- // Sort the annotations by start sample so that decoders
- // can't confuse us by creating annotations out of order
- stable_sort(annotations.begin(), annotations.end(),
- [](const Annotation &a, const Annotation &b) {
- return a.start_sample() < b.start_sample(); });
-
- // Gather all annotations that form a visual "block" and draw them as such
- for (const Annotation &a : annotations) {
-
- const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
- const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
- const int a_width = a_end - a_start;
-
- const int delta = a_end - p_end;
-
- bool a_is_separate = false;
-
- // Annotation wider than the threshold for a useful label width?
- if (a_width >= min_useful_label_width_) {
- for (const QString &ann_text : a.annotations()) {
- const int w = p.boundingRect(QRectF(), 0, ann_text).width();
- // Annotation wide enough to fit a label? Don't put it in a block then
- if (w <= a_width) {
- a_is_separate = true;
- break;
- }
- }
- }
-
- // Were the previous and this annotation more than a pixel apart?
- if ((abs(delta) > 1) || a_is_separate) {
- // Block was broken, draw annotations that form the current block
- if (a_block.size() == 1) {
- draw_annotation(a_block.front(), p, h, pp, y, base_colour,
- row_title_width);
- }
- else
- draw_annotation_block(a_block, p, h, y, base_colour);
-
- a_block.clear();
- }
-
- if (a_is_separate) {
- draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
- // Next annotation must start a new block. delta will be > 1
- // because we set p_end to INT_MIN but that's okay since
- // a_block will be empty, so nothing will be drawn
- p_end = INT_MIN;
- } else {
- a_block.push_back(a);
- p_end = a_end;
- }
- }
-
- if (a_block.size() == 1)
- draw_annotation(a_block.front(), p, h, pp, y, base_colour,
- row_title_width);
- else
- draw_annotation_block(a_block, p, h, y, base_colour);
-}
-
-void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
- QPainter &p, int h, const ViewItemPaintParams &pp, int y,
- size_t base_colour, int row_title_width) const
-{
- double samples_per_pixel, pixels_offset;
- tie(pixels_offset, samples_per_pixel) =
- get_pixels_offset_samples_per_pixel();
-
- const double start = a.start_sample() / samples_per_pixel -
- pixels_offset;
- const double end = a.end_sample() / samples_per_pixel - pixels_offset;
-
- const size_t colour = (base_colour + a.format()) % countof(Colours);
- p.setPen(OutlineColours[colour]);
- p.setBrush(Colours[colour]);
-
- if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
- return;
-
- if (a.start_sample() == a.end_sample())
- draw_instant(a, p, h, start, y);
- else
- draw_range(a, p, h, start, end, y, pp, row_title_width);
-}
-
-void DecodeTrace::draw_annotation_block(
- vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
- int y, size_t base_colour) const
-{
- using namespace pv::data::decode;
-
- if (annotations.empty())
- return;
-
- double samples_per_pixel, pixels_offset;
- tie(pixels_offset, samples_per_pixel) =
- get_pixels_offset_samples_per_pixel();
-
- const double start = annotations.front().start_sample() /
- samples_per_pixel - pixels_offset;
- const double end = annotations.back().end_sample() /
- samples_per_pixel - pixels_offset;
-
- const double top = y + .5 - h / 2;
- const double bottom = y + .5 + h / 2;
-
- const size_t colour = (base_colour + annotations.front().format()) %
- countof(Colours);
-
- // Check if all annotations are of the same type (i.e. we can use one color)
- // or if we should use a neutral color (i.e. gray)
- const int format = annotations.front().format();
- const bool single_format = all_of(
- annotations.begin(), annotations.end(),
- [&](const Annotation &a) { return a.format() == format; });
-
- const QRectF rect(start, top, end - start, bottom - top);
- const int r = h / 4;
-
- p.setPen(QPen(Qt::NoPen));
- p.setBrush(Qt::white);
- p.drawRoundedRect(rect, r, r);
-
- p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
- p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
- Qt::Dense4Pattern));
- p.drawRoundedRect(rect, r, r);
-}
-
-void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
- int h, double x, int y) const
-{
- const QString text = a.annotations().empty() ?
- QString() : a.annotations().back();
- const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
- 0.0) + h;
- const QRectF rect(x - w / 2, y - h / 2, w, h);
-
- p.drawRoundedRect(rect, h / 2, h / 2);
-
- p.setPen(Qt::black);
- p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
-}
-
-void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
- int h, double start, double end, int y, const ViewItemPaintParams &pp,
- int row_title_width) const
-{
- const double top = y + .5 - h / 2;
- const double bottom = y + .5 + h / 2;
- const vector<QString> annotations = a.annotations();
-
- // If the two ends are within 1 pixel, draw a vertical line
- if (start + 1.0 > end) {
- p.drawLine(QPointF(start, top), QPointF(start, bottom));
- return;
- }
-
- const double cap_width = min((end - start) / 4, EndCapWidth);
-
- QPointF pts[] = {
- QPointF(start, y + .5f),
- QPointF(start + cap_width, top),
- QPointF(end - cap_width, top),
- QPointF(end, y + .5f),
- QPointF(end - cap_width, bottom),
- QPointF(start + cap_width, bottom)
- };
-
- p.drawConvexPolygon(pts, countof(pts));
-
- if (annotations.empty())
- return;
-
- const int ann_start = start + cap_width;
- const int ann_end = end - cap_width;
-
- const int real_start = max(ann_start, pp.left() + row_title_width);
- const int real_end = min(ann_end, pp.right());
- const int real_width = real_end - real_start;
-
- QRectF rect(real_start, y - h / 2, real_width, h);
- if (rect.width() <= 4)
- return;
-
- p.setPen(Qt::black);
-
- // Try to find an annotation that will fit
- QString best_annotation;
- int best_width = 0;
-
- for (const QString &a : annotations) {
- const int w = p.boundingRect(QRectF(), 0, a).width();
- if (w <= rect.width() && w > best_width)
- best_annotation = a, best_width = w;
- }
-
- if (best_annotation.isEmpty())
- best_annotation = annotations.back();
-
- // If not ellide the last in the list
- p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
- best_annotation, Qt::ElideRight, rect.width()));
-}
-
-void DecodeTrace::draw_error(QPainter &p, const QString &message,
- const ViewItemPaintParams &pp)
-{
- const int y = get_visual_y();
-
- p.setPen(ErrorBgColour.darker());
- p.setBrush(ErrorBgColour);
-
- const QRectF bounding_rect =
- QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
- const QRectF text_rect = p.boundingRect(bounding_rect,
- Qt::AlignCenter, message);
- const float r = text_rect.height() / 4;
-
- p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
- Qt::AbsoluteSize);
-
- p.setPen(Qt::black);
- p.drawText(text_rect, message);
-}
-
-void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
- int right) const
-{
- using namespace pv::data;
- using pv::data::decode::Decoder;
-
- double samples_per_pixel, pixels_offset;
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(decoder_stack);
-
- shared_ptr<Logic> data;
- shared_ptr<data::SignalBase> signalbase;
-
- const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
-
- // We get the logic data of the first channel in the list.
- // This works because we are currently assuming all
- // LogicSignals have the same data/segment
- for (const shared_ptr<Decoder> &dec : stack)
- if (dec && !dec->channels().empty() &&
- ((signalbase = (*dec->channels().begin()).second)) &&
- ((data = signalbase->logic_data())))
- break;
-
- if (!data || data->logic_segments().empty())
- return;
-
- const shared_ptr<LogicSegment> segment = data->logic_segments().front();
- assert(segment);
- const int64_t sample_count = (int64_t)segment->get_sample_count();
- if (sample_count == 0)
- return;
-
- const int64_t samples_decoded = decoder_stack->samples_decoded();
- if (sample_count == samples_decoded)
- return;
-
- const int y = get_visual_y();
-
- tie(pixels_offset, samples_per_pixel) =
- get_pixels_offset_samples_per_pixel();
-
- const double start = max(samples_decoded /
- samples_per_pixel - pixels_offset, left - 1.0);
- const double end = min(sample_count / samples_per_pixel -
- pixels_offset, right + 1.0);
- const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
-
- p.setPen(QPen(Qt::NoPen));
- p.setBrush(Qt::white);
- p.drawRect(no_decode_rect);
-
- p.setPen(NoDecodeColour);
- p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
- p.drawRect(no_decode_rect);
-}
-
-pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
-{
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(owner_);
- assert(decoder_stack);
-
- const View *view = owner_->view();
- assert(view);
-
- const double scale = view->scale();
- assert(scale > 0);
-
- const double pixels_offset =
- ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
-
- double samplerate = decoder_stack->samplerate();
-
- // Show sample rate as 1Hz when it is unknown
- if (samplerate == 0.0)
- samplerate = 1.0;
-
- return make_pair(pixels_offset, samplerate * scale);
-}
-
-pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
- int x_start, int x_end) const
-{
- double samples_per_pixel, pixels_offset;
- tie(pixels_offset, samples_per_pixel) =
- get_pixels_offset_samples_per_pixel();
-
- const uint64_t start = (uint64_t)max(
- (x_start + pixels_offset) * samples_per_pixel, 0.0);
- const uint64_t end = (uint64_t)max(
- (x_end + pixels_offset) * samples_per_pixel, 0.0);
-
- return make_pair(start, end);
-}
-
-int DecodeTrace::get_row_at_point(const QPoint &point)
-{
- if (!row_height_)
- return -1;
-
- const int y = (point.y() - get_visual_y() + row_height_ / 2);
-
- /* Integer divison of (x-1)/x would yield 0, so we check for this. */
- if (y < 0)
- return -1;
-
- const int row = y / row_height_;
-
- if (row >= (int)visible_rows_.size())
- return -1;
-
- return row;
-}
-
-const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
-{
- using namespace pv::data::decode;
-
- if (!enabled())
- return QString();
-
- const pair<uint64_t, uint64_t> sample_range =
- get_sample_range(point.x(), point.x() + 1);
- const int row = get_row_at_point(point);
- if (row < 0)
- return QString();
-
- vector<pv::data::decode::Annotation> annotations;
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(decoder_stack);
- decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
- sample_range.first, sample_range.second);
-
- return (annotations.empty()) ?
- QString() : annotations[0].annotations().front();
-}
-
-void DecodeTrace::hover_point_changed()
-{
- assert(owner_);
-
- const View *const view = owner_->view();
- assert(view);
-
- QPoint hp = view->hover_point();
- QString ann = get_annotation_at_point(hp);
-
- assert(view);
-
- if (!row_height_ || ann.isEmpty()) {
- QToolTip::hideText();
- return;
- }
-
- const int hover_row = get_row_at_point(hp);
-
- QFontMetrics m(QToolTip::font());
- const QRect text_size = m.boundingRect(QRect(), 0, ann);
-
- // This is OS-specific and unfortunately we can't query it, so
- // use an approximation to at least try to minimize the error.
- const int padding = 8;
-
- // Make sure the tool tip doesn't overlap with the mouse cursor.
- // If it did, the tool tip would constantly hide and re-appear.
- // We also push it up by one row so that it appears above the
- // decode trace, not below.
- hp.setX(hp.x() - (text_size.width() / 2) - padding);
-
- hp.setY(get_visual_y() - (row_height_ / 2) +
- (hover_row * row_height_) -
- row_height_ - text_size.height() - padding);
-
- QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
-}
-
-void DecodeTrace::create_decoder_form(int index,
- shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
- QFormLayout *form)
-{
- const GSList *l;
- GlobalSettings settings;
-
- assert(dec);
- const srd_decoder *const decoder = dec->decoder();
- assert(decoder);
-
- const bool decoder_deletable = index > 0;
-
- pv::widgets::DecoderGroupBox *const group =
- new pv::widgets::DecoderGroupBox(
- QString::fromUtf8(decoder->name),
- tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
- QString::fromUtf8(decoder->desc)),
- nullptr, decoder_deletable);
- group->set_decoder_visible(dec->shown());
-
- if (decoder_deletable) {
- delete_mapper_.setMapping(group, index);
- connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
- }
-
- show_hide_mapper_.setMapping(group, index);
- connect(group, SIGNAL(show_hide_decoder()),
- &show_hide_mapper_, SLOT(map()));
-
- QFormLayout *const decoder_form = new QFormLayout;
- group->add_layout(decoder_form);
-
- // Add the mandatory channels
- for (l = decoder->channels; l; l = l->next) {
- const struct srd_channel *const pdch =
- (struct srd_channel *)l->data;
-
- QComboBox *const combo = create_channel_selector(parent, dec, pdch);
- QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
-
- connect(combo, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_channel_selected(int)));
- connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_initial_pin_selected(int)));
-
- QHBoxLayout *const hlayout = new QHBoxLayout;
- hlayout->addWidget(combo);
- hlayout->addWidget(combo_initial_pin);
-
- if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
- combo_initial_pin->hide();
-
- decoder_form->addRow(tr("<b>%1</b> (%2) *")
- .arg(QString::fromUtf8(pdch->name),
- QString::fromUtf8(pdch->desc)), hlayout);
-
- const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
- channel_selectors_.push_back(s);
- }
-
- // Add the optional channels
- for (l = decoder->opt_channels; l; l = l->next) {
- const struct srd_channel *const pdch =
- (struct srd_channel *)l->data;
-
- QComboBox *const combo = create_channel_selector(parent, dec, pdch);
- QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
-
- connect(combo, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_channel_selected(int)));
- connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_initial_pin_selected(int)));
-
- QHBoxLayout *const hlayout = new QHBoxLayout;
- hlayout->addWidget(combo);
- hlayout->addWidget(combo_initial_pin);
-
- if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
- combo_initial_pin->hide();
-
- decoder_form->addRow(tr("<b>%1</b> (%2)")
- .arg(QString::fromUtf8(pdch->name),
- QString::fromUtf8(pdch->desc)), hlayout);
-
- const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
- channel_selectors_.push_back(s);
- }
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- // Add the options
- shared_ptr<binding::Decoder> binding(
- new binding::Decoder(decoder_stack, dec));
- binding->add_properties_to_form(decoder_form, true);
-
- bindings_.push_back(binding);
-
- form->addRow(group);
- decoder_forms_.push_back(group);
-}
-
-QComboBox* DecodeTrace::create_channel_selector(
- QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
- const srd_channel *const pdch)
-{
- assert(dec);
-
- const auto sigs(session_.signalbases());
-
- vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
- sort(sig_list.begin(), sig_list.end(),
- [](const shared_ptr<data::SignalBase> &a,
- const shared_ptr<data::SignalBase> &b) {
- return strnatcasecmp(a->name().toStdString(),
- b->name().toStdString()) < 0; });
-
- const auto channel_iter = dec->channels().find(pdch);
-
- QComboBox *selector = new QComboBox(parent);
-
- selector->addItem("-", qVariantFromValue((void*)nullptr));
-
- if (channel_iter == dec->channels().end())
- selector->setCurrentIndex(0);
-
- for (const shared_ptr<data::SignalBase> &b : sig_list) {
- assert(b);
- if (b->logic_data() && b->enabled()) {
- selector->addItem(b->name(),
- qVariantFromValue((void*)b.get()));
-
- if (channel_iter != dec->channels().end() &&
- (*channel_iter).second == b)
- selector->setCurrentIndex(
- selector->count() - 1);
- }
- }
-
- return selector;
-}
-
-QComboBox* DecodeTrace::create_channel_selector_initial_pin(QWidget *parent,
- const shared_ptr<data::decode::Decoder> &dec, const srd_channel *const pdch)
-{
- QComboBox *selector = new QComboBox(parent);
-
- selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
- selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
- selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
-
- // Default to index 2 (SRD_INITIAL_PIN_SAME_AS_SAMPLE0).
- const int idx = (!dec->initial_pins()) ? 2 : dec->initial_pins()->data[pdch->order];
- selector->setCurrentIndex(idx);
-
- selector->setToolTip("Initial (assumed) pin value before the first sample");
-
- return selector;
-}
-
-void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
-{
- assert(dec);
-
- map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
-
- const unordered_set< shared_ptr<data::SignalBase> >
- sigs(session_.signalbases());
-
- GArray *const initial_pins = g_array_sized_new(FALSE, TRUE,
- sizeof(uint8_t), channel_selectors_.size());
- g_array_set_size(initial_pins, channel_selectors_.size());
-
- for (const ChannelSelector &s : channel_selectors_) {
- if (s.decoder_ != dec)
- break;
-
- const data::SignalBase *const selection =
- (data::SignalBase*)s.combo_->itemData(
- s.combo_->currentIndex()).value<void*>();
-
- for (shared_ptr<data::SignalBase> sig : sigs)
- if (sig.get() == selection) {
- channel_map[s.pdch_] = sig;
- break;
- }
-
- int selection_initial_pin = s.combo_initial_pin_->itemData(
- s.combo_initial_pin_->currentIndex()).value<int>();
-
- initial_pins->data[s.pdch_->order] = selection_initial_pin;
- }
-
- dec->set_channels(channel_map);
- dec->set_initial_pins(initial_pins);
-}
-
-void DecodeTrace::commit_channels()
-{
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(decoder_stack);
- for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
- commit_decoder_channels(dec);
-
- decoder_stack->begin_decode();
-}
-
-void DecodeTrace::on_new_decode_data()
-{
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-void DecodeTrace::delete_pressed()
-{
- on_delete();
-}
-
-void DecodeTrace::on_delete()
-{
- session_.remove_decode_signal(base_);
-}
-
-void DecodeTrace::on_channel_selected(int)
-{
- commit_channels();
-}
-
-void DecodeTrace::on_initial_pin_selected(int)
-{
- commit_channels();
-}
-
-void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
-{
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- assert(decoder);
- assert(decoder_stack);
- decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
- decoder_stack->begin_decode();
-
- create_popup_form();
-}
-
-void DecodeTrace::on_delete_decoder(int index)
-{
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- decoder_stack->remove(index);
-
- // Update the popup
- create_popup_form();
-
- decoder_stack->begin_decode();
-}
-
-void DecodeTrace::on_show_hide_decoder(int index)
-{
- using pv::data::decode::Decoder;
-
- shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
-
- const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
-
- // Find the decoder in the stack
- auto iter = stack.cbegin();
- for (int i = 0; i < index; i++, iter++)
- assert(iter != stack.end());
-
- shared_ptr<Decoder> dec = *iter;
- assert(dec);
-
- const bool show = !dec->shown();
- dec->show(show);
-
- assert(index < (int)decoder_forms_.size());
- decoder_forms_[index]->set_decoder_visible(show);
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
-
-#include "trace.hpp"
-
-#include <list>
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <QSignalMapper>
-
-#include <pv/binding/decoder.hpp>
-#include <pv/data/decode/row.hpp>
-#include <pv/data/signalbase.hpp>
-
-using std::list;
-using std::map;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-struct srd_channel;
-struct srd_decoder;
-
-class QComboBox;
-
-namespace pv {
-
-class Session;
-
-namespace data {
-class DecoderStack;
-class SignalBase;
-
-namespace decode {
-class Annotation;
-class Decoder;
-class Row;
-}
-}
-
-namespace widgets {
-class DecoderGroupBox;
-}
-
-namespace views {
-namespace TraceView {
-
-class DecodeTrace : public Trace
-{
- Q_OBJECT
-
-private:
- struct ChannelSelector
- {
- const QComboBox *combo_;
- const QComboBox *combo_initial_pin_;
- const shared_ptr<pv::data::decode::Decoder> decoder_;
- const srd_channel *pdch_;
- };
-
-private:
- static const QColor DecodeColours[4];
- static const QColor ErrorBgColour;
- static const QColor NoDecodeColour;
-
- static const int ArrowSize;
- static const double EndCapWidth;
- static const int RowTitleMargin;
- static const int DrawPadding;
-
- static const QColor Colours[16];
- static const QColor OutlineColours[16];
-
-public:
- DecodeTrace(pv::Session &session, shared_ptr<data::SignalBase> signalbase,
- int index);
-
- bool enabled() const;
-
- const shared_ptr<pv::data::DecoderStack>& decoder() const;
-
- shared_ptr<data::SignalBase> base() const;
-
- /**
- * Computes the vertical extents of the contents of this row item.
- * @return A pair containing the minimum and maximum y-values.
- */
- pair<int, int> v_extents() const;
-
- /**
- * Paints the background layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with..
- */
- void paint_back(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the mid-layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_mid(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the foreground layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_fore(QPainter &p, ViewItemPaintParams &pp);
-
- void populate_popup_form(QWidget *parent, QFormLayout *form);
-
- QMenu* create_context_menu(QWidget *parent);
-
- void delete_pressed();
-
-private:
- void draw_annotations(vector<pv::data::decode::Annotation> annotations,
- QPainter &p, int h, const ViewItemPaintParams &pp, int y,
- size_t base_colour, int row_title_width);
-
- void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p,
- int h, const ViewItemPaintParams &pp, int y,
- size_t base_colour, int row_title_width) const;
-
- void draw_annotation_block(vector<pv::data::decode::Annotation> annotations,
- QPainter &p, int h, int y, size_t base_colour) const;
-
- void draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
- int h, double x, int y) const;
-
- void draw_range(const pv::data::decode::Annotation &a, QPainter &p,
- int h, double start, double end, int y, const ViewItemPaintParams &pp,
- int row_title_width) const;
-
- void draw_error(QPainter &p, const QString &message,
- const ViewItemPaintParams &pp);
-
- void draw_unresolved_period(QPainter &p, int h, int left,
- int right) const;
-
- pair<double, double> get_pixels_offset_samples_per_pixel() const;
-
- /**
- * Determines the start and end sample for a given pixel range.
- * @param x_start the X coordinate of the start sample in the view
- * @param x_end the X coordinate of the end sample in the view
- * @return Returns a pair containing the start sample and the end
- * sample that correspond to the start and end coordinates.
- */
- pair<uint64_t, uint64_t> get_sample_range(int x_start, int x_end) const;
-
- int get_row_at_point(const QPoint &point);
-
- const QString get_annotation_at_point(const QPoint &point);
-
- void create_decoder_form(int index,
- shared_ptr<pv::data::decode::Decoder> &dec,
- QWidget *parent, QFormLayout *form);
-
- QComboBox* create_channel_selector(QWidget *parent,
- const shared_ptr<pv::data::decode::Decoder> &dec,
- const srd_channel *const pdch);
-
- QComboBox* create_channel_selector_initial_pin(QWidget *parent,
- const shared_ptr<pv::data::decode::Decoder> &dec,
- const srd_channel *const pdch);
-
- void commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec);
-
- void commit_channels();
-
-public:
- void hover_point_changed();
-
-private Q_SLOTS:
- void on_new_decode_data();
-
- void on_delete();
-
- void on_channel_selected(int);
-
- void on_initial_pin_selected(int);
-
- void on_stack_decoder(srd_decoder *decoder);
-
- void on_delete_decoder(int index);
-
- void on_show_hide_decoder(int index);
-
-private:
- pv::Session &session_;
-
- vector<data::decode::Row> visible_rows_;
- uint64_t decode_start_, decode_end_;
-
- list< shared_ptr<pv::binding::Decoder> > bindings_;
-
- list<ChannelSelector> channel_selectors_;
- vector<pv::widgets::DecoderGroupBox*> decoder_forms_;
-
- map<data::decode::Row, int> row_title_widths_;
- int row_height_, max_visible_rows_;
-
- int min_useful_label_width_;
-
- QSignalMapper delete_mapper_, show_hide_mapper_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "timemarker.hpp"
-#include "view.hpp"
-
-#include <QColor>
-#include <QFormLayout>
-#include <QLineEdit>
-#include <QMenu>
-
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
-#include <pv/widgets/popup.hpp>
-
-using std::enable_shared_from_this;
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor Flag::FillColour(0x73, 0xD2, 0x16);
-
-Flag::Flag(View &view, const pv::util::Timestamp& time, const QString &text) :
- TimeMarker(view, FillColour, time),
- text_(text)
-{
-}
-
-Flag::Flag(const Flag &flag) :
- TimeMarker(flag.view_, FillColour, flag.time_),
- enable_shared_from_this<Flag>(flag)
-{
-}
-
-bool Flag::enabled() const
-{
- return true;
-}
-
-QString Flag::get_text() const
-{
- return text_;
-}
-
-pv::widgets::Popup* Flag::create_popup(QWidget *parent)
-{
- using pv::widgets::Popup;
-
- Popup *const popup = TimeMarker::create_popup(parent);
- popup->set_position(parent->mapToGlobal(
- point(parent->rect())), Popup::Bottom);
-
- QFormLayout *const form = (QFormLayout*)popup->layout();
-
- QLineEdit *const text_edit = new QLineEdit(popup);
- text_edit->setText(text_);
-
- connect(text_edit, SIGNAL(textChanged(const QString&)),
- this, SLOT(on_text_changed(const QString&)));
-
- form->insertRow(0, tr("Text"), text_edit);
-
- return popup;
-}
-
-QMenu* Flag::create_context_menu(QWidget *parent)
-{
- QMenu *const menu = new QMenu(parent);
-
- QAction *const del = new QAction(tr("Delete"), this);
- del->setShortcuts(QKeySequence::Delete);
- connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
- menu->addAction(del);
-
- return menu;
-}
-
-void Flag::delete_pressed()
-{
- on_delete();
-}
-
-void Flag::on_delete()
-{
- view_.remove_flag(shared_ptr<Flag>(shared_from_this()));
-}
-
-void Flag::on_text_changed(const QString &text)
-{
- text_ = text;
- view_.time_item_appearance_changed(true, false);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
-
-#include <memory>
-
-#include "timemarker.hpp"
-
-using std::enable_shared_from_this;
-
-class QMenu;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class Flag : public TimeMarker, public enable_shared_from_this<Flag>
-{
- Q_OBJECT
-
-public:
- static const QColor FillColour;
-
-public:
- /**
- * Constructor.
- * @param view A reference to the view that owns this cursor pair.
- * @param time The time to set the flag to.
- * @param text The text of the marker.
- */
- Flag(View &view, const pv::util::Timestamp& time, const QString &text);
-
- /**
- * Copy constructor.
- */
- Flag(const Flag &flag);
-
- /**
- * Returns true if the item is visible and enabled.
- */
- bool enabled() const;
-
- /**
- * Gets the text to show in the marker.
- */
- QString get_text() const;
-
- pv::widgets::Popup* create_popup(QWidget *parent);
-
- QMenu* create_context_menu(QWidget *parent);
-
- void delete_pressed();
-
-private Q_SLOTS:
- void on_delete();
-
- void on_text_changed(const QString &text);
-
-private:
- QString text_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "header.hpp"
-#include "view.hpp"
-
-#include "signal.hpp"
-#include "tracegroup.hpp"
-
-#include <algorithm>
-#include <cassert>
-
-#include <boost/iterator/filter_iterator.hpp>
-
-#include <QApplication>
-#include <QMenu>
-#include <QMouseEvent>
-#include <QPainter>
-#include <QRect>
-
-#include <pv/session.hpp>
-#include <pv/widgets/popup.hpp>
-
-using boost::make_filter_iterator;
-
-using std::count_if;
-using std::dynamic_pointer_cast;
-using std::shared_ptr;
-using std::stable_sort;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const int Header::Padding = 12;
-
-static bool item_selected(shared_ptr<TraceTreeItem> r)
-{
- return r->selected();
-}
-
-Header::Header(View &parent) :
- MarginWidget(parent)
-{
-}
-
-QSize Header::sizeHint() const
-{
- QRectF max_rect(-Padding, 0, Padding, 0);
- const vector<shared_ptr<TraceTreeItem>> items(
- view_.list_by_type<TraceTreeItem>());
- for (auto &i : items)
- if (i->enabled())
- max_rect = max_rect.united(i->label_rect(QRect()));
- return QSize(max_rect.width() + Padding, 0);
-}
-
-QSize Header::extended_size_hint() const
-{
- return sizeHint() + QSize(ViewItem::HighlightRadius, 0);
-}
-
-vector< shared_ptr<ViewItem> > Header::items()
-{
- const vector<shared_ptr<TraceTreeItem>> items(
- view_.list_by_type<TraceTreeItem>());
- return vector< shared_ptr<ViewItem> >(items.begin(), items.end());
-}
-
-shared_ptr<ViewItem> Header::get_mouse_over_item(const QPoint &pt)
-{
- const QRect r(0, 0, width(), height());
- const vector<shared_ptr<TraceTreeItem>> items(
- view_.list_by_type<TraceTreeItem>());
- for (auto i = items.rbegin(); i != items.rend(); i++)
- if ((*i)->enabled() && (*i)->label_rect(r).contains(pt))
- return *i;
- return shared_ptr<TraceTreeItem>();
-}
-
-void Header::paintEvent(QPaintEvent*)
-{
- const QRect rect(0, 0, width(), height());
-
- vector< shared_ptr<RowItem> > items(view_.list_by_type<RowItem>());
-
- stable_sort(items.begin(), items.end(),
- [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
- return a->point(QRect()).y() < b->point(QRect()).y(); });
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
-
- for (const shared_ptr<RowItem> r : items) {
- assert(r);
-
- const bool highlight = !item_dragging_ &&
- r->label_rect(rect).contains(mouse_point_);
- r->paint_label(painter, rect, highlight);
- }
-
- painter.end();
-}
-
-void Header::contextMenuEvent(QContextMenuEvent *event)
-{
- const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
- if (!r)
- return;
-
- QMenu *menu = r->create_context_menu(this);
- if (!menu)
- menu = new QMenu(this);
-
- const vector< shared_ptr<TraceTreeItem> > items(
- view_.list_by_type<TraceTreeItem>());
- if (count_if(items.begin(), items.end(), item_selected) > 1) {
- menu->addSeparator();
-
- QAction *const group = new QAction(tr("Group"), this);
- QList<QKeySequence> shortcuts;
- shortcuts.append(QKeySequence(Qt::ControlModifier | Qt::Key_G));
- group->setShortcuts(shortcuts);
- connect(group, SIGNAL(triggered()), this, SLOT(on_group()));
- menu->addAction(group);
- }
-
- menu->exec(event->globalPos());
-}
-
-void Header::keyPressEvent(QKeyEvent *event)
-{
- assert(event);
-
- MarginWidget::keyPressEvent(event);
-
- if (event->key() == Qt::Key_G && event->modifiers() == Qt::ControlModifier)
- on_group();
- else if (event->key() == Qt::Key_U && event->modifiers() == Qt::ControlModifier)
- on_ungroup();
-}
-
-void Header::on_group()
-{
- const vector< shared_ptr<TraceTreeItem> > items(
- view_.list_by_type<TraceTreeItem>());
- vector< shared_ptr<TraceTreeItem> > selected_items(
- make_filter_iterator(item_selected, items.begin(), items.end()),
- make_filter_iterator(item_selected, items.end(), items.end()));
- stable_sort(selected_items.begin(), selected_items.end(),
- [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
- return a->visual_v_offset() < b->visual_v_offset(); });
-
- shared_ptr<TraceGroup> group(new TraceGroup());
- shared_ptr<TraceTreeItem> mouse_down_item(
- dynamic_pointer_cast<TraceTreeItem>(mouse_down_item_));
- shared_ptr<TraceTreeItem> focus_item(
- mouse_down_item ? mouse_down_item : selected_items.front());
-
- assert(focus_item);
- assert(focus_item->owner());
- focus_item->owner()->add_child_item(group);
-
- // Set the group v_offset here before reparenting
- group->force_to_v_offset(focus_item->layout_v_offset() +
- focus_item->v_extents().first);
-
- for (size_t i = 0; i < selected_items.size(); i++) {
- const shared_ptr<TraceTreeItem> &r = selected_items[i];
- assert(r->owner());
- r->owner()->remove_child_item(r);
- group->add_child_item(r);
-
- // Put the items at 1-pixel offsets, so that restack will
- // stack them in the right order
- r->set_layout_v_offset(i);
- }
-}
-
-void Header::on_ungroup()
-{
- bool restart;
- do {
- restart = false;
- const vector< shared_ptr<TraceGroup> > groups(
- view_.list_by_type<TraceGroup>());
- for (const shared_ptr<TraceGroup> tg : groups)
- if (tg->selected()) {
- tg->ungroup();
- restart = true;
- break;
- }
- } while (restart);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
-
-#include <list>
-#include <memory>
-#include <utility>
-
-#include "marginwidget.hpp"
-
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TraceTreeItem;
-class View;
-class ViewItem;
-
-class Header : public MarginWidget
-{
- Q_OBJECT
-
-private:
- static const int Padding;
-
-public:
- Header(View &parent);
-
- QSize sizeHint() const;
-
- /**
- * The extended area that the header widget would like to be sized to.
- * @remarks This area is the area specified by sizeHint, extended by
- * the area to overlap the viewport.
- */
- QSize extended_size_hint() const;
-
-private:
- /**
- * Gets the row items.
- */
- vector< shared_ptr<ViewItem> > items();
-
- /**
- * Gets the first view item which has a label that contains @c pt .
- * @param pt the point to search with.
- * @return the view item that has been found, or and empty
- * @c shared_ptr if no item was found.
- */
- shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt);
-
-private:
- void paintEvent(QPaintEvent *event);
-
-private:
- void contextMenuEvent(QContextMenuEvent *event);
-
- void keyPressEvent(QKeyEvent *event);
-
-private Q_SLOTS:
- void on_group();
-
- void on_ungroup();
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <cassert>
-#include <cmath>
-
-#include <algorithm>
-
-#include <QApplication>
-#include <QFormLayout>
-#include <QToolBar>
-
-#include "logicsignal.hpp"
-#include "view.hpp"
-
-#include <pv/data/logic.hpp>
-#include <pv/data/logicsegment.hpp>
-#include <pv/data/signalbase.hpp>
-#include <pv/devicemanager.hpp>
-#include <pv/devices/device.hpp>
-#include <pv/globalsettings.hpp>
-#include <pv/session.hpp>
-#include <pv/view/view.hpp>
-
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
-using std::deque;
-using std::max;
-using std::make_pair;
-using std::min;
-using std::none_of;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-using sigrok::ConfigKey;
-using sigrok::Capability;
-using sigrok::Trigger;
-using sigrok::TriggerMatch;
-using sigrok::TriggerMatchType;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const float LogicSignal::Oversampling = 2.0f;
-
-const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80);
-const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00);
-const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00);
-const QColor LogicSignal::SamplingPointColour(0x77, 0x77, 0x77);
-
-const QColor LogicSignal::SignalColours[10] = {
- QColor(0x16, 0x19, 0x1A), // Black
- QColor(0x8F, 0x52, 0x02), // Brown
- QColor(0xCC, 0x00, 0x00), // Red
- QColor(0xF5, 0x79, 0x00), // Orange
- QColor(0xED, 0xD4, 0x00), // Yellow
- QColor(0x73, 0xD2, 0x16), // Green
- QColor(0x34, 0x65, 0xA4), // Blue
- QColor(0x75, 0x50, 0x7B), // Violet
- QColor(0x88, 0x8A, 0x85), // Grey
- QColor(0xEE, 0xEE, 0xEC), // White
-};
-
-QColor LogicSignal::TriggerMarkerBackgroundColour = QColor(0xED, 0xD4, 0x00);
-const int LogicSignal::TriggerMarkerPadding = 2;
-const char* LogicSignal::TriggerMarkerIcons[8] = {
- nullptr,
- ":/icons/trigger-marker-low.svg",
- ":/icons/trigger-marker-high.svg",
- ":/icons/trigger-marker-rising.svg",
- ":/icons/trigger-marker-falling.svg",
- ":/icons/trigger-marker-change.svg",
- nullptr,
- nullptr
-};
-
-QCache<QString, const QIcon> LogicSignal::icon_cache_;
-QCache<QString, const QPixmap> LogicSignal::pixmap_cache_;
-
-LogicSignal::LogicSignal(
- pv::Session &session,
- shared_ptr<devices::Device> device,
- shared_ptr<data::SignalBase> base) :
- Signal(session, base),
- signal_height_(QFontMetrics(QApplication::font()).height() * 2),
- device_(device),
- trigger_none_(nullptr),
- trigger_rising_(nullptr),
- trigger_high_(nullptr),
- trigger_falling_(nullptr),
- trigger_low_(nullptr),
- trigger_change_(nullptr)
-{
- shared_ptr<Trigger> trigger;
-
- base_->set_colour(SignalColours[base->index() % countof(SignalColours)]);
-
- /* Populate this channel's trigger setting with whatever we
- * find in the current session trigger, if anything. */
- trigger_match_ = nullptr;
- if ((trigger = session_.session()->trigger()))
- for (auto stage : trigger->stages())
- for (auto match : stage->matches())
- if (match->channel() == base_->channel())
- trigger_match_ = match->type();
-}
-
-shared_ptr<pv::data::SignalData> LogicSignal::data() const
-{
- return base_->logic_data();
-}
-
-shared_ptr<pv::data::Logic> LogicSignal::logic_data() const
-{
- return base_->logic_data();
-}
-
-pair<int, int> LogicSignal::v_extents() const
-{
- const int signal_margin =
- QFontMetrics(QApplication::font()).height() / 2;
- return make_pair(-signal_height_ - signal_margin, signal_margin);
-}
-
-int LogicSignal::scale_handle_offset() const
-{
- return -signal_height_;
-}
-
-void LogicSignal::scale_handle_dragged(int offset)
-{
- const int font_height = QFontMetrics(QApplication::font()).height();
- const int units = (-offset / font_height);
- signal_height_ = ((units < 1) ? 1 : units) * font_height;
-}
-
-void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
-{
- QLineF *line;
-
- vector< pair<int64_t, bool> > edges;
-
- assert(base_);
- assert(owner_);
-
- const int y = get_visual_y();
-
- if (!base_->enabled())
- return;
-
- const float high_offset = y - signal_height_ + 0.5f;
- const float low_offset = y + 0.5f;
-
- const deque< shared_ptr<pv::data::LogicSegment> > &segments =
- base_->logic_data()->logic_segments();
- if (segments.empty())
- return;
-
- const shared_ptr<pv::data::LogicSegment> &segment = segments.front();
-
- double samplerate = segment->samplerate();
-
- // Show sample rate as 1Hz when it is unknown
- if (samplerate == 0.0)
- samplerate = 1.0;
-
- const double pixels_offset = pp.pixels_offset();
- const pv::util::Timestamp& start_time = segment->start_time();
- const int64_t last_sample = segment->get_sample_count() - 1;
- const double samples_per_pixel = samplerate * pp.scale();
- const double pixels_per_sample = 1 / samples_per_pixel;
- const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
- const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
-
- const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
- (int64_t)0), last_sample);
- const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
- (int64_t)0), last_sample);
-
- segment->get_subsampled_edges(edges, start_sample, end_sample,
- samples_per_pixel / Oversampling, base_->index());
- assert(edges.size() >= 2);
-
- // Check whether we need to paint the sampling points
- GlobalSettings settings;
- const bool show_sampling_points =
- settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
- (samples_per_pixel < 0.25);
-
- vector<QRectF> sampling_points;
- float sampling_point_x = 0.0f;
- int64_t sampling_point_sample = start_sample;
- const int w = 2;
-
- if (show_sampling_points) {
- sampling_points.reserve(end_sample - start_sample + 1);
- sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left();
- }
-
- // Paint the edges
- const unsigned int edge_count = edges.size() - 2;
- QLineF *const edge_lines = new QLineF[edge_count];
- line = edge_lines;
-
- for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
- const float x = ((*i).first / samples_per_pixel -
- pixels_offset) + pp.left();
- *line++ = QLineF(x, high_offset, x, low_offset);
-
- if (show_sampling_points)
- while (sampling_point_sample < (*i).first) {
- const float y = (*i).second ? low_offset : high_offset;
- sampling_points.emplace_back(
- QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
- sampling_point_sample++;
- sampling_point_x += pixels_per_sample;
- };
- }
-
- // Calculate the sample points from the last edge to the end of the trace
- if (show_sampling_points)
- while ((uint64_t)sampling_point_sample <= end_sample) {
- // Signal changed after the last edge, so the level is inverted
- const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
- sampling_points.emplace_back(
- QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
- sampling_point_sample++;
- sampling_point_x += pixels_per_sample;
- };
-
- p.setPen(EdgeColour);
- p.drawLines(edge_lines, edge_count);
- delete[] edge_lines;
-
- // Paint the caps
- const unsigned int max_cap_line_count = edges.size();
- QLineF *const cap_lines = new QLineF[max_cap_line_count];
-
- p.setPen(HighColour);
- paint_caps(p, cap_lines, edges, true, samples_per_pixel,
- pixels_offset, pp.left(), high_offset);
- p.setPen(LowColour);
- paint_caps(p, cap_lines, edges, false, samples_per_pixel,
- pixels_offset, pp.left(), low_offset);
-
- delete[] cap_lines;
-
- // Paint the sampling points
- if (show_sampling_points) {
- p.setPen(SamplingPointColour);
- p.drawRects(sampling_points.data(), sampling_points.size());
- }
-}
-
-void LogicSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- // Draw the trigger marker
- if (!trigger_match_ || !base_->enabled())
- return;
-
- const int y = get_visual_y();
- const vector<int32_t> trig_types = get_trigger_types();
- for (int32_t type_id : trig_types) {
- const TriggerMatchType *const type =
- TriggerMatchType::get(type_id);
- if (trigger_match_ != type || type_id < 0 ||
- (size_t)type_id >= countof(TriggerMarkerIcons) ||
- !TriggerMarkerIcons[type_id])
- continue;
-
- const QPixmap *const pixmap = get_pixmap(
- TriggerMarkerIcons[type_id]);
- if (!pixmap)
- continue;
-
- const float pad = TriggerMarkerPadding - 0.5f;
- const QSize size = pixmap->size();
- const QPoint point(
- pp.right() - size.width() - pad * 2,
- y - (signal_height_ + size.height()) / 2);
-
- p.setPen(QPen(TriggerMarkerBackgroundColour.darker()));
- p.setBrush(TriggerMarkerBackgroundColour);
- p.drawRoundedRect(QRectF(point, size).adjusted(
- -pad, -pad, pad, pad), pad, pad);
- p.drawPixmap(point, *pixmap);
-
- break;
- }
-}
-
-void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
- vector< pair<int64_t, bool> > &edges, bool level,
- double samples_per_pixel, double pixels_offset, float x_offset,
- float y_offset)
-{
- QLineF *line = lines;
-
- for (auto i = edges.begin(); i != (edges.end() - 1); i++)
- if ((*i).second == level) {
- *line++ = QLineF(
- ((*i).first / samples_per_pixel -
- pixels_offset) + x_offset, y_offset,
- ((*(i+1)).first / samples_per_pixel -
- pixels_offset) + x_offset, y_offset);
- }
-
- p.drawLines(lines, line - lines);
-}
-
-void LogicSignal::init_trigger_actions(QWidget *parent)
-{
- trigger_none_ = new QAction(*get_icon(":/icons/trigger-none.svg"),
- tr("No trigger"), parent);
- trigger_none_->setCheckable(true);
- connect(trigger_none_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-
- trigger_rising_ = new QAction(*get_icon(":/icons/trigger-rising.svg"),
- tr("Trigger on rising edge"), parent);
- trigger_rising_->setCheckable(true);
- connect(trigger_rising_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-
- trigger_high_ = new QAction(*get_icon(":/icons/trigger-high.svg"),
- tr("Trigger on high level"), parent);
- trigger_high_->setCheckable(true);
- connect(trigger_high_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-
- trigger_falling_ = new QAction(*get_icon(":/icons/trigger-falling.svg"),
- tr("Trigger on falling edge"), parent);
- trigger_falling_->setCheckable(true);
- connect(trigger_falling_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-
- trigger_low_ = new QAction(*get_icon(":/icons/trigger-low.svg"),
- tr("Trigger on low level"), parent);
- trigger_low_->setCheckable(true);
- connect(trigger_low_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-
- trigger_change_ = new QAction(*get_icon(":/icons/trigger-change.svg"),
- tr("Trigger on rising or falling edge"), parent);
- trigger_change_->setCheckable(true);
- connect(trigger_change_, SIGNAL(triggered()), this, SLOT(on_trigger()));
-}
-
-const vector<int32_t> LogicSignal::get_trigger_types() const
-{
- // We may not be associated with a device
- if (!device_)
- return vector<int32_t>();
-
- const auto sr_dev = device_->device();
- if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
- const Glib::VariantContainerBase gvar =
- sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
-
- vector<int32_t> ttypes;
-
- for (unsigned int i = 0; i < gvar.get_n_children(); i++) {
- Glib::VariantBase tmp_vb;
- gvar.get_child(tmp_vb, i);
-
- Glib::Variant<int32_t> tmp_v =
- Glib::VariantBase::cast_dynamic< Glib::Variant<int32_t> >(tmp_vb);
-
- ttypes.push_back(tmp_v.get());
- }
-
- return ttypes;
- } else {
- return vector<int32_t>();
- }
-}
-
-QAction* LogicSignal::action_from_trigger_type(const TriggerMatchType *type)
-{
- QAction *action;
-
- action = trigger_none_;
- if (type) {
- switch (type->id()) {
- case SR_TRIGGER_ZERO:
- action = trigger_low_;
- break;
- case SR_TRIGGER_ONE:
- action = trigger_high_;
- break;
- case SR_TRIGGER_RISING:
- action = trigger_rising_;
- break;
- case SR_TRIGGER_FALLING:
- action = trigger_falling_;
- break;
- case SR_TRIGGER_EDGE:
- action = trigger_change_;
- break;
- default:
- assert(false);
- }
- }
-
- return action;
-}
-
-const TriggerMatchType *LogicSignal::trigger_type_from_action(QAction *action)
-{
- if (action == trigger_low_)
- return TriggerMatchType::ZERO;
- else if (action == trigger_high_)
- return TriggerMatchType::ONE;
- else if (action == trigger_rising_)
- return TriggerMatchType::RISING;
- else if (action == trigger_falling_)
- return TriggerMatchType::FALLING;
- else if (action == trigger_change_)
- return TriggerMatchType::EDGE;
- else
- return nullptr;
-}
-
-void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
-{
- Signal::populate_popup_form(parent, form);
-
- const vector<int32_t> trig_types = get_trigger_types();
-
- if (!trig_types.empty()) {
- trigger_bar_ = new QToolBar(parent);
- init_trigger_actions(trigger_bar_);
- trigger_bar_->addAction(trigger_none_);
- trigger_none_->setChecked(!trigger_match_);
-
- for (auto type_id : trig_types) {
- const TriggerMatchType *const type =
- TriggerMatchType::get(type_id);
- QAction *const action = action_from_trigger_type(type);
- trigger_bar_->addAction(action);
- action->setChecked(trigger_match_ == type);
- }
- form->addRow(tr("Trigger"), trigger_bar_);
- }
-}
-
-void LogicSignal::modify_trigger()
-{
- auto trigger = session_.session()->trigger();
- auto new_trigger = session_.device_manager().context()->create_trigger("pulseview");
-
- if (trigger) {
- for (auto stage : trigger->stages()) {
- const auto &matches = stage->matches();
- if (none_of(matches.begin(), matches.end(),
- [&](shared_ptr<TriggerMatch> match) {
- return match->channel() != base_->channel(); }))
- continue;
-
- auto new_stage = new_trigger->add_stage();
- for (auto match : stage->matches()) {
- if (match->channel() == base_->channel())
- continue;
- new_stage->add_match(match->channel(), match->type());
- }
- }
- }
-
- if (trigger_match_) {
- // Until we can let the user decide how to group trigger matches
- // into stages, put all of the matches into a single stage --
- // most devices only support a single trigger stage.
- if (new_trigger->stages().empty())
- new_trigger->add_stage();
-
- new_trigger->stages().back()->add_match(base_->channel(),
- trigger_match_);
- }
-
- session_.session()->set_trigger(
- new_trigger->stages().empty() ? nullptr : new_trigger);
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
-const QIcon* LogicSignal::get_icon(const char *path)
-{
- if (!icon_cache_.contains(path)) {
- const QIcon *icon = new QIcon(path);
- icon_cache_.insert(path, icon);
- }
-
- return icon_cache_.take(path);
-}
-
-const QPixmap* LogicSignal::get_pixmap(const char *path)
-{
- if (!pixmap_cache_.contains(path)) {
- const QPixmap *pixmap = new QPixmap(path);
- pixmap_cache_.insert(path, pixmap);
- }
-
- return pixmap_cache_.take(path);
-}
-
-void LogicSignal::on_trigger()
-{
- QAction *action;
-
- action_from_trigger_type(trigger_match_)->setChecked(false);
-
- action = (QAction *)sender();
- action->setChecked(true);
- trigger_match_ = trigger_type_from_action(action);
-
- modify_trigger();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
-
-#include <QCache>
-
-#include "signal.hpp"
-
-#include <memory>
-
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-class QIcon;
-class QToolBar;
-
-namespace sigrok {
-class TriggerMatchType;
-}
-
-namespace pv {
-
-namespace devices {
-class Device;
-}
-
-namespace data {
-class Logic;
-}
-
-namespace views {
-namespace TraceView {
-
-class LogicSignal : public Signal
-{
- Q_OBJECT
-
-public:
- static const float Oversampling;
-
- static const QColor EdgeColour;
- static const QColor HighColour;
- static const QColor LowColour;
- static const QColor SamplingPointColour;
-
- static const QColor SignalColours[10];
-
- static QColor TriggerMarkerBackgroundColour;
- static const int TriggerMarkerPadding;
- static const char* TriggerMarkerIcons[8];
-
- LogicSignal(pv::Session &session,
- shared_ptr<devices::Device> device,
- shared_ptr<data::SignalBase> base);
-
- virtual ~LogicSignal() = default;
-
- shared_ptr<pv::data::SignalData> data() const;
-
- shared_ptr<pv::data::Logic> logic_data() const;
-
- /**
- * Computes the vertical extents of the contents of this row item.
- * @return A pair containing the minimum and maximum y-values.
- */
- pair<int, int> v_extents() const;
-
- /**
- * Returns the offset to show the drag handle.
- */
- int scale_handle_offset() const;
-
- /**
- * Handles the scale handle being dragged to an offset.
- * @param offset the offset the scale handle was dragged to.
- */
- void scale_handle_dragged(int offset);
-
- /**
- * Paints the mid-layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with..
- */
- void paint_mid(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the foreground layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
-
-private:
- void paint_caps(QPainter &p, QLineF *const lines,
- vector< pair<int64_t, bool> > &edges,
- bool level, double samples_per_pixel, double pixels_offset,
- float x_offset, float y_offset);
-
- void init_trigger_actions(QWidget *parent);
-
- const vector<int32_t> get_trigger_types() const;
- QAction* action_from_trigger_type(const sigrok::TriggerMatchType *type);
- const sigrok::TriggerMatchType* trigger_type_from_action(
- QAction *action);
- void populate_popup_form(QWidget *parent, QFormLayout *form);
- void modify_trigger();
-
- static const QIcon* get_icon(const char *path);
- static const QPixmap* get_pixmap(const char *path);
-
-private Q_SLOTS:
- void on_trigger();
-
-private:
- int signal_height_;
-
- shared_ptr<pv::devices::Device> device_;
-
- const sigrok::TriggerMatchType *trigger_match_;
- QToolBar *trigger_bar_;
- QAction *trigger_none_;
- QAction *trigger_rising_;
- QAction *trigger_high_;
- QAction *trigger_falling_;
- QAction *trigger_low_;
- QAction *trigger_change_;
-
- static QCache<QString, const QIcon> icon_cache_;
- static QCache<QString, const QPixmap> pixmap_cache_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QMenu>
-#include <QMouseEvent>
-
-#include "view.hpp"
-
-#include "marginwidget.hpp"
-
-#include <pv/widgets/popup.hpp>
-
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-MarginWidget::MarginWidget(View &parent) :
- ViewWidget(parent)
-{
- setAttribute(Qt::WA_NoSystemBackground, true);
-}
-
-void MarginWidget::item_clicked(const shared_ptr<ViewItem> &item)
-{
- if (item && item->enabled())
- show_popup(item);
-}
-
-void MarginWidget::show_popup(const shared_ptr<ViewItem> &item)
-{
- pv::widgets::Popup *const p = item->create_popup(this);
- if (p)
- p->show();
-}
-
-void MarginWidget::contextMenuEvent(QContextMenuEvent *event)
-{
- const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
- if (!r)
- return;
-
- QMenu *menu = r->create_context_menu(this);
- if (menu)
- menu->exec(event->globalPos());
-}
-
-void MarginWidget::keyPressEvent(QKeyEvent *event)
-{
- assert(event);
-
- if (event->key() == Qt::Key_Delete) {
- const auto items = this->items();
- for (auto &i : items)
- if (i->selected())
- i->delete_pressed();
- }
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_MARGINWIDGET_HPP
-#define PULSEVIEW_PV_MARGINWIDGET_HPP
-
-#include <memory>
-
-#include <QPoint>
-
-#include "viewwidget.hpp"
-
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class ViewItem;
-
-class MarginWidget : public ViewWidget
-{
- Q_OBJECT
-
-public:
- MarginWidget(View &parent);
-
- /**
- * The extended area that the margin widget would like to be sized to.
- * @remarks This area is the area specified by sizeHint, extended by
- * the area to overlap the viewport.
- */
- virtual QSize extended_size_hint() const = 0;
-
-protected:
- /**
- * Indicates the event an a view item has been clicked.
- * @param item the view item that has been clicked.
- */
- virtual void item_clicked(const shared_ptr<ViewItem> &item);
-
- /**
- * Shows the popup of a the specified @c ViewItem .
- * @param item The item to show the popup for.
- */
- void show_popup(const shared_ptr<ViewItem> &item);
-
-protected:
- virtual void contextMenuEvent(QContextMenuEvent *event);
-
- virtual void keyPressEvent(QKeyEvent *event);
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_MARGINWIDGET_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "rowitem.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-void RowItem::hover_point_changed()
-{
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
-
-#include <pv/view/viewitem.hpp>
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class RowItem : public ViewItem
-{
- Q_OBJECT
-
-public:
- virtual void hover_point_changed();
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <QApplication>
-#include <QFontMetrics>
-#include <QMouseEvent>
-
-#include "ruler.hpp"
-#include "view.hpp"
-
-using namespace Qt;
-
-using std::function;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const float Ruler::RulerHeight = 2.5f; // x Text Height
-const int Ruler::MinorTickSubdivision = 4;
-
-const float Ruler::HoverArrowSize = 0.5f; // x Text Height
-
-Ruler::Ruler(View &parent) :
- MarginWidget(parent)
-{
- setMouseTracking(true);
-
- connect(&view_, SIGNAL(hover_point_changed()),
- this, SLOT(hover_point_changed()));
- connect(&view_, SIGNAL(offset_changed()),
- this, SLOT(invalidate_tick_position_cache()));
- connect(&view_, SIGNAL(scale_changed()),
- this, SLOT(invalidate_tick_position_cache()));
- connect(&view_, SIGNAL(tick_prefix_changed()),
- this, SLOT(invalidate_tick_position_cache()));
- connect(&view_, SIGNAL(tick_precision_changed()),
- this, SLOT(invalidate_tick_position_cache()));
- connect(&view_, SIGNAL(tick_period_changed()),
- this, SLOT(invalidate_tick_position_cache()));
- connect(&view_, SIGNAL(time_unit_changed()),
- this, SLOT(invalidate_tick_position_cache()));
-}
-
-QSize Ruler::sizeHint() const
-{
- const int text_height = calculate_text_height();
- return QSize(0, RulerHeight * text_height);
-}
-
-QSize Ruler::extended_size_hint() const
-{
- QRectF max_rect;
- vector< shared_ptr<TimeItem> > items(view_.time_items());
- for (auto &i : items)
- max_rect = max_rect.united(i->label_rect(QRect()));
- return QSize(0, sizeHint().height() - max_rect.top() / 2 +
- ViewItem::HighlightRadius);
-}
-
-QString Ruler::format_time_with_distance(
- const pv::util::Timestamp& distance,
- const pv::util::Timestamp& t,
- pv::util::SIPrefix prefix,
- pv::util::TimeUnit unit,
- unsigned precision,
- bool sign)
-{
- const unsigned limit = 60;
-
- if (t.is_zero())
- return "0";
-
- // If we have to use samples then we have no alternative formats
- if (unit == pv::util::TimeUnit::Samples)
- return pv::util::format_time_si_adjusted(t, prefix, precision, "sa", sign);
-
- // View zoomed way out -> low precision (0), big distance (>=60s)
- // -> DD:HH:MM
- if ((precision == 0) && (distance >= limit))
- return pv::util::format_time_minutes(t, 0, sign);
-
- // View in "normal" range -> medium precision, medium step size
- // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
- // View zoomed way in -> high precision (>3), low step size (<1s)
- // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
- if (abs(t) < limit)
- return pv::util::format_time_si_adjusted(t, prefix, precision, "s", sign);
- else
- return pv::util::format_time_minutes(t, precision, sign);
-}
-
-vector< shared_ptr<ViewItem> > Ruler::items()
-{
- const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
- return vector< shared_ptr<ViewItem> >(
- time_items.begin(), time_items.end());
-}
-
-shared_ptr<ViewItem> Ruler::get_mouse_over_item(const QPoint &pt)
-{
- const vector< shared_ptr<TimeItem> > items(view_.time_items());
- for (auto i = items.rbegin(); i != items.rend(); i++)
- if ((*i)->enabled() && (*i)->label_rect(rect()).contains(pt))
- return *i;
- return nullptr;
-}
-
-void Ruler::paintEvent(QPaintEvent*)
-{
- if (!tick_position_cache_) {
- auto ffunc = [this](const pv::util::Timestamp& t) {
- return format_time_with_distance(
- this->view_.tick_period(),
- t,
- this->view_.tick_prefix(),
- this->view_.time_unit(),
- this->view_.tick_precision());
- };
-
- tick_position_cache_ = calculate_tick_positions(
- view_.tick_period(),
- view_.offset(),
- view_.scale(),
- width(),
- ffunc);
- }
-
- const int ValueMargin = 3;
-
- const int text_height = calculate_text_height();
- const int ruler_height = RulerHeight * text_height;
- const int major_tick_y1 = text_height + ValueMargin * 2;
- const int minor_tick_y1 = (major_tick_y1 + ruler_height) / 2;
-
- QPainter p(this);
-
- // Draw the tick marks
- p.setPen(palette().color(foregroundRole()));
-
- for (const auto& tick: tick_position_cache_->major) {
- p.drawText(tick.first, ValueMargin, 0, text_height,
- AlignCenter | AlignTop | TextDontClip, tick.second);
- p.drawLine(QPointF(tick.first, major_tick_y1),
- QPointF(tick.first, ruler_height));
- }
-
- for (const auto& tick: tick_position_cache_->minor) {
- p.drawLine(QPointF(tick, minor_tick_y1),
- QPointF(tick, ruler_height));
- }
-
- // Draw the hover mark
- draw_hover_mark(p, text_height);
-
- p.setRenderHint(QPainter::Antialiasing);
-
- // The cursor labels are not drawn with the arrows exactly on the
- // bottom line of the widget, because then the selection shadow
- // would be clipped away.
- const QRect r = rect().adjusted(0, 0, 0, -ViewItem::HighlightRadius);
-
- // Draw the items
- const vector< shared_ptr<TimeItem> > items(view_.time_items());
- for (auto &i : items) {
- const bool highlight = !item_dragging_ &&
- i->label_rect(r).contains(mouse_point_);
- i->paint_label(p, r, highlight);
- }
-}
-
-Ruler::TickPositions Ruler::calculate_tick_positions(
- const pv::util::Timestamp& major_period,
- const pv::util::Timestamp& offset,
- const double scale,
- const int width,
- function<QString(const pv::util::Timestamp&)> format_function)
-{
- TickPositions tp;
-
- const pv::util::Timestamp minor_period = major_period / MinorTickSubdivision;
- const pv::util::Timestamp first_major_division = floor(offset / major_period);
- const pv::util::Timestamp first_minor_division = ceil(offset / minor_period);
- const pv::util::Timestamp t0 = first_major_division * major_period;
-
- int division = (round(first_minor_division -
- first_major_division * MinorTickSubdivision)).convert_to<int>() - 1;
-
- double x;
-
- do {
- pv::util::Timestamp t = t0 + division * minor_period;
- x = ((t - offset) / scale).convert_to<double>();
-
- if (division % MinorTickSubdivision == 0) {
- // Recalculate 't' without using 'minor_period' which is a fraction
- t = t0 + division / MinorTickSubdivision * major_period;
- tp.major.emplace_back(x, format_function(t));
- } else {
- tp.minor.emplace_back(x);
- }
-
- division++;
- } while (x < width);
-
- return tp;
-}
-
-void Ruler::mouseDoubleClickEvent(QMouseEvent *event)
-{
- view_.add_flag(view_.offset() + ((double)event->x() + 0.5) * view_.scale());
-}
-
-void Ruler::draw_hover_mark(QPainter &p, int text_height)
-{
- const int x = view_.hover_point().x();
-
- if (x == -1)
- return;
-
- p.setPen(QPen(Qt::NoPen));
- p.setBrush(QBrush(palette().color(foregroundRole())));
-
- const int b = RulerHeight * text_height;
- const float hover_arrow_size = HoverArrowSize * text_height;
- const QPointF points[] = {
- QPointF(x, b),
- QPointF(x - hover_arrow_size, b - hover_arrow_size),
- QPointF(x + hover_arrow_size, b - hover_arrow_size)
- };
- p.drawPolygon(points, countof(points));
-}
-
-int Ruler::calculate_text_height() const
-{
- return QFontMetrics(font()).ascent();
-}
-
-void Ruler::hover_point_changed()
-{
- update();
-}
-
-void Ruler::invalidate_tick_position_cache()
-{
- tick_position_cache_ = boost::none;
-}
-
-void Ruler::resizeEvent(QResizeEvent*)
-{
- // the tick calculation depends on the width of this widget
- invalidate_tick_position_cache();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
-
-#include <functional>
-#include <memory>
-
-#include <boost/optional.hpp>
-
-#include "marginwidget.hpp"
-#include <pv/util.hpp>
-
-using std::function;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-namespace RulerTest {
-struct tick_position_test_0;
-struct tick_position_test_1;
-struct tick_position_test_2;
-}
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TimeItem;
-class ViewItem;
-
-class Ruler : public MarginWidget
-{
- Q_OBJECT
-
- friend struct RulerTest::tick_position_test_0;
- friend struct RulerTest::tick_position_test_1;
- friend struct RulerTest::tick_position_test_2;
-
-private:
- /// Height of the ruler in multipes of the text height
- static const float RulerHeight;
-
- static const int MinorTickSubdivision;
-
- /// Height of the hover arrow in multiples of the text height
- static const float HoverArrowSize;
-
-public:
- Ruler(View &parent);
-
-public:
- QSize sizeHint() const override;
-
- /**
- * The extended area that the header widget would like to be sized to.
- * @remarks This area is the area specified by sizeHint, extended by
- * the area to overlap the viewport.
- */
- QSize extended_size_hint() const override;
-
- /**
- * Formats a timestamp depending on its distance to another timestamp.
- *
- * Heuristic function, useful when multiple timestamps should be put side by
- * side. The function procedes in the following order:
- * - If 't' is zero, "0" is returned.
- * - If 'unit' is 'TimeUnit::Samples', 'pv::util::format_time_si_adjusted()'
- * is used to format 't'.
- * - If a zoomed out view is detected (determined by 'precision' and
- * 'distance'), 'pv::util::format_time_minutes() is used.
- * - For timestamps "near the origin" (determined by 'distance'),
- * 'pv::util::format_time_si_adjusted()' is used.
- * - If none of the previous was true, 'pv::util::format_time_minutes()'
- * is used again.
- *
- * @param distance The distance between the timestamp to format and
- * an adjacent one.
- * @param t The value to format
- * @param prefix The SI prefix to use.
- * @param unit The representation of the timestamp value.
- * @param precision The number of digits after the decimal separator.
- * @param sign Whether or not to add a sign also for positive numbers.
- *
- * @return The formated value.
- */
- static QString format_time_with_distance(
- const pv::util::Timestamp& distance,
- const pv::util::Timestamp& t,
- pv::util::SIPrefix prefix = pv::util::SIPrefix::unspecified,
- pv::util::TimeUnit unit = pv::util::TimeUnit::Time,
- unsigned precision = 0,
- bool sign = true);
-
-private:
- /**
- * Gets the time items.
- */
- vector< shared_ptr<ViewItem> > items() override;
-
- /**
- * Gets the first view item which has a label that contains @c pt .
- * @param pt the point to search with.
- * @return the view item that has been found, or and empty
- * @c shared_ptr if no item was found.
- */
- shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt) override;
-
- void paintEvent(QPaintEvent *event) override;
-
- void mouseDoubleClickEvent(QMouseEvent *event) override;
-
- /**
- * Draw a hover arrow under the cursor position.
- * @param p The painter to draw into.
- * @param text_height The height of a single text ascent.
- */
- void draw_hover_mark(QPainter &p, int text_height);
-
- int calculate_text_height() const;
-
- struct TickPositions
- {
- vector<pair<double, QString>> major;
- vector<double> minor;
- };
-
- /**
- * Holds the tick positions so that they don't have to be recalculated on
- * every redraw. Set by 'paintEvent()' when needed.
- */
- boost::optional<TickPositions> tick_position_cache_;
-
- /**
- * Calculates the major and minor tick positions.
- *
- * @param major_period The period between the major ticks.
- * @param offset The time at the left border of the ruler.
- * @param scale The scale in seconds per pixel.
- * @param width the Width of the ruler.
- * @param format_function A function used to format the major tick times.
- * @return An object of type 'TickPositions' that contains the major tick
- * positions together with the labels at that ticks, and the minor
- * tick positions.
- */
- static TickPositions calculate_tick_positions(
- const pv::util::Timestamp& major_period,
- const pv::util::Timestamp& offset,
- const double scale,
- const int width,
- function<QString(const pv::util::Timestamp&)> format_function);
-
-protected:
- void resizeEvent(QResizeEvent*) override;
-
-private Q_SLOTS:
- void hover_point_changed();
-
- // Resets the 'tick_position_cache_'.
- void invalidate_tick_position_cache();
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <cassert>
-#include <cmath>
-
-#include <QApplication>
-#include <QFormLayout>
-#include <QKeyEvent>
-#include <QLineEdit>
-#include <QMenu>
-
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
-#include "pv/data/signalbase.hpp"
-
-#include "signal.hpp"
-#include "view.hpp"
-
-using std::shared_ptr;
-using std::make_shared;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const char *const ChannelNames[] = {
- "CLK",
- "DATA",
- "IN",
- "OUT",
- "RST",
- "TX",
- "RX",
- "EN",
- "SCLK",
- "MOSI",
- "MISO",
- "/SS",
- "SDA",
- "SCL"
-};
-
-Signal::Signal(pv::Session &session,
- shared_ptr<data::SignalBase> channel) :
- Trace(channel),
- session_(session),
- scale_handle_(make_shared<SignalScaleHandle>(*this)),
- items_({scale_handle_}),
- name_widget_(nullptr)
-{
- assert(base_);
-
- connect(base_.get(), SIGNAL(enabled_changed(bool)),
- this, SLOT(on_enabled_changed(bool)));
-}
-
-void Signal::set_name(QString name)
-{
- Trace::set_name(name);
-
- if (name != name_widget_->currentText())
- name_widget_->setEditText(name);
-}
-
-bool Signal::enabled() const
-{
- return base_->enabled();
-}
-
-shared_ptr<data::SignalBase> Signal::base() const
-{
- return base_;
-}
-
-void Signal::save_settings(QSettings &settings) const
-{
- (void)settings;
-}
-
-void Signal::restore_settings(QSettings &settings)
-{
- (void)settings;
-}
-
-const ViewItemOwner::item_list& Signal::child_items() const
-{
- return items_;
-}
-
-void Signal::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- if (base_->enabled())
- Trace::paint_back(p, pp);
-}
-
-void Signal::populate_popup_form(QWidget *parent, QFormLayout *form)
-{
- name_widget_ = new QComboBox(parent);
- name_widget_->setEditable(true);
- name_widget_->setCompleter(nullptr);
-
- for (unsigned int i = 0; i < countof(ChannelNames); i++)
- name_widget_->insertItem(i, ChannelNames[i]);
-
- const int index = name_widget_->findText(base_->name(), Qt::MatchExactly);
-
- if (index == -1) {
- name_widget_->insertItem(0, base_->name());
- name_widget_->setCurrentIndex(0);
- } else {
- name_widget_->setCurrentIndex(index);
- }
-
- connect(name_widget_, SIGNAL(editTextChanged(const QString&)),
- this, SLOT(on_nameedit_changed(const QString&)));
-
- form->addRow(tr("Name"), name_widget_);
-
- add_colour_option(parent, form);
-}
-
-QMenu* Signal::create_context_menu(QWidget *parent)
-{
- QMenu *const menu = Trace::create_context_menu(parent);
-
- menu->addSeparator();
-
- QAction *const disable = new QAction(tr("Disable"), this);
- disable->setShortcuts(QKeySequence::Delete);
- connect(disable, SIGNAL(triggered()), this, SLOT(on_disable()));
- menu->addAction(disable);
-
- return menu;
-}
-
-void Signal::delete_pressed()
-{
- on_disable();
-}
-
-void Signal::on_name_changed(const QString &text)
-{
- // On startup, this event is fired when a session restores signal
- // names. However, the name widget hasn't yet been created.
- if (!name_widget_)
- return;
-
- if (text != name_widget_->currentText())
- name_widget_->setEditText(text);
-
- Trace::on_name_changed(text);
-}
-
-void Signal::on_disable()
-{
- base_->set_enabled(false);
-}
-
-void Signal::on_enabled_changed(bool enabled)
-{
- (void)enabled;
-
- if (owner_)
- owner_->extents_changed(true, true);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
-
-#include <memory>
-
-#include <QComboBox>
-#include <QWidgetAction>
-
-#include <cstdint>
-
-#include "signalscalehandle.hpp"
-#include "trace.hpp"
-#include "viewitemowner.hpp"
-
-using std::shared_ptr;
-
-namespace pv {
-
-class Session;
-
-namespace data {
-class SignalBase;
-class SignalData;
-}
-
-namespace views {
-namespace TraceView {
-
-class Signal : public Trace, public ViewItemOwner
-{
- Q_OBJECT
-
-protected:
- Signal(pv::Session &session, shared_ptr<data::SignalBase> channel);
-
-public:
- /**
- * Sets the name of the signal.
- */
- virtual void set_name(QString name);
-
- virtual shared_ptr<pv::data::SignalData> data() const = 0;
-
- /**
- * Returns true if the trace is visible and enabled.
- */
- bool enabled() const;
-
- shared_ptr<data::SignalBase> base() const;
-
- virtual void save_settings(QSettings &settings) const;
-
- virtual void restore_settings(QSettings &settings);
-
- /**
- * Returns a list of row items owned by this object.
- */
- const item_list& child_items() const;
-
- void paint_back(QPainter &p, ViewItemPaintParams &pp);
-
- virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
-
- QMenu* create_context_menu(QWidget *parent);
-
- void delete_pressed();
-
- /**
- * Returns the offset to show the drag handle.
- */
- virtual int scale_handle_offset() const = 0;
-
- /**
- * Handles the scale handle being dragged to an offset.
- * @param offset the offset the scale handle was dragged to.
- */
- virtual void scale_handle_dragged(int offset) = 0;
-
- /**
- * Handles the scale handle being being released.
- */
- virtual void scale_handle_released() {};
-
-protected Q_SLOTS:
- virtual void on_name_changed(const QString &text);
-
- void on_disable();
-
- void on_enabled_changed(bool enabled);
-
-protected:
- pv::Session &session_;
-
- const shared_ptr<SignalScaleHandle> scale_handle_;
- const item_list items_;
-
- QComboBox *name_widget_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <algorithm>
-
-#include <QRadialGradient>
-
-#include "signal.hpp"
-#include "signalscalehandle.hpp"
-#include "tracetreeitemowner.hpp"
-
-using std::max;
-using std::min;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-SignalScaleHandle::SignalScaleHandle(Signal &owner) :
- owner_(owner)
-{
-}
-
-bool SignalScaleHandle::enabled() const
-{
- return selected() || owner_.selected();
-}
-
-void SignalScaleHandle::select(bool select)
-{
- ViewItem::select(select);
- owner_.owner()->row_item_appearance_changed(true, true);
-}
-
-void SignalScaleHandle::drag_release()
-{
- RowItem::drag_release();
- owner_.scale_handle_released();
- owner_.owner()->row_item_appearance_changed(true, true);
-}
-
-void SignalScaleHandle::drag_by(const QPoint &delta)
-{
- owner_.scale_handle_dragged(
- drag_point_.y() + delta.y() - owner_.get_visual_y());
- owner_.owner()->row_item_appearance_changed(true, true);
-}
-
-QPoint SignalScaleHandle::point(const QRect &rect) const
-{
- return owner_.point(rect) + QPoint(0, owner_.scale_handle_offset());
-}
-
-QRectF SignalScaleHandle::hit_box_rect(const ViewItemPaintParams &pp) const
-{
- const int text_height = ViewItemPaintParams::text_height();
- const double x = -pp.pixels_offset() - text_height / 2;
- const double min_x = pp.left() + text_height;
- const double max_x = pp.right() - text_height * 2;
- return QRectF(min(max(x, min_x), max_x),
- owner_.get_visual_y() + owner_.scale_handle_offset() -
- text_height / 2,
- text_height, text_height);
-}
-
-void SignalScaleHandle::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- if (!enabled())
- return;
-
- const QRectF r(hit_box_rect(pp));
- const QPointF c = (r.topLeft() + 2 * r.center()) / 3;
- QRadialGradient gradient(c, r.width(), c);
-
- if (selected()) {
- gradient.setColorAt(0.0, QColor(255, 255, 255));
- gradient.setColorAt(0.75, QColor(192, 192, 192));
- gradient.setColorAt(1.0, QColor(128, 128, 128));
- } else {
- gradient.setColorAt(0.0, QColor(192, 192, 192));
- gradient.setColorAt(0.75, QColor(128, 128, 128));
- gradient.setColorAt(1.0, QColor(128, 128, 128));
- }
-
- p.setBrush(QBrush(gradient));
- p.setPen(QColor(128, 128, 128));
- p.drawEllipse(r);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
-
-#include "rowitem.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class Signal;
-
-/**
- * A row item owned by a @c Signal that implements the v-scale adjustment grab
- * handle.
- */
-class SignalScaleHandle : public RowItem
-{
- Q_OBJECT
-public:
- /**
- * Constructor
- */
- explicit SignalScaleHandle(Signal &owner);
-
-public:
- /**
- * Returns true if the parent item is enabled.
- */
- bool enabled() const;
-
- /**
- * Selects or deselects the signal.
- */
- void select(bool select = true);
-
- /**
- * Sets this item into the un-dragged state.
- */
- void drag_release();
-
- /**
- * Drags the item to a delta relative to the drag point.
- * @param delta the offset from the drag point.
- */
- void drag_by(const QPoint &delta);
-
- /**
- * Get the drag point.
- * @param rect the rectangle of the widget area.
- */
- QPoint point(const QRect &rect) const;
-
- /**
- * Computes the outline rectangle of the viewport hit-box.
- * @param rect the rectangle of the viewport area.
- * @return Returns the rectangle of the hit-box.
- */
- QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
-
- /**
- * Paints the foreground layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_fore(QPainter &p, ViewItemPaintParams &pp);
-
-private:
- Signal &owner_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "timeitem.hpp"
-#include "view.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-TimeItem::TimeItem(View &view) :
- view_(view) {
-}
-
-void TimeItem::drag_by(const QPoint &delta)
-{
- set_time(view_.offset() + (drag_point_.x() + delta.x() - 0.5) *
- view_.scale());
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
-
-#include "viewitem.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class View;
-
-class TimeItem : public ViewItem
-
-{
- Q_OBJECT
-
-protected:
- /**
- * Constructor.
- * @param view A reference to the view that owns this marker.
- */
- TimeItem(View &view);
-
-public:
- /**
- * Sets the time of the marker.
- */
- virtual void set_time(const pv::util::Timestamp& time) = 0;
-
- virtual float get_x() const = 0;
-
- /**
- * Drags the item to a delta relative to the drag point.
- * @param delta the offset from the drag point.
- */
- void drag_by(const QPoint &delta);
-
-protected:
- View &view_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <algorithm>
-#include <cmath>
-
-#include <extdef.h>
-
-#include "timemarker.hpp"
-
-#include "pv/widgets/timestampspinbox.hpp"
-#include "view.hpp"
-
-#include <QApplication>
-#include <QFontMetrics>
-#include <QFormLayout>
-#include <QPainter>
-
-#include <pv/widgets/popup.hpp>
-
-using std::max;
-using std::min;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const int TimeMarker::ArrowSize = 4;
-
-TimeMarker::TimeMarker(
- View &view, const QColor &colour, const pv::util::Timestamp& time) :
- TimeItem(view),
- colour_(colour),
- time_(time),
- value_action_(nullptr),
- value_widget_(nullptr),
- updating_value_widget_(false)
-{
-}
-
-const pv::util::Timestamp& TimeMarker::time() const
-{
- return time_;
-}
-
-void TimeMarker::set_time(const pv::util::Timestamp& time)
-{
- time_ = time;
-
- if (value_widget_) {
- updating_value_widget_ = true;
- value_widget_->setValue(time);
- updating_value_widget_ = false;
- }
-
- view_.time_item_appearance_changed(true, true);
-}
-
-float TimeMarker::get_x() const
-{
- // Use roundf() from cmath, std::roundf() causes Android issues (see #945).
- return roundf(((time_ - view_.offset()) / view_.scale()).convert_to<float>()) + 0.5f;
-}
-
-QPoint TimeMarker::point(const QRect &rect) const
-{
- return QPoint(get_x(), rect.bottom());
-}
-
-QRectF TimeMarker::label_rect(const QRectF &rect) const
-{
- QFontMetrics m(QApplication::font());
- const QSizeF text_size(
- max(m.boundingRect(get_text()).size().width(), ArrowSize),
- m.height());
- const QSizeF label_size(text_size + LabelPadding * 2);
- const float top = rect.height() - label_size.height() -
- TimeMarker::ArrowSize - 0.5f;
- const float x = get_x();
-
- return QRectF(QPointF(x - label_size.width() / 2, top), label_size);
-}
-
-QRectF TimeMarker::hit_box_rect(const ViewItemPaintParams &pp) const
-{
- const float x = get_x();
- const float h = QFontMetrics(QApplication::font()).height();
- return QRectF(x - h / 2.0f, pp.top(), h, pp.height());
-}
-
-void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
- if (!enabled())
- return;
-
- const qreal x = get_x();
- const QRectF r(label_rect(rect));
-
- const QPointF points[] = {
- r.topLeft(),
- r.bottomLeft(),
- QPointF(max(r.left(), x - ArrowSize), r.bottom()),
- QPointF(x, rect.bottom()),
- QPointF(min(r.right(), x + ArrowSize), r.bottom()),
- r.bottomRight(),
- r.topRight()
- };
-
- const QPointF highlight_points[] = {
- QPointF(r.left() + 1, r.top() + 1),
- QPointF(r.left() + 1, r.bottom() - 1),
- QPointF(max(r.left() + 1, x - ArrowSize), r.bottom() - 1),
- QPointF(min(max(r.left() + 1, x), r.right() - 1),
- rect.bottom() - 1),
- QPointF(min(r.right() - 1, x + ArrowSize), r.bottom() - 1),
- QPointF(r.right() - 1, r.bottom() - 1),
- QPointF(r.right() - 1, r.top() + 1),
- };
-
- if (selected()) {
- p.setPen(highlight_pen());
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(points));
- }
-
- p.setPen(Qt::transparent);
- p.setBrush(hover ? colour_.lighter() : colour_);
- p.drawPolygon(points, countof(points));
-
- p.setPen(colour_.lighter());
- p.setBrush(Qt::transparent);
- p.drawPolygon(highlight_points, countof(highlight_points));
-
- p.setPen(colour_.darker());
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(points));
-
- p.setPen(select_text_colour(colour_));
- p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_text());
-}
-
-void TimeMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- if (!enabled())
- return;
-
- const float x = get_x();
- p.setPen(colour_.darker());
- p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
-}
-
-pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
-{
- using pv::widgets::Popup;
-
- Popup *const popup = new Popup(parent);
- popup->set_position(parent->mapToGlobal(
- point(parent->rect())), Popup::Bottom);
-
- QFormLayout *const form = new QFormLayout(popup);
- popup->setLayout(form);
-
- value_widget_ = new pv::widgets::TimestampSpinBox(parent);
- value_widget_->setValue(time_);
-
- connect(value_widget_, SIGNAL(valueChanged(const pv::util::Timestamp&)),
- this, SLOT(on_value_changed(const pv::util::Timestamp&)));
-
- form->addRow(tr("Time"), value_widget_);
-
- return popup;
-}
-
-void TimeMarker::on_value_changed(const pv::util::Timestamp& value)
-{
- if (!updating_value_widget_)
- set_time(value);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
-
-#include <QColor>
-#include <QDoubleSpinBox>
-#include <QObject>
-#include <QRectF>
-#include <QWidgetAction>
-
-#include "timeitem.hpp"
-
-class QPainter;
-class QRect;
-
-namespace pv {
-namespace widgets {
- class TimestampSpinBox;
-}
-
-namespace views {
-namespace TraceView {
-
-class View;
-
-class TimeMarker : public TimeItem
-{
- Q_OBJECT
-
-public:
- static const int ArrowSize;
-
-protected:
- /**
- * Constructor.
- * @param view A reference to the view that owns this marker.
- * @param colour A reference to the colour of this cursor.
- * @param time The time to set the flag to.
- */
- TimeMarker(View &view, const QColor &colour, const pv::util::Timestamp& time);
-
-public:
- /**
- * Gets the time of the marker.
- */
- const pv::util::Timestamp& time() const;
-
- /**
- * Sets the time of the marker.
- */
- void set_time(const pv::util::Timestamp& time) override;
-
- float get_x() const override;
-
- /**
- * Gets the arrow-tip point of the time marker.
- * @param rect the rectangle of the ruler area.
- */
- QPoint point(const QRect &rect) const override;
-
- /**
- * Computes the outline rectangle of a label.
- * @param rect the rectangle of the header area.
- * @return Returns the rectangle of the signal label.
- */
- QRectF label_rect(const QRectF &rect) const override;
-
- /**
- * Computes the outline rectangle of the viewport hit-box.
- * @param rect the rectangle of the viewport area.
- * @return Returns the rectangle of the hit-box.
- */
- QRectF hit_box_rect(const ViewItemPaintParams &pp) const override;
-
- /**
- * Gets the text to show in the marker.
- */
- virtual QString get_text() const = 0;
-
- /**
- * Paints the marker's label to the ruler.
- * @param p The painter to draw with.
- * @param rect The rectangle of the ruler client area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- void paint_label(QPainter &p, const QRect &rect, bool hover) override;
-
- /**
- * Paints the foreground layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_fore(QPainter &p, ViewItemPaintParams &pp) override;
-
- virtual pv::widgets::Popup* create_popup(QWidget *parent) override;
-
-private Q_SLOTS:
- void on_value_changed(const pv::util::Timestamp& value);
-
-protected:
- const QColor &colour_;
-
- pv::util::Timestamp time_;
-
- QSizeF text_size_;
-
- QWidgetAction *value_action_;
- pv::widgets::TimestampSpinBox *value_widget_;
- bool updating_value_widget_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <cassert>
-#include <cmath>
-
-#include <QApplication>
-#include <QFormLayout>
-#include <QKeyEvent>
-#include <QLineEdit>
-
-#include "trace.hpp"
-#include "tracepalette.hpp"
-#include "view.hpp"
-
-#include "pv/globalsettings.hpp"
-#include "pv/widgets/colourbutton.hpp"
-#include "pv/widgets/popup.hpp"
-
-using std::pair;
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QPen Trace::AxisPen(QColor(0, 0, 0, 30 * 256 / 100));
-const int Trace::LabelHitPadding = 2;
-
-const QColor Trace::BrightGrayBGColour = QColor(0, 0, 0, 10 * 255 / 100);
-const QColor Trace::DarkGrayBGColour = QColor(0, 0, 0, 15 * 255 / 100);
-
-Trace::Trace(shared_ptr<data::SignalBase> channel) :
- base_(channel),
- popup_(nullptr),
- popup_form_(nullptr)
-{
- connect(channel.get(), SIGNAL(name_changed(const QString&)),
- this, SLOT(on_name_changed(const QString&)));
- connect(channel.get(), SIGNAL(colour_changed(const QColor&)),
- this, SLOT(on_colour_changed(const QColor&)));
-}
-
-shared_ptr<data::SignalBase> Trace::base() const
-{
- return base_;
-}
-
-void Trace::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
- const int y = get_visual_y();
-
- p.setBrush(base_->colour());
-
- if (!enabled())
- return;
-
- const QRectF r = label_rect(rect);
-
- // When selected, move the arrow to the left so that the border can show
- const QPointF offs = (selected()) ? QPointF(-2, 0) : QPointF(0, 0);
-
- // Paint the label
- const float label_arrow_length = r.height() / 2;
- QPointF points[] = {
- offs + r.topLeft(),
- offs + QPointF(r.right() - label_arrow_length, r.top()),
- offs + QPointF(r.right(), y),
- offs + QPointF(r.right() - label_arrow_length, r.bottom()),
- offs + r.bottomLeft()
- };
- QPointF highlight_points[] = {
- offs + QPointF(r.left() + 1, r.top() + 1),
- offs + QPointF(r.right() - label_arrow_length, r.top() + 1),
- offs + QPointF(r.right() - 1, y),
- offs + QPointF(r.right() - label_arrow_length, r.bottom() - 1),
- offs + QPointF(r.left() + 1, r.bottom() - 1)
- };
-
- if (selected()) {
- p.setPen(highlight_pen());
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(points));
- }
-
- p.setPen(Qt::transparent);
- p.setBrush(hover ? base_->colour().lighter() : base_->colour());
- p.drawPolygon(points, countof(points));
-
- p.setPen(base_->colour().lighter());
- p.setBrush(Qt::transparent);
- p.drawPolygon(highlight_points, countof(highlight_points));
-
- p.setPen(base_->colour().darker());
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(points));
-
- // Paint the text
- p.setPen(select_text_colour(base_->colour()));
- p.setFont(QApplication::font());
- p.drawText(QRectF(r.x(), r.y(),
- r.width() - label_arrow_length, r.height()),
- Qt::AlignCenter | Qt::AlignVCenter, base_->name());
-}
-
-QMenu* Trace::create_context_menu(QWidget *parent)
-{
- QMenu *const menu = ViewItem::create_context_menu(parent);
-
- return menu;
-}
-
-pv::widgets::Popup* Trace::create_popup(QWidget *parent)
-{
- using pv::widgets::Popup;
-
- popup_ = new Popup(parent);
- popup_->set_position(parent->mapToGlobal(
- point(parent->rect())), Popup::Right);
-
- create_popup_form();
-
- connect(popup_, SIGNAL(closed()), this, SLOT(on_popup_closed()));
-
- return popup_;
-}
-
-QRectF Trace::label_rect(const QRectF &rect) const
-{
- QFontMetrics m(QApplication::font());
- const QSize text_size(
- m.boundingRect(QRect(), 0, base_->name()).width(), m.height());
- const QSizeF label_size(
- text_size.width() + LabelPadding.width() * 2,
- ceilf((text_size.height() + LabelPadding.height() * 2) / 2) * 2);
- const float half_height = label_size.height() / 2;
- return QRectF(
- rect.right() - half_height - label_size.width() - 0.5,
- get_visual_y() + 0.5f - half_height,
- label_size.width() + half_height,
- label_size.height());
-}
-
-void Trace::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- const View *view = owner_->view();
- assert(view);
-
- if (view->coloured_bg())
- p.setBrush(base_->bgcolour());
- else
- p.setBrush(pp.next_bg_colour_state() ? BrightGrayBGColour : DarkGrayBGColour);
-
- p.setPen(QPen(Qt::NoPen));
-
- const pair<int, int> extents = v_extents();
- p.drawRect(pp.left(), get_visual_y() + extents.first,
- pp.width(), extents.second - extents.first);
-}
-
-void Trace::paint_axis(QPainter &p, ViewItemPaintParams &pp, int y)
-{
- p.setRenderHint(QPainter::Antialiasing, false);
-
- p.setPen(AxisPen);
- p.drawLine(QPointF(pp.left(), y), QPointF(pp.right(), y));
-
- p.setRenderHint(QPainter::Antialiasing, true);
-}
-
-void Trace::add_colour_option(QWidget *parent, QFormLayout *form)
-{
- using pv::widgets::ColourButton;
-
- ColourButton *const colour_button = new ColourButton(
- TracePalette::Rows, TracePalette::Cols, parent);
- colour_button->set_palette(TracePalette::Colours);
- colour_button->set_colour(base_->colour());
- connect(colour_button, SIGNAL(selected(const QColor&)),
- this, SLOT(on_colouredit_changed(const QColor&)));
-
- form->addRow(tr("Colour"), colour_button);
-}
-
-void Trace::create_popup_form()
-{
- // Clear the layout
-
- // Transfer the layout and the child widgets to a temporary widget
- // which we delete after the event was handled. This way, the layout
- // and all widgets contained therein are deleted after the event was
- // handled, leaving the parent popup_ time to handle the change.
- if (popup_form_) {
- QWidget *suicidal = new QWidget();
- suicidal->setLayout(popup_form_);
- suicidal->deleteLater();
- }
-
- // Repopulate the popup
- popup_form_ = new QFormLayout(popup_);
- popup_->setLayout(popup_form_);
- populate_popup_form(popup_, popup_form_);
-}
-
-void Trace::populate_popup_form(QWidget *parent, QFormLayout *form)
-{
- QLineEdit *const name_edit = new QLineEdit(parent);
- name_edit->setText(base_->name());
- connect(name_edit, SIGNAL(textChanged(const QString&)),
- this, SLOT(on_nameedit_changed(const QString&)));
- form->addRow(tr("Name"), name_edit);
-
- add_colour_option(parent, form);
-}
-
-void Trace::set_name(QString name)
-{
- base_->set_name(name);
-}
-
-void Trace::set_colour(QColor colour)
-{
- base_->set_colour(colour);
-}
-
-void Trace::on_name_changed(const QString &text)
-{
- /* This event handler is called by SignalBase when the name was changed there */
- (void)text;
-
- if (owner_) {
- owner_->extents_changed(true, false);
- owner_->row_item_appearance_changed(true, false);
- }
-}
-
-void Trace::on_colour_changed(const QColor &colour)
-{
- /* This event handler is called by SignalBase when the colour was changed there */
- (void)colour;
-
- if (owner_)
- owner_->row_item_appearance_changed(true, true);
-}
-
-void Trace::on_popup_closed()
-{
- popup_ = nullptr;
- popup_form_ = nullptr;
-}
-
-void Trace::on_nameedit_changed(const QString &name)
-{
- /* This event handler notifies SignalBase that the name changed */
- set_name(name);
-}
-
-void Trace::on_colouredit_changed(const QColor &colour)
-{
- /* This event handler notifies SignalBase that the colour changed */
- set_colour(colour);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
-
-#include <QColor>
-#include <QPainter>
-#include <QPen>
-#include <QRect>
-#include <QString>
-
-#include <cstdint>
-
-#include "tracetreeitem.hpp"
-
-#include "pv/data/signalbase.hpp"
-
-using std::shared_ptr;
-
-class QFormLayout;
-
-namespace pv {
-
-namespace data {
-class SignalBase;
-}
-
-namespace widgets {
-class Popup;
-}
-
-namespace views {
-namespace TraceView {
-
-class Trace : public TraceTreeItem
-{
- Q_OBJECT
-
-private:
- static const QPen AxisPen;
- static const int LabelHitPadding;
-
- static const QColor BrightGrayBGColour;
- static const QColor DarkGrayBGColour;
-
-protected:
- Trace(shared_ptr<data::SignalBase> channel);
-
-public:
- /**
- * Returns the underlying SignalBase instance.
- */
- shared_ptr<data::SignalBase> base() const;
-
- /**
- * Sets the name of the signal.
- */
- virtual void set_name(QString name);
-
- /**
- * Set the colour of the signal.
- */
- virtual void set_colour(QColor colour);
-
- /**
- * Paints the signal label.
- * @param p the QPainter to paint into.
- * @param rect the rectangle of the header area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
-
- virtual QMenu* create_context_menu(QWidget *parent);
-
- pv::widgets::Popup* create_popup(QWidget *parent);
-
- /**
- * Computes the outline rectangle of a label.
- * @param rect the rectangle of the header area.
- * @return Returns the rectangle of the signal label.
- */
- QRectF label_rect(const QRectF &rect) const;
-
-protected:
- /**
- * Paints the background layer of the signal with a QPainter.
- * @param p The QPainter to paint into.
- * @param pp The painting parameters object to paint with.
- */
- virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints a zero axis across the viewport.
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- * @param y the y-offset of the axis.
- */
- void paint_axis(QPainter &p, ViewItemPaintParams &pp, int y);
-
- void add_colour_option(QWidget *parent, QFormLayout *form);
-
- void create_popup_form();
-
- virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
-
-protected Q_SLOTS:
- virtual void on_name_changed(const QString &text);
-
- virtual void on_colour_changed(const QColor &colour);
-
- void on_popup_closed();
-
-private Q_SLOTS:
- void on_nameedit_changed(const QString &name);
-
- void on_colouredit_changed(const QColor &colour);
-
-protected:
- shared_ptr<data::SignalBase> base_;
-
-private:
- pv::widgets::Popup *popup_;
- QFormLayout *popup_form_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <extdef.h>
-
-#include <algorithm>
-#include <cassert>
-
-#include <QMenu>
-#include <QPainter>
-
-#include "tracegroup.hpp"
-
-using std::any_of;
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const int TraceGroup::Padding = 8;
-const int TraceGroup::Width = 12;
-const int TraceGroup::LineThickness = 5;
-const QColor TraceGroup::LineColour(QColor(0x55, 0x57, 0x53));
-
-TraceGroup::~TraceGroup()
-{
- owner_ = nullptr;
- clear_child_items();
-}
-
-bool TraceGroup::enabled() const
-{
- return any_of(child_items().begin(), child_items().end(),
- [](const shared_ptr<ViewItem> &r) { return r->enabled(); });
-}
-
-pv::Session& TraceGroup::session()
-{
- assert(owner_);
- return owner_->session();
-}
-
-const pv::Session& TraceGroup::session() const
-{
- assert(owner_);
- return owner_->session();
-}
-
-View* TraceGroup::view()
-{
- assert(owner_);
- return owner_->view();
-}
-
-const View* TraceGroup::view() const
-{
- assert(owner_);
- return owner_->view();
-}
-
-pair<int, int> TraceGroup::v_extents() const
-{
- return TraceTreeItemOwner::v_extents();
-}
-
-void TraceGroup::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
- const QRectF r = label_rect(rect).adjusted(
- LineThickness / 2, LineThickness / 2,
- -LineThickness / 2, -LineThickness / 2);
-
- // Paint the label
- const QPointF points[] = {
- r.topRight(),
- r.topLeft(),
- r.bottomLeft(),
- r.bottomRight()
- };
-
- if (selected()) {
- const QPen pen(highlight_pen());
- p.setPen(QPen(pen.brush(), pen.width() + LineThickness,
- Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
- p.setBrush(Qt::transparent);
- p.drawPolyline(points, countof(points));
- }
-
- p.setPen(QPen(QBrush(LineColour.darker()), LineThickness,
- Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
- p.drawPolyline(points, countof(points));
- p.setPen(QPen(QBrush(hover ? LineColour.lighter() : LineColour),
- LineThickness - 2, Qt::SolidLine, Qt::SquareCap,
- Qt::RoundJoin));
- p.drawPolyline(points, countof(points));
-}
-
-QRectF TraceGroup::label_rect(const QRectF &rect) const
-{
- QRectF child_rect;
- for (const shared_ptr<ViewItem> r : child_items())
- if (r && r->enabled())
- child_rect = child_rect.united(r->label_rect(rect));
-
- return QRectF(child_rect.x() - Width - Padding, child_rect.y(),
- Width, child_rect.height());
-}
-
-bool TraceGroup::pt_in_label_rect(int left, int right, const QPoint &point)
-{
- (void)left;
- (void)right;
- (void)point;
-
- return false;
-}
-
-QMenu* TraceGroup::create_context_menu(QWidget *parent)
-{
- QMenu *const menu = new QMenu(parent);
-
- QAction *const ungroup = new QAction(tr("Ungroup"), this);
- ungroup->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
- connect(ungroup, SIGNAL(triggered()), this, SLOT(on_ungroup()));
- menu->addAction(ungroup);
-
- return menu;
-}
-
-pv::widgets::Popup* TraceGroup::create_popup(QWidget *parent)
-{
- (void)parent;
- return nullptr;
-}
-
-int TraceGroup::owner_visual_v_offset() const
-{
- return owner_ ? visual_v_offset() + owner_->owner_visual_v_offset() : 0;
-}
-
-void TraceGroup::restack_items()
-{
- vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
-
- // Sort by the centre line of the extents
- stable_sort(items.begin(), items.end(),
- [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
- const auto aext = a->v_extents();
- const auto bext = b->v_extents();
- return a->layout_v_offset() +
- (aext.first + aext.second) / 2 <
- b->layout_v_offset() +
- (bext.first + bext.second) / 2;
- });
-
- int total_offset = 0;
- for (shared_ptr<TraceTreeItem> r : items) {
- const pair<int, int> extents = r->v_extents();
- if (extents.first == 0 && extents.second == 0)
- continue;
-
- // We position disabled traces, so that they are close to the
- // animation target positon should they be re-enabled
- if (r->enabled())
- total_offset += -extents.first;
-
- if (!r->dragging())
- r->set_layout_v_offset(total_offset);
-
- if (r->enabled())
- total_offset += extents.second;
- }
-}
-
-unsigned int TraceGroup::depth() const
-{
- return owner_ ? owner_->depth() + 1 : 0;
-}
-
-void TraceGroup::ungroup()
-{
- const vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
- clear_child_items();
-
- for (shared_ptr<TraceTreeItem> r : items)
- owner_->add_child_item(r);
-
- owner_->remove_child_item(shared_from_this());
-}
-
-void TraceGroup::on_ungroup()
-{
- ungroup();
-}
-
-void TraceGroup::row_item_appearance_changed(bool label, bool content)
-{
- if (owner_)
- owner_->row_item_appearance_changed(label, content);
-}
-
-void TraceGroup::extents_changed(bool horz, bool vert)
-{
- if (owner_)
- owner_->extents_changed(horz, vert);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
-
-#include "tracetreeitem.hpp"
-#include "tracetreeitemowner.hpp"
-
-using std::pair;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TraceGroup : public TraceTreeItem, public TraceTreeItemOwner
-{
- Q_OBJECT
-
-private:
- static const int Padding;
- static const int Width;
- static const int LineThickness;
- static const QColor LineColour;
-
-public:
- /**
- * Virtual destructor
- */
- virtual ~TraceGroup();
-
- /**
- * Returns true if the item is visible and enabled.
- */
- bool enabled() const;
-
- /**
- * Returns the session of the onwer.
- */
- pv::Session& session();
-
- /**
- * Returns the session of the onwer.
- */
- const pv::Session& session() const;
-
- /**
- * Returns the view of the owner.
- */
- virtual View* view();
-
- /**
- * Returns the view of the owner.
- */
- virtual const View* view() const;
-
- /**
- * Computes the vertical extents of the contents of this row item.
- * @return A pair containing the minimum and maximum y-values.
- */
- pair<int, int> v_extents() const;
-
- /**
- * Paints the signal label.
- * @param p the QPainter to paint into.
- * @param right the x-coordinate of the right edge of the header
- * area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- void paint_label(QPainter &p, const QRect &rect, bool hover);
-
- /**
- * Computes the outline rectangle of a label.
- * @param rect the rectangle of the header area.
- * @return Returns the rectangle of the signal label.
- */
- QRectF label_rect(const QRectF &rect) const;
-
- /**
- * Determines if a point is in the header label rect.
- * @param left the x-coordinate of the left edge of the header
- * area.
- * @param right the x-coordinate of the right edge of the header
- * area.
- * @param point the point to test.
- */
- bool pt_in_label_rect(int left, int right, const QPoint &point);
-
- QMenu* create_context_menu(QWidget *parent);
-
- pv::widgets::Popup* create_popup(QWidget *parent);
-
- /**
- * Returns the total vertical offset of this trace and all it's owners
- */
- int owner_visual_v_offset() const;
-
- void restack_items();
-
- /**
- * Returns the number of nested parents that this row item owner has.
- */
- unsigned int depth() const;
-
- void ungroup();
-
-public:
- void row_item_appearance_changed(bool label, bool content);
-
- void extents_changed(bool horz, bool vert);
-
-private Q_SLOTS:
- void on_ungroup();
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "tracepalette.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor TracePalette::Colours[Cols * Rows] = {
-
- // Light Colours
- QColor(0xFC, 0xE9, 0x4F), // Butter
- QColor(0xFC, 0xAF, 0x3E), // Orange
- QColor(0xE9, 0xB9, 0x6E), // Chocolate
- QColor(0x8A, 0xE2, 0x34), // Chameleon
- QColor(0x72, 0x9F, 0xCF), // Sky Blue
- QColor(0xAD, 0x7F, 0xA8), // Plum
- QColor(0xCF, 0x72, 0xC3), // Magenta
- QColor(0xEF, 0x29, 0x29), // Scarlet Red
-
- // Mid Colours
- QColor(0xED, 0xD4, 0x00), // Butter
- QColor(0xF5, 0x79, 0x00), // Orange
- QColor(0xC1, 0x7D, 0x11), // Chocolate
- QColor(0x73, 0xD2, 0x16), // Chameleon
- QColor(0x34, 0x65, 0xA4), // Sky Blue
- QColor(0x75, 0x50, 0x7B), // Plum
- QColor(0xA3, 0x34, 0x96), // Magenta
- QColor(0xCC, 0x00, 0x00), // Scarlet Red
-
- // Dark Colours
- QColor(0xC4, 0xA0, 0x00), // Butter
- QColor(0xCE, 0x5C, 0x00), // Orange
- QColor(0x8F, 0x59, 0x02), // Chocolate
- QColor(0x4E, 0x9A, 0x06), // Chameleon
- QColor(0x20, 0x4A, 0x87), // Sky Blue
- QColor(0x5C, 0x35, 0x66), // Plum
- QColor(0x87, 0x20, 0x7A), // Magenta
- QColor(0xA4, 0x00, 0x00), // Scarlet Red
-
- // Greys
- QColor(0x16, 0x19, 0x1A), // Black
- QColor(0x2E, 0x34, 0x36), // Grey 1
- QColor(0x55, 0x57, 0x53), // Grey 2
- QColor(0x88, 0x8A, 0x8F), // Grey 3
- QColor(0xBA, 0xBD, 0xB6), // Grey 4
- QColor(0xD3, 0xD7, 0xCF), // Grey 5
- QColor(0xEE, 0xEE, 0xEC), // Grey 6
- QColor(0xFF, 0xFF, 0xFF), // White
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
-
-#include <QColor>
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TracePalette
-{
-public:
- static const unsigned int Cols = 8;
- static const unsigned int Rows = 4;
- static const QColor Colours[Cols * Rows];
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <cassert>
-
-#include "view.hpp"
-
-#include "tracetreeitem.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-TraceTreeItem::TraceTreeItem() :
- owner_(nullptr),
- layout_v_offset_(0),
- visual_v_offset_(0),
- v_offset_animation_(this, "visual_v_offset")
-{
-}
-
-void TraceTreeItem::select(bool select)
-{
- ViewItem::select(select);
- owner_->row_item_appearance_changed(true, true);
-}
-
-int TraceTreeItem::layout_v_offset() const
-{
- return layout_v_offset_;
-}
-
-void TraceTreeItem::set_layout_v_offset(int v_offset)
-{
- if (layout_v_offset_ == v_offset)
- return;
-
- layout_v_offset_ = v_offset;
-
- if (owner_)
- owner_->extents_changed(false, true);
-}
-
-int TraceTreeItem::visual_v_offset() const
-{
- return visual_v_offset_;
-}
-
-void TraceTreeItem::set_visual_v_offset(int v_offset)
-{
- visual_v_offset_ = v_offset;
-
- if (owner_)
- owner_->row_item_appearance_changed(true, true);
-}
-
-void TraceTreeItem::force_to_v_offset(int v_offset)
-{
- v_offset_animation_.stop();
- layout_v_offset_ = visual_v_offset_ = v_offset;
-
- if (owner_) {
- owner_->row_item_appearance_changed(true, true);
- owner_->extents_changed(false, true);
- }
-}
-
-void TraceTreeItem::animate_to_layout_v_offset()
-{
- if (visual_v_offset_ == layout_v_offset_ ||
- (v_offset_animation_.endValue() == layout_v_offset_ &&
- v_offset_animation_.state() == QAbstractAnimation::Running))
- return;
-
- v_offset_animation_.setDuration(100);
- v_offset_animation_.setStartValue(visual_v_offset_);
- v_offset_animation_.setEndValue(layout_v_offset_);
- v_offset_animation_.setEasingCurve(QEasingCurve::OutQuad);
- v_offset_animation_.start();
-}
-
-TraceTreeItemOwner* TraceTreeItem::owner() const
-{
- return owner_;
-}
-
-void TraceTreeItem::set_owner(TraceTreeItemOwner *owner)
-{
- assert(owner_ || owner);
- v_offset_animation_.stop();
-
- if (owner_) {
- const int owner_offset = owner_->owner_visual_v_offset();
- layout_v_offset_ += owner_offset;
- visual_v_offset_ += owner_offset;
- }
-
- owner_ = owner;
-
- if (owner_) {
- const int owner_offset = owner_->owner_visual_v_offset();
- layout_v_offset_ -= owner_offset;
- visual_v_offset_ -= owner_offset;
- }
-}
-
-int TraceTreeItem::get_visual_y() const
-{
- assert(owner_);
- return visual_v_offset_ + owner_->owner_visual_v_offset();
-}
-
-void TraceTreeItem::drag_by(const QPoint &delta)
-{
- assert(owner_);
- force_to_v_offset(drag_point_.y() + delta.y() -
- owner_->owner_visual_v_offset());
-}
-
-QPoint TraceTreeItem::point(const QRect &rect) const
-{
- return QPoint(rect.right(), get_visual_y());
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
-
-#include <memory>
-
-#include <QPropertyAnimation>
-
-#include "rowitem.hpp"
-
-using std::enable_shared_from_this;
-using std::pair;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TraceTreeItemOwner;
-
-class TraceTreeItem : public RowItem,
- public enable_shared_from_this<TraceTreeItem>
-{
- Q_OBJECT
- Q_PROPERTY(int visual_v_offset
- READ visual_v_offset
- WRITE set_visual_v_offset)
-
-public:
- /**
- * Constructor.
- */
- TraceTreeItem();
-
- /**
- * Gets the owner of this item in the view item hierachy.
- */
- TraceTreeItemOwner* owner() const;
-
- /**
- * Selects or deselects the signal.
- */
- void select(bool select = true);
-
- /**
- * Gets the vertical layout offset of this signal.
- */
- int layout_v_offset() const;
-
- /**
- * Sets the vertical layout offset of this signal.
- */
- void set_layout_v_offset(int v_offset);
-
- /**
- * Gets the vertical visual offset of this signal.
- */
- int visual_v_offset() const;
-
- /**
- * Sets the vertical visual offset of this signal.
- */
- void set_visual_v_offset(int v_offset);
-
- /**
- * Sets the visual and layout offset of this signal.
- */
- void force_to_v_offset(int v_offset);
-
- /**
- * Begins an animation that will animate the visual offset toward
- * the layout offset.
- */
- void animate_to_layout_v_offset();
-
- /**
- * Sets the owner this trace in the view trace hierachy.
- * @param The new owner of the trace.
- */
- void set_owner(TraceTreeItemOwner *owner);
-
- /**
- * Gets the visual y-offset of the axis.
- */
- int get_visual_y() const;
-
- /**
- * Drags the item to a delta relative to the drag point.
- * @param delta the offset from the drag point.
- */
- void drag_by(const QPoint &delta);
-
- /**
- * Gets the arrow-tip point of the row item marker.
- * @param rect the rectangle of the header area.
- */
- QPoint point(const QRect &rect) const;
-
- /**
- * Computes the vertical extents of the contents of this row item.
- * @return A pair containing the minimum and maximum y-values.
- */
- virtual pair<int, int> v_extents() const = 0;
-
-protected:
- TraceTreeItemOwner *owner_;
-
- int layout_v_offset_;
- int visual_v_offset_;
-
-private:
- QPropertyAnimation v_offset_animation_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <cassert>
-
-#include "tracetreeitem.hpp"
-#include "trace.hpp"
-#include "tracetreeitemowner.hpp"
-
-using std::find;
-using std::make_pair;
-using std::max;
-using std::min;
-using std::pair;
-using std::shared_ptr;
-using std::static_pointer_cast;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const ViewItemOwner::item_list& TraceTreeItemOwner::child_items() const
-{
- return items_;
-}
-
-vector< shared_ptr<TraceTreeItem> >
-TraceTreeItemOwner::trace_tree_child_items() const
-{
- vector< shared_ptr<TraceTreeItem> > items;
- for (auto &i : items_) {
- assert(dynamic_pointer_cast<TraceTreeItem>(i));
- const shared_ptr<TraceTreeItem> t(
- static_pointer_cast<TraceTreeItem>(i));
- items.push_back(t);
- }
-
- return items;
-}
-
-void TraceTreeItemOwner::clear_child_items()
-{
- for (auto &t : trace_tree_child_items()) {
- assert(t->owner() == this);
- t->set_owner(nullptr);
- }
- items_.clear();
-}
-
-void TraceTreeItemOwner::add_child_item(shared_ptr<TraceTreeItem> item)
-{
- assert(!item->owner());
- item->set_owner(this);
- items_.push_back(item);
-
- extents_changed(true, true);
-}
-
-void TraceTreeItemOwner::remove_child_item(shared_ptr<TraceTreeItem> item)
-{
- assert(item->owner() == this);
- item->set_owner(nullptr);
- auto iter = find(items_.begin(), items_.end(), item);
- assert(iter != items_.end());
- items_.erase(iter);
-
- extents_changed(true, true);
-}
-
-pair<int, int> TraceTreeItemOwner::v_extents() const
-{
- bool has_children = false;
-
- pair<int, int> extents(INT_MAX, INT_MIN);
- for (const shared_ptr<TraceTreeItem> t : trace_tree_child_items()) {
- assert(t);
- if (!t->enabled())
- continue;
-
- has_children = true;
-
- const int child_offset = t->layout_v_offset();
- const pair<int, int> child_extents = t->v_extents();
- extents.first = min(child_extents.first + child_offset,
- extents.first);
- extents.second = max(child_extents.second + child_offset,
- extents.second);
- }
-
- if (!has_children)
- extents = make_pair(0, 0);
-
- return extents;
-}
-
-void TraceTreeItemOwner::restack_items()
-{
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
-
-#include "viewitemowner.hpp"
-#include "tracetreeitem.hpp"
-
-using std::pair;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-
-class Session;
-
-namespace views {
-namespace TraceView {
-
-class TraceTreeItem;
-class View;
-
-class TraceTreeItemOwner : public ViewItemOwner
-{
-public:
- /**
- * Returns the view of the owner.
- */
- virtual View* view() = 0;
-
- /**
- * Returns the view of the owner.
- */
- virtual const View* view() const = 0;
-
- virtual int owner_visual_v_offset() const = 0;
-
- /**
- * Returns the session of the owner.
- */
- virtual Session& session() = 0;
-
- /**
- * Returns the session of the owner.
- */
- virtual const Session& session() const = 0;
-
- /**
- * Returns the number of nested parents that this row item owner has.
- */
- virtual unsigned int depth() const = 0;
-
- /**
- * Returns a list of row items owned by this object.
- */
- virtual const item_list& child_items() const;
-
- /**
- * Returns a list of row items owned by this object.
- */
- vector< shared_ptr<TraceTreeItem> > trace_tree_child_items() const;
-
- /**
- * Clears the list of child items.
- */
- void clear_child_items();
-
- /**
- * Adds a child item to this object.
- */
- void add_child_item(shared_ptr<TraceTreeItem> item);
-
- /**
- * Removes a child item from this object.
- */
- void remove_child_item(shared_ptr<TraceTreeItem> item);
-
- virtual void restack_items();
-
- /**
- * Computes the vertical extents of the contents of this row item owner.
- * @return A pair containing the minimum and maximum y-values.
- */
- pair<int, int> v_extents() const;
-
-public:
- virtual void row_item_appearance_changed(bool label, bool content) = 0;
-
- virtual void extents_changed(bool horz, bool vert) = 0;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "triggermarker.hpp"
-#include "view.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QColor TriggerMarker::Colour(0x00, 0x00, 0xB0);
-
-TriggerMarker::TriggerMarker(View &view, const pv::util::Timestamp& time) :
- TimeItem(view),
- time_(time)
-{
-}
-
-TriggerMarker::TriggerMarker(const TriggerMarker &marker) :
- TimeItem(marker.view_),
- time_(marker.time_)
-{
-}
-
-bool TriggerMarker::enabled() const
-{
- return true;
-}
-
-bool TriggerMarker::is_draggable() const
-{
- return false;
-}
-
-void TriggerMarker::set_time(const pv::util::Timestamp& time)
-{
- time_ = time;
-
- view_.time_item_appearance_changed(true, true);
-}
-
-float TriggerMarker::get_x() const
-{
- return ((time_ - view_.offset()) / view_.scale()).convert_to<float>();
-}
-
-QPoint TriggerMarker::point(const QRect &rect) const
-{
- return QPoint(get_x(), rect.bottom());
-}
-
-void TriggerMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- if (!enabled())
- return;
-
- QPen pen(Colour);
- pen.setStyle(Qt::DashLine);
-
- const float x = get_x();
- p.setPen(pen);
- p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
-
-#include "timeitem.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class TriggerMarker : public TimeItem
-{
- Q_OBJECT
-
-public:
- static const QColor Colour;
-
-public:
- /**
- * Constructor.
- * @param view A reference to the view that owns this marker.
- * @param time The time to set the marker to.
- */
- TriggerMarker(View &view, const pv::util::Timestamp& time);
-
- /**
- * Copy constructor.
- */
- TriggerMarker(const TriggerMarker &marker);
-
- /**
- * Returns true if the item is visible and enabled.
- */
- bool enabled() const override;
-
- /**
- Returns true if the item may be dragged/moved.
- */
- bool is_draggable() const override;
-
- /**
- * Sets the time of the marker.
- */
- void set_time(const pv::util::Timestamp& time) override;
-
- float get_x() const override;
-
- /**
- * Gets the arrow-tip point of the time marker.
- * @param rect the rectangle of the ruler area.
- */
- QPoint point(const QRect &rect) const override;
-
- /**
- * Paints the foreground layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- void paint_fore(QPainter &p, ViewItemPaintParams &pp) override;
-
-private:
- pv::util::Timestamp time_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef ENABLE_DECODE
-#include <libsigrokdecode/libsigrokdecode.h>
-#endif
-
-#include <extdef.h>
-
-#include <algorithm>
-#include <cassert>
-#include <climits>
-#include <cmath>
-#include <iostream>
-#include <iterator>
-#include <unordered_set>
-
-#include <boost/archive/text_iarchive.hpp>
-#include <boost/archive/text_oarchive.hpp>
-#include <boost/serialization/serialization.hpp>
-
-#include <QApplication>
-#include <QEvent>
-#include <QFontMetrics>
-#include <QMouseEvent>
-#include <QScrollBar>
-#include <QVBoxLayout>
-
-#include <libsigrokcxx/libsigrokcxx.hpp>
-
-#include "analogsignal.hpp"
-#include "header.hpp"
-#include "logicsignal.hpp"
-#include "ruler.hpp"
-#include "signal.hpp"
-#include "tracegroup.hpp"
-#include "triggermarker.hpp"
-#include "view.hpp"
-#include "viewport.hpp"
-
-#include "pv/data/logic.hpp"
-#include "pv/data/logicsegment.hpp"
-#include "pv/devices/device.hpp"
-#include "pv/globalsettings.hpp"
-#include "pv/session.hpp"
-#include "pv/util.hpp"
-
-#ifdef ENABLE_DECODE
-#include "decodetrace.hpp"
-#endif
-
-using pv::data::SignalData;
-using pv::data::Segment;
-using pv::util::TimeUnit;
-using pv::util::Timestamp;
-
-using std::back_inserter;
-using std::copy_if;
-using std::count_if;
-using std::dynamic_pointer_cast;
-using std::inserter;
-using std::max;
-using std::make_pair;
-using std::make_shared;
-using std::min;
-using std::pair;
-using std::set;
-using std::set_difference;
-using std::shared_ptr;
-using std::stringstream;
-using std::unordered_map;
-using std::unordered_set;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const Timestamp View::MaxScale("1e9");
-const Timestamp View::MinScale("1e-12");
-
-const int View::MaxScrollValue = INT_MAX / 2;
-
-const int View::ScaleUnits[3] = {1, 2, 5};
-
-
-CustomScrollArea::CustomScrollArea(QWidget *parent) :
- QAbstractScrollArea(parent)
-{
-}
-
-bool CustomScrollArea::viewportEvent(QEvent *event)
-{
- switch (event->type()) {
- case QEvent::Paint:
- case QEvent::MouseButtonPress:
- case QEvent::MouseButtonRelease:
- case QEvent::MouseButtonDblClick:
- case QEvent::MouseMove:
- case QEvent::Wheel:
- case QEvent::TouchBegin:
- case QEvent::TouchUpdate:
- case QEvent::TouchEnd:
- return false;
- default:
- return QAbstractScrollArea::viewportEvent(event);
- }
-}
-
-View::View(Session &session, bool is_main_view, QWidget *parent) :
- ViewBase(session, is_main_view, parent),
- splitter_(new QSplitter()),
- scale_(1e-3),
- offset_(0),
- updating_scroll_(false),
- settings_restored_(false),
- sticky_scrolling_(false), // Default setting is set in MainWindow::setup_ui()
- always_zoom_to_fit_(false),
- tick_period_(0),
- tick_prefix_(pv::util::SIPrefix::yocto),
- tick_precision_(0),
- time_unit_(util::TimeUnit::Time),
- show_cursors_(false),
- cursors_(new CursorPair(*this)),
- next_flag_text_('A'),
- trigger_markers_(),
- hover_point_(-1, -1),
- scroll_needs_defaults_(true),
- saved_v_offset_(0)
-{
- QVBoxLayout *root_layout = new QVBoxLayout(this);
- root_layout->setContentsMargins(0, 0, 0, 0);
- root_layout->addWidget(splitter_);
-
- viewport_ = new Viewport(*this);
- scrollarea_ = new CustomScrollArea(splitter_);
- scrollarea_->setViewport(viewport_);
- scrollarea_->setFrameShape(QFrame::NoFrame);
-
- ruler_ = new Ruler(*this);
-
- header_ = new Header(*this);
- header_->setMinimumWidth(10); // So that the arrow tips show at least
-
- // We put the header into a simple layout so that we can add the top margin,
- // allowing us to make it line up with the bottom of the ruler
- QWidget *header_container = new QWidget();
- header_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- QVBoxLayout *header_layout = new QVBoxLayout(header_container);
- header_layout->setContentsMargins(0, ruler_->sizeHint().height(), 0, 0);
- header_layout->addWidget(header_);
-
- // To let the ruler and scrollarea be on the same split pane, we need a layout
- QWidget *trace_container = new QWidget();
- trace_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- QVBoxLayout *trace_layout = new QVBoxLayout(trace_container);
- trace_layout->setSpacing(0); // We don't want space between the ruler and scrollarea
- trace_layout->setContentsMargins(0, 0, 0, 0);
- trace_layout->addWidget(ruler_);
- trace_layout->addWidget(scrollarea_);
-
- splitter_->addWidget(header_container);
- splitter_->addWidget(trace_container);
- splitter_->setHandleWidth(1); // Don't show a visible rubber band
- splitter_->setCollapsible(0, false); // Prevent the header from collapsing
- splitter_->setCollapsible(1, false); // Prevent the traces from collapsing
- splitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-
- viewport_->installEventFilter(this);
- ruler_->installEventFilter(this);
- header_->installEventFilter(this);
-
- // Set up settings and event handlers
- GlobalSettings settings;
- coloured_bg_ = settings.value(GlobalSettings::Key_View_ColouredBG).toBool();
-
- connect(scrollarea_->horizontalScrollBar(), SIGNAL(valueChanged(int)),
- this, SLOT(h_scroll_value_changed(int)));
- connect(scrollarea_->verticalScrollBar(), SIGNAL(valueChanged(int)),
- this, SLOT(v_scroll_value_changed()));
-
- connect(header_, SIGNAL(selection_changed()),
- ruler_, SLOT(clear_selection()));
- connect(ruler_, SIGNAL(selection_changed()),
- header_, SLOT(clear_selection()));
-
- connect(header_, SIGNAL(selection_changed()),
- this, SIGNAL(selection_changed()));
- connect(ruler_, SIGNAL(selection_changed()),
- this, SIGNAL(selection_changed()));
-
- connect(splitter_, SIGNAL(splitterMoved(int, int)),
- this, SLOT(on_splitter_moved()));
-
- connect(this, SIGNAL(hover_point_changed()),
- this, SLOT(on_hover_point_changed()));
-
- connect(&lazy_event_handler_, SIGNAL(timeout()),
- this, SLOT(process_sticky_events()));
- lazy_event_handler_.setSingleShot(true);
-
- // Trigger the initial event manually. The default device has signals
- // which were created before this object came into being
- signals_changed();
-
- // make sure the transparent widgets are on the top
- ruler_->raise();
- header_->raise();
-
- // Update the zoom state
- calculate_tick_spacing();
-}
-
-Session& View::session()
-{
- return session_;
-}
-
-const Session& View::session() const
-{
- return session_;
-}
-
-unordered_set< shared_ptr<Signal> > View::signals() const
-{
- return signals_;
-}
-
-void View::clear_signals()
-{
- ViewBase::clear_signalbases();
- signals_.clear();
-}
-
-void View::add_signal(const shared_ptr<Signal> signal)
-{
- ViewBase::add_signalbase(signal->base());
- signals_.insert(signal);
-}
-
-#ifdef ENABLE_DECODE
-void View::clear_decode_signals()
-{
- decode_traces_.clear();
-}
-
-void View::add_decode_signal(shared_ptr<data::SignalBase> signalbase)
-{
- shared_ptr<DecodeTrace> d(
- new DecodeTrace(session_, signalbase, decode_traces_.size()));
- decode_traces_.push_back(d);
-}
-
-void View::remove_decode_signal(shared_ptr<data::SignalBase> signalbase)
-{
- for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++)
- if ((*i)->base() == signalbase) {
- decode_traces_.erase(i);
- signals_changed();
- return;
- }
-}
-#endif
-
-View* View::view()
-{
- return this;
-}
-
-const View* View::view() const
-{
- return this;
-}
-
-Viewport* View::viewport()
-{
- return viewport_;
-}
-
-const Viewport* View::viewport() const
-{
- return viewport_;
-}
-
-void View::save_settings(QSettings &settings) const
-{
- settings.setValue("scale", scale_);
- settings.setValue("v_offset",
- scrollarea_->verticalScrollBar()->sliderPosition());
-
- settings.setValue("splitter_state", splitter_->saveState());
-
- stringstream ss;
- boost::archive::text_oarchive oa(ss);
- oa << boost::serialization::make_nvp("offset", offset_);
- settings.setValue("offset", QString::fromStdString(ss.str()));
-
- for (shared_ptr<Signal> signal : signals_) {
- settings.beginGroup(signal->base()->internal_name());
- signal->save_settings(settings);
- settings.endGroup();
- }
-}
-
-void View::restore_settings(QSettings &settings)
-{
- // Note: It is assumed that this function is only called once,
- // immediately after restoring a previous session.
-
- if (settings.contains("scale"))
- set_scale(settings.value("scale").toDouble());
-
- if (settings.contains("offset")) {
- util::Timestamp offset;
- stringstream ss;
- ss << settings.value("offset").toString().toStdString();
-
- boost::archive::text_iarchive ia(ss);
- ia >> boost::serialization::make_nvp("offset", offset);
-
- set_offset(offset);
- }
-
- if (settings.contains("splitter_state"))
- splitter_->restoreState(settings.value("splitter_state").toByteArray());
-
- for (shared_ptr<Signal> signal : signals_) {
- settings.beginGroup(signal->base()->internal_name());
- signal->restore_settings(settings);
- settings.endGroup();
- }
-
- if (settings.contains("v_offset")) {
- saved_v_offset_ = settings.value("v_offset").toInt();
- set_v_offset(saved_v_offset_);
- scroll_needs_defaults_ = false;
- // Note: see eventFilter() for additional information
- }
-
- settings_restored_ = true;
-}
-
-vector< shared_ptr<TimeItem> > View::time_items() const
-{
- const vector<shared_ptr<Flag>> f(flags());
- vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
- items.push_back(cursors_);
- items.push_back(cursors_->first());
- items.push_back(cursors_->second());
-
- for (auto trigger_marker : trigger_markers_)
- items.push_back(trigger_marker);
-
- return items;
-}
-
-double View::scale() const
-{
- return scale_;
-}
-
-void View::set_scale(double scale)
-{
- if (scale_ != scale) {
- scale_ = scale;
- scale_changed();
- }
-}
-
-const Timestamp& View::offset() const
-{
- return offset_;
-}
-
-void View::set_offset(const pv::util::Timestamp& offset)
-{
- if (offset_ != offset) {
- offset_ = offset;
- offset_changed();
- }
-}
-
-int View::owner_visual_v_offset() const
-{
- return -scrollarea_->verticalScrollBar()->sliderPosition();
-}
-
-void View::set_v_offset(int offset)
-{
- scrollarea_->verticalScrollBar()->setSliderPosition(offset);
- header_->update();
- viewport_->update();
-}
-
-unsigned int View::depth() const
-{
- return 0;
-}
-
-pv::util::SIPrefix View::tick_prefix() const
-{
- return tick_prefix_;
-}
-
-void View::set_tick_prefix(pv::util::SIPrefix tick_prefix)
-{
- if (tick_prefix_ != tick_prefix) {
- tick_prefix_ = tick_prefix;
- tick_prefix_changed();
- }
-}
-
-unsigned int View::tick_precision() const
-{
- return tick_precision_;
-}
-
-void View::set_tick_precision(unsigned tick_precision)
-{
- if (tick_precision_ != tick_precision) {
- tick_precision_ = tick_precision;
- tick_precision_changed();
- }
-}
-
-const pv::util::Timestamp& View::tick_period() const
-{
- return tick_period_;
-}
-
-void View::set_tick_period(const pv::util::Timestamp& tick_period)
-{
- if (tick_period_ != tick_period) {
- tick_period_ = tick_period;
- tick_period_changed();
- }
-}
-
-TimeUnit View::time_unit() const
-{
- return time_unit_;
-}
-
-void View::set_time_unit(pv::util::TimeUnit time_unit)
-{
- if (time_unit_ != time_unit) {
- time_unit_ = time_unit;
- time_unit_changed();
- }
-}
-
-void View::zoom(double steps)
-{
- zoom(steps, viewport_->width() / 2);
-}
-
-void View::zoom(double steps, int offset)
-{
- set_zoom(scale_ * pow(3.0 / 2.0, -steps), offset);
-}
-
-void View::zoom_fit(bool gui_state)
-{
- // Act as one-shot when stopped, toggle along with the GUI otherwise
- if (session_.get_capture_state() == Session::Stopped) {
- always_zoom_to_fit_ = false;
- always_zoom_to_fit_changed(false);
- } else {
- always_zoom_to_fit_ = gui_state;
- always_zoom_to_fit_changed(gui_state);
- }
-
- const pair<Timestamp, Timestamp> extents = get_time_extents();
- const Timestamp delta = extents.second - extents.first;
- if (delta < Timestamp("1e-12"))
- return;
-
- assert(viewport_);
- const int w = viewport_->width();
- if (w <= 0)
- return;
-
- const Timestamp scale = max(min(delta / w, MaxScale), MinScale);
- set_scale_offset(scale.convert_to<double>(), extents.first);
-}
-
-void View::zoom_one_to_one()
-{
- using pv::data::SignalData;
-
- // Make a set of all the visible data objects
- set< shared_ptr<SignalData> > visible_data = get_visible_data();
- if (visible_data.empty())
- return;
-
- assert(viewport_);
- const int w = viewport_->width();
- if (w <= 0)
- return;
-
- set_zoom(1.0 / session_.get_samplerate(), w / 2);
-}
-
-void View::set_scale_offset(double scale, const Timestamp& offset)
-{
- // Disable sticky scrolling / always zoom to fit when acquisition runs
- // and user drags the viewport
- if ((scale_ == scale) && (offset_ != offset) &&
- (session_.get_capture_state() == Session::Running)) {
-
- if (sticky_scrolling_) {
- sticky_scrolling_ = false;
- sticky_scrolling_changed(false);
- }
-
- if (always_zoom_to_fit_) {
- always_zoom_to_fit_ = false;
- always_zoom_to_fit_changed(false);
- }
- }
-
- set_scale(scale);
- set_offset(offset);
-
- calculate_tick_spacing();
-
- update_scroll();
- ruler_->update();
- viewport_->update();
-}
-
-set< shared_ptr<SignalData> > View::get_visible_data() const
-{
- // Make a set of all the visible data objects
- set< shared_ptr<SignalData> > visible_data;
- for (const shared_ptr<Signal> sig : signals_)
- if (sig->enabled())
- visible_data.insert(sig->data());
-
- return visible_data;
-}
-
-pair<Timestamp, Timestamp> View::get_time_extents() const
-{
- boost::optional<Timestamp> left_time, right_time;
- const set< shared_ptr<SignalData> > visible_data = get_visible_data();
- for (const shared_ptr<SignalData> d : visible_data) {
- const vector< shared_ptr<Segment> > segments = d->segments();
- for (const shared_ptr<Segment> &s : segments) {
- double samplerate = s->samplerate();
- samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
-
- const Timestamp start_time = s->start_time();
- left_time = left_time ?
- min(*left_time, start_time) :
- start_time;
- right_time = right_time ?
- max(*right_time, start_time + d->max_sample_count() / samplerate) :
- start_time + d->max_sample_count() / samplerate;
- }
- }
-
- if (!left_time || !right_time)
- return make_pair(0, 0);
-
- assert(*left_time < *right_time);
- return make_pair(*left_time, *right_time);
-}
-
-void View::enable_show_sampling_points(bool state)
-{
- (void)state;
-
- viewport_->update();
-}
-
-void View::enable_show_analog_minor_grid(bool state)
-{
- (void)state;
-
- viewport_->update();
-}
-
-void View::enable_coloured_bg(bool state)
-{
- coloured_bg_ = state;
- viewport_->update();
-}
-
-bool View::coloured_bg() const
-{
- return coloured_bg_;
-}
-
-bool View::cursors_shown() const
-{
- return show_cursors_;
-}
-
-void View::show_cursors(bool show)
-{
- show_cursors_ = show;
- ruler_->update();
- viewport_->update();
-}
-
-void View::centre_cursors()
-{
- const double time_width = scale_ * viewport_->width();
- cursors_->first()->set_time(offset_ + time_width * 0.4);
- cursors_->second()->set_time(offset_ + time_width * 0.6);
- ruler_->update();
- viewport_->update();
-}
-
-shared_ptr<CursorPair> View::cursors() const
-{
- return cursors_;
-}
-
-void View::add_flag(const Timestamp& time)
-{
- flags_.push_back(make_shared<Flag>(*this, time,
- QString("%1").arg(next_flag_text_)));
-
- next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
- (next_flag_text_ + 1);
-
- time_item_appearance_changed(true, true);
-}
-
-void View::remove_flag(shared_ptr<Flag> flag)
-{
- flags_.remove(flag);
- time_item_appearance_changed(true, true);
-}
-
-vector< shared_ptr<Flag> > View::flags() const
-{
- vector< shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
- stable_sort(flags.begin(), flags.end(),
- [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
- return a->time() < b->time();
- });
-
- return flags;
-}
-
-const QPoint& View::hover_point() const
-{
- return hover_point_;
-}
-
-void View::restack_all_trace_tree_items()
-{
- // Make a list of owners that is sorted from deepest first
- const vector<shared_ptr<TraceTreeItem>> items(
- list_by_type<TraceTreeItem>());
- set< TraceTreeItemOwner* > owners;
- for (const auto &r : items)
- owners.insert(r->owner());
- vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end());
- sort(sorted_owners.begin(), sorted_owners.end(),
- [](const TraceTreeItemOwner* a, const TraceTreeItemOwner *b) {
- return a->depth() > b->depth(); });
-
- // Restack the items recursively
- for (auto &o : sorted_owners)
- o->restack_items();
-
- // Animate the items to their destination
- for (const auto &i : items)
- i->animate_to_layout_v_offset();
-}
-
-void View::trigger_event(util::Timestamp location)
-{
- trigger_markers_.push_back(make_shared<TriggerMarker>(*this, location));
-}
-
-void View::get_scroll_layout(double &length, Timestamp &offset) const
-{
- const pair<Timestamp, Timestamp> extents = get_time_extents();
- length = ((extents.second - extents.first) / scale_).convert_to<double>();
- offset = offset_ / scale_;
-}
-
-void View::set_zoom(double scale, int offset)
-{
- // Reset the "always zoom to fit" feature as the user changed the zoom
- always_zoom_to_fit_ = false;
- always_zoom_to_fit_changed(false);
-
- const Timestamp cursor_offset = offset_ + scale_ * offset;
- const Timestamp new_scale = max(min(Timestamp(scale), MaxScale), MinScale);
- const Timestamp new_offset = cursor_offset - new_scale * offset;
- set_scale_offset(new_scale.convert_to<double>(), new_offset);
-}
-
-void View::calculate_tick_spacing()
-{
- const double SpacingIncrement = 10.0f;
- const double MinValueSpacing = 40.0f;
-
- // Figure out the highest numeric value visible on a label
- const QSize areaSize = viewport_->size();
- const Timestamp max_time = max(fabs(offset_),
- fabs(offset_ + scale_ * areaSize.width()));
-
- double min_width = SpacingIncrement;
- double label_width, tick_period_width;
-
- QFontMetrics m(QApplication::font());
-
- // Copies of the member variables with the same name, used in the calculation
- // and written back afterwards, so that we don't emit signals all the time
- // during the calculation.
- pv::util::Timestamp tick_period = tick_period_;
- pv::util::SIPrefix tick_prefix = tick_prefix_;
- unsigned tick_precision = tick_precision_;
-
- do {
- const double min_period = scale_ * min_width;
-
- const int order = (int)floorf(log10f(min_period));
- const pv::util::Timestamp order_decimal =
- pow(pv::util::Timestamp(10), order);
-
- // Allow for a margin of error so that a scale unit of 1 can be used.
- // Otherwise, for a SU of 1 the tick period will almost always be below
- // the min_period by a small amount - and thus skipped in favor of 2.
- // Note: margin assumes that SU[0] and SU[1] contain the smallest values
- double tp_margin = (ScaleUnits[0] + ScaleUnits[1]) / 2.0;
- double tp_with_margin;
- unsigned int unit = 0;
-
- do {
- tp_with_margin = order_decimal.convert_to<double>() *
- (ScaleUnits[unit++] + tp_margin);
- } while (tp_with_margin < min_period && unit < countof(ScaleUnits));
-
- tick_period = order_decimal * ScaleUnits[unit - 1];
- tick_prefix = static_cast<pv::util::SIPrefix>(
- (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3);
-
- // Precision is the number of fractional digits required, not
- // taking the prefix into account (and it must never be negative)
- tick_precision = max(ceil(log10(1 / tick_period)).convert_to<int>(), 0);
-
- tick_period_width = (tick_period / scale_).convert_to<double>();
-
- const QString label_text = Ruler::format_time_with_distance(
- tick_period, max_time, tick_prefix, time_unit_, tick_precision);
-
- label_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
- Qt::AlignLeft | Qt::AlignTop, label_text).width() +
- MinValueSpacing;
-
- min_width += SpacingIncrement;
- } while (tick_period_width < label_width);
-
- set_tick_period(tick_period);
- set_tick_prefix(tick_prefix);
- set_tick_precision(tick_precision);
-}
-
-void View::adjust_top_margin()
-{
- assert(viewport_);
-
- const QSize areaSize = viewport_->size();
-
- const pair<int, int> extents = v_extents();
- const int top_margin = owner_visual_v_offset() + extents.first;
- const int trace_bottom = owner_visual_v_offset() + extents.first + extents.second;
-
- // Do we have empty space at the top while the last trace goes out of screen?
- if ((top_margin > 0) && (trace_bottom > areaSize.height())) {
- const int trace_height = extents.second - extents.first;
-
- // Center everything vertically if there is enough space
- if (areaSize.height() >= trace_height)
- set_v_offset(extents.first -
- ((areaSize.height() - trace_height) / 2));
- else
- // Remove the top margin to make as many traces fit on screen as possible
- set_v_offset(extents.first);
- }
-}
-
-void View::update_scroll()
-{
- assert(viewport_);
- QScrollBar *hscrollbar = scrollarea_->horizontalScrollBar();
- QScrollBar *vscrollbar = scrollarea_->verticalScrollBar();
-
- const QSize areaSize = viewport_->size();
-
- // Set the horizontal scroll bar
- double length = 0;
- Timestamp offset;
- get_scroll_layout(length, offset);
- length = max(length - areaSize.width(), 0.0);
-
- int major_tick_distance = (tick_period_ / scale_).convert_to<int>();
-
- hscrollbar->setPageStep(areaSize.width() / 2);
- hscrollbar->setSingleStep(major_tick_distance);
-
- updating_scroll_ = true;
-
- if (length < MaxScrollValue) {
- hscrollbar->setRange(0, length);
- hscrollbar->setSliderPosition(offset.convert_to<double>());
- } else {
- hscrollbar->setRange(0, MaxScrollValue);
- hscrollbar->setSliderPosition(
- (offset_ * MaxScrollValue / (scale_ * length)).convert_to<double>());
- }
-
- updating_scroll_ = false;
-
- // Set the vertical scrollbar
- vscrollbar->setPageStep(areaSize.height());
- vscrollbar->setSingleStep(areaSize.height() / 8);
-
- const pair<int, int> extents = v_extents();
-
- // Don't change the scrollbar range if there are no traces
- if (extents.first != extents.second)
- vscrollbar->setRange(extents.first - areaSize.height(),
- extents.second);
-
- if (scroll_needs_defaults_)
- set_scroll_default();
-}
-
-void View::reset_scroll()
-{
- scrollarea_->verticalScrollBar()->setRange(0, 0);
-}
-
-void View::set_scroll_default()
-{
- assert(viewport_);
-
- const QSize areaSize = viewport_->size();
-
- const pair<int, int> extents = v_extents();
- const int trace_height = extents.second - extents.first;
-
- // Do all traces fit in the view?
- if (areaSize.height() >= trace_height)
- // Center all traces vertically
- set_v_offset(extents.first -
- ((areaSize.height() - trace_height) / 2));
- else
- // Put the first trace at the top, letting the bottom ones overflow
- set_v_offset(extents.first);
-}
-
-bool View::header_was_shrunk() const
-{
- const int header_pane_width = splitter_->sizes().front();
- const int header_width = header_->extended_size_hint().width();
-
- // Allow for a slight margin of error so that we also accept
- // slight differences when e.g. a label name change increased
- // the overall width
- return (header_pane_width < (header_width - 10));
-}
-
-void View::expand_header_to_fit()
-{
- int splitter_area_width = 0;
- for (int w : splitter_->sizes())
- splitter_area_width += w;
-
- // Make sure the header has enough horizontal space to show all labels fully
- QList<int> pane_sizes;
- pane_sizes.push_back(header_->extended_size_hint().width());
- pane_sizes.push_back(splitter_area_width - header_->extended_size_hint().width());
- splitter_->setSizes(pane_sizes);
-}
-
-void View::update_layout()
-{
- update_scroll();
-}
-
-TraceTreeItemOwner* View::find_prevalent_trace_group(
- const shared_ptr<sigrok::ChannelGroup> &group,
- const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
- &signal_map)
-{
- assert(group);
-
- unordered_set<TraceTreeItemOwner*> owners;
- vector<TraceTreeItemOwner*> owner_list;
-
- // Make a set and a list of all the owners
- for (const auto &channel : group->channels()) {
- for (auto entry : signal_map) {
- if (entry.first->channel() == channel) {
- TraceTreeItemOwner *const o = (entry.second)->owner();
- owner_list.push_back(o);
- owners.insert(o);
- }
- }
- }
-
- // Iterate through the list of owners, and find the most prevalent
- size_t max_prevalence = 0;
- TraceTreeItemOwner *prevalent_owner = nullptr;
- for (TraceTreeItemOwner *owner : owners) {
- const size_t prevalence = count_if(
- owner_list.begin(), owner_list.end(),
- [&](TraceTreeItemOwner *o) { return o == owner; });
- if (prevalence > max_prevalence) {
- max_prevalence = prevalence;
- prevalent_owner = owner;
- }
- }
-
- return prevalent_owner;
-}
-
-vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
- const vector< shared_ptr<sigrok::Channel> > &channels,
- const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
- &signal_map,
- set< shared_ptr<Trace> > &add_list)
-{
- vector< shared_ptr<Trace> > filtered_traces;
-
- for (const auto &channel : channels) {
- for (auto entry : signal_map) {
- if (entry.first->channel() == channel) {
- shared_ptr<Trace> trace = entry.second;
- const auto list_iter = add_list.find(trace);
- if (list_iter == add_list.end())
- continue;
-
- filtered_traces.push_back(trace);
- add_list.erase(list_iter);
- }
- }
- }
-
- return filtered_traces;
-}
-
-void View::determine_time_unit()
-{
- // Check whether we know the sample rate and hence can use time as the unit
- if (time_unit_ == util::TimeUnit::Samples) {
- // Check all signals but...
- for (const shared_ptr<Signal> signal : signals_) {
- const shared_ptr<SignalData> data = signal->data();
-
- // ...only check first segment of each
- const vector< shared_ptr<Segment> > segments = data->segments();
- if (!segments.empty())
- if (segments[0]->samplerate()) {
- set_time_unit(util::TimeUnit::Time);
- break;
- }
- }
- }
-}
-
-bool View::eventFilter(QObject *object, QEvent *event)
-{
- const QEvent::Type type = event->type();
- if (type == QEvent::MouseMove) {
-
- const QMouseEvent *const mouse_event = (QMouseEvent*)event;
- if (object == viewport_)
- hover_point_ = mouse_event->pos();
- else if (object == ruler_)
- hover_point_ = QPoint(mouse_event->x(), 0);
- else if (object == header_)
- hover_point_ = QPoint(0, mouse_event->y());
- else
- hover_point_ = QPoint(-1, -1);
-
- hover_point_changed();
-
- } else if (type == QEvent::Leave) {
- hover_point_ = QPoint(-1, -1);
- hover_point_changed();
- } else if (type == QEvent::Show) {
-
- // This is somewhat of a hack, unfortunately. We cannot use
- // set_v_offset() from within restore_settings() as the view
- // at that point is neither visible nor properly sized.
- // This is the least intrusive workaround I could come up
- // with: set the vertical offset (or scroll defaults) when
- // the view is shown, which happens after all widgets were
- // resized to their final sizes.
- update_layout();
-
- if (!settings_restored_)
- expand_header_to_fit();
-
- if (scroll_needs_defaults_) {
- set_scroll_default();
- scroll_needs_defaults_ = false;
- }
-
- if (saved_v_offset_) {
- set_v_offset(saved_v_offset_);
- saved_v_offset_ = 0;
- }
- }
-
- return QObject::eventFilter(object, event);
-}
-
-void View::resizeEvent(QResizeEvent* event)
-{
- // Only adjust the top margin if we shrunk vertically
- if (event->size().height() < event->oldSize().height())
- adjust_top_margin();
-
- update_layout();
-}
-
-void View::row_item_appearance_changed(bool label, bool content)
-{
- if (label)
- header_->update();
- if (content)
- viewport_->update();
-}
-
-void View::time_item_appearance_changed(bool label, bool content)
-{
- if (label) {
- ruler_->update();
-
- // Make sure the header pane width is updated, too
- update_layout();
- }
-
- if (content)
- viewport_->update();
-}
-
-void View::extents_changed(bool horz, bool vert)
-{
- sticky_events_ |=
- (horz ? TraceTreeItemHExtentsChanged : 0) |
- (vert ? TraceTreeItemVExtentsChanged : 0);
-
- lazy_event_handler_.start();
-}
-
-void View::on_splitter_moved()
-{
- // Setting the maximum width of the header widget doesn't work as
- // expected because the splitter would allow the user to make the
- // pane wider than that, creating empty space as a result.
- // To make this work, we stricly enforce the maximum width by
- // expanding the header unless the user shrunk it on purpose.
- // As we're then setting the width of the header pane, we set the
- // splitter to the maximum allowed position.
- if (!header_was_shrunk())
- expand_header_to_fit();
-}
-
-void View::h_scroll_value_changed(int value)
-{
- if (updating_scroll_)
- return;
-
- // Disable sticky scrolling when user moves the horizontal scroll bar
- // during a running acquisition
- if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) {
- sticky_scrolling_ = false;
- sticky_scrolling_changed(false);
- }
-
- const int range = scrollarea_->horizontalScrollBar()->maximum();
- if (range < MaxScrollValue)
- set_offset(scale_ * value);
- else {
- double length = 0;
- Timestamp offset;
- get_scroll_layout(length, offset);
- set_offset(scale_ * length * value / MaxScrollValue);
- }
-
- ruler_->update();
- viewport_->update();
-}
-
-void View::v_scroll_value_changed()
-{
- header_->update();
- viewport_->update();
-}
-
-void View::signals_changed()
-{
- using sigrok::Channel;
-
- vector< shared_ptr<Channel> > channels;
- shared_ptr<sigrok::Device> sr_dev;
-
- // Do we need to set the vertical scrollbar to its default position later?
- // We do if there are no traces, i.e. the scroll bar has no range set
- bool reset_scrollbar =
- (scrollarea_->verticalScrollBar()->minimum() ==
- scrollarea_->verticalScrollBar()->maximum());
-
- if (!session_.device()) {
- reset_scroll();
- signals_.clear();
- } else {
- sr_dev = session_.device()->device();
- assert(sr_dev);
- channels = sr_dev->channels();
- }
-
- vector< shared_ptr<TraceTreeItem> > new_top_level_items;
-
- // Make a list of traces that are being added, and a list of traces
- // that are being removed
- const vector<shared_ptr<Trace>> prev_trace_list = list_by_type<Trace>();
- const set<shared_ptr<Trace>> prev_traces(
- prev_trace_list.begin(), prev_trace_list.end());
-
- set< shared_ptr<Trace> > traces(signals_.begin(), signals_.end());
-
-#ifdef ENABLE_DECODE
- traces.insert(decode_traces_.begin(), decode_traces_.end());
-#endif
-
- set< shared_ptr<Trace> > add_traces;
- set_difference(traces.begin(), traces.end(),
- prev_traces.begin(), prev_traces.end(),
- inserter(add_traces, add_traces.begin()));
-
- set< shared_ptr<Trace> > remove_traces;
- set_difference(prev_traces.begin(), prev_traces.end(),
- traces.begin(), traces.end(),
- inserter(remove_traces, remove_traces.begin()));
-
- // Make a look-up table of sigrok Channels to pulseview Signals
- unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
- signal_map;
- for (const shared_ptr<Signal> &sig : signals_)
- signal_map[sig->base()] = sig;
-
- // Populate channel groups
- if (sr_dev)
- for (auto entry : sr_dev->channel_groups()) {
- const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
-
- if (group->channels().size() <= 1)
- continue;
-
- // Find best trace group to add to
- TraceTreeItemOwner *owner = find_prevalent_trace_group(
- group, signal_map);
-
- // If there is no trace group, create one
- shared_ptr<TraceGroup> new_trace_group;
- if (!owner) {
- new_trace_group.reset(new TraceGroup());
- owner = new_trace_group.get();
- }
-
- // Extract traces for the trace group, removing them from
- // the add list
- const vector< shared_ptr<Trace> > new_traces_in_group =
- extract_new_traces_for_channels(group->channels(),
- signal_map, add_traces);
-
- // Add the traces to the group
- const pair<int, int> prev_v_extents = owner->v_extents();
- int offset = prev_v_extents.second - prev_v_extents.first;
- for (shared_ptr<Trace> trace : new_traces_in_group) {
- assert(trace);
- owner->add_child_item(trace);
-
- const pair<int, int> extents = trace->v_extents();
- if (trace->enabled())
- offset += -extents.first;
- trace->force_to_v_offset(offset);
- if (trace->enabled())
- offset += extents.second;
- }
-
- if (new_trace_group) {
- // Assign proper vertical offsets to each channel in the group
- new_trace_group->restack_items();
-
- // If this is a new group, enqueue it in the new top level
- // items list
- if (!new_traces_in_group.empty())
- new_top_level_items.push_back(new_trace_group);
- }
- }
-
- // Enqueue the remaining logic channels in a group
- vector< shared_ptr<Channel> > logic_channels;
- copy_if(channels.begin(), channels.end(), back_inserter(logic_channels),
- [](const shared_ptr<Channel>& c) {
- return c->type() == sigrok::ChannelType::LOGIC; });
-
- const vector< shared_ptr<Trace> > non_grouped_logic_signals =
- extract_new_traces_for_channels(logic_channels, signal_map, add_traces);
-
- if (non_grouped_logic_signals.size() > 0) {
- const shared_ptr<TraceGroup> non_grouped_trace_group(
- make_shared<TraceGroup>());
- for (shared_ptr<Trace> trace : non_grouped_logic_signals)
- non_grouped_trace_group->add_child_item(trace);
-
- non_grouped_trace_group->restack_items();
- new_top_level_items.push_back(non_grouped_trace_group);
- }
-
- // Enqueue the remaining channels as free ungrouped traces
- const vector< shared_ptr<Trace> > new_top_level_signals =
- extract_new_traces_for_channels(channels, signal_map, add_traces);
- new_top_level_items.insert(new_top_level_items.end(),
- new_top_level_signals.begin(), new_top_level_signals.end());
-
- // Enqueue any remaining traces i.e. decode traces
- new_top_level_items.insert(new_top_level_items.end(),
- add_traces.begin(), add_traces.end());
-
- // Remove any removed traces
- for (shared_ptr<Trace> trace : remove_traces) {
- TraceTreeItemOwner *const owner = trace->owner();
- assert(owner);
- owner->remove_child_item(trace);
- }
-
- // Remove any empty trace groups
- for (shared_ptr<TraceGroup> group : list_by_type<TraceGroup>())
- if (group->child_items().size() == 0) {
- remove_child_item(group);
- group.reset();
- }
-
- // Add and position the pending top levels items
- int offset = v_extents().second;
- for (auto item : new_top_level_items) {
- add_child_item(item);
-
- // Position the item after the last item or at the top if there is none
- const pair<int, int> extents = item->v_extents();
-
- if (item->enabled())
- offset += -extents.first;
-
- item->force_to_v_offset(offset);
-
- if (item->enabled())
- offset += extents.second;
- }
-
-
- if (!new_top_level_items.empty())
- // Expand the header pane because the header should become fully
- // visible when new signals are added
- expand_header_to_fit();
-
- update_layout();
-
- header_->update();
- viewport_->update();
-
- if (reset_scrollbar)
- set_scroll_default();
-}
-
-void View::capture_state_updated(int state)
-{
- if (state == Session::Running) {
- set_time_unit(util::TimeUnit::Samples);
-
- trigger_markers_.clear();
-
- // Activate "always zoom to fit" if the setting is enabled and we're
- // the main view of this session (other trace views may be used for
- // zooming and we don't want to mess them up)
- GlobalSettings settings;
- bool state = settings.value(GlobalSettings::Key_View_AlwaysZoomToFit).toBool();
- if (is_main_view_ && state) {
- always_zoom_to_fit_ = true;
- always_zoom_to_fit_changed(always_zoom_to_fit_);
- }
-
- // Enable sticky scrolling if the setting is enabled
- sticky_scrolling_ = settings.value(GlobalSettings::Key_View_StickyScrolling).toBool();
- }
-
- if (state == Session::Stopped) {
- // After acquisition has stopped we need to re-calculate the ticks once
- // as it's otherwise done when the user pans or zooms, which is too late
- calculate_tick_spacing();
-
- // Reset "always zoom to fit", the acquisition has stopped
- if (always_zoom_to_fit_) {
- // Perform a final zoom-to-fit before disabling
- zoom_fit(always_zoom_to_fit_);
- always_zoom_to_fit_ = false;
- always_zoom_to_fit_changed(always_zoom_to_fit_);
- }
- }
-}
-
-void View::perform_delayed_view_update()
-{
- if (always_zoom_to_fit_) {
- zoom_fit(true);
- } else if (sticky_scrolling_) {
- // Make right side of the view sticky
- double length = 0;
- Timestamp offset;
- get_scroll_layout(length, offset);
-
- const QSize areaSize = viewport_->size();
- length = max(length - areaSize.width(), 0.0);
-
- set_offset(scale_ * length);
- }
-
- determine_time_unit();
- update_scroll();
- ruler_->update();
- viewport_->update();
-}
-
-void View::process_sticky_events()
-{
- if (sticky_events_ & TraceTreeItemHExtentsChanged)
- update_layout();
- if (sticky_events_ & TraceTreeItemVExtentsChanged) {
- restack_all_trace_tree_items();
- update_scroll();
- }
-
- // Clear the sticky events
- sticky_events_ = 0;
-}
-
-void View::on_hover_point_changed()
-{
- const vector<shared_ptr<TraceTreeItem>> trace_tree_items(
- list_by_type<TraceTreeItem>());
- for (shared_ptr<TraceTreeItem> r : trace_tree_items)
- r->hover_point_changed();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
-
-#include <cstdint>
-#include <list>
-#include <memory>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include <QAbstractScrollArea>
-#include <QSizeF>
-#include <QSplitter>
-
-#include <pv/data/signaldata.hpp>
-#include <pv/util.hpp>
-#include <pv/views/viewbase.hpp>
-
-#include "cursorpair.hpp"
-#include "flag.hpp"
-#include "tracetreeitemowner.hpp"
-
-using std::list;
-using std::unordered_map;
-using std::unordered_set;
-using std::set;
-using std::shared_ptr;
-using std::vector;
-
-namespace sigrok {
-class ChannelGroup;
-}
-
-namespace pv {
-
-class Session;
-
-namespace data {
-class Logic;
-}
-
-namespace views {
-
-namespace TraceView {
-
-class CursorHeader;
-class DecodeTrace;
-class Header;
-class Ruler;
-class Signal;
-class Trace;
-class Viewport;
-class TriggerMarker;
-
-class CustomScrollArea : public QAbstractScrollArea
-{
- Q_OBJECT
-
-public:
- CustomScrollArea(QWidget *parent = nullptr);
- bool viewportEvent(QEvent *event);
-};
-
-class View : public ViewBase, public TraceTreeItemOwner
-{
- Q_OBJECT
-
-private:
- enum StickyEvents {
- TraceTreeItemHExtentsChanged = 1,
- TraceTreeItemVExtentsChanged = 2
- };
-
-private:
- static const pv::util::Timestamp MaxScale;
- static const pv::util::Timestamp MinScale;
-
- static const int MaxScrollValue;
-
- static const int ScaleUnits[3];
-
-public:
- explicit View(Session &session, bool is_main_view=false, QWidget *parent = nullptr);
-
- Session& session();
- const Session& session() const;
-
- /**
- * Returns the signals contained in this view.
- */
- unordered_set< shared_ptr<Signal> > signals() const;
-
- virtual void clear_signals();
-
- void add_signal(const shared_ptr<Signal> signal);
-
-#ifdef ENABLE_DECODE
- virtual void clear_decode_signals();
-
- virtual void add_decode_signal(shared_ptr<data::SignalBase> signalbase);
-
- virtual void remove_decode_signal(shared_ptr<data::SignalBase> signalbase);
-#endif
-
- /**
- * Returns the view of the owner.
- */
- virtual View* view();
-
- /**
- * Returns the view of the owner.
- */
- virtual const View* view() const;
-
- Viewport* viewport();
-
- const Viewport* viewport() const;
-
- virtual void save_settings(QSettings &settings) const;
-
- virtual void restore_settings(QSettings &settings);
-
- /**
- * Gets a list of time markers.
- */
- vector< shared_ptr<TimeItem> > time_items() const;
-
- /**
- * Returns the view time scale in seconds per pixel.
- */
- double scale() const;
-
- /**
- * Returns the time offset of the left edge of the view in
- * seconds.
- */
- const pv::util::Timestamp& offset() const;
-
- /**
- * Returns the vertical scroll offset.
- */
- int owner_visual_v_offset() const;
-
- /**
- * Sets the visual v-offset.
- */
- void set_v_offset(int offset);
-
- /**
- * Returns the SI prefix to apply to the graticule time markings.
- */
- pv::util::SIPrefix tick_prefix() const;
-
- /**
- * Returns the number of fractional digits shown for the time markings.
- */
- unsigned int tick_precision() const;
-
- /**
- * Returns period of the graticule time markings.
- */
- const pv::util::Timestamp& tick_period() const;
-
- /**
- * Returns the unit of time currently used.
- */
- util::TimeUnit time_unit() const;
-
- /**
- * Returns the number of nested parents that this row item owner has.
- */
- unsigned int depth() const;
-
- void zoom(double steps);
- void zoom(double steps, int offset);
-
- void zoom_fit(bool gui_state);
-
- void zoom_one_to_one();
-
- /**
- * Sets the scale and offset.
- * @param scale The new view scale in seconds per pixel.
- * @param offset The view time offset in seconds.
- */
- void set_scale_offset(double scale, const pv::util::Timestamp& offset);
-
- set< shared_ptr<pv::data::SignalData> > get_visible_data() const;
-
- pair<pv::util::Timestamp, pv::util::Timestamp> get_time_extents() const;
-
- /**
- * Enables or disables coloured trace backgrounds. If they're not
- * coloured then they will use alternating colors.
- */
- void enable_coloured_bg(bool state);
-
- /**
- * Returns true if the trace background should be drawn with a coloured background.
- */
- bool coloured_bg() const;
-
- /**
- * Enable or disable showing sampling points.
- */
- void enable_show_sampling_points(bool state);
-
- /**
- * Enable or disable showing the analog minor grid.
- */
- void enable_show_analog_minor_grid(bool state);
-
- /**
- * Returns true if cursors are displayed. false otherwise.
- */
- bool cursors_shown() const;
-
- /**
- * Shows or hides the cursors.
- */
- void show_cursors(bool show = true);
-
- /**
- * Moves the cursors to a convenient position in the view.
- */
- void centre_cursors();
-
- /**
- * Returns a reference to the pair of cursors.
- */
- shared_ptr<CursorPair> cursors() const;
-
- /**
- * Adds a new flag at a specified time.
- */
- void add_flag(const pv::util::Timestamp& time);
-
- /**
- * Removes a flag from the list.
- */
- void remove_flag(shared_ptr<Flag> flag);
-
- /**
- * Gets the list of flags.
- */
- vector< shared_ptr<Flag> > flags() const;
-
- const QPoint& hover_point() const;
-
- void restack_all_trace_tree_items();
-
-Q_SIGNALS:
- void hover_point_changed();
-
- void selection_changed();
-
- /// Emitted when the offset changed.
- void offset_changed();
-
- /// Emitted when the scale changed.
- void scale_changed();
-
- void sticky_scrolling_changed(bool state);
-
- void always_zoom_to_fit_changed(bool state);
-
- /// Emitted when the tick_prefix changed.
- void tick_prefix_changed();
-
- /// Emitted when the tick_precision changed.
- void tick_precision_changed();
-
- /// Emitted when the tick_period changed.
- void tick_period_changed();
-
- /// Emitted when the time_unit changed.
- void time_unit_changed();
-
-public Q_SLOTS:
- void trigger_event(util::Timestamp location);
-
-private:
- void get_scroll_layout(double &length, pv::util::Timestamp &offset) const;
-
- /**
- * Simultaneously sets the zoom and offset.
- * @param scale The scale to set the view to in seconds per pixel. This
- * value is clamped between MinScale and MaxScale.
- * @param offset The offset of the left edge of the view in seconds.
- */
- void set_zoom(double scale, int offset);
-
- /**
- * Find a tick spacing and number formatting that does not cause
- * the values to collide.
- */
- void calculate_tick_spacing();
-
- void adjust_top_margin();
-
- void update_scroll();
-
- void reset_scroll();
-
- void set_scroll_default();
-
- bool header_was_shrunk() const;
-
- void expand_header_to_fit();
-
- void update_layout();
-
- TraceTreeItemOwner* find_prevalent_trace_group(
- const shared_ptr<sigrok::ChannelGroup> &group,
- const unordered_map<shared_ptr<data::SignalBase>,
- shared_ptr<Signal> > &signal_map);
-
- static vector< shared_ptr<Trace> >
- extract_new_traces_for_channels(
- const vector< shared_ptr<sigrok::Channel> > &channels,
- const unordered_map<shared_ptr<data::SignalBase>,
- shared_ptr<Signal> > &signal_map,
- set< shared_ptr<Trace> > &add_list);
-
- void determine_time_unit();
-
- bool eventFilter(QObject *object, QEvent *event);
-
- void resizeEvent(QResizeEvent *event);
-
-public:
- void row_item_appearance_changed(bool label, bool content);
- void time_item_appearance_changed(bool label, bool content);
-
- void extents_changed(bool horz, bool vert);
-
-private Q_SLOTS:
-
- void on_splitter_moved();
-
- void h_scroll_value_changed(int value);
- void v_scroll_value_changed();
-
- void signals_changed();
- void capture_state_updated(int state);
-
- virtual void perform_delayed_view_update();
-
- void process_sticky_events();
-
- void on_hover_point_changed();
-
- /**
- * Sets the 'offset_' member and emits the 'offset_changed'
- * signal if needed.
- */
- void set_offset(const pv::util::Timestamp& offset);
-
- /**
- * Sets the 'scale_' member and emits the 'scale_changed'
- * signal if needed.
- */
- void set_scale(double scale);
-
- /**
- * Sets the 'tick_prefix_' member and emits the 'tick_prefix_changed'
- * signal if needed.
- */
- void set_tick_prefix(pv::util::SIPrefix tick_prefix);
-
- /**
- * Sets the 'tick_precision_' member and emits the 'tick_precision_changed'
- * signal if needed.
- */
- void set_tick_precision(unsigned tick_precision);
-
- /**
- * Sets the 'tick_period_' member and emits the 'tick_period_changed'
- * signal if needed.
- */
- void set_tick_period(const pv::util::Timestamp& tick_period);
-
- /**
- * Sets the 'time_unit' member and emits the 'time_unit_changed'
- * signal if needed.
- */
- void set_time_unit(pv::util::TimeUnit time_unit);
-
-private:
- CustomScrollArea *scrollarea_;
- Viewport *viewport_;
- Ruler *ruler_;
- Header *header_;
- QSplitter *splitter_;
-
- unordered_set< shared_ptr<Signal> > signals_;
-
-#ifdef ENABLE_DECODE
- vector< shared_ptr<DecodeTrace> > decode_traces_;
-#endif
-
- /// The view time scale in seconds per pixel.
- double scale_;
-
- /// The view time offset in seconds.
- pv::util::Timestamp offset_;
-
- bool updating_scroll_;
- bool settings_restored_;
-
- bool sticky_scrolling_;
- bool coloured_bg_;
- bool always_zoom_to_fit_;
-
- pv::util::Timestamp tick_period_;
- pv::util::SIPrefix tick_prefix_;
- unsigned int tick_precision_;
- util::TimeUnit time_unit_;
-
- bool show_cursors_;
- shared_ptr<CursorPair> cursors_;
-
- list< shared_ptr<Flag> > flags_;
- char next_flag_text_;
-
- vector< shared_ptr<TriggerMarker> > trigger_markers_;
-
- QPoint hover_point_;
-
- unsigned int sticky_events_;
- QTimer lazy_event_handler_;
-
- // This is true when the defaults couldn't be set due to insufficient info
- bool scroll_needs_defaults_;
-
- // A nonzero value indicates the v offset to restore. See View::resizeEvent()
- int saved_v_offset_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "viewitem.hpp"
-
-#include <climits>
-
-#include <QApplication>
-#include <QMenu>
-#include <QPalette>
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-const QSizeF ViewItem::LabelPadding(4, 0);
-const int ViewItem::HighlightRadius = 3;
-
-ViewItem::ViewItem() :
- context_parent_(nullptr),
- drag_point_(INT_MIN, INT_MIN),
- selected_(false)
-{
-}
-
-bool ViewItem::selected() const
-{
- return selected_;
-}
-
-void ViewItem::select(bool select)
-{
- selected_ = select;
-}
-
-bool ViewItem::is_draggable() const
-{
- return true;
-}
-
-bool ViewItem::dragging() const
-{
- return drag_point_.x() != INT_MIN && drag_point_.y() != INT_MIN;
-}
-
-void ViewItem::drag()
-{
- if (is_draggable())
- drag_point_ = point(QRect());
-}
-
-void ViewItem::drag_release()
-{
- drag_point_ = QPoint(INT_MIN, INT_MIN);
-}
-
-QRectF ViewItem::label_rect(const QRectF &rect) const
-{
- (void)rect;
- return QRectF();
-}
-
-QRectF ViewItem::hit_box_rect(const ViewItemPaintParams &pp) const
-{
- (void)pp;
- return QRectF();
-}
-
-QMenu* ViewItem::create_context_menu(QWidget *parent)
-{
- context_parent_ = parent;
- return new QMenu(parent);
-}
-
-widgets::Popup* ViewItem::create_popup(QWidget *parent)
-{
- (void)parent;
- return nullptr;
-}
-
-void ViewItem::delete_pressed()
-{
-}
-
-QPen ViewItem::highlight_pen()
-{
- return QPen(QApplication::palette().brush(
- QPalette::Highlight), HighlightRadius * 2,
- Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
-}
-
-void ViewItem::paint_label(QPainter &p, const QRect &rect, bool hover)
-{
- (void)p;
- (void)rect;
- (void)hover;
-}
-
-void ViewItem::paint_back(QPainter &p, ViewItemPaintParams &pp)
-{
- (void)p;
- (void)pp;
-}
-
-void ViewItem::paint_mid(QPainter &p, ViewItemPaintParams &pp)
-{
- (void)p;
- (void)pp;
-}
-
-void ViewItem::paint_fore(QPainter &p, ViewItemPaintParams &pp)
-{
- (void)p;
- (void)pp;
-}
-
-QColor ViewItem::select_text_colour(QColor background)
-{
- return (background.lightness() > 110) ? Qt::black : Qt::white;
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWITEM_HPP
-#define PULSEVIEW_PV_VIEWITEM_HPP
-
-#include <list>
-
-#include <QPen>
-
-#include "viewitempaintparams.hpp"
-
-class QAction;
-class QMenu;
-class QWidget;
-
-namespace pv {
-
-namespace widgets {
-class Popup;
-}
-
-namespace views {
-namespace TraceView {
-
-class ViewItemOwner;
-
-class ViewItem : public QObject
-{
- Q_OBJECT
-
-public:
- static const QSizeF LabelPadding;
- static const int HighlightRadius;
-
-public:
- ViewItem();
-
-public:
- /**
- * Returns true if the item is visible and enabled.
- */
- virtual bool enabled() const = 0;
-
- /**
- * Returns true if the item has been selected by the user.
- */
- bool selected() const;
-
- /**
- * Selects or deselects the signal.
- */
- virtual void select(bool select = true);
-
- /**
- * Returns true if the item may be dragged/moved.
- */
- virtual bool is_draggable() const;
-
- /**
- * Returns true if the item is being dragged.
- */
- bool dragging() const;
-
- /**
- * Sets this item into the dragged state.
- */
- void drag();
-
- /**
- * Sets this item into the un-dragged state.
- */
- virtual void drag_release();
-
- /**
- * Drags the item to a delta relative to the drag point.
- * @param delta the offset from the drag point.
- */
- virtual void drag_by(const QPoint &delta) = 0;
-
- /**
- * Get the drag point.
- * @param rect the rectangle of the widget area.
- */
- virtual QPoint point(const QRect &rect) const = 0;
-
- /**
- * Computes the outline rectangle of a label.
- * @param rect the rectangle of the header area.
- * @return Returns the rectangle of the signal label.
- * @remarks The default implementation returns an empty rectangle.
- */
- virtual QRectF label_rect(const QRectF &rect) const;
-
- /**
- * Computes the outline rectangle of the viewport hit-box.
- * @param rect the rectangle of the viewport area.
- * @return Returns the rectangle of the hit-box.
- * @remarks The default implementation returns an empty hit-box.
- */
- virtual QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
-
- /**
- * Paints the signal label.
- * @param p the QPainter to paint into.
- * @param rect the rectangle of the header area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
-
- /**
- * Paints the background layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the mid-layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp);
-
- /**
- * Paints the foreground layer of the item with a QPainter
- * @param p the QPainter to paint into.
- * @param pp the painting parameters object to paint with.
- */
- virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
-
-public:
- /**
- * Gets the text colour.
- * @remarks This colour is computed by comparing the lightness
- * of the trace colour against a threshold to determine whether
- * white or black would be more visible.
- */
- static QColor select_text_colour(QColor background);
-
-public:
- virtual QMenu* create_context_menu(QWidget *parent);
-
- virtual pv::widgets::Popup* create_popup(QWidget *parent);
-
- virtual void delete_pressed();
-
-protected:
- static QPen highlight_pen();
-
-protected:
- QWidget *context_parent_;
- QPoint drag_point_;
-
-private:
- bool selected_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWITEM_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <memory>
-#include <stack>
-#include <type_traits>
-#include <vector>
-
-#include <pv/session.hpp>
-
-using std::dynamic_pointer_cast;
-using std::forward_iterator_tag;
-using std::shared_ptr;
-using std::stack;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-template<class Owner, class Item> class ViewItemIterator
-{
-public:
- typedef typename Owner::item_list::const_iterator child_iterator;
- typedef shared_ptr<Item> value_type;
- typedef ptrdiff_t difference_type;
- typedef value_type pointer;
- typedef const value_type& reference;
- typedef forward_iterator_tag iterator_category;
-
-public:
- ViewItemIterator(Owner *owner) :
- owner_stack_({owner}) {}
-
- ViewItemIterator(Owner *owner, child_iterator iter) :
- owner_stack_({owner}) {
- assert(owner);
- if (iter != owner->child_items().end())
- iter_stack_.push(iter);
- }
-
- ViewItemIterator(const ViewItemIterator<Owner, Item> &o) :
- owner_stack_(o.owner_stack_),
- iter_stack_(o.iter_stack_) {}
-
- reference operator*() const {
- return *iter_stack_.top();
- }
-
- reference operator->() const {
- return *this;
- }
-
- ViewItemIterator<Owner, Item>& operator++() {
- assert(!owner_stack_.empty());
- assert(!iter_stack_.empty());
-
- shared_ptr<Owner> owner(dynamic_pointer_cast<Owner>(
- *iter_stack_.top()));
- if (owner && !owner->child_items().empty()) {
- owner_stack_.push(owner.get());
- iter_stack_.push(owner->child_items().begin());
- } else {
- while (!iter_stack_.empty() && (++iter_stack_.top()) ==
- owner_stack_.top()->child_items().end()) {
- owner_stack_.pop();
- iter_stack_.pop();
- }
- }
-
- return *this;
- }
-
- ViewItemIterator<Owner, Item> operator++(int) {
- ViewItemIterator<Owner, Item> pre = *this;
- ++*this;
- return pre;
- }
-
- bool operator==(const ViewItemIterator &o) const {
- return (iter_stack_.empty() && o.iter_stack_.empty()) || (
- iter_stack_.size() == o.iter_stack_.size() &&
- owner_stack_.top() == o.owner_stack_.top() &&
- iter_stack_.top() == o.iter_stack_.top());
- }
-
- bool operator!=(const ViewItemIterator &o) const {
- return !((const ViewItemIterator&)*this == o);
- }
-
- void swap(ViewItemIterator<Owner, Item>& other) {
- swap(owner_stack_, other.owner_stack_);
- swap(iter_stack_, other.iter_stack_);
- }
-
-private:
- stack<Owner*> owner_stack_;
- stack<child_iterator> iter_stack_;
-};
-
-template<class Owner, class Item>
-void swap(ViewItemIterator<Owner, Item>& a, ViewItemIterator<Owner, Item>& b)
-{
- a.swap(b);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <cassert>
-
-#include "tracetreeitem.hpp"
-#include "trace.hpp"
-#include "tracetreeitemowner.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-ViewItemOwner::iterator ViewItemOwner::begin()
-{
- return iterator(this, items_.begin());
-}
-
-ViewItemOwner::iterator ViewItemOwner::end()
-{
- return iterator(this);
-}
-
-ViewItemOwner::const_iterator ViewItemOwner::begin() const
-{
- return const_iterator(this, items_.cbegin());
-}
-
-ViewItemOwner::const_iterator ViewItemOwner::end() const
-{
- return const_iterator(this);
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
-
-#include <memory>
-#include <vector>
-
-#include "viewitemiterator.hpp"
-
-using std::dynamic_pointer_cast;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-
-class Session;
-
-namespace views {
-namespace TraceView {
-
-class ViewItem;
-class View;
-
-class ViewItemOwner
-{
-public:
- typedef vector< shared_ptr<ViewItem> > item_list;
- typedef ViewItemIterator<ViewItemOwner, ViewItem> iterator;
- typedef ViewItemIterator<const ViewItemOwner, ViewItem> const_iterator;
-
-public:
- /**
- * Returns a list of row items owned by this object.
- */
- virtual const item_list& child_items() const = 0;
-
- /**
- * Returns a depth-first iterator at the beginning of the child ViewItem
- * tree.
- */
- iterator begin();
-
- /**
- * Returns a depth-first iterator at the end of the child ViewItem tree.
- */
- iterator end();
-
- /**
- * Returns a constant depth-first iterator at the beginning of the
- * child ViewItem tree.
- */
- const_iterator begin() const;
-
- /**
- * Returns a constant depth-first iterator at the end of the child
- * ViewItem tree.
- */
- const_iterator end() const;
-
- /**
- * Creates a list of descendant signals filtered by type.
- */
- template<class T>
- vector< shared_ptr<T> > list_by_type() {
- vector< shared_ptr<T> > items;
- for (const auto &r : *this) {
- shared_ptr<T> p = dynamic_pointer_cast<T>(r);
- if (p)
- items.push_back(p);
- }
-
- return items;
- }
-
-protected:
- item_list items_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <cassert>
-
-#include <QApplication>
-#include <QFontMetrics>
-
-#include "viewitempaintparams.hpp"
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-ViewItemPaintParams::ViewItemPaintParams(
- const QRect &rect, double scale, const pv::util::Timestamp& offset) :
- rect_(rect),
- scale_(scale),
- offset_(offset),
- bg_colour_state_(false)
-{
- assert(scale > 0.0);
-}
-
-QFont ViewItemPaintParams::font()
-{
- return QApplication::font();
-}
-
-int ViewItemPaintParams::text_height()
-{
- return QFontMetrics(font()).height();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
-
-#include "pv/util.hpp"
-
-#include <QFont>
-#include <QRect>
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class ViewItemPaintParams
-{
-public:
- ViewItemPaintParams(
- const QRect &rect, double scale, const pv::util::Timestamp& offset);
-
- QRect rect() const {
- return rect_;
- }
-
- double scale() const {
- return scale_;
- }
-
- const pv::util::Timestamp& offset() const {
- return offset_;
- }
-
- int left() const {
- return rect_.left();
- }
-
- int right() const {
- return rect_.right();
- }
-
- int top() const {
- return rect_.top();
- }
-
- int bottom() const {
- return rect_.bottom();
- }
-
- int width() const {
- return rect_.width();
- }
-
- int height() const {
- return rect_.height();
- }
-
- double pixels_offset() const {
- return (offset_ / scale_).convert_to<double>();
- }
-
- bool next_bg_colour_state() {
- const bool state = bg_colour_state_;
- bg_colour_state_ = !bg_colour_state_;
- return state;
- }
-
-public:
- static QFont font();
-
- static int text_height();
-
-private:
- QRect rect_;
- double scale_;
- pv::util::Timestamp offset_;
- bool bg_colour_state_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <algorithm>
-#include <cassert>
-#include <cmath>
-#include <limits>
-
-#include "signal.hpp"
-#include "view.hpp"
-#include "viewitempaintparams.hpp"
-#include "viewport.hpp"
-
-#include <pv/session.hpp>
-
-#include <QMouseEvent>
-
-#include <QDebug>
-
-using std::abs;
-using std::back_inserter;
-using std::copy;
-using std::dynamic_pointer_cast;
-using std::none_of; // Used in assert()s.
-using std::shared_ptr;
-using std::stable_sort;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-Viewport::Viewport(View &parent) :
- ViewWidget(parent),
- pinch_zoom_active_(false)
-{
- setAutoFillBackground(true);
- setBackgroundRole(QPalette::Base);
-}
-
-shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
-{
- const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
- const vector< shared_ptr<ViewItem> > items(this->items());
- for (auto i = items.rbegin(); i != items.rend(); i++)
- if ((*i)->enabled() && (*i)->hit_box_rect(pp).contains(pt))
- return *i;
- return nullptr;
-}
-
-void Viewport::item_hover(const shared_ptr<ViewItem> &item)
-{
- if (item && item->is_draggable())
- setCursor(dynamic_pointer_cast<RowItem>(item) ?
- Qt::SizeVerCursor : Qt::SizeHorCursor);
- else
- unsetCursor();
-}
-
-void Viewport::drag()
-{
- drag_offset_ = view_.offset();
- drag_v_offset_ = view_.owner_visual_v_offset();
-}
-
-void Viewport::drag_by(const QPoint &delta)
-{
- if (drag_offset_ == boost::none)
- return;
-
- view_.set_scale_offset(view_.scale(),
- (*drag_offset_ - delta.x() * view_.scale()));
-
- view_.set_v_offset(-drag_v_offset_ - delta.y());
-}
-
-void Viewport::drag_release()
-{
- drag_offset_ = boost::none;
-}
-
-vector< shared_ptr<ViewItem> > Viewport::items()
-{
- vector< shared_ptr<ViewItem> > items;
- const vector< shared_ptr<ViewItem> > view_items(
- view_.list_by_type<ViewItem>());
- copy(view_items.begin(), view_items.end(), back_inserter(items));
- const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
- copy(time_items.begin(), time_items.end(), back_inserter(items));
- return items;
-}
-
-bool Viewport::touch_event(QTouchEvent *event)
-{
- QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
-
- if (touchPoints.count() != 2) {
- pinch_zoom_active_ = false;
- return false;
- }
-
- const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
- const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
-
- if (!pinch_zoom_active_ ||
- (event->touchPointStates() & Qt::TouchPointPressed)) {
- pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.pos().x()).convert_to<double>();
- pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.pos().x()).convert_to<double>();
- pinch_zoom_active_ = true;
- }
-
- double w = touchPoint1.pos().x() - touchPoint0.pos().x();
- if (abs(w) >= 1.0) {
- const double scale =
- fabs((pinch_offset1_ - pinch_offset0_) / w);
- double offset = pinch_offset0_ - touchPoint0.pos().x() * scale;
- if (scale > 0)
- view_.set_scale_offset(scale, offset);
- }
-
- if (event->touchPointStates() & Qt::TouchPointReleased) {
- pinch_zoom_active_ = false;
-
- if (touchPoint0.state() & Qt::TouchPointReleased) {
- // Primary touch released
- drag_release();
- } else {
- // Update the mouse down fields so that continued
- // dragging with the primary touch will work correctly
- mouse_down_point_ = touchPoint0.pos().toPoint();
- drag();
- }
- }
-
- return true;
-}
-
-void Viewport::paintEvent(QPaintEvent*)
-{
- typedef void (ViewItem::*LayerPaintFunc)(
- QPainter &p, ViewItemPaintParams &pp);
- LayerPaintFunc layer_paint_funcs[] = {
- &ViewItem::paint_back, &ViewItem::paint_mid,
- &ViewItem::paint_fore, nullptr};
-
- vector< shared_ptr<RowItem> > row_items(view_.list_by_type<RowItem>());
- assert(none_of(row_items.begin(), row_items.end(),
- [](const shared_ptr<RowItem> &r) { return !r; }));
-
- stable_sort(row_items.begin(), row_items.end(),
- [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
- return a->point(QRect()).y() < b->point(QRect()).y(); });
-
- const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
- assert(none_of(time_items.begin(), time_items.end(),
- [](const shared_ptr<TimeItem> &t) { return !t; }));
-
- QPainter p(this);
- p.setRenderHint(QPainter::Antialiasing);
-
- for (LayerPaintFunc *paint_func = layer_paint_funcs;
- *paint_func; paint_func++) {
- ViewItemPaintParams time_pp(rect(), view_.scale(), view_.offset());
- for (const shared_ptr<TimeItem> t : time_items)
- (t.get()->*(*paint_func))(p, time_pp);
-
- ViewItemPaintParams row_pp(rect(), view_.scale(), view_.offset());
- for (const shared_ptr<RowItem> r : row_items)
- (r.get()->*(*paint_func))(p, row_pp);
- }
-
- p.end();
-}
-
-void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
-{
- assert(event);
-
- if (event->buttons() & Qt::LeftButton)
- view_.zoom(2.0, event->x());
- else if (event->buttons() & Qt::RightButton)
- view_.zoom(-2.0, event->x());
-}
-
-void Viewport::wheelEvent(QWheelEvent *event)
-{
- assert(event);
-
- if (event->orientation() == Qt::Vertical) {
- if (event->modifiers() & Qt::ControlModifier) {
- // Vertical scrolling with the control key pressed
- // is intrepretted as vertical scrolling
- view_.set_v_offset(-view_.owner_visual_v_offset() -
- (event->delta() * height()) / (8 * 120));
- } else {
- // Vertical scrolling is interpreted as zooming in/out
- view_.zoom(event->delta() / 120.0, event->x());
- }
- } else if (event->orientation() == Qt::Horizontal) {
- // Horizontal scrolling is interpreted as moving left/right
- view_.set_scale_offset(view_.scale(),
- event->delta() * view_.scale() + view_.offset());
- }
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
-
-#include <boost/optional.hpp>
-
-#include <QTimer>
-#include <QTouchEvent>
-
-#include "pv/util.hpp"
-#include "viewwidget.hpp"
-
-using std::shared_ptr;
-using std::vector;
-
-class QPainter;
-class QPaintEvent;
-class Session;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class View;
-
-class Viewport : public ViewWidget
-{
- Q_OBJECT
-
-public:
- explicit Viewport(View &parent);
-
-private:
- /**
- * Indicates when a view item is being hovered over.
- * @param item The item that is being hovered over, or @c nullptr
- * if no view item is being hovered over.
- */
- void item_hover(const shared_ptr<ViewItem> &item);
-
- /**
- * Gets the first view item which has a hit-box that contains @c pt .
- * @param pt the point to search with.
- * @return the view item that has been found, or and empty
- * @c shared_ptr if no item was found.
- */
- shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt);
-
- /**
- * Sets this item into the dragged state.
- */
- void drag();
-
- /**
- * Drag the background by the delta offset.
- * @param delta the drag offset in pixels.
- */
- void drag_by(const QPoint &delta);
-
- /**
- * Sets this item into the un-dragged state.
- */
- void drag_release();
-
- /**
- * Gets the items in the view widget.
- */
- vector< shared_ptr<ViewItem> > items();
-
- /**
- * Handles touch begin update and end events.
- * @param e the event that triggered this handler.
- */
- bool touch_event(QTouchEvent *event);
-
-private:
- void paintEvent(QPaintEvent *event);
-
- void mouseDoubleClickEvent(QMouseEvent *event);
- void wheelEvent(QWheelEvent *event);
-
-private:
- boost::optional<pv::util::Timestamp> drag_offset_;
- int drag_v_offset_;
-
- double pinch_offset0_;
- double pinch_offset1_;
- bool pinch_zoom_active_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QApplication>
-#include <QMouseEvent>
-#include <QTouchEvent>
-
-#include "tracetreeitem.hpp"
-#include "view.hpp"
-#include "viewwidget.hpp"
-
-using std::any_of;
-using std::shared_ptr;
-using std::vector;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-ViewWidget::ViewWidget(View &parent) :
- QWidget(&parent),
- view_(parent),
- item_dragging_(false)
-{
- setFocusPolicy(Qt::ClickFocus);
- setAttribute(Qt::WA_AcceptTouchEvents, true);
- setMouseTracking(true);
-}
-
-void ViewWidget::clear_selection()
-{
- const auto items = this->items();
- for (auto &i : items)
- i->select(false);
-}
-
-void ViewWidget::item_hover(const shared_ptr<ViewItem> &item)
-{
- (void)item;
-}
-
-void ViewWidget::item_clicked(const shared_ptr<ViewItem> &item)
-{
- (void)item;
-}
-
-bool ViewWidget::accept_drag() const
-{
- const vector< shared_ptr<TimeItem> > items(view_.time_items());
- const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
- view_.list_by_type<TraceTreeItem>());
-
- const bool any_row_items_selected = any_of(
- trace_tree_items.begin(), trace_tree_items.end(),
- [](const shared_ptr<TraceTreeItem> &r) { return r->selected(); });
-
- const bool any_time_items_selected = any_of(items.begin(), items.end(),
- [](const shared_ptr<TimeItem> &i) { return i->selected(); });
-
- if (any_row_items_selected && !any_time_items_selected) {
- // Check all the drag items share a common owner
- TraceTreeItemOwner *item_owner = nullptr;
- for (shared_ptr<TraceTreeItem> r : trace_tree_items)
- if (r->dragging()) {
- if (!item_owner)
- item_owner = r->owner();
- else if (item_owner != r->owner())
- return false;
- }
-
- return true;
- } else if (any_time_items_selected && !any_row_items_selected) {
- return true;
- }
-
- // A background drag is beginning
- return true;
-}
-
-bool ViewWidget::mouse_down() const
-{
- return mouse_down_point_.x() != INT_MIN &&
- mouse_down_point_.y() != INT_MIN;
-}
-
-void ViewWidget::drag_items(const QPoint &delta)
-{
- bool item_dragged = false;
-
- // Drag the row items
- const vector< shared_ptr<RowItem> > row_items(
- view_.list_by_type<RowItem>());
- for (shared_ptr<RowItem> r : row_items)
- if (r->dragging()) {
- r->drag_by(delta);
-
- // Ensure the trace is selected
- r->select();
-
- item_dragged = true;
- }
-
- // If an item is being dragged, update the stacking
- TraceTreeItemOwner *item_owner = nullptr;
- const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
- view_.list_by_type<TraceTreeItem>());
- for (shared_ptr<TraceTreeItem> i : trace_tree_items)
- if (i->dragging())
- item_owner = i->owner();
-
- if (item_owner) {
- item_owner->restack_items();
- for (shared_ptr<TraceTreeItem> i : trace_tree_items)
- i->animate_to_layout_v_offset();
- }
-
- // Drag the time items
- const vector< shared_ptr<TimeItem> > items(view_.time_items());
- for (auto &i : items)
- if (i->dragging()) {
- i->drag_by(delta);
- item_dragged = true;
- }
-
- // Do the background drag
- if (!item_dragged)
- drag_by(delta);
-}
-
-void ViewWidget::drag()
-{
-}
-
-void ViewWidget::drag_by(const QPoint &delta)
-{
- (void)delta;
-}
-
-void ViewWidget::drag_release()
-{
-}
-
-void ViewWidget::mouse_left_press_event(QMouseEvent *event)
-{
- (void)event;
-
- const bool ctrl_pressed =
- QApplication::keyboardModifiers() & Qt::ControlModifier;
-
- // Clear selection if control is not pressed and this item is unselected
- if ((!mouse_down_item_ || !mouse_down_item_->selected()) &&
- !ctrl_pressed)
- clear_selection();
-
- // Set the signal selection state if the item has been clicked
- if (mouse_down_item_) {
- if (ctrl_pressed)
- mouse_down_item_->select(!mouse_down_item_->selected());
- else
- mouse_down_item_->select(true);
- }
-
- // Save the offsets of any signals which will be dragged
- bool item_dragged = false;
- const auto items = this->items();
- for (auto &i : items)
- if (i->selected()) {
- item_dragged = true;
- i->drag();
- }
-
- // Do the background drag
- if (!item_dragged)
- drag();
-
- selection_changed();
-}
-
-void ViewWidget::mouse_left_release_event(QMouseEvent *event)
-{
- assert(event);
-
- auto items = this->items();
- const bool ctrl_pressed =
- QApplication::keyboardModifiers() & Qt::ControlModifier;
-
- // Unselect everything if control is not pressed
- const shared_ptr<ViewItem> mouse_over =
- get_mouse_over_item(event->pos());
-
- for (auto &i : items)
- i->drag_release();
-
- if (item_dragging_)
- view_.restack_all_trace_tree_items();
- else {
- if (!ctrl_pressed) {
- for (shared_ptr<ViewItem> i : items)
- if (mouse_down_item_ != i)
- i->select(false);
-
- if (mouse_down_item_)
- item_clicked(mouse_down_item_);
- }
- }
-
- item_dragging_ = false;
-}
-
-bool ViewWidget::touch_event(QTouchEvent *event)
-{
- (void)event;
-
- return false;
-}
-
-bool ViewWidget::event(QEvent *event)
-{
- switch (event->type()) {
- case QEvent::TouchBegin:
- case QEvent::TouchUpdate:
- case QEvent::TouchEnd:
- if (touch_event(static_cast<QTouchEvent *>(event)))
- return true;
- break;
-
- default:
- break;
- }
-
- return QWidget::event(event);
-}
-
-void ViewWidget::mousePressEvent(QMouseEvent *event)
-{
- assert(event);
-
- /* Ignore right click events as they will open context menus when
- * used on trace labels. Those menus prevent ViewWidget::mouseReleaseEvent()
- * to be triggered upon button release, making mouse_down_item_
- * hold the last reference to a view item that might have been deleted
- * from the context menu, preventing it from being freed as intended.
- */
- if (event->button() & Qt::LeftButton) {
- mouse_down_point_ = event->pos();
- mouse_down_item_ = get_mouse_over_item(event->pos());
- mouse_left_press_event(event);
- }
-}
-
-void ViewWidget::mouseReleaseEvent(QMouseEvent *event)
-{
- assert(event);
-
- if (event->button() & Qt::LeftButton)
- mouse_left_release_event(event);
-
- mouse_down_point_ = QPoint(INT_MIN, INT_MIN);
- mouse_down_item_ = nullptr;
-}
-
-void ViewWidget::mouseMoveEvent(QMouseEvent *event)
-{
- assert(event);
- mouse_point_ = event->pos();
-
- if (!event->buttons())
- item_hover(get_mouse_over_item(event->pos()));
- else if (event->buttons() & Qt::LeftButton) {
- if (!item_dragging_) {
- if ((event->pos() - mouse_down_point_).manhattanLength() <
- QApplication::startDragDistance())
- return;
-
- if (!accept_drag())
- return;
-
- item_dragging_ = true;
- }
-
- // Do the drag
- drag_items(event->pos() - mouse_down_point_);
- }
-}
-
-void ViewWidget::leaveEvent(QEvent*)
-{
- mouse_point_ = QPoint(-1, -1);
- update();
-}
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
+++ /dev/null
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
- *
- * 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 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWWIDGET_HPP
-#define PULSEVIEW_PV_VIEWWIDGET_HPP
-
-#include <memory>
-
-#include <QWidget>
-
-using std::shared_ptr;
-using std::vector;
-
-class QTouchEvent;
-
-namespace pv {
-namespace views {
-namespace TraceView {
-
-class View;
-class ViewItem;
-
-class ViewWidget : public QWidget
-{
- Q_OBJECT
-
-protected:
- ViewWidget(View &parent);
-
- /**
- * Indicates when a view item is being hovered over.
- * @param item The item that is being hovered over, or @c nullptr
- * if no view item is being hovered over.
- * @remarks the default implementation does nothing.
- */
- virtual void item_hover(const shared_ptr<ViewItem> &item);
-
- /**
- * Indicates the event an a view item has been clicked.
- * @param item the view item that has been clicked.
- * @remarks the default implementation does nothing.
- */
- virtual void item_clicked(const shared_ptr<ViewItem> &item);
-
- /**
- * Returns true if the selection of row items allows dragging.
- * @return Returns true if the drag is acceptable.
- */
- bool accept_drag() const;
-
- /**
- * Returns true if the mouse button is down.
- */
- bool mouse_down() const;
-
- /**
- * Drag the dragging items by the delta offset.
- * @param delta the drag offset in pixels.
- */
- void drag_items(const QPoint &delta);
-
- /**
- * Sets this item into the dragged state.
- */
- virtual void drag();
-
- /**
- * Drag the background by the delta offset.
- * @param delta the drag offset in pixels.
- * @remarks The default implementation does nothing.
- */
- virtual void drag_by(const QPoint &delta);
-
- /**
- * Sets this item into the un-dragged state.
- */
- virtual void drag_release();
-
- /**
- * Gets the items in the view widget.
- */
- virtual vector< shared_ptr<ViewItem> > items() = 0;
-
- /**
- * Gets the first view item which has a hit-box that contains @c pt .
- * @param pt the point to search with.
- * @return the view item that has been found, or and empty
- * @c shared_ptr if no item was found.
- */
- virtual shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt) = 0;
-
- /**
- * Handles left mouse button press events.
- * @param event the mouse event that triggered this handler.
- */
- void mouse_left_press_event(QMouseEvent *event);
-
- /**
- * Handles left mouse button release events.
- * @param event the mouse event that triggered this handler.
- */
- void mouse_left_release_event(QMouseEvent *event);
-
- /**
- * Handles touch begin update and end events.
- * @param e the event that triggered this handler.
- */
- virtual bool touch_event(QTouchEvent *event);
-
-protected:
- bool event(QEvent *event);
-
- void mousePressEvent(QMouseEvent *event);
- void mouseReleaseEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
-
- void leaveEvent(QEvent *event);
-
-public Q_SLOTS:
- void clear_selection();
-
-Q_SIGNALS:
- void selection_changed();
-
-protected:
- pv::views::TraceView::View &view_;
- QPoint mouse_point_;
- QPoint mouse_down_point_;
- shared_ptr<ViewItem> mouse_down_item_;
- bool item_dragging_;
-};
-
-} // namespace TraceView
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWWIDGET_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <vector>
+
+#include <QApplication>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QString>
+
+#include "analogsignal.hpp"
+#include "logicsignal.hpp"
+#include "view.hpp"
+
+#include "pv/data/analog.hpp"
+#include "pv/data/analogsegment.hpp"
+#include "pv/data/logic.hpp"
+#include "pv/data/logicsegment.hpp"
+#include "pv/data/signalbase.hpp"
+#include "pv/globalsettings.hpp"
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using std::deque;
+using std::div;
+using std::div_t;
+using std::max;
+using std::make_pair;
+using std::min;
+using std::numeric_limits;
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor AnalogSignal::SignalColours[4] = {
+ QColor(0xC4, 0xA0, 0x00), // Yellow
+ QColor(0x87, 0x20, 0x7A), // Magenta
+ QColor(0x20, 0x4A, 0x87), // Blue
+ QColor(0x4E, 0x9A, 0x06) // Green
+};
+
+const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40 * 256 / 100);
+const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20 * 256 / 100);
+
+const QColor AnalogSignal::SamplingPointColour(0x77, 0x77, 0x77);
+
+const int64_t AnalogSignal::TracePaintBlockSize = 1024 * 1024; // 4 MiB (due to float)
+const float AnalogSignal::EnvelopeThreshold = 64.0f;
+
+const int AnalogSignal::MaximumVDivs = 10;
+const int AnalogSignal::MinScaleIndex = -6;
+const int AnalogSignal::MaxScaleIndex = 7;
+
+const int AnalogSignal::InfoTextMarginRight = 20;
+const int AnalogSignal::InfoTextMarginBottom = 5;
+
+AnalogSignal::AnalogSignal(
+ pv::Session &session,
+ shared_ptr<data::SignalBase> base) :
+ Signal(session, base),
+ scale_index_(4), // 20 per div
+ scale_index_drag_offset_(0),
+ div_height_(3 * QFontMetrics(QApplication::font()).height()),
+ pos_vdivs_(1),
+ neg_vdivs_(1),
+ resolution_(0),
+ conversion_type_(data::SignalBase::NoConversion),
+ display_type_(DisplayBoth),
+ autoranging_(true)
+{
+ pv::data::Analog* analog_data =
+ dynamic_cast<pv::data::Analog*>(data().get());
+
+ connect(analog_data, SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
+ this, SLOT(on_samples_added()));
+
+ base_->set_colour(SignalColours[base_->index() % countof(SignalColours)]);
+ update_scale();
+}
+
+shared_ptr<pv::data::SignalData> AnalogSignal::data() const
+{
+ return base_->analog_data();
+}
+
+void AnalogSignal::save_settings(QSettings &settings) const
+{
+ settings.setValue("pos_vdivs", pos_vdivs_);
+ settings.setValue("neg_vdivs", neg_vdivs_);
+ settings.setValue("scale_index", scale_index_);
+ settings.setValue("conversion_type", conversion_type_);
+ settings.setValue("display_type", display_type_);
+ settings.setValue("autoranging", autoranging_);
+}
+
+void AnalogSignal::restore_settings(QSettings &settings)
+{
+ if (settings.contains("pos_vdivs"))
+ pos_vdivs_ = settings.value("pos_vdivs").toInt();
+
+ if (settings.contains("neg_vdivs"))
+ neg_vdivs_ = settings.value("neg_vdivs").toInt();
+
+ if (settings.contains("scale_index")) {
+ scale_index_ = settings.value("scale_index").toInt();
+ update_scale();
+ }
+
+ if (settings.contains("conversion_type")) {
+ conversion_type_ = (data::SignalBase::ConversionType)(settings.value("conversion_type").toInt());
+ update_conversion_type();
+ }
+
+ if (settings.contains("display_type"))
+ display_type_ = (DisplayType)(settings.value("display_type").toInt());
+
+ if (settings.contains("autoranging"))
+ autoranging_ = settings.value("autoranging").toBool();
+}
+
+pair<int, int> AnalogSignal::v_extents() const
+{
+ const int ph = pos_vdivs_ * div_height_;
+ const int nh = neg_vdivs_ * div_height_;
+ return make_pair(-ph, nh);
+}
+
+int AnalogSignal::scale_handle_offset() const
+{
+ const int h = (pos_vdivs_ + neg_vdivs_) * div_height_;
+
+ return ((scale_index_drag_offset_ - scale_index_) * h / 4) - h / 2;
+}
+
+void AnalogSignal::scale_handle_dragged(int offset)
+{
+ const int h = (pos_vdivs_ + neg_vdivs_) * div_height_;
+
+ scale_index_ = scale_index_drag_offset_ - (offset + h / 2) / (h / 4);
+
+ update_scale();
+}
+
+void AnalogSignal::scale_handle_drag_release()
+{
+ scale_index_drag_offset_ = scale_index_;
+ update_scale();
+}
+
+void AnalogSignal::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (base_->enabled()) {
+ Trace::paint_back(p, pp);
+ paint_axis(p, pp, get_visual_y());
+ }
+}
+
+void AnalogSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
+{
+ assert(base_->analog_data());
+ assert(owner_);
+
+ const int y = get_visual_y();
+
+ if (!base_->enabled())
+ return;
+
+ if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) {
+ paint_grid(p, y, pp.left(), pp.right());
+
+ const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
+ base_->analog_data()->analog_segments();
+ if (segments.empty())
+ return;
+
+ const shared_ptr<pv::data::AnalogSegment> &segment =
+ segments.front();
+
+ const double pixels_offset = pp.pixels_offset();
+ const double samplerate = max(1.0, segment->samplerate());
+ const pv::util::Timestamp& start_time = segment->start_time();
+ const int64_t last_sample = segment->get_sample_count() - 1;
+ const double samples_per_pixel = samplerate * pp.scale();
+ const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+ const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
+
+ const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+ const int64_t end_sample = min(max((ceil(end) + 1).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+
+ if (samples_per_pixel < EnvelopeThreshold)
+ paint_trace(p, segment, y, pp.left(),
+ start_sample, end_sample,
+ pixels_offset, samples_per_pixel);
+ else
+ paint_envelope(p, segment, y, pp.left(),
+ start_sample, end_sample,
+ pixels_offset, samples_per_pixel);
+ }
+
+ if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth)) {
+ if (((conversion_type_ == data::SignalBase::A2LConversionByTreshold) ||
+ (conversion_type_ == data::SignalBase::A2LConversionBySchmittTrigger))) {
+
+ paint_logic_mid(p, pp);
+ }
+ }
+}
+
+void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) {
+ const int y = get_visual_y();
+
+ // Show the info section on the right side of the trace
+ const QString infotext = QString("%1 V/div").arg(resolution_);
+
+ p.setPen(base_->colour());
+ p.setFont(QApplication::font());
+
+ const QRectF bounding_rect = QRectF(pp.left(),
+ y + v_extents().first,
+ pp.width() - InfoTextMarginRight,
+ v_extents().second - v_extents().first - InfoTextMarginBottom);
+
+ p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext);
+ }
+}
+
+void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right)
+{
+ p.setRenderHint(QPainter::Antialiasing, false);
+
+ GlobalSettings settings;
+ const bool show_analog_minor_grid =
+ settings.value(GlobalSettings::Key_View_ShowAnalogMinorGrid).toBool();
+
+ if (pos_vdivs_ > 0) {
+ p.setPen(QPen(GridMajorColor, 1, Qt::DashLine));
+ for (int i = 1; i <= pos_vdivs_; i++) {
+ const float dy = i * div_height_;
+ p.drawLine(QLineF(left, y - dy, right, y - dy));
+ }
+ }
+
+ if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
+ p.setPen(QPen(GridMinorColor, 1, Qt::DashLine));
+ for (int i = 0; i < pos_vdivs_; i++) {
+ const float dy = i * div_height_;
+ const float dy25 = dy + (0.25 * div_height_);
+ const float dy50 = dy + (0.50 * div_height_);
+ const float dy75 = dy + (0.75 * div_height_);
+ p.drawLine(QLineF(left, y - dy25, right, y - dy25));
+ p.drawLine(QLineF(left, y - dy50, right, y - dy50));
+ p.drawLine(QLineF(left, y - dy75, right, y - dy75));
+ }
+ }
+
+ if (neg_vdivs_ > 0) {
+ p.setPen(QPen(GridMajorColor, 1, Qt::DashLine));
+ for (int i = 1; i <= neg_vdivs_; i++) {
+ const float dy = i * div_height_;
+ p.drawLine(QLineF(left, y + dy, right, y + dy));
+ }
+ }
+
+ if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
+ p.setPen(QPen(GridMinorColor, 1, Qt::DashLine));
+ for (int i = 0; i < neg_vdivs_; i++) {
+ const float dy = i * div_height_;
+ const float dy25 = dy + (0.25 * div_height_);
+ const float dy50 = dy + (0.50 * div_height_);
+ const float dy75 = dy + (0.75 * div_height_);
+ p.drawLine(QLineF(left, y + dy25, right, y + dy25));
+ p.drawLine(QLineF(left, y + dy50, right, y + dy50));
+ p.drawLine(QLineF(left, y + dy75, right, y + dy75));
+ }
+ }
+
+ p.setRenderHint(QPainter::Antialiasing, true);
+}
+
+void AnalogSignal::paint_trace(QPainter &p,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
+ int y, int left, const int64_t start, const int64_t end,
+ const double pixels_offset, const double samples_per_pixel)
+{
+ if (end <= start)
+ return;
+
+ // Calculate and paint the sampling points if enabled and useful
+ GlobalSettings settings;
+ const bool show_sampling_points =
+ settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
+ (samples_per_pixel < 0.25);
+
+ p.setPen(base_->colour());
+
+ const int64_t points_count = end - start;
+
+ QPointF *points = new QPointF[points_count];
+ QPointF *point = points;
+
+ QRectF *sampling_points = nullptr;
+ if (show_sampling_points)
+ sampling_points = new QRectF[points_count];
+ QRectF *sampling_point = sampling_points;
+
+ int64_t sample_count = min(points_count, TracePaintBlockSize);
+ int64_t block_sample = 0;
+ const float *sample_block = segment->get_samples(start, start + sample_count);
+
+ const int w = 2;
+ for (int64_t sample = start; sample != end; sample++, block_sample++) {
+
+ if (block_sample == TracePaintBlockSize) {
+ block_sample = 0;
+ delete[] sample_block;
+ sample_count = min(points_count - sample, TracePaintBlockSize);
+ sample_block = segment->get_samples(sample, sample + sample_count);
+ }
+
+ const float x = (sample / samples_per_pixel -
+ pixels_offset) + left;
+
+ *point++ = QPointF(x, y - sample_block[block_sample] * scale_);
+
+ if (show_sampling_points)
+ *sampling_point++ =
+ QRectF(x - (w / 2), y - sample_block[block_sample] * scale_ - (w / 2), w, w);
+ }
+ delete[] sample_block;
+
+ p.drawPolyline(points, points_count);
+
+ if (show_sampling_points) {
+ p.setPen(SamplingPointColour);
+ p.drawRects(sampling_points, points_count);
+ delete[] sampling_points;
+ }
+
+ delete[] points;
+}
+
+void AnalogSignal::paint_envelope(QPainter &p,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
+ int y, int left, const int64_t start, const int64_t end,
+ const double pixels_offset, const double samples_per_pixel)
+{
+ using pv::data::AnalogSegment;
+
+ AnalogSegment::EnvelopeSection e;
+ segment->get_envelope_section(e, start, end, samples_per_pixel);
+
+ if (e.length < 2)
+ return;
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(base_->colour());
+
+ QRectF *const rects = new QRectF[e.length];
+ QRectF *rect = rects;
+
+ for (uint64_t sample = 0; sample < e.length - 1; sample++) {
+ const float x = ((e.scale * sample + e.start) /
+ samples_per_pixel - pixels_offset) + left;
+ const AnalogSegment::EnvelopeSample *const s =
+ e.samples + sample;
+
+ // We overlap this sample with the next so that vertical
+ // gaps do not appear during steep rising or falling edges
+ const float b = y - max(s->max, (s + 1)->min) * scale_;
+ const float t = y - min(s->min, (s + 1)->max) * scale_;
+
+ float h = b - t;
+ if (h >= 0.0f && h <= 1.0f)
+ h = 1.0f;
+ if (h <= 0.0f && h >= -1.0f)
+ h = -1.0f;
+
+ *rect++ = QRectF(x, t, 1.0f, h);
+ }
+
+ p.drawRects(rects, e.length);
+
+ delete[] rects;
+ delete[] e.samples;
+}
+
+void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp)
+{
+ QLineF *line;
+
+ vector< pair<int64_t, bool> > edges;
+
+ assert(base_);
+
+ const int y = get_visual_y();
+
+ if (!base_->enabled() || !base_->logic_data())
+ return;
+
+ const int signal_margin =
+ QFontMetrics(QApplication::font()).height() / 2;
+
+ const int ph = min(pos_vdivs_, 1) * div_height_;
+ const int nh = min(neg_vdivs_, 1) * div_height_;
+ const float high_offset = y - ph + signal_margin + 0.5f;
+ const float low_offset = y + nh - signal_margin - 0.5f;
+
+ const deque< shared_ptr<pv::data::LogicSegment> > &segments =
+ base_->logic_data()->logic_segments();
+
+ if (segments.empty())
+ return;
+
+ const shared_ptr<pv::data::LogicSegment> &segment =
+ segments.front();
+
+ double samplerate = segment->samplerate();
+
+ // Show sample rate as 1Hz when it is unknown
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ const double pixels_offset = pp.pixels_offset();
+ const pv::util::Timestamp& start_time = segment->start_time();
+ const int64_t last_sample = segment->get_sample_count() - 1;
+ const double samples_per_pixel = samplerate * pp.scale();
+ const double pixels_per_sample = 1 / samples_per_pixel;
+ const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+ const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
+
+ const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+ const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+
+ segment->get_subsampled_edges(edges, start_sample, end_sample,
+ samples_per_pixel / LogicSignal::Oversampling, 0);
+ assert(edges.size() >= 2);
+
+ // Check whether we need to paint the sampling points
+ GlobalSettings settings;
+ const bool show_sampling_points =
+ settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
+ (samples_per_pixel < 0.25);
+
+ vector<QRectF> sampling_points;
+ float sampling_point_x = 0.0f;
+ int64_t sampling_point_sample = start_sample;
+ const int w = 2;
+
+ if (show_sampling_points) {
+ sampling_points.reserve(end_sample - start_sample + 1);
+ sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left();
+ }
+
+ // Paint the edges
+ const unsigned int edge_count = edges.size() - 2;
+ QLineF *const edge_lines = new QLineF[edge_count];
+ line = edge_lines;
+
+ for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
+ const float x = ((*i).first / samples_per_pixel -
+ pixels_offset) + pp.left();
+ *line++ = QLineF(x, high_offset, x, low_offset);
+
+ if (show_sampling_points)
+ while (sampling_point_sample < (*i).first) {
+ const float y = (*i).second ? low_offset : high_offset;
+ sampling_points.emplace_back(
+ QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
+ sampling_point_sample++;
+ sampling_point_x += pixels_per_sample;
+ };
+ }
+
+ // Calculate the sample points from the last edge to the end of the trace
+ if (show_sampling_points)
+ while ((uint64_t)sampling_point_sample <= end_sample) {
+ // Signal changed after the last edge, so the level is inverted
+ const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
+ sampling_points.emplace_back(
+ QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
+ sampling_point_sample++;
+ sampling_point_x += pixels_per_sample;
+ };
+
+ p.setPen(LogicSignal::EdgeColour);
+ p.drawLines(edge_lines, edge_count);
+ delete[] edge_lines;
+
+ // Paint the caps
+ const unsigned int max_cap_line_count = edges.size();
+ QLineF *const cap_lines = new QLineF[max_cap_line_count];
+
+ p.setPen(LogicSignal::HighColour);
+ paint_logic_caps(p, cap_lines, edges, true, samples_per_pixel,
+ pixels_offset, pp.left(), high_offset);
+ p.setPen(LogicSignal::LowColour);
+ paint_logic_caps(p, cap_lines, edges, false, samples_per_pixel,
+ pixels_offset, pp.left(), low_offset);
+
+ delete[] cap_lines;
+
+ // Paint the sampling points
+ if (show_sampling_points) {
+ p.setPen(SamplingPointColour);
+ p.drawRects(sampling_points.data(), sampling_points.size());
+ }
+}
+
+void AnalogSignal::paint_logic_caps(QPainter &p, QLineF *const lines,
+ vector< pair<int64_t, bool> > &edges, bool level,
+ double samples_per_pixel, double pixels_offset, float x_offset,
+ float y_offset)
+{
+ QLineF *line = lines;
+
+ for (auto i = edges.begin(); i != (edges.end() - 1); i++)
+ if ((*i).second == level) {
+ *line++ = QLineF(
+ ((*i).first / samples_per_pixel -
+ pixels_offset) + x_offset, y_offset,
+ ((*(i+1)).first / samples_per_pixel -
+ pixels_offset) + x_offset, y_offset);
+ }
+
+ p.drawLines(lines, line - lines);
+}
+
+float AnalogSignal::get_resolution(int scale_index)
+{
+ const float seq[] = {1.0f, 2.0f, 5.0f};
+
+ const int offset = numeric_limits<int>::max() / (2 * countof(seq));
+ const div_t d = div((int)(scale_index + countof(seq) * offset),
+ countof(seq));
+
+ return powf(10.0f, d.quot - offset) * seq[d.rem];
+}
+
+void AnalogSignal::update_scale()
+{
+ resolution_ = get_resolution(scale_index_);
+ scale_ = div_height_ / resolution_;
+}
+
+void AnalogSignal::update_conversion_type()
+{
+ base_->set_conversion_type(conversion_type_);
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
+{
+ const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
+ base_->analog_data()->analog_segments();
+
+ if (segments.empty())
+ return;
+
+ static double prev_min = 0, prev_max = 0;
+ double min = 0, max = 0;
+
+ for (shared_ptr<pv::data::AnalogSegment> segment : segments) {
+ pair<double, double> mm = segment->get_min_max();
+ min = std::min(min, mm.first);
+ max = std::max(max, mm.second);
+ }
+
+ if ((min == prev_min) && (max == prev_max) && !force_update)
+ return;
+
+ prev_min = min;
+ prev_max = max;
+
+ // If we're allowed to alter the div assignment...
+ if (!keep_divs) {
+ // Use all divs for the positive range if there are no negative values
+ if ((min == 0) && (neg_vdivs_ > 0)) {
+ pos_vdivs_ += neg_vdivs_;
+ neg_vdivs_ = 0;
+ }
+
+ // Split up the divs if there are negative values but no negative divs
+ if ((min < 0) && (neg_vdivs_ == 0)) {
+ neg_vdivs_ = pos_vdivs_ / 2;
+ pos_vdivs_ -= neg_vdivs_;
+ }
+ }
+
+ // If there is still no positive div when we need it, add one
+ // (this can happen when pos_vdivs==neg_vdivs==0)
+ if ((max > 0) && (pos_vdivs_ == 0)) {
+ pos_vdivs_ = 1;
+ owner_->extents_changed(false, true);
+ }
+
+ // If there is still no negative div when we need it, add one
+ // (this can happen when pos_vdivs was 0 or 1 when trying to split)
+ if ((min < 0) && (neg_vdivs_ == 0)) {
+ neg_vdivs_ = 1;
+ owner_->extents_changed(false, true);
+ }
+
+ double min_value_per_div;
+ if ((pos_vdivs_ > 0) && (neg_vdivs_ > 0))
+ min_value_per_div = std::max(max / pos_vdivs_, -min / neg_vdivs_);
+ else if (pos_vdivs_ > 0)
+ min_value_per_div = max / pos_vdivs_;
+ else
+ min_value_per_div = -min / neg_vdivs_;
+
+ // Find first scale value that is bigger than the value we need
+ for (int i = MinScaleIndex; i < MaxScaleIndex; i++)
+ if (get_resolution(i) > min_value_per_div) {
+ scale_index_ = i;
+ break;
+ }
+
+ update_scale();
+}
+
+void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+ // Add the standard options
+ Signal::populate_popup_form(parent, form);
+
+ QFormLayout *const layout = new QFormLayout;
+
+ // Add the number of vdivs
+ pvdiv_sb_ = new QSpinBox(parent);
+ pvdiv_sb_->setRange(0, MaximumVDivs);
+ pvdiv_sb_->setValue(pos_vdivs_);
+ connect(pvdiv_sb_, SIGNAL(valueChanged(int)),
+ this, SLOT(on_pos_vdivs_changed(int)));
+ layout->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
+
+ nvdiv_sb_ = new QSpinBox(parent);
+ nvdiv_sb_->setRange(0, MaximumVDivs);
+ nvdiv_sb_->setValue(neg_vdivs_);
+ connect(nvdiv_sb_, SIGNAL(valueChanged(int)),
+ this, SLOT(on_neg_vdivs_changed(int)));
+ layout->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
+
+ // Add the vertical resolution
+ resolution_cb_ = new QComboBox(parent);
+
+ for (int i = MinScaleIndex; i < MaxScaleIndex; i++) {
+ const QString label = QString("%1").arg(get_resolution(i));
+ resolution_cb_->insertItem(0, label, QVariant(i));
+ }
+
+ int cur_idx = resolution_cb_->findData(QVariant(scale_index_));
+ resolution_cb_->setCurrentIndex(cur_idx);
+
+ connect(resolution_cb_, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_resolution_changed(int)));
+
+ QGridLayout *const vdiv_layout = new QGridLayout;
+ QLabel *const vdiv_unit = new QLabel(tr("V/div"));
+ vdiv_layout->addWidget(resolution_cb_, 0, 0);
+ vdiv_layout->addWidget(vdiv_unit, 0, 1);
+
+ layout->addRow(tr("Vertical resolution"), vdiv_layout);
+
+ // Add the autoranging checkbox
+ QCheckBox* autoranging_cb = new QCheckBox();
+ autoranging_cb->setCheckState(autoranging_ ? Qt::Checked : Qt::Unchecked);
+
+ connect(autoranging_cb, SIGNAL(stateChanged(int)),
+ this, SLOT(on_autoranging_changed(int)));
+
+ layout->addRow(tr("Autoranging"), autoranging_cb);
+
+ // Add the conversion type dropdown
+ conversion_cb_ = new QComboBox();
+
+ conversion_cb_->addItem("none", data::SignalBase::NoConversion);
+ conversion_cb_->addItem("to logic via threshold", data::SignalBase::A2LConversionByTreshold);
+ conversion_cb_->addItem("to logic via schmitt-trigger", data::SignalBase::A2LConversionBySchmittTrigger);
+
+ cur_idx = conversion_cb_->findData(QVariant(conversion_type_));
+ conversion_cb_->setCurrentIndex(cur_idx);
+
+// layout->addRow(tr("Conversion"), conversion_cb_);
+
+ connect(conversion_cb_, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_conversion_changed(int)));
+
+ // Add the display type dropdown
+ display_type_cb_ = new QComboBox();
+
+ display_type_cb_->addItem(tr("Analog"), DisplayAnalog);
+ display_type_cb_->addItem(tr("Converted"), DisplayConverted);
+ display_type_cb_->addItem(tr("Both"), DisplayBoth);
+
+ cur_idx = display_type_cb_->findData(QVariant(display_type_));
+ display_type_cb_->setCurrentIndex(cur_idx);
+
+// layout->addRow(tr("Traces to show:"), display_type_cb_);
+
+ connect(display_type_cb_, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_display_type_changed(int)));
+
+ form->addRow(layout);
+}
+
+void AnalogSignal::on_samples_added()
+{
+ perform_autoranging(false, false);
+}
+
+void AnalogSignal::on_pos_vdivs_changed(int vdivs)
+{
+ if (vdivs == pos_vdivs_)
+ return;
+
+ pos_vdivs_ = vdivs;
+
+ // There has to be at least one div, positive or negative
+ if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) {
+ pos_vdivs_ = 1;
+ if (pvdiv_sb_)
+ pvdiv_sb_->setValue(pos_vdivs_);
+ }
+
+ if (autoranging_) {
+ perform_autoranging(true, true);
+
+ // It could be that a positive or negative div was added, so update
+ if (pvdiv_sb_) {
+ pvdiv_sb_->setValue(pos_vdivs_);
+ nvdiv_sb_->setValue(neg_vdivs_);
+ }
+ }
+
+ if (owner_) {
+ // Call order is important, otherwise the lazy event handler won't work
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+ }
+}
+
+void AnalogSignal::on_neg_vdivs_changed(int vdivs)
+{
+ if (vdivs == neg_vdivs_)
+ return;
+
+ neg_vdivs_ = vdivs;
+
+ // There has to be at least one div, positive or negative
+ if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) {
+ pos_vdivs_ = 1;
+ if (pvdiv_sb_)
+ pvdiv_sb_->setValue(pos_vdivs_);
+ }
+
+ if (autoranging_) {
+ perform_autoranging(true, true);
+
+ // It could be that a positive or negative div was added, so update
+ if (pvdiv_sb_) {
+ pvdiv_sb_->setValue(pos_vdivs_);
+ nvdiv_sb_->setValue(neg_vdivs_);
+ }
+ }
+
+ if (owner_) {
+ // Call order is important, otherwise the lazy event handler won't work
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+ }
+}
+
+void AnalogSignal::on_resolution_changed(int index)
+{
+ scale_index_ = resolution_cb_->itemData(index).toInt();
+ update_scale();
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void AnalogSignal::on_autoranging_changed(int state)
+{
+ autoranging_ = (state == Qt::Checked);
+
+ if (autoranging_)
+ perform_autoranging(false, true);
+
+ if (owner_) {
+ // Call order is important, otherwise the lazy event handler won't work
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+ }
+}
+
+void AnalogSignal::on_conversion_changed(int index)
+{
+ data::SignalBase::ConversionType old_conv_type = conversion_type_;
+
+ conversion_type_ = (data::SignalBase::ConversionType)(conversion_cb_->itemData(index).toInt());
+
+ if (conversion_type_ != old_conv_type) {
+ base_->set_conversion_type(conversion_type_);
+ update_conversion_type();
+ }
+}
+
+void AnalogSignal::on_display_type_changed(int index)
+{
+ display_type_ = (DisplayType)(display_type_cb_->itemData(index).toInt());
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
+
+#include "signal.hpp"
+
+#include <memory>
+
+#include <QComboBox>
+#include <QSpinBox>
+
+using std::pair;
+using std::shared_ptr;
+
+namespace pv {
+
+namespace data {
+class Analog;
+class AnalogSegment;
+class SignalBase;
+}
+
+namespace views {
+namespace trace {
+
+class AnalogSignal : public Signal
+{
+ Q_OBJECT
+
+private:
+ static const QColor SignalColours[4];
+ static const QColor GridMajorColor, GridMinorColor;
+ static const QColor SamplingPointColour;
+
+ static const int64_t TracePaintBlockSize;
+ static const float EnvelopeThreshold;
+
+ static const int MaximumVDivs;
+ static const int MaxScaleIndex, MinScaleIndex;
+ static const int InfoTextMarginRight, InfoTextMarginBottom;
+
+ enum DisplayType {
+ DisplayAnalog = 0,
+ DisplayConverted = 1,
+ DisplayBoth = 2
+ };
+
+public:
+ AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
+
+ virtual ~AnalogSignal() = default;
+
+ shared_ptr<pv::data::SignalData> data() const;
+
+ virtual void save_settings(QSettings &settings) const;
+
+ virtual void restore_settings(QSettings &settings);
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ pair<int, int> v_extents() const;
+
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ int scale_handle_offset() const;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ void scale_handle_dragged(int offset);
+
+ /**
+ * @copydoc pv::view::Signal::signal_scale_handle_drag_release()
+ */
+ void scale_handle_drag_release();
+
+ /**
+ * Paints the background layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_back(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the mid-layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+
+private:
+ void paint_grid(QPainter &p, int y, int left, int right);
+
+ void paint_trace(QPainter &p,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
+ int y, int left, const int64_t start, const int64_t end,
+ const double pixels_offset, const double samples_per_pixel);
+
+ void paint_envelope(QPainter &p,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
+ int y, int left, const int64_t start, const int64_t end,
+ const double pixels_offset, const double samples_per_pixel);
+
+ void paint_logic_mid(QPainter &p, ViewItemPaintParams &pp);
+
+ void paint_logic_caps(QPainter &p, QLineF *const lines,
+ vector< pair<int64_t, bool> > &edges,
+ bool level, double samples_per_pixel, double pixels_offset,
+ float x_offset, float y_offset);
+
+ /**
+ * Computes the scale factor from the scale index and vdiv settings.
+ */
+ float get_resolution(int scale_index);
+
+ void update_scale();
+
+ void update_conversion_type();
+
+ void perform_autoranging(bool keep_divs, bool force_update);
+
+protected:
+ void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+private Q_SLOTS:
+ void on_samples_added();
+
+ void on_pos_vdivs_changed(int vdivs);
+ void on_neg_vdivs_changed(int vdivs);
+
+ void on_resolution_changed(int index);
+
+ void on_autoranging_changed(int state);
+
+ void on_conversion_changed(int index);
+
+ void on_display_type_changed(int index);
+
+private:
+ QComboBox *resolution_cb_, *conversion_cb_, *display_type_cb_;
+ QSpinBox *pvdiv_sb_, *nvdiv_sb_;
+
+ float scale_;
+ int scale_index_;
+ int scale_index_drag_offset_;
+
+ int div_height_;
+ int pos_vdivs_, neg_vdivs_; // divs per positive/negative side
+ float resolution_; // e.g. 10 for 10 V/div
+
+ data::SignalBase::ConversionType conversion_type_;
+ DisplayType display_type_;
+ bool autoranging_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cursor.hpp"
+
+#include "pv/util.hpp"
+#include "ruler.hpp"
+#include "view.hpp"
+
+#include <QApplication>
+#include <QBrush>
+#include <QPainter>
+#include <QPointF>
+#include <QRect>
+#include <QRectF>
+
+#include <cassert>
+#include <cstdio>
+#include <limits>
+
+using std::abs; // Force usage of std::abs() instead of C's abs().
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor Cursor::FillColour(52, 101, 164);
+
+Cursor::Cursor(View &view, double time) :
+ TimeMarker(view, FillColour, time)
+{
+}
+
+bool Cursor::enabled() const
+{
+ return view_.cursors_shown();
+}
+
+QString Cursor::get_text() const
+{
+ const shared_ptr<Cursor> other = get_other_cursor();
+ const pv::util::Timestamp& diff = abs(time_ - other->time_);
+
+ return Ruler::format_time_with_distance(
+ diff, time_, view_.tick_prefix(), view_.time_unit(), view_.tick_precision());
+}
+
+QRectF Cursor::label_rect(const QRectF &rect) const
+{
+ const shared_ptr<Cursor> other(get_other_cursor());
+ assert(other);
+
+ const float x = get_x();
+
+ QFontMetrics m(QApplication::font());
+ QSize text_size = m.boundingRect(get_text()).size();
+
+ const QSizeF label_size(
+ text_size.width() + LabelPadding.width() * 2,
+ text_size.height() + LabelPadding.height() * 2);
+ const float top = rect.height() - label_size.height() -
+ TimeMarker::ArrowSize - 0.5f;
+ const float height = label_size.height();
+
+ const pv::util::Timestamp& other_time = other->time();
+
+ if (time_ > other_time ||
+ (abs(time_ - other_time).is_zero() && this > other.get()))
+ return QRectF(x, top, label_size.width(), height);
+ else
+ return QRectF(x - label_size.width(), top, label_size.width(), height);
+}
+
+shared_ptr<Cursor> Cursor::get_other_cursor() const
+{
+ const shared_ptr<CursorPair> cursors(view_.cursors());
+ assert(cursors);
+ return (cursors->first().get() == this) ?
+ cursors->second() : cursors->first();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
+
+#include "timemarker.hpp"
+
+#include <memory>
+
+#include <QSizeF>
+
+using std::shared_ptr;
+
+class QPainter;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class Cursor : public TimeMarker
+{
+ Q_OBJECT
+
+public:
+ static const QColor FillColour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this cursor pair.
+ * @param time The time to set the flag to.
+ */
+ Cursor(View &view, double time);
+
+public:
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Gets the text to show in the marker.
+ */
+ QString get_text() const;
+
+ /**
+ * Gets the marker label rectangle.
+ * @param rect The rectangle of the ruler client area.
+ * @return Returns the label rectangle.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+private:
+ shared_ptr<Cursor> get_other_cursor() const;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cursorpair.hpp"
+
+#include "pv/util.hpp"
+#include "ruler.hpp"
+#include "view.hpp"
+
+#include <algorithm>
+#include <cassert>
+
+using std::max;
+using std::make_pair;
+using std::min;
+using std::shared_ptr;
+using std::pair;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const int CursorPair::DeltaPadding = 8;
+const QColor CursorPair::ViewportFillColour(220, 231, 243);
+
+CursorPair::CursorPair(View &view) :
+ TimeItem(view),
+ first_(new Cursor(view, 0.0)),
+ second_(new Cursor(view, 1.0))
+{
+}
+
+bool CursorPair::enabled() const
+{
+ return view_.cursors_shown();
+}
+
+shared_ptr<Cursor> CursorPair::first() const
+{
+ return first_;
+}
+
+shared_ptr<Cursor> CursorPair::second() const
+{
+ return second_;
+}
+
+void CursorPair::set_time(const pv::util::Timestamp& time)
+{
+ const pv::util::Timestamp delta = second_->time() - first_->time();
+ first_->set_time(time);
+ second_->set_time(time + delta);
+}
+
+float CursorPair::get_x() const
+{
+ return (first_->get_x() + second_->get_x()) / 2.0f;
+}
+
+QPoint CursorPair::point(const QRect &rect) const
+{
+ return first_->point(rect);
+}
+
+pv::widgets::Popup* CursorPair::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+QRectF CursorPair::label_rect(const QRectF &rect) const
+{
+ const QSizeF label_size(text_size_ + LabelPadding * 2);
+ const pair<float, float> offsets(get_cursor_offsets());
+ const pair<float, float> normal_offsets(
+ (offsets.first < offsets.second) ? offsets :
+ make_pair(offsets.second, offsets.first));
+
+ const float height = label_size.height();
+ const float left = max(normal_offsets.first + DeltaPadding, -height);
+ const float right = min(normal_offsets.second - DeltaPadding,
+ (float)rect.width() + height);
+
+ return QRectF(left, rect.height() - label_size.height() -
+ TimeMarker::ArrowSize - 0.5f,
+ right - left, height);
+}
+
+void CursorPair::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ assert(first_);
+ assert(second_);
+
+ if (!enabled())
+ return;
+
+ const QColor text_colour =
+ ViewItem::select_text_colour(Cursor::FillColour);
+
+ p.setPen(text_colour);
+ compute_text_size(p);
+ QRectF delta_rect(label_rect(rect));
+
+ const int radius = delta_rect.height() / 2;
+ const QRectF text_rect(delta_rect.intersected(
+ rect).adjusted(radius, 0, -radius, 0));
+ if (text_rect.width() >= text_size_.width()) {
+ const int highlight_radius = delta_rect.height() / 2 - 2;
+
+ if (selected()) {
+ p.setBrush(Qt::transparent);
+ p.setPen(highlight_pen());
+ p.drawRoundedRect(delta_rect, radius, radius);
+ }
+
+ p.setBrush(hover ? Cursor::FillColour.lighter() :
+ Cursor::FillColour);
+ p.setPen(Cursor::FillColour.darker());
+ p.drawRoundedRect(delta_rect, radius, radius);
+
+ delta_rect.adjust(1, 1, -1, -1);
+ p.setPen(Cursor::FillColour.lighter());
+ p.drawRoundedRect(delta_rect, highlight_radius, highlight_radius);
+
+ p.setPen(text_colour);
+ p.drawText(text_rect, Qt::AlignCenter | Qt::AlignVCenter,
+ format_string());
+ }
+}
+
+void CursorPair::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ p.setPen(Qt::NoPen);
+ p.setBrush(QBrush(ViewportFillColour));
+
+ const pair<float, float> offsets(get_cursor_offsets());
+ const int l = (int)max(min(
+ offsets.first, offsets.second), 0.0f);
+ const int r = (int)min(max(
+ offsets.first, offsets.second), (float)pp.width());
+
+ p.drawRect(l, pp.top(), r - l, pp.height());
+}
+
+QString CursorPair::format_string()
+{
+ const pv::util::SIPrefix prefix = view_.tick_prefix();
+ const pv::util::Timestamp diff = abs(second_->time() - first_->time());
+
+ const QString s1 = Ruler::format_time_with_distance(
+ diff, diff, prefix, view_.time_unit(), view_.tick_precision(), false);
+ const QString s2 = util::format_time_si(
+ 1 / diff, pv::util::SIPrefix::unspecified, 4, "Hz", false);
+
+ return QString("%1 / %2").arg(s1).arg(s2);
+}
+
+void CursorPair::compute_text_size(QPainter &p)
+{
+ assert(first_);
+ assert(second_);
+
+ text_size_ = p.boundingRect(QRectF(), 0, format_string()).size();
+}
+
+pair<float, float> CursorPair::get_cursor_offsets() const
+{
+ assert(first_);
+ assert(second_);
+
+ return pair<float, float>(first_->get_x(), second_->get_x());
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
+
+#include "cursor.hpp"
+
+#include <memory>
+
+#include <QPainter>
+
+using std::pair;
+using std::shared_ptr;
+
+class QPainter;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class CursorPair : public TimeItem
+{
+private:
+ static const int DeltaPadding;
+ static const QColor ViewportFillColour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this cursor pair.
+ */
+ CursorPair(View &view);
+
+public:
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const override;
+
+ /**
+ * Returns a pointer to the first cursor.
+ */
+ shared_ptr<Cursor> first() const;
+
+ /**
+ * Returns a pointer to the second cursor.
+ */
+ shared_ptr<Cursor> second() const;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const override;
+
+ QPoint point(const QRect &rect) const override;
+
+ pv::widgets::Popup* create_popup(QWidget *parent) override;
+
+public:
+ QRectF label_rect(const QRectF &rect) const override;
+
+ /**
+ * Paints the marker's label to the ruler.
+ * @param p The painter to draw with.
+ * @param rect The rectangle of the ruler client area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover) override;
+
+ /**
+ * Paints the background layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_back(QPainter &p, ViewItemPaintParams &pp) override;
+
+ /**
+ * Constructs the string to display.
+ */
+ QString format_string();
+
+ void compute_text_size(QPainter &p);
+
+ pair<float, float> get_cursor_offsets() const;
+
+private:
+ shared_ptr<Cursor> first_, second_;
+
+ QSizeF text_size_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+extern "C" {
+#include <libsigrokdecode/libsigrokdecode.h>
+}
+
+#include <mutex>
+
+#include <extdef.h>
+
+#include <tuple>
+
+#include <boost/functional/hash.hpp>
+
+#include <QAction>
+#include <QApplication>
+#include <QComboBox>
+#include <QFormLayout>
+#include <QLabel>
+#include <QMenu>
+#include <QPushButton>
+#include <QToolTip>
+
+#include "decodetrace.hpp"
+#include "view.hpp"
+#include "viewport.hpp"
+
+#include <pv/globalsettings.hpp>
+#include <pv/data/decode/annotation.hpp>
+#include <pv/data/decode/decoder.hpp>
+#include <pv/data/decoderstack.hpp>
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/session.hpp>
+#include <pv/strnatcmp.hpp>
+#include <pv/widgets/decodergroupbox.hpp>
+#include <pv/widgets/decodermenu.hpp>
+
+using std::all_of;
+using std::list;
+using std::make_pair;
+using std::max;
+using std::make_pair;
+using std::map;
+using std::min;
+using std::out_of_range;
+using std::pair;
+using std::shared_ptr;
+using std::make_shared;
+using std::tie;
+using std::unordered_set;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor DecodeTrace::DecodeColours[4] = {
+ QColor(0xEF, 0x29, 0x29), // Red
+ QColor(0xFC, 0xE9, 0x4F), // Yellow
+ QColor(0x8A, 0xE2, 0x34), // Green
+ QColor(0x72, 0x9F, 0xCF) // Blue
+};
+
+const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
+const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
+
+const int DecodeTrace::ArrowSize = 4;
+const double DecodeTrace::EndCapWidth = 5;
+const int DecodeTrace::RowTitleMargin = 10;
+const int DecodeTrace::DrawPadding = 100;
+
+const QColor DecodeTrace::Colours[16] = {
+ QColor(0xEF, 0x29, 0x29),
+ QColor(0xF6, 0x6A, 0x32),
+ QColor(0xFC, 0xAE, 0x3E),
+ QColor(0xFB, 0xCA, 0x47),
+ QColor(0xFC, 0xE9, 0x4F),
+ QColor(0xCD, 0xF0, 0x40),
+ QColor(0x8A, 0xE2, 0x34),
+ QColor(0x4E, 0xDC, 0x44),
+ QColor(0x55, 0xD7, 0x95),
+ QColor(0x64, 0xD1, 0xD2),
+ QColor(0x72, 0x9F, 0xCF),
+ QColor(0xD4, 0x76, 0xC4),
+ QColor(0x9D, 0x79, 0xB9),
+ QColor(0xAD, 0x7F, 0xA8),
+ QColor(0xC2, 0x62, 0x9B),
+ QColor(0xD7, 0x47, 0x6F)
+};
+
+const QColor DecodeTrace::OutlineColours[16] = {
+ QColor(0x77, 0x14, 0x14),
+ QColor(0x7B, 0x35, 0x19),
+ QColor(0x7E, 0x57, 0x1F),
+ QColor(0x7D, 0x65, 0x23),
+ QColor(0x7E, 0x74, 0x27),
+ QColor(0x66, 0x78, 0x20),
+ QColor(0x45, 0x71, 0x1A),
+ QColor(0x27, 0x6E, 0x22),
+ QColor(0x2A, 0x6B, 0x4A),
+ QColor(0x32, 0x68, 0x69),
+ QColor(0x39, 0x4F, 0x67),
+ QColor(0x6A, 0x3B, 0x62),
+ QColor(0x4E, 0x3C, 0x5C),
+ QColor(0x56, 0x3F, 0x54),
+ QColor(0x61, 0x31, 0x4D),
+ QColor(0x6B, 0x23, 0x37)
+};
+
+DecodeTrace::DecodeTrace(pv::Session &session,
+ shared_ptr<data::SignalBase> signalbase, int index) :
+ Trace(signalbase),
+ session_(session),
+ row_height_(0),
+ max_visible_rows_(0),
+ delete_mapper_(this),
+ show_hide_mapper_(this)
+{
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ // Determine shortest string we want to see displayed in full
+ QFontMetrics m(QApplication::font());
+ min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
+
+ base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
+ base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
+
+ connect(decoder_stack.get(), SIGNAL(new_decode_data()),
+ this, SLOT(on_new_decode_data()));
+ connect(&delete_mapper_, SIGNAL(mapped(int)),
+ this, SLOT(on_delete_decoder(int)));
+ connect(&show_hide_mapper_, SIGNAL(mapped(int)),
+ this, SLOT(on_show_hide_decoder(int)));
+}
+
+bool DecodeTrace::enabled() const
+{
+ return true;
+}
+
+shared_ptr<data::SignalBase> DecodeTrace::base() const
+{
+ return base_;
+}
+
+pair<int, int> DecodeTrace::v_extents() const
+{
+ const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
+
+ // Make an empty decode trace appear symmetrical
+ const int row_count = max(1, max_visible_rows_);
+
+ return make_pair(-row_height, row_height * row_count);
+}
+
+void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ Trace::paint_back(p, pp);
+ paint_axis(p, pp, get_visual_y());
+}
+
+void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
+{
+ using namespace pv::data::decode;
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ const int text_height = ViewItemPaintParams::text_height();
+ row_height_ = (text_height * 6) / 4;
+ const int annotation_height = (text_height * 5) / 4;
+
+ assert(decoder_stack);
+ const QString err = decoder_stack->error_message();
+ if (!err.isEmpty()) {
+ draw_unresolved_period(
+ p, annotation_height, pp.left(), pp.right());
+ draw_error(p, err, pp);
+ return;
+ }
+
+ // Set default pen to allow for text width calculation
+ p.setPen(Qt::black);
+
+ // Iterate through the rows
+ int y = get_visual_y();
+ pair<uint64_t, uint64_t> sample_range = get_sample_range(
+ pp.left(), pp.right());
+
+ const vector<Row> rows(decoder_stack->get_visible_rows());
+
+ visible_rows_.clear();
+ for (const Row& row : rows) {
+ // Cache the row title widths
+ int row_title_width;
+ try {
+ row_title_width = row_title_widths_.at(row);
+ } catch (out_of_range) {
+ const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
+ RowTitleMargin;
+ row_title_widths_[row] = w;
+ row_title_width = w;
+ }
+
+ // Determine the row's color
+ size_t base_colour = 0x13579BDF;
+ boost::hash_combine(base_colour, this);
+ boost::hash_combine(base_colour, row.decoder());
+ boost::hash_combine(base_colour, row.row());
+ base_colour >>= 16;
+
+ vector<Annotation> annotations;
+ decoder_stack->get_annotation_subset(annotations, row,
+ sample_range.first, sample_range.second);
+ if (!annotations.empty()) {
+ draw_annotations(annotations, p, annotation_height, pp, y,
+ base_colour, row_title_width);
+
+ y += row_height_;
+
+ visible_rows_.push_back(row);
+ }
+ }
+
+ // Draw the hatching
+ draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
+
+ if ((int)visible_rows_.size() > max_visible_rows_)
+ owner_->extents_changed(false, true);
+
+ // Update the maximum row count if needed
+ max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
+}
+
+void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ using namespace pv::data::decode;
+
+ assert(row_height_);
+
+ for (size_t i = 0; i < visible_rows_.size(); i++) {
+ const int y = i * row_height_ + get_visual_y();
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(QApplication::palette().brush(QPalette::WindowText));
+
+ if (i != 0) {
+ const QPointF points[] = {
+ QPointF(pp.left(), y - ArrowSize),
+ QPointF(pp.left() + ArrowSize, y),
+ QPointF(pp.left(), y + ArrowSize)
+ };
+ p.drawPolygon(points, countof(points));
+ }
+
+ const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
+ pp.right() - pp.left(), row_height_);
+ const QString h(visible_rows_[i].title());
+ const int f = Qt::AlignLeft | Qt::AlignVCenter |
+ Qt::TextDontClip;
+
+ // Draw the outline
+ p.setPen(QApplication::palette().color(QPalette::Base));
+ for (int dx = -1; dx <= 1; dx++)
+ for (int dy = -1; dy <= 1; dy++)
+ if (dx != 0 && dy != 0)
+ p.drawText(r.translated(dx, dy), f, h);
+
+ // Draw the text
+ p.setPen(QApplication::palette().color(QPalette::WindowText));
+ p.drawText(r, f, h);
+ }
+}
+
+void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+ using pv::data::decode::Decoder;
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(form);
+ assert(parent);
+ assert(decoder_stack);
+
+ // Add the standard options
+ Trace::populate_popup_form(parent, form);
+
+ // Add the decoder options
+ bindings_.clear();
+ channel_selectors_.clear();
+ decoder_forms_.clear();
+
+ const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
+
+ if (stack.empty()) {
+ QLabel *const l = new QLabel(
+ tr("<p><i>No decoders in the stack</i></p>"));
+ l->setAlignment(Qt::AlignCenter);
+ form->addRow(l);
+ } else {
+ auto iter = stack.cbegin();
+ for (int i = 0; i < (int)stack.size(); i++, iter++) {
+ shared_ptr<Decoder> dec(*iter);
+ create_decoder_form(i, dec, parent, form);
+ }
+
+ form->addRow(new QLabel(
+ tr("<i>* Required channels</i>"), parent));
+ }
+
+ // Add stacking button
+ pv::widgets::DecoderMenu *const decoder_menu =
+ new pv::widgets::DecoderMenu(parent);
+ connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
+ this, SLOT(on_stack_decoder(srd_decoder*)));
+
+ QPushButton *const stack_button =
+ new QPushButton(tr("Stack Decoder"), parent);
+ stack_button->setMenu(decoder_menu);
+ stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
+
+ QHBoxLayout *stack_button_box = new QHBoxLayout;
+ stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
+ form->addRow(stack_button_box);
+}
+
+QMenu* DecodeTrace::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = Trace::create_context_menu(parent);
+
+ menu->addSeparator();
+
+ QAction *const del = new QAction(tr("Delete"), this);
+ del->setShortcuts(QKeySequence::Delete);
+ connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
+ menu->addAction(del);
+
+ return menu;
+}
+
+void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour, int row_title_width)
+{
+ using namespace pv::data::decode;
+
+ vector<Annotation> a_block;
+ int p_end = INT_MIN;
+
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ // Sort the annotations by start sample so that decoders
+ // can't confuse us by creating annotations out of order
+ stable_sort(annotations.begin(), annotations.end(),
+ [](const Annotation &a, const Annotation &b) {
+ return a.start_sample() < b.start_sample(); });
+
+ // Gather all annotations that form a visual "block" and draw them as such
+ for (const Annotation &a : annotations) {
+
+ const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
+ const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
+ const int a_width = a_end - a_start;
+
+ const int delta = a_end - p_end;
+
+ bool a_is_separate = false;
+
+ // Annotation wider than the threshold for a useful label width?
+ if (a_width >= min_useful_label_width_) {
+ for (const QString &ann_text : a.annotations()) {
+ const int w = p.boundingRect(QRectF(), 0, ann_text).width();
+ // Annotation wide enough to fit a label? Don't put it in a block then
+ if (w <= a_width) {
+ a_is_separate = true;
+ break;
+ }
+ }
+ }
+
+ // Were the previous and this annotation more than a pixel apart?
+ if ((abs(delta) > 1) || a_is_separate) {
+ // Block was broken, draw annotations that form the current block
+ if (a_block.size() == 1) {
+ draw_annotation(a_block.front(), p, h, pp, y, base_colour,
+ row_title_width);
+ }
+ else
+ draw_annotation_block(a_block, p, h, y, base_colour);
+
+ a_block.clear();
+ }
+
+ if (a_is_separate) {
+ draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
+ // Next annotation must start a new block. delta will be > 1
+ // because we set p_end to INT_MIN but that's okay since
+ // a_block will be empty, so nothing will be drawn
+ p_end = INT_MIN;
+ } else {
+ a_block.push_back(a);
+ p_end = a_end;
+ }
+ }
+
+ if (a_block.size() == 1)
+ draw_annotation(a_block.front(), p, h, pp, y, base_colour,
+ row_title_width);
+ else
+ draw_annotation_block(a_block, p, h, y, base_colour);
+}
+
+void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour, int row_title_width) const
+{
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const double start = a.start_sample() / samples_per_pixel -
+ pixels_offset;
+ const double end = a.end_sample() / samples_per_pixel - pixels_offset;
+
+ const size_t colour = (base_colour + a.format()) % countof(Colours);
+ p.setPen(OutlineColours[colour]);
+ p.setBrush(Colours[colour]);
+
+ if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
+ return;
+
+ if (a.start_sample() == a.end_sample())
+ draw_instant(a, p, h, start, y);
+ else
+ draw_range(a, p, h, start, end, y, pp, row_title_width);
+}
+
+void DecodeTrace::draw_annotation_block(
+ vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
+ int y, size_t base_colour) const
+{
+ using namespace pv::data::decode;
+
+ if (annotations.empty())
+ return;
+
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const double start = annotations.front().start_sample() /
+ samples_per_pixel - pixels_offset;
+ const double end = annotations.back().end_sample() /
+ samples_per_pixel - pixels_offset;
+
+ const double top = y + .5 - h / 2;
+ const double bottom = y + .5 + h / 2;
+
+ const size_t colour = (base_colour + annotations.front().format()) %
+ countof(Colours);
+
+ // Check if all annotations are of the same type (i.e. we can use one color)
+ // or if we should use a neutral color (i.e. gray)
+ const int format = annotations.front().format();
+ const bool single_format = all_of(
+ annotations.begin(), annotations.end(),
+ [&](const Annotation &a) { return a.format() == format; });
+
+ const QRectF rect(start, top, end - start, bottom - top);
+ const int r = h / 4;
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(Qt::white);
+ p.drawRoundedRect(rect, r, r);
+
+ p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
+ p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
+ Qt::Dense4Pattern));
+ p.drawRoundedRect(rect, r, r);
+}
+
+void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
+ int h, double x, int y) const
+{
+ const QString text = a.annotations().empty() ?
+ QString() : a.annotations().back();
+ const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
+ 0.0) + h;
+ const QRectF rect(x - w / 2, y - h / 2, w, h);
+
+ p.drawRoundedRect(rect, h / 2, h / 2);
+
+ p.setPen(Qt::black);
+ p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
+}
+
+void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
+ int h, double start, double end, int y, const ViewItemPaintParams &pp,
+ int row_title_width) const
+{
+ const double top = y + .5 - h / 2;
+ const double bottom = y + .5 + h / 2;
+ const vector<QString> annotations = a.annotations();
+
+ // If the two ends are within 1 pixel, draw a vertical line
+ if (start + 1.0 > end) {
+ p.drawLine(QPointF(start, top), QPointF(start, bottom));
+ return;
+ }
+
+ const double cap_width = min((end - start) / 4, EndCapWidth);
+
+ QPointF pts[] = {
+ QPointF(start, y + .5f),
+ QPointF(start + cap_width, top),
+ QPointF(end - cap_width, top),
+ QPointF(end, y + .5f),
+ QPointF(end - cap_width, bottom),
+ QPointF(start + cap_width, bottom)
+ };
+
+ p.drawConvexPolygon(pts, countof(pts));
+
+ if (annotations.empty())
+ return;
+
+ const int ann_start = start + cap_width;
+ const int ann_end = end - cap_width;
+
+ const int real_start = max(ann_start, pp.left() + row_title_width);
+ const int real_end = min(ann_end, pp.right());
+ const int real_width = real_end - real_start;
+
+ QRectF rect(real_start, y - h / 2, real_width, h);
+ if (rect.width() <= 4)
+ return;
+
+ p.setPen(Qt::black);
+
+ // Try to find an annotation that will fit
+ QString best_annotation;
+ int best_width = 0;
+
+ for (const QString &a : annotations) {
+ const int w = p.boundingRect(QRectF(), 0, a).width();
+ if (w <= rect.width() && w > best_width)
+ best_annotation = a, best_width = w;
+ }
+
+ if (best_annotation.isEmpty())
+ best_annotation = annotations.back();
+
+ // If not ellide the last in the list
+ p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
+ best_annotation, Qt::ElideRight, rect.width()));
+}
+
+void DecodeTrace::draw_error(QPainter &p, const QString &message,
+ const ViewItemPaintParams &pp)
+{
+ const int y = get_visual_y();
+
+ p.setPen(ErrorBgColour.darker());
+ p.setBrush(ErrorBgColour);
+
+ const QRectF bounding_rect =
+ QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
+ const QRectF text_rect = p.boundingRect(bounding_rect,
+ Qt::AlignCenter, message);
+ const float r = text_rect.height() / 4;
+
+ p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
+ Qt::AbsoluteSize);
+
+ p.setPen(Qt::black);
+ p.drawText(text_rect, message);
+}
+
+void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
+ int right) const
+{
+ using namespace pv::data;
+ using pv::data::decode::Decoder;
+
+ double samples_per_pixel, pixels_offset;
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(decoder_stack);
+
+ shared_ptr<Logic> data;
+ shared_ptr<data::SignalBase> signalbase;
+
+ const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
+
+ // We get the logic data of the first channel in the list.
+ // This works because we are currently assuming all
+ // LogicSignals have the same data/segment
+ for (const shared_ptr<Decoder> &dec : stack)
+ if (dec && !dec->channels().empty() &&
+ ((signalbase = (*dec->channels().begin()).second)) &&
+ ((data = signalbase->logic_data())))
+ break;
+
+ if (!data || data->logic_segments().empty())
+ return;
+
+ const shared_ptr<LogicSegment> segment = data->logic_segments().front();
+ assert(segment);
+ const int64_t sample_count = (int64_t)segment->get_sample_count();
+ if (sample_count == 0)
+ return;
+
+ const int64_t samples_decoded = decoder_stack->samples_decoded();
+ if (sample_count == samples_decoded)
+ return;
+
+ const int y = get_visual_y();
+
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const double start = max(samples_decoded /
+ samples_per_pixel - pixels_offset, left - 1.0);
+ const double end = min(sample_count / samples_per_pixel -
+ pixels_offset, right + 1.0);
+ const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(Qt::white);
+ p.drawRect(no_decode_rect);
+
+ p.setPen(NoDecodeColour);
+ p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
+ p.drawRect(no_decode_rect);
+}
+
+pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
+{
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(owner_);
+ assert(decoder_stack);
+
+ const View *view = owner_->view();
+ assert(view);
+
+ const double scale = view->scale();
+ assert(scale > 0);
+
+ const double pixels_offset =
+ ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
+
+ double samplerate = decoder_stack->samplerate();
+
+ // Show sample rate as 1Hz when it is unknown
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ return make_pair(pixels_offset, samplerate * scale);
+}
+
+pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
+ int x_start, int x_end) const
+{
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const uint64_t start = (uint64_t)max(
+ (x_start + pixels_offset) * samples_per_pixel, 0.0);
+ const uint64_t end = (uint64_t)max(
+ (x_end + pixels_offset) * samples_per_pixel, 0.0);
+
+ return make_pair(start, end);
+}
+
+int DecodeTrace::get_row_at_point(const QPoint &point)
+{
+ if (!row_height_)
+ return -1;
+
+ const int y = (point.y() - get_visual_y() + row_height_ / 2);
+
+ /* Integer divison of (x-1)/x would yield 0, so we check for this. */
+ if (y < 0)
+ return -1;
+
+ const int row = y / row_height_;
+
+ if (row >= (int)visible_rows_.size())
+ return -1;
+
+ return row;
+}
+
+const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
+{
+ using namespace pv::data::decode;
+
+ if (!enabled())
+ return QString();
+
+ const pair<uint64_t, uint64_t> sample_range =
+ get_sample_range(point.x(), point.x() + 1);
+ const int row = get_row_at_point(point);
+ if (row < 0)
+ return QString();
+
+ vector<pv::data::decode::Annotation> annotations;
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(decoder_stack);
+ decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
+ sample_range.first, sample_range.second);
+
+ return (annotations.empty()) ?
+ QString() : annotations[0].annotations().front();
+}
+
+void DecodeTrace::hover_point_changed()
+{
+ assert(owner_);
+
+ const View *const view = owner_->view();
+ assert(view);
+
+ QPoint hp = view->hover_point();
+ QString ann = get_annotation_at_point(hp);
+
+ assert(view);
+
+ if (!row_height_ || ann.isEmpty()) {
+ QToolTip::hideText();
+ return;
+ }
+
+ const int hover_row = get_row_at_point(hp);
+
+ QFontMetrics m(QToolTip::font());
+ const QRect text_size = m.boundingRect(QRect(), 0, ann);
+
+ // This is OS-specific and unfortunately we can't query it, so
+ // use an approximation to at least try to minimize the error.
+ const int padding = 8;
+
+ // Make sure the tool tip doesn't overlap with the mouse cursor.
+ // If it did, the tool tip would constantly hide and re-appear.
+ // We also push it up by one row so that it appears above the
+ // decode trace, not below.
+ hp.setX(hp.x() - (text_size.width() / 2) - padding);
+
+ hp.setY(get_visual_y() - (row_height_ / 2) +
+ (hover_row * row_height_) -
+ row_height_ - text_size.height() - padding);
+
+ QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
+}
+
+void DecodeTrace::create_decoder_form(int index,
+ shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
+ QFormLayout *form)
+{
+ const GSList *l;
+ GlobalSettings settings;
+
+ assert(dec);
+ const srd_decoder *const decoder = dec->decoder();
+ assert(decoder);
+
+ const bool decoder_deletable = index > 0;
+
+ pv::widgets::DecoderGroupBox *const group =
+ new pv::widgets::DecoderGroupBox(
+ QString::fromUtf8(decoder->name),
+ tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
+ QString::fromUtf8(decoder->desc)),
+ nullptr, decoder_deletable);
+ group->set_decoder_visible(dec->shown());
+
+ if (decoder_deletable) {
+ delete_mapper_.setMapping(group, index);
+ connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
+ }
+
+ show_hide_mapper_.setMapping(group, index);
+ connect(group, SIGNAL(show_hide_decoder()),
+ &show_hide_mapper_, SLOT(map()));
+
+ QFormLayout *const decoder_form = new QFormLayout;
+ group->add_layout(decoder_form);
+
+ // Add the mandatory channels
+ for (l = decoder->channels; l; l = l->next) {
+ const struct srd_channel *const pdch =
+ (struct srd_channel *)l->data;
+
+ QComboBox *const combo = create_channel_selector(parent, dec, pdch);
+ QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
+
+ connect(combo, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_channel_selected(int)));
+ connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_initial_pin_selected(int)));
+
+ QHBoxLayout *const hlayout = new QHBoxLayout;
+ hlayout->addWidget(combo);
+ hlayout->addWidget(combo_initial_pin);
+
+ if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
+ combo_initial_pin->hide();
+
+ decoder_form->addRow(tr("<b>%1</b> (%2) *")
+ .arg(QString::fromUtf8(pdch->name),
+ QString::fromUtf8(pdch->desc)), hlayout);
+
+ const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
+ channel_selectors_.push_back(s);
+ }
+
+ // Add the optional channels
+ for (l = decoder->opt_channels; l; l = l->next) {
+ const struct srd_channel *const pdch =
+ (struct srd_channel *)l->data;
+
+ QComboBox *const combo = create_channel_selector(parent, dec, pdch);
+ QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
+
+ connect(combo, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_channel_selected(int)));
+ connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(on_initial_pin_selected(int)));
+
+ QHBoxLayout *const hlayout = new QHBoxLayout;
+ hlayout->addWidget(combo);
+ hlayout->addWidget(combo_initial_pin);
+
+ if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
+ combo_initial_pin->hide();
+
+ decoder_form->addRow(tr("<b>%1</b> (%2)")
+ .arg(QString::fromUtf8(pdch->name),
+ QString::fromUtf8(pdch->desc)), hlayout);
+
+ const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
+ channel_selectors_.push_back(s);
+ }
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ // Add the options
+ shared_ptr<binding::Decoder> binding(
+ new binding::Decoder(decoder_stack, dec));
+ binding->add_properties_to_form(decoder_form, true);
+
+ bindings_.push_back(binding);
+
+ form->addRow(group);
+ decoder_forms_.push_back(group);
+}
+
+QComboBox* DecodeTrace::create_channel_selector(
+ QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
+ const srd_channel *const pdch)
+{
+ assert(dec);
+
+ const auto sigs(session_.signalbases());
+
+ vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
+ sort(sig_list.begin(), sig_list.end(),
+ [](const shared_ptr<data::SignalBase> &a,
+ const shared_ptr<data::SignalBase> &b) {
+ return strnatcasecmp(a->name().toStdString(),
+ b->name().toStdString()) < 0; });
+
+ const auto channel_iter = dec->channels().find(pdch);
+
+ QComboBox *selector = new QComboBox(parent);
+
+ selector->addItem("-", qVariantFromValue((void*)nullptr));
+
+ if (channel_iter == dec->channels().end())
+ selector->setCurrentIndex(0);
+
+ for (const shared_ptr<data::SignalBase> &b : sig_list) {
+ assert(b);
+ if (b->logic_data() && b->enabled()) {
+ selector->addItem(b->name(),
+ qVariantFromValue((void*)b.get()));
+
+ if (channel_iter != dec->channels().end() &&
+ (*channel_iter).second == b)
+ selector->setCurrentIndex(
+ selector->count() - 1);
+ }
+ }
+
+ return selector;
+}
+
+QComboBox* DecodeTrace::create_channel_selector_initial_pin(QWidget *parent,
+ const shared_ptr<data::decode::Decoder> &dec, const srd_channel *const pdch)
+{
+ QComboBox *selector = new QComboBox(parent);
+
+ selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
+ selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
+ selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
+
+ // Default to index 2 (SRD_INITIAL_PIN_SAME_AS_SAMPLE0).
+ const int idx = (!dec->initial_pins()) ? 2 : dec->initial_pins()->data[pdch->order];
+ selector->setCurrentIndex(idx);
+
+ selector->setToolTip("Initial (assumed) pin value before the first sample");
+
+ return selector;
+}
+
+void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
+{
+ assert(dec);
+
+ map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
+
+ const unordered_set< shared_ptr<data::SignalBase> >
+ sigs(session_.signalbases());
+
+ GArray *const initial_pins = g_array_sized_new(FALSE, TRUE,
+ sizeof(uint8_t), channel_selectors_.size());
+ g_array_set_size(initial_pins, channel_selectors_.size());
+
+ for (const ChannelSelector &s : channel_selectors_) {
+ if (s.decoder_ != dec)
+ break;
+
+ const data::SignalBase *const selection =
+ (data::SignalBase*)s.combo_->itemData(
+ s.combo_->currentIndex()).value<void*>();
+
+ for (shared_ptr<data::SignalBase> sig : sigs)
+ if (sig.get() == selection) {
+ channel_map[s.pdch_] = sig;
+ break;
+ }
+
+ int selection_initial_pin = s.combo_initial_pin_->itemData(
+ s.combo_initial_pin_->currentIndex()).value<int>();
+
+ initial_pins->data[s.pdch_->order] = selection_initial_pin;
+ }
+
+ dec->set_channels(channel_map);
+ dec->set_initial_pins(initial_pins);
+}
+
+void DecodeTrace::commit_channels()
+{
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(decoder_stack);
+ for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
+ commit_decoder_channels(dec);
+
+ decoder_stack->begin_decode();
+}
+
+void DecodeTrace::on_new_decode_data()
+{
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+void DecodeTrace::delete_pressed()
+{
+ on_delete();
+}
+
+void DecodeTrace::on_delete()
+{
+ session_.remove_decode_signal(base_);
+}
+
+void DecodeTrace::on_channel_selected(int)
+{
+ commit_channels();
+}
+
+void DecodeTrace::on_initial_pin_selected(int)
+{
+ commit_channels();
+}
+
+void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
+{
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ assert(decoder);
+ assert(decoder_stack);
+ decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
+ decoder_stack->begin_decode();
+
+ create_popup_form();
+}
+
+void DecodeTrace::on_delete_decoder(int index)
+{
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ decoder_stack->remove(index);
+
+ // Update the popup
+ create_popup_form();
+
+ decoder_stack->begin_decode();
+}
+
+void DecodeTrace::on_show_hide_decoder(int index)
+{
+ using pv::data::decode::Decoder;
+
+ shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
+
+ const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
+
+ // Find the decoder in the stack
+ auto iter = stack.cbegin();
+ for (int i = 0; i < index; i++, iter++)
+ assert(iter != stack.end());
+
+ shared_ptr<Decoder> dec = *iter;
+ assert(dec);
+
+ const bool show = !dec->shown();
+ dec->show(show);
+
+ assert(index < (int)decoder_forms_.size());
+ decoder_forms_[index]->set_decoder_visible(show);
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
+
+#include "trace.hpp"
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <QSignalMapper>
+
+#include <pv/binding/decoder.hpp>
+#include <pv/data/decode/row.hpp>
+#include <pv/data/signalbase.hpp>
+
+using std::list;
+using std::map;
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+struct srd_channel;
+struct srd_decoder;
+
+class QComboBox;
+
+namespace pv {
+
+class Session;
+
+namespace data {
+class DecoderStack;
+class SignalBase;
+
+namespace decode {
+class Annotation;
+class Decoder;
+class Row;
+}
+}
+
+namespace widgets {
+class DecoderGroupBox;
+}
+
+namespace views {
+namespace trace {
+
+class DecodeTrace : public Trace
+{
+ Q_OBJECT
+
+private:
+ struct ChannelSelector
+ {
+ const QComboBox *combo_;
+ const QComboBox *combo_initial_pin_;
+ const shared_ptr<pv::data::decode::Decoder> decoder_;
+ const srd_channel *pdch_;
+ };
+
+private:
+ static const QColor DecodeColours[4];
+ static const QColor ErrorBgColour;
+ static const QColor NoDecodeColour;
+
+ static const int ArrowSize;
+ static const double EndCapWidth;
+ static const int RowTitleMargin;
+ static const int DrawPadding;
+
+ static const QColor Colours[16];
+ static const QColor OutlineColours[16];
+
+public:
+ DecodeTrace(pv::Session &session, shared_ptr<data::SignalBase> signalbase,
+ int index);
+
+ bool enabled() const;
+
+ const shared_ptr<pv::data::DecoderStack>& decoder() const;
+
+ shared_ptr<data::SignalBase> base() const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ pair<int, int> v_extents() const;
+
+ /**
+ * Paints the background layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_back(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the mid-layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+
+ void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ void delete_pressed();
+
+private:
+ void draw_annotations(vector<pv::data::decode::Annotation> annotations,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour, int row_title_width);
+
+ void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p,
+ int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour, int row_title_width) const;
+
+ void draw_annotation_block(vector<pv::data::decode::Annotation> annotations,
+ QPainter &p, int h, int y, size_t base_colour) const;
+
+ void draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
+ int h, double x, int y) const;
+
+ void draw_range(const pv::data::decode::Annotation &a, QPainter &p,
+ int h, double start, double end, int y, const ViewItemPaintParams &pp,
+ int row_title_width) const;
+
+ void draw_error(QPainter &p, const QString &message,
+ const ViewItemPaintParams &pp);
+
+ void draw_unresolved_period(QPainter &p, int h, int left,
+ int right) const;
+
+ pair<double, double> get_pixels_offset_samples_per_pixel() const;
+
+ /**
+ * Determines the start and end sample for a given pixel range.
+ * @param x_start the X coordinate of the start sample in the view
+ * @param x_end the X coordinate of the end sample in the view
+ * @return Returns a pair containing the start sample and the end
+ * sample that correspond to the start and end coordinates.
+ */
+ pair<uint64_t, uint64_t> get_sample_range(int x_start, int x_end) const;
+
+ int get_row_at_point(const QPoint &point);
+
+ const QString get_annotation_at_point(const QPoint &point);
+
+ void create_decoder_form(int index,
+ shared_ptr<pv::data::decode::Decoder> &dec,
+ QWidget *parent, QFormLayout *form);
+
+ QComboBox* create_channel_selector(QWidget *parent,
+ const shared_ptr<pv::data::decode::Decoder> &dec,
+ const srd_channel *const pdch);
+
+ QComboBox* create_channel_selector_initial_pin(QWidget *parent,
+ const shared_ptr<pv::data::decode::Decoder> &dec,
+ const srd_channel *const pdch);
+
+ void commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec);
+
+ void commit_channels();
+
+public:
+ void hover_point_changed();
+
+private Q_SLOTS:
+ void on_new_decode_data();
+
+ void on_delete();
+
+ void on_channel_selected(int);
+
+ void on_initial_pin_selected(int);
+
+ void on_stack_decoder(srd_decoder *decoder);
+
+ void on_delete_decoder(int index);
+
+ void on_show_hide_decoder(int index);
+
+private:
+ pv::Session &session_;
+
+ vector<data::decode::Row> visible_rows_;
+ uint64_t decode_start_, decode_end_;
+
+ list< shared_ptr<pv::binding::Decoder> > bindings_;
+
+ list<ChannelSelector> channel_selectors_;
+ vector<pv::widgets::DecoderGroupBox*> decoder_forms_;
+
+ map<data::decode::Row, int> row_title_widths_;
+ int row_height_, max_visible_rows_;
+
+ int min_useful_label_width_;
+
+ QSignalMapper delete_mapper_, show_hide_mapper_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "timemarker.hpp"
+#include "view.hpp"
+
+#include <QColor>
+#include <QFormLayout>
+#include <QLineEdit>
+#include <QMenu>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <pv/widgets/popup.hpp>
+
+using std::enable_shared_from_this;
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor Flag::FillColour(0x73, 0xD2, 0x16);
+
+Flag::Flag(View &view, const pv::util::Timestamp& time, const QString &text) :
+ TimeMarker(view, FillColour, time),
+ text_(text)
+{
+}
+
+Flag::Flag(const Flag &flag) :
+ TimeMarker(flag.view_, FillColour, flag.time_),
+ enable_shared_from_this<Flag>(flag)
+{
+}
+
+bool Flag::enabled() const
+{
+ return true;
+}
+
+QString Flag::get_text() const
+{
+ return text_;
+}
+
+pv::widgets::Popup* Flag::create_popup(QWidget *parent)
+{
+ using pv::widgets::Popup;
+
+ Popup *const popup = TimeMarker::create_popup(parent);
+ popup->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Bottom);
+
+ QFormLayout *const form = (QFormLayout*)popup->layout();
+
+ QLineEdit *const text_edit = new QLineEdit(popup);
+ text_edit->setText(text_);
+
+ connect(text_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(on_text_changed(const QString&)));
+
+ form->insertRow(0, tr("Text"), text_edit);
+
+ return popup;
+}
+
+QMenu* Flag::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = new QMenu(parent);
+
+ QAction *const del = new QAction(tr("Delete"), this);
+ del->setShortcuts(QKeySequence::Delete);
+ connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
+ menu->addAction(del);
+
+ return menu;
+}
+
+void Flag::delete_pressed()
+{
+ on_delete();
+}
+
+void Flag::on_delete()
+{
+ view_.remove_flag(shared_ptr<Flag>(shared_from_this()));
+}
+
+void Flag::on_text_changed(const QString &text)
+{
+ text_ = text;
+ view_.time_item_appearance_changed(true, false);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
+
+#include <memory>
+
+#include "timemarker.hpp"
+
+using std::enable_shared_from_this;
+
+class QMenu;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class Flag : public TimeMarker, public enable_shared_from_this<Flag>
+{
+ Q_OBJECT
+
+public:
+ static const QColor FillColour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this cursor pair.
+ * @param time The time to set the flag to.
+ * @param text The text of the marker.
+ */
+ Flag(View &view, const pv::util::Timestamp& time, const QString &text);
+
+ /**
+ * Copy constructor.
+ */
+ Flag(const Flag &flag);
+
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Gets the text to show in the marker.
+ */
+ QString get_text() const;
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ void delete_pressed();
+
+private Q_SLOTS:
+ void on_delete();
+
+ void on_text_changed(const QString &text);
+
+private:
+ QString text_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "header.hpp"
+#include "view.hpp"
+
+#include "signal.hpp"
+#include "tracegroup.hpp"
+
+#include <algorithm>
+#include <cassert>
+
+#include <boost/iterator/filter_iterator.hpp>
+
+#include <QApplication>
+#include <QMenu>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QRect>
+
+#include <pv/session.hpp>
+#include <pv/widgets/popup.hpp>
+
+using boost::make_filter_iterator;
+
+using std::count_if;
+using std::dynamic_pointer_cast;
+using std::shared_ptr;
+using std::stable_sort;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const int Header::Padding = 12;
+
+static bool item_selected(shared_ptr<TraceTreeItem> r)
+{
+ return r->selected();
+}
+
+Header::Header(View &parent) :
+ MarginWidget(parent)
+{
+}
+
+QSize Header::sizeHint() const
+{
+ QRectF max_rect(-Padding, 0, Padding, 0);
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ for (auto &i : items)
+ if (i->enabled())
+ max_rect = max_rect.united(i->label_rect(QRect()));
+ return QSize(max_rect.width() + Padding, 0);
+}
+
+QSize Header::extended_size_hint() const
+{
+ return sizeHint() + QSize(ViewItem::HighlightRadius, 0);
+}
+
+vector< shared_ptr<ViewItem> > Header::items()
+{
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ return vector< shared_ptr<ViewItem> >(items.begin(), items.end());
+}
+
+shared_ptr<ViewItem> Header::get_mouse_over_item(const QPoint &pt)
+{
+ const QRect r(0, 0, width(), height());
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() && (*i)->label_rect(r).contains(pt))
+ return *i;
+ return shared_ptr<TraceTreeItem>();
+}
+
+void Header::paintEvent(QPaintEvent*)
+{
+ const QRect rect(0, 0, width(), height());
+
+ vector< shared_ptr<RowItem> > items(view_.list_by_type<RowItem>());
+
+ stable_sort(items.begin(), items.end(),
+ [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
+ return a->point(QRect()).y() < b->point(QRect()).y(); });
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ for (const shared_ptr<RowItem> r : items) {
+ assert(r);
+
+ const bool highlight = !item_dragging_ &&
+ r->label_rect(rect).contains(mouse_point_);
+ r->paint_label(painter, rect, highlight);
+ }
+
+ painter.end();
+}
+
+void Header::contextMenuEvent(QContextMenuEvent *event)
+{
+ const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
+ if (!r)
+ return;
+
+ QMenu *menu = r->create_context_menu(this);
+ if (!menu)
+ menu = new QMenu(this);
+
+ const vector< shared_ptr<TraceTreeItem> > items(
+ view_.list_by_type<TraceTreeItem>());
+ if (count_if(items.begin(), items.end(), item_selected) > 1) {
+ menu->addSeparator();
+
+ QAction *const group = new QAction(tr("Group"), this);
+ QList<QKeySequence> shortcuts;
+ shortcuts.append(QKeySequence(Qt::ControlModifier | Qt::Key_G));
+ group->setShortcuts(shortcuts);
+ connect(group, SIGNAL(triggered()), this, SLOT(on_group()));
+ menu->addAction(group);
+ }
+
+ menu->exec(event->globalPos());
+}
+
+void Header::keyPressEvent(QKeyEvent *event)
+{
+ assert(event);
+
+ MarginWidget::keyPressEvent(event);
+
+ if (event->key() == Qt::Key_G && event->modifiers() == Qt::ControlModifier)
+ on_group();
+ else if (event->key() == Qt::Key_U && event->modifiers() == Qt::ControlModifier)
+ on_ungroup();
+}
+
+void Header::on_group()
+{
+ const vector< shared_ptr<TraceTreeItem> > items(
+ view_.list_by_type<TraceTreeItem>());
+ vector< shared_ptr<TraceTreeItem> > selected_items(
+ make_filter_iterator(item_selected, items.begin(), items.end()),
+ make_filter_iterator(item_selected, items.end(), items.end()));
+ stable_sort(selected_items.begin(), selected_items.end(),
+ [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
+ return a->visual_v_offset() < b->visual_v_offset(); });
+
+ shared_ptr<TraceGroup> group(new TraceGroup());
+ shared_ptr<TraceTreeItem> mouse_down_item(
+ dynamic_pointer_cast<TraceTreeItem>(mouse_down_item_));
+ shared_ptr<TraceTreeItem> focus_item(
+ mouse_down_item ? mouse_down_item : selected_items.front());
+
+ assert(focus_item);
+ assert(focus_item->owner());
+ focus_item->owner()->add_child_item(group);
+
+ // Set the group v_offset here before reparenting
+ group->force_to_v_offset(focus_item->layout_v_offset() +
+ focus_item->v_extents().first);
+
+ for (size_t i = 0; i < selected_items.size(); i++) {
+ const shared_ptr<TraceTreeItem> &r = selected_items[i];
+ assert(r->owner());
+ r->owner()->remove_child_item(r);
+ group->add_child_item(r);
+
+ // Put the items at 1-pixel offsets, so that restack will
+ // stack them in the right order
+ r->set_layout_v_offset(i);
+ }
+}
+
+void Header::on_ungroup()
+{
+ bool restart;
+ do {
+ restart = false;
+ const vector< shared_ptr<TraceGroup> > groups(
+ view_.list_by_type<TraceGroup>());
+ for (const shared_ptr<TraceGroup> tg : groups)
+ if (tg->selected()) {
+ tg->ungroup();
+ restart = true;
+ break;
+ }
+ } while (restart);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
+
+#include <list>
+#include <memory>
+#include <utility>
+
+#include "marginwidget.hpp"
+
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TraceTreeItem;
+class View;
+class ViewItem;
+
+class Header : public MarginWidget
+{
+ Q_OBJECT
+
+private:
+ static const int Padding;
+
+public:
+ Header(View &parent);
+
+ QSize sizeHint() const;
+
+ /**
+ * The extended area that the header widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
+ */
+ QSize extended_size_hint() const;
+
+private:
+ /**
+ * Gets the row items.
+ */
+ vector< shared_ptr<ViewItem> > items();
+
+ /**
+ * Gets the first view item which has a label that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt);
+
+private:
+ void paintEvent(QPaintEvent *event);
+
+private:
+ void contextMenuEvent(QContextMenuEvent *event);
+
+ void keyPressEvent(QKeyEvent *event);
+
+private Q_SLOTS:
+ void on_group();
+
+ void on_ungroup();
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <cassert>
+#include <cmath>
+
+#include <algorithm>
+
+#include <QApplication>
+#include <QFormLayout>
+#include <QToolBar>
+
+#include "logicsignal.hpp"
+#include "view.hpp"
+
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/data/signalbase.hpp>
+#include <pv/devicemanager.hpp>
+#include <pv/devices/device.hpp>
+#include <pv/globalsettings.hpp>
+#include <pv/session.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using std::deque;
+using std::max;
+using std::make_pair;
+using std::min;
+using std::none_of;
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+using sigrok::ConfigKey;
+using sigrok::Capability;
+using sigrok::Trigger;
+using sigrok::TriggerMatch;
+using sigrok::TriggerMatchType;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const float LogicSignal::Oversampling = 2.0f;
+
+const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80);
+const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00);
+const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00);
+const QColor LogicSignal::SamplingPointColour(0x77, 0x77, 0x77);
+
+const QColor LogicSignal::SignalColours[10] = {
+ QColor(0x16, 0x19, 0x1A), // Black
+ QColor(0x8F, 0x52, 0x02), // Brown
+ QColor(0xCC, 0x00, 0x00), // Red
+ QColor(0xF5, 0x79, 0x00), // Orange
+ QColor(0xED, 0xD4, 0x00), // Yellow
+ QColor(0x73, 0xD2, 0x16), // Green
+ QColor(0x34, 0x65, 0xA4), // Blue
+ QColor(0x75, 0x50, 0x7B), // Violet
+ QColor(0x88, 0x8A, 0x85), // Grey
+ QColor(0xEE, 0xEE, 0xEC), // White
+};
+
+QColor LogicSignal::TriggerMarkerBackgroundColour = QColor(0xED, 0xD4, 0x00);
+const int LogicSignal::TriggerMarkerPadding = 2;
+const char* LogicSignal::TriggerMarkerIcons[8] = {
+ nullptr,
+ ":/icons/trigger-marker-low.svg",
+ ":/icons/trigger-marker-high.svg",
+ ":/icons/trigger-marker-rising.svg",
+ ":/icons/trigger-marker-falling.svg",
+ ":/icons/trigger-marker-change.svg",
+ nullptr,
+ nullptr
+};
+
+QCache<QString, const QIcon> LogicSignal::icon_cache_;
+QCache<QString, const QPixmap> LogicSignal::pixmap_cache_;
+
+LogicSignal::LogicSignal(
+ pv::Session &session,
+ shared_ptr<devices::Device> device,
+ shared_ptr<data::SignalBase> base) :
+ Signal(session, base),
+ signal_height_(QFontMetrics(QApplication::font()).height() * 2),
+ device_(device),
+ trigger_none_(nullptr),
+ trigger_rising_(nullptr),
+ trigger_high_(nullptr),
+ trigger_falling_(nullptr),
+ trigger_low_(nullptr),
+ trigger_change_(nullptr)
+{
+ shared_ptr<Trigger> trigger;
+
+ base_->set_colour(SignalColours[base->index() % countof(SignalColours)]);
+
+ /* Populate this channel's trigger setting with whatever we
+ * find in the current session trigger, if anything. */
+ trigger_match_ = nullptr;
+ if ((trigger = session_.session()->trigger()))
+ for (auto stage : trigger->stages())
+ for (auto match : stage->matches())
+ if (match->channel() == base_->channel())
+ trigger_match_ = match->type();
+}
+
+shared_ptr<pv::data::SignalData> LogicSignal::data() const
+{
+ return base_->logic_data();
+}
+
+shared_ptr<pv::data::Logic> LogicSignal::logic_data() const
+{
+ return base_->logic_data();
+}
+
+pair<int, int> LogicSignal::v_extents() const
+{
+ const int signal_margin =
+ QFontMetrics(QApplication::font()).height() / 2;
+ return make_pair(-signal_height_ - signal_margin, signal_margin);
+}
+
+int LogicSignal::scale_handle_offset() const
+{
+ return -signal_height_;
+}
+
+void LogicSignal::scale_handle_dragged(int offset)
+{
+ const int font_height = QFontMetrics(QApplication::font()).height();
+ const int units = (-offset / font_height);
+ signal_height_ = ((units < 1) ? 1 : units) * font_height;
+}
+
+void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
+{
+ QLineF *line;
+
+ vector< pair<int64_t, bool> > edges;
+
+ assert(base_);
+ assert(owner_);
+
+ const int y = get_visual_y();
+
+ if (!base_->enabled())
+ return;
+
+ const float high_offset = y - signal_height_ + 0.5f;
+ const float low_offset = y + 0.5f;
+
+ const deque< shared_ptr<pv::data::LogicSegment> > &segments =
+ base_->logic_data()->logic_segments();
+ if (segments.empty())
+ return;
+
+ const shared_ptr<pv::data::LogicSegment> &segment = segments.front();
+
+ double samplerate = segment->samplerate();
+
+ // Show sample rate as 1Hz when it is unknown
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ const double pixels_offset = pp.pixels_offset();
+ const pv::util::Timestamp& start_time = segment->start_time();
+ const int64_t last_sample = segment->get_sample_count() - 1;
+ const double samples_per_pixel = samplerate * pp.scale();
+ const double pixels_per_sample = 1 / samples_per_pixel;
+ const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+ const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
+
+ const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+ const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+
+ segment->get_subsampled_edges(edges, start_sample, end_sample,
+ samples_per_pixel / Oversampling, base_->index());
+ assert(edges.size() >= 2);
+
+ // Check whether we need to paint the sampling points
+ GlobalSettings settings;
+ const bool show_sampling_points =
+ settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
+ (samples_per_pixel < 0.25);
+
+ vector<QRectF> sampling_points;
+ float sampling_point_x = 0.0f;
+ int64_t sampling_point_sample = start_sample;
+ const int w = 2;
+
+ if (show_sampling_points) {
+ sampling_points.reserve(end_sample - start_sample + 1);
+ sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left();
+ }
+
+ // Paint the edges
+ const unsigned int edge_count = edges.size() - 2;
+ QLineF *const edge_lines = new QLineF[edge_count];
+ line = edge_lines;
+
+ for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
+ const float x = ((*i).first / samples_per_pixel -
+ pixels_offset) + pp.left();
+ *line++ = QLineF(x, high_offset, x, low_offset);
+
+ if (show_sampling_points)
+ while (sampling_point_sample < (*i).first) {
+ const float y = (*i).second ? low_offset : high_offset;
+ sampling_points.emplace_back(
+ QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
+ sampling_point_sample++;
+ sampling_point_x += pixels_per_sample;
+ };
+ }
+
+ // Calculate the sample points from the last edge to the end of the trace
+ if (show_sampling_points)
+ while ((uint64_t)sampling_point_sample <= end_sample) {
+ // Signal changed after the last edge, so the level is inverted
+ const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
+ sampling_points.emplace_back(
+ QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
+ sampling_point_sample++;
+ sampling_point_x += pixels_per_sample;
+ };
+
+ p.setPen(EdgeColour);
+ p.drawLines(edge_lines, edge_count);
+ delete[] edge_lines;
+
+ // Paint the caps
+ const unsigned int max_cap_line_count = edges.size();
+ QLineF *const cap_lines = new QLineF[max_cap_line_count];
+
+ p.setPen(HighColour);
+ paint_caps(p, cap_lines, edges, true, samples_per_pixel,
+ pixels_offset, pp.left(), high_offset);
+ p.setPen(LowColour);
+ paint_caps(p, cap_lines, edges, false, samples_per_pixel,
+ pixels_offset, pp.left(), low_offset);
+
+ delete[] cap_lines;
+
+ // Paint the sampling points
+ if (show_sampling_points) {
+ p.setPen(SamplingPointColour);
+ p.drawRects(sampling_points.data(), sampling_points.size());
+ }
+}
+
+void LogicSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ // Draw the trigger marker
+ if (!trigger_match_ || !base_->enabled())
+ return;
+
+ const int y = get_visual_y();
+ const vector<int32_t> trig_types = get_trigger_types();
+ for (int32_t type_id : trig_types) {
+ const TriggerMatchType *const type =
+ TriggerMatchType::get(type_id);
+ if (trigger_match_ != type || type_id < 0 ||
+ (size_t)type_id >= countof(TriggerMarkerIcons) ||
+ !TriggerMarkerIcons[type_id])
+ continue;
+
+ const QPixmap *const pixmap = get_pixmap(
+ TriggerMarkerIcons[type_id]);
+ if (!pixmap)
+ continue;
+
+ const float pad = TriggerMarkerPadding - 0.5f;
+ const QSize size = pixmap->size();
+ const QPoint point(
+ pp.right() - size.width() - pad * 2,
+ y - (signal_height_ + size.height()) / 2);
+
+ p.setPen(QPen(TriggerMarkerBackgroundColour.darker()));
+ p.setBrush(TriggerMarkerBackgroundColour);
+ p.drawRoundedRect(QRectF(point, size).adjusted(
+ -pad, -pad, pad, pad), pad, pad);
+ p.drawPixmap(point, *pixmap);
+
+ break;
+ }
+}
+
+void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
+ vector< pair<int64_t, bool> > &edges, bool level,
+ double samples_per_pixel, double pixels_offset, float x_offset,
+ float y_offset)
+{
+ QLineF *line = lines;
+
+ for (auto i = edges.begin(); i != (edges.end() - 1); i++)
+ if ((*i).second == level) {
+ *line++ = QLineF(
+ ((*i).first / samples_per_pixel -
+ pixels_offset) + x_offset, y_offset,
+ ((*(i+1)).first / samples_per_pixel -
+ pixels_offset) + x_offset, y_offset);
+ }
+
+ p.drawLines(lines, line - lines);
+}
+
+void LogicSignal::init_trigger_actions(QWidget *parent)
+{
+ trigger_none_ = new QAction(*get_icon(":/icons/trigger-none.svg"),
+ tr("No trigger"), parent);
+ trigger_none_->setCheckable(true);
+ connect(trigger_none_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+
+ trigger_rising_ = new QAction(*get_icon(":/icons/trigger-rising.svg"),
+ tr("Trigger on rising edge"), parent);
+ trigger_rising_->setCheckable(true);
+ connect(trigger_rising_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+
+ trigger_high_ = new QAction(*get_icon(":/icons/trigger-high.svg"),
+ tr("Trigger on high level"), parent);
+ trigger_high_->setCheckable(true);
+ connect(trigger_high_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+
+ trigger_falling_ = new QAction(*get_icon(":/icons/trigger-falling.svg"),
+ tr("Trigger on falling edge"), parent);
+ trigger_falling_->setCheckable(true);
+ connect(trigger_falling_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+
+ trigger_low_ = new QAction(*get_icon(":/icons/trigger-low.svg"),
+ tr("Trigger on low level"), parent);
+ trigger_low_->setCheckable(true);
+ connect(trigger_low_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+
+ trigger_change_ = new QAction(*get_icon(":/icons/trigger-change.svg"),
+ tr("Trigger on rising or falling edge"), parent);
+ trigger_change_->setCheckable(true);
+ connect(trigger_change_, SIGNAL(triggered()), this, SLOT(on_trigger()));
+}
+
+const vector<int32_t> LogicSignal::get_trigger_types() const
+{
+ // We may not be associated with a device
+ if (!device_)
+ return vector<int32_t>();
+
+ const auto sr_dev = device_->device();
+ if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
+ const Glib::VariantContainerBase gvar =
+ sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
+
+ vector<int32_t> ttypes;
+
+ for (unsigned int i = 0; i < gvar.get_n_children(); i++) {
+ Glib::VariantBase tmp_vb;
+ gvar.get_child(tmp_vb, i);
+
+ Glib::Variant<int32_t> tmp_v =
+ Glib::VariantBase::cast_dynamic< Glib::Variant<int32_t> >(tmp_vb);
+
+ ttypes.push_back(tmp_v.get());
+ }
+
+ return ttypes;
+ } else {
+ return vector<int32_t>();
+ }
+}
+
+QAction* LogicSignal::action_from_trigger_type(const TriggerMatchType *type)
+{
+ QAction *action;
+
+ action = trigger_none_;
+ if (type) {
+ switch (type->id()) {
+ case SR_TRIGGER_ZERO:
+ action = trigger_low_;
+ break;
+ case SR_TRIGGER_ONE:
+ action = trigger_high_;
+ break;
+ case SR_TRIGGER_RISING:
+ action = trigger_rising_;
+ break;
+ case SR_TRIGGER_FALLING:
+ action = trigger_falling_;
+ break;
+ case SR_TRIGGER_EDGE:
+ action = trigger_change_;
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ return action;
+}
+
+const TriggerMatchType *LogicSignal::trigger_type_from_action(QAction *action)
+{
+ if (action == trigger_low_)
+ return TriggerMatchType::ZERO;
+ else if (action == trigger_high_)
+ return TriggerMatchType::ONE;
+ else if (action == trigger_rising_)
+ return TriggerMatchType::RISING;
+ else if (action == trigger_falling_)
+ return TriggerMatchType::FALLING;
+ else if (action == trigger_change_)
+ return TriggerMatchType::EDGE;
+ else
+ return nullptr;
+}
+
+void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+ Signal::populate_popup_form(parent, form);
+
+ const vector<int32_t> trig_types = get_trigger_types();
+
+ if (!trig_types.empty()) {
+ trigger_bar_ = new QToolBar(parent);
+ init_trigger_actions(trigger_bar_);
+ trigger_bar_->addAction(trigger_none_);
+ trigger_none_->setChecked(!trigger_match_);
+
+ for (auto type_id : trig_types) {
+ const TriggerMatchType *const type =
+ TriggerMatchType::get(type_id);
+ QAction *const action = action_from_trigger_type(type);
+ trigger_bar_->addAction(action);
+ action->setChecked(trigger_match_ == type);
+ }
+ form->addRow(tr("Trigger"), trigger_bar_);
+ }
+}
+
+void LogicSignal::modify_trigger()
+{
+ auto trigger = session_.session()->trigger();
+ auto new_trigger = session_.device_manager().context()->create_trigger("pulseview");
+
+ if (trigger) {
+ for (auto stage : trigger->stages()) {
+ const auto &matches = stage->matches();
+ if (none_of(matches.begin(), matches.end(),
+ [&](shared_ptr<TriggerMatch> match) {
+ return match->channel() != base_->channel(); }))
+ continue;
+
+ auto new_stage = new_trigger->add_stage();
+ for (auto match : stage->matches()) {
+ if (match->channel() == base_->channel())
+ continue;
+ new_stage->add_match(match->channel(), match->type());
+ }
+ }
+ }
+
+ if (trigger_match_) {
+ // Until we can let the user decide how to group trigger matches
+ // into stages, put all of the matches into a single stage --
+ // most devices only support a single trigger stage.
+ if (new_trigger->stages().empty())
+ new_trigger->add_stage();
+
+ new_trigger->stages().back()->add_match(base_->channel(),
+ trigger_match_);
+ }
+
+ session_.session()->set_trigger(
+ new_trigger->stages().empty() ? nullptr : new_trigger);
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+}
+
+const QIcon* LogicSignal::get_icon(const char *path)
+{
+ if (!icon_cache_.contains(path)) {
+ const QIcon *icon = new QIcon(path);
+ icon_cache_.insert(path, icon);
+ }
+
+ return icon_cache_.take(path);
+}
+
+const QPixmap* LogicSignal::get_pixmap(const char *path)
+{
+ if (!pixmap_cache_.contains(path)) {
+ const QPixmap *pixmap = new QPixmap(path);
+ pixmap_cache_.insert(path, pixmap);
+ }
+
+ return pixmap_cache_.take(path);
+}
+
+void LogicSignal::on_trigger()
+{
+ QAction *action;
+
+ action_from_trigger_type(trigger_match_)->setChecked(false);
+
+ action = (QAction *)sender();
+ action->setChecked(true);
+ trigger_match_ = trigger_type_from_action(action);
+
+ modify_trigger();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
+
+#include <QCache>
+
+#include "signal.hpp"
+
+#include <memory>
+
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+class QIcon;
+class QToolBar;
+
+namespace sigrok {
+class TriggerMatchType;
+}
+
+namespace pv {
+
+namespace devices {
+class Device;
+}
+
+namespace data {
+class Logic;
+}
+
+namespace views {
+namespace trace {
+
+class LogicSignal : public Signal
+{
+ Q_OBJECT
+
+public:
+ static const float Oversampling;
+
+ static const QColor EdgeColour;
+ static const QColor HighColour;
+ static const QColor LowColour;
+ static const QColor SamplingPointColour;
+
+ static const QColor SignalColours[10];
+
+ static QColor TriggerMarkerBackgroundColour;
+ static const int TriggerMarkerPadding;
+ static const char* TriggerMarkerIcons[8];
+
+ LogicSignal(pv::Session &session,
+ shared_ptr<devices::Device> device,
+ shared_ptr<data::SignalBase> base);
+
+ virtual ~LogicSignal() = default;
+
+ shared_ptr<pv::data::SignalData> data() const;
+
+ shared_ptr<pv::data::Logic> logic_data() const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ pair<int, int> v_extents() const;
+
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ int scale_handle_offset() const;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ void scale_handle_dragged(int offset);
+
+ /**
+ * Paints the mid-layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+
+private:
+ void paint_caps(QPainter &p, QLineF *const lines,
+ vector< pair<int64_t, bool> > &edges,
+ bool level, double samples_per_pixel, double pixels_offset,
+ float x_offset, float y_offset);
+
+ void init_trigger_actions(QWidget *parent);
+
+ const vector<int32_t> get_trigger_types() const;
+ QAction* action_from_trigger_type(const sigrok::TriggerMatchType *type);
+ const sigrok::TriggerMatchType* trigger_type_from_action(
+ QAction *action);
+ void populate_popup_form(QWidget *parent, QFormLayout *form);
+ void modify_trigger();
+
+ static const QIcon* get_icon(const char *path);
+ static const QPixmap* get_pixmap(const char *path);
+
+private Q_SLOTS:
+ void on_trigger();
+
+private:
+ int signal_height_;
+
+ shared_ptr<pv::devices::Device> device_;
+
+ const sigrok::TriggerMatchType *trigger_match_;
+ QToolBar *trigger_bar_;
+ QAction *trigger_none_;
+ QAction *trigger_rising_;
+ QAction *trigger_high_;
+ QAction *trigger_falling_;
+ QAction *trigger_low_;
+ QAction *trigger_change_;
+
+ static QCache<QString, const QIcon> icon_cache_;
+ static QCache<QString, const QPixmap> pixmap_cache_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QMenu>
+#include <QMouseEvent>
+
+#include "view.hpp"
+
+#include "marginwidget.hpp"
+
+#include <pv/widgets/popup.hpp>
+
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+MarginWidget::MarginWidget(View &parent) :
+ ViewWidget(parent)
+{
+ setAttribute(Qt::WA_NoSystemBackground, true);
+}
+
+void MarginWidget::item_clicked(const shared_ptr<ViewItem> &item)
+{
+ if (item && item->enabled())
+ show_popup(item);
+}
+
+void MarginWidget::show_popup(const shared_ptr<ViewItem> &item)
+{
+ pv::widgets::Popup *const p = item->create_popup(this);
+ if (p)
+ p->show();
+}
+
+void MarginWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
+ if (!r)
+ return;
+
+ QMenu *menu = r->create_context_menu(this);
+ if (menu)
+ menu->exec(event->globalPos());
+}
+
+void MarginWidget::keyPressEvent(QKeyEvent *event)
+{
+ assert(event);
+
+ if (event->key() == Qt::Key_Delete) {
+ const auto items = this->items();
+ for (auto &i : items)
+ if (i->selected())
+ i->delete_pressed();
+ }
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_MARGINWIDGET_HPP
+#define PULSEVIEW_PV_MARGINWIDGET_HPP
+
+#include <memory>
+
+#include <QPoint>
+
+#include "viewwidget.hpp"
+
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class ViewItem;
+
+class MarginWidget : public ViewWidget
+{
+ Q_OBJECT
+
+public:
+ MarginWidget(View &parent);
+
+ /**
+ * The extended area that the margin widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
+ */
+ virtual QSize extended_size_hint() const = 0;
+
+protected:
+ /**
+ * Indicates the event an a view item has been clicked.
+ * @param item the view item that has been clicked.
+ */
+ virtual void item_clicked(const shared_ptr<ViewItem> &item);
+
+ /**
+ * Shows the popup of a the specified @c ViewItem .
+ * @param item The item to show the popup for.
+ */
+ void show_popup(const shared_ptr<ViewItem> &item);
+
+protected:
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+
+ virtual void keyPressEvent(QKeyEvent *event);
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_MARGINWIDGET_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rowitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+void RowItem::hover_point_changed()
+{
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
+
+#include "viewitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class RowItem : public ViewItem
+{
+ Q_OBJECT
+
+public:
+ virtual void hover_point_changed();
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_ROWITEM_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <QApplication>
+#include <QFontMetrics>
+#include <QMouseEvent>
+
+#include "ruler.hpp"
+#include "view.hpp"
+
+using namespace Qt;
+
+using std::function;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const float Ruler::RulerHeight = 2.5f; // x Text Height
+const int Ruler::MinorTickSubdivision = 4;
+
+const float Ruler::HoverArrowSize = 0.5f; // x Text Height
+
+Ruler::Ruler(View &parent) :
+ MarginWidget(parent)
+{
+ setMouseTracking(true);
+
+ connect(&view_, SIGNAL(hover_point_changed()),
+ this, SLOT(hover_point_changed()));
+ connect(&view_, SIGNAL(offset_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(scale_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_prefix_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_precision_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_period_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(time_unit_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+}
+
+QSize Ruler::sizeHint() const
+{
+ const int text_height = calculate_text_height();
+ return QSize(0, RulerHeight * text_height);
+}
+
+QSize Ruler::extended_size_hint() const
+{
+ QRectF max_rect;
+ vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items)
+ max_rect = max_rect.united(i->label_rect(QRect()));
+ return QSize(0, sizeHint().height() - max_rect.top() / 2 +
+ ViewItem::HighlightRadius);
+}
+
+QString Ruler::format_time_with_distance(
+ const pv::util::Timestamp& distance,
+ const pv::util::Timestamp& t,
+ pv::util::SIPrefix prefix,
+ pv::util::TimeUnit unit,
+ unsigned precision,
+ bool sign)
+{
+ const unsigned limit = 60;
+
+ if (t.is_zero())
+ return "0";
+
+ // If we have to use samples then we have no alternative formats
+ if (unit == pv::util::TimeUnit::Samples)
+ return pv::util::format_time_si_adjusted(t, prefix, precision, "sa", sign);
+
+ // View zoomed way out -> low precision (0), big distance (>=60s)
+ // -> DD:HH:MM
+ if ((precision == 0) && (distance >= limit))
+ return pv::util::format_time_minutes(t, 0, sign);
+
+ // View in "normal" range -> medium precision, medium step size
+ // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
+ // View zoomed way in -> high precision (>3), low step size (<1s)
+ // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
+ if (abs(t) < limit)
+ return pv::util::format_time_si_adjusted(t, prefix, precision, "s", sign);
+ else
+ return pv::util::format_time_minutes(t, precision, sign);
+}
+
+vector< shared_ptr<ViewItem> > Ruler::items()
+{
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ return vector< shared_ptr<ViewItem> >(
+ time_items.begin(), time_items.end());
+}
+
+shared_ptr<ViewItem> Ruler::get_mouse_over_item(const QPoint &pt)
+{
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() && (*i)->label_rect(rect()).contains(pt))
+ return *i;
+ return nullptr;
+}
+
+void Ruler::paintEvent(QPaintEvent*)
+{
+ if (!tick_position_cache_) {
+ auto ffunc = [this](const pv::util::Timestamp& t) {
+ return format_time_with_distance(
+ this->view_.tick_period(),
+ t,
+ this->view_.tick_prefix(),
+ this->view_.time_unit(),
+ this->view_.tick_precision());
+ };
+
+ tick_position_cache_ = calculate_tick_positions(
+ view_.tick_period(),
+ view_.offset(),
+ view_.scale(),
+ width(),
+ ffunc);
+ }
+
+ const int ValueMargin = 3;
+
+ const int text_height = calculate_text_height();
+ const int ruler_height = RulerHeight * text_height;
+ const int major_tick_y1 = text_height + ValueMargin * 2;
+ const int minor_tick_y1 = (major_tick_y1 + ruler_height) / 2;
+
+ QPainter p(this);
+
+ // Draw the tick marks
+ p.setPen(palette().color(foregroundRole()));
+
+ for (const auto& tick: tick_position_cache_->major) {
+ p.drawText(tick.first, ValueMargin, 0, text_height,
+ AlignCenter | AlignTop | TextDontClip, tick.second);
+ p.drawLine(QPointF(tick.first, major_tick_y1),
+ QPointF(tick.first, ruler_height));
+ }
+
+ for (const auto& tick: tick_position_cache_->minor) {
+ p.drawLine(QPointF(tick, minor_tick_y1),
+ QPointF(tick, ruler_height));
+ }
+
+ // Draw the hover mark
+ draw_hover_mark(p, text_height);
+
+ p.setRenderHint(QPainter::Antialiasing);
+
+ // The cursor labels are not drawn with the arrows exactly on the
+ // bottom line of the widget, because then the selection shadow
+ // would be clipped away.
+ const QRect r = rect().adjusted(0, 0, 0, -ViewItem::HighlightRadius);
+
+ // Draw the items
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items) {
+ const bool highlight = !item_dragging_ &&
+ i->label_rect(r).contains(mouse_point_);
+ i->paint_label(p, r, highlight);
+ }
+}
+
+Ruler::TickPositions Ruler::calculate_tick_positions(
+ const pv::util::Timestamp& major_period,
+ const pv::util::Timestamp& offset,
+ const double scale,
+ const int width,
+ function<QString(const pv::util::Timestamp&)> format_function)
+{
+ TickPositions tp;
+
+ const pv::util::Timestamp minor_period = major_period / MinorTickSubdivision;
+ const pv::util::Timestamp first_major_division = floor(offset / major_period);
+ const pv::util::Timestamp first_minor_division = ceil(offset / minor_period);
+ const pv::util::Timestamp t0 = first_major_division * major_period;
+
+ int division = (round(first_minor_division -
+ first_major_division * MinorTickSubdivision)).convert_to<int>() - 1;
+
+ double x;
+
+ do {
+ pv::util::Timestamp t = t0 + division * minor_period;
+ x = ((t - offset) / scale).convert_to<double>();
+
+ if (division % MinorTickSubdivision == 0) {
+ // Recalculate 't' without using 'minor_period' which is a fraction
+ t = t0 + division / MinorTickSubdivision * major_period;
+ tp.major.emplace_back(x, format_function(t));
+ } else {
+ tp.minor.emplace_back(x);
+ }
+
+ division++;
+ } while (x < width);
+
+ return tp;
+}
+
+void Ruler::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ view_.add_flag(view_.offset() + ((double)event->x() + 0.5) * view_.scale());
+}
+
+void Ruler::draw_hover_mark(QPainter &p, int text_height)
+{
+ const int x = view_.hover_point().x();
+
+ if (x == -1)
+ return;
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(QBrush(palette().color(foregroundRole())));
+
+ const int b = RulerHeight * text_height;
+ const float hover_arrow_size = HoverArrowSize * text_height;
+ const QPointF points[] = {
+ QPointF(x, b),
+ QPointF(x - hover_arrow_size, b - hover_arrow_size),
+ QPointF(x + hover_arrow_size, b - hover_arrow_size)
+ };
+ p.drawPolygon(points, countof(points));
+}
+
+int Ruler::calculate_text_height() const
+{
+ return QFontMetrics(font()).ascent();
+}
+
+void Ruler::hover_point_changed()
+{
+ update();
+}
+
+void Ruler::invalidate_tick_position_cache()
+{
+ tick_position_cache_ = boost::none;
+}
+
+void Ruler::resizeEvent(QResizeEvent*)
+{
+ // the tick calculation depends on the width of this widget
+ invalidate_tick_position_cache();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
+
+#include <functional>
+#include <memory>
+
+#include <boost/optional.hpp>
+
+#include "marginwidget.hpp"
+#include <pv/util.hpp>
+
+using std::function;
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+namespace RulerTest {
+struct tick_position_test_0;
+struct tick_position_test_1;
+struct tick_position_test_2;
+}
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TimeItem;
+class ViewItem;
+
+class Ruler : public MarginWidget
+{
+ Q_OBJECT
+
+ friend struct RulerTest::tick_position_test_0;
+ friend struct RulerTest::tick_position_test_1;
+ friend struct RulerTest::tick_position_test_2;
+
+private:
+ /// Height of the ruler in multipes of the text height
+ static const float RulerHeight;
+
+ static const int MinorTickSubdivision;
+
+ /// Height of the hover arrow in multiples of the text height
+ static const float HoverArrowSize;
+
+public:
+ Ruler(View &parent);
+
+public:
+ QSize sizeHint() const override;
+
+ /**
+ * The extended area that the header widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
+ */
+ QSize extended_size_hint() const override;
+
+ /**
+ * Formats a timestamp depending on its distance to another timestamp.
+ *
+ * Heuristic function, useful when multiple timestamps should be put side by
+ * side. The function procedes in the following order:
+ * - If 't' is zero, "0" is returned.
+ * - If 'unit' is 'TimeUnit::Samples', 'pv::util::format_time_si_adjusted()'
+ * is used to format 't'.
+ * - If a zoomed out view is detected (determined by 'precision' and
+ * 'distance'), 'pv::util::format_time_minutes() is used.
+ * - For timestamps "near the origin" (determined by 'distance'),
+ * 'pv::util::format_time_si_adjusted()' is used.
+ * - If none of the previous was true, 'pv::util::format_time_minutes()'
+ * is used again.
+ *
+ * @param distance The distance between the timestamp to format and
+ * an adjacent one.
+ * @param t The value to format
+ * @param prefix The SI prefix to use.
+ * @param unit The representation of the timestamp value.
+ * @param precision The number of digits after the decimal separator.
+ * @param sign Whether or not to add a sign also for positive numbers.
+ *
+ * @return The formated value.
+ */
+ static QString format_time_with_distance(
+ const pv::util::Timestamp& distance,
+ const pv::util::Timestamp& t,
+ pv::util::SIPrefix prefix = pv::util::SIPrefix::unspecified,
+ pv::util::TimeUnit unit = pv::util::TimeUnit::Time,
+ unsigned precision = 0,
+ bool sign = true);
+
+private:
+ /**
+ * Gets the time items.
+ */
+ vector< shared_ptr<ViewItem> > items() override;
+
+ /**
+ * Gets the first view item which has a label that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt) override;
+
+ void paintEvent(QPaintEvent *event) override;
+
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+
+ /**
+ * Draw a hover arrow under the cursor position.
+ * @param p The painter to draw into.
+ * @param text_height The height of a single text ascent.
+ */
+ void draw_hover_mark(QPainter &p, int text_height);
+
+ int calculate_text_height() const;
+
+ struct TickPositions
+ {
+ vector<pair<double, QString>> major;
+ vector<double> minor;
+ };
+
+ /**
+ * Holds the tick positions so that they don't have to be recalculated on
+ * every redraw. Set by 'paintEvent()' when needed.
+ */
+ boost::optional<TickPositions> tick_position_cache_;
+
+ /**
+ * Calculates the major and minor tick positions.
+ *
+ * @param major_period The period between the major ticks.
+ * @param offset The time at the left border of the ruler.
+ * @param scale The scale in seconds per pixel.
+ * @param width the Width of the ruler.
+ * @param format_function A function used to format the major tick times.
+ * @return An object of type 'TickPositions' that contains the major tick
+ * positions together with the labels at that ticks, and the minor
+ * tick positions.
+ */
+ static TickPositions calculate_tick_positions(
+ const pv::util::Timestamp& major_period,
+ const pv::util::Timestamp& offset,
+ const double scale,
+ const int width,
+ function<QString(const pv::util::Timestamp&)> format_function);
+
+protected:
+ void resizeEvent(QResizeEvent*) override;
+
+private Q_SLOTS:
+ void hover_point_changed();
+
+ // Resets the 'tick_position_cache_'.
+ void invalidate_tick_position_cache();
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <cassert>
+#include <cmath>
+
+#include <QApplication>
+#include <QFormLayout>
+#include <QKeyEvent>
+#include <QLineEdit>
+#include <QMenu>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "pv/data/signalbase.hpp"
+
+#include "signal.hpp"
+#include "view.hpp"
+
+using std::shared_ptr;
+using std::make_shared;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const char *const ChannelNames[] = {
+ "CLK",
+ "DATA",
+ "IN",
+ "OUT",
+ "RST",
+ "TX",
+ "RX",
+ "EN",
+ "SCLK",
+ "MOSI",
+ "MISO",
+ "/SS",
+ "SDA",
+ "SCL"
+};
+
+Signal::Signal(pv::Session &session,
+ shared_ptr<data::SignalBase> channel) :
+ Trace(channel),
+ session_(session),
+ scale_handle_(make_shared<SignalScaleHandle>(*this)),
+ items_({scale_handle_}),
+ name_widget_(nullptr)
+{
+ assert(base_);
+
+ connect(base_.get(), SIGNAL(enabled_changed(bool)),
+ this, SLOT(on_enabled_changed(bool)));
+}
+
+void Signal::set_name(QString name)
+{
+ Trace::set_name(name);
+
+ if (name != name_widget_->currentText())
+ name_widget_->setEditText(name);
+}
+
+bool Signal::enabled() const
+{
+ return base_->enabled();
+}
+
+shared_ptr<data::SignalBase> Signal::base() const
+{
+ return base_;
+}
+
+void Signal::save_settings(QSettings &settings) const
+{
+ (void)settings;
+}
+
+void Signal::restore_settings(QSettings &settings)
+{
+ (void)settings;
+}
+
+const ViewItemOwner::item_list& Signal::child_items() const
+{
+ return items_;
+}
+
+void Signal::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (base_->enabled())
+ Trace::paint_back(p, pp);
+}
+
+void Signal::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+ name_widget_ = new QComboBox(parent);
+ name_widget_->setEditable(true);
+ name_widget_->setCompleter(nullptr);
+
+ for (unsigned int i = 0; i < countof(ChannelNames); i++)
+ name_widget_->insertItem(i, ChannelNames[i]);
+
+ const int index = name_widget_->findText(base_->name(), Qt::MatchExactly);
+
+ if (index == -1) {
+ name_widget_->insertItem(0, base_->name());
+ name_widget_->setCurrentIndex(0);
+ } else {
+ name_widget_->setCurrentIndex(index);
+ }
+
+ connect(name_widget_, SIGNAL(editTextChanged(const QString&)),
+ this, SLOT(on_nameedit_changed(const QString&)));
+
+ form->addRow(tr("Name"), name_widget_);
+
+ add_colour_option(parent, form);
+}
+
+QMenu* Signal::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = Trace::create_context_menu(parent);
+
+ menu->addSeparator();
+
+ QAction *const disable = new QAction(tr("Disable"), this);
+ disable->setShortcuts(QKeySequence::Delete);
+ connect(disable, SIGNAL(triggered()), this, SLOT(on_disable()));
+ menu->addAction(disable);
+
+ return menu;
+}
+
+void Signal::delete_pressed()
+{
+ on_disable();
+}
+
+void Signal::on_name_changed(const QString &text)
+{
+ // On startup, this event is fired when a session restores signal
+ // names. However, the name widget hasn't yet been created.
+ if (!name_widget_)
+ return;
+
+ if (text != name_widget_->currentText())
+ name_widget_->setEditText(text);
+
+ Trace::on_name_changed(text);
+}
+
+void Signal::on_disable()
+{
+ base_->set_enabled(false);
+}
+
+void Signal::on_enabled_changed(bool enabled)
+{
+ (void)enabled;
+
+ if (owner_)
+ owner_->extents_changed(true, true);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
+
+#include <memory>
+
+#include <QComboBox>
+#include <QWidgetAction>
+
+#include <cstdint>
+
+#include "signalscalehandle.hpp"
+#include "trace.hpp"
+#include "viewitemowner.hpp"
+
+using std::shared_ptr;
+
+namespace pv {
+
+class Session;
+
+namespace data {
+class SignalBase;
+class SignalData;
+}
+
+namespace views {
+namespace trace {
+
+class Signal : public Trace, public ViewItemOwner
+{
+ Q_OBJECT
+
+protected:
+ Signal(pv::Session &session, shared_ptr<data::SignalBase> channel);
+
+public:
+ /**
+ * Sets the name of the signal.
+ */
+ virtual void set_name(QString name);
+
+ virtual shared_ptr<pv::data::SignalData> data() const = 0;
+
+ /**
+ * Returns true if the trace is visible and enabled.
+ */
+ bool enabled() const;
+
+ shared_ptr<data::SignalBase> base() const;
+
+ virtual void save_settings(QSettings &settings) const;
+
+ virtual void restore_settings(QSettings &settings);
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ const item_list& child_items() const;
+
+ void paint_back(QPainter &p, ViewItemPaintParams &pp);
+
+ virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ void delete_pressed();
+
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ virtual int scale_handle_offset() const = 0;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ virtual void scale_handle_dragged(int offset) = 0;
+
+ /**
+ * Handles the scale handle being being released.
+ */
+ virtual void scale_handle_released() {};
+
+protected Q_SLOTS:
+ virtual void on_name_changed(const QString &text);
+
+ void on_disable();
+
+ void on_enabled_changed(bool enabled);
+
+protected:
+ pv::Session &session_;
+
+ const shared_ptr<SignalScaleHandle> scale_handle_;
+ const item_list items_;
+
+ QComboBox *name_widget_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+
+#include <QRadialGradient>
+
+#include "signal.hpp"
+#include "signalscalehandle.hpp"
+#include "tracetreeitemowner.hpp"
+
+using std::max;
+using std::min;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+SignalScaleHandle::SignalScaleHandle(Signal &owner) :
+ owner_(owner)
+{
+}
+
+bool SignalScaleHandle::enabled() const
+{
+ return selected() || owner_.selected();
+}
+
+void SignalScaleHandle::select(bool select)
+{
+ ViewItem::select(select);
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+void SignalScaleHandle::drag_release()
+{
+ RowItem::drag_release();
+ owner_.scale_handle_released();
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+void SignalScaleHandle::drag_by(const QPoint &delta)
+{
+ owner_.scale_handle_dragged(
+ drag_point_.y() + delta.y() - owner_.get_visual_y());
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+QPoint SignalScaleHandle::point(const QRect &rect) const
+{
+ return owner_.point(rect) + QPoint(0, owner_.scale_handle_offset());
+}
+
+QRectF SignalScaleHandle::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ const int text_height = ViewItemPaintParams::text_height();
+ const double x = -pp.pixels_offset() - text_height / 2;
+ const double min_x = pp.left() + text_height;
+ const double max_x = pp.right() - text_height * 2;
+ return QRectF(min(max(x, min_x), max_x),
+ owner_.get_visual_y() + owner_.scale_handle_offset() -
+ text_height / 2,
+ text_height, text_height);
+}
+
+void SignalScaleHandle::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ const QRectF r(hit_box_rect(pp));
+ const QPointF c = (r.topLeft() + 2 * r.center()) / 3;
+ QRadialGradient gradient(c, r.width(), c);
+
+ if (selected()) {
+ gradient.setColorAt(0.0, QColor(255, 255, 255));
+ gradient.setColorAt(0.75, QColor(192, 192, 192));
+ gradient.setColorAt(1.0, QColor(128, 128, 128));
+ } else {
+ gradient.setColorAt(0.0, QColor(192, 192, 192));
+ gradient.setColorAt(0.75, QColor(128, 128, 128));
+ gradient.setColorAt(1.0, QColor(128, 128, 128));
+ }
+
+ p.setBrush(QBrush(gradient));
+ p.setPen(QColor(128, 128, 128));
+ p.drawEllipse(r);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
+
+#include "rowitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class Signal;
+
+/**
+ * A row item owned by a @c Signal that implements the v-scale adjustment grab
+ * handle.
+ */
+class SignalScaleHandle : public RowItem
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ explicit SignalScaleHandle(Signal &owner);
+
+public:
+ /**
+ * Returns true if the parent item is enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ void select(bool select = true);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ void drag_release();
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Get the drag point.
+ * @param rect the rectangle of the widget area.
+ */
+ QPoint point(const QRect &rect) const;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ */
+ QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+
+private:
+ Signal &owner_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNALSCALEHANDLE_HPP
#include <QMessageBox>
#include "standardbar.hpp"
+#include "view.hpp"
#include <pv/mainwindow.hpp>
-#include <pv/view/view.hpp>
using pv::views::TraceView::View;
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "timeitem.hpp"
+#include "view.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+TimeItem::TimeItem(View &view) :
+ view_(view) {
+}
+
+void TimeItem::drag_by(const QPoint &delta)
+{
+ set_time(view_.offset() + (drag_point_.x() + delta.x() - 0.5) *
+ view_.scale());
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
+
+#include "viewitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class View;
+
+class TimeItem : public ViewItem
+
+{
+ Q_OBJECT
+
+protected:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ */
+ TimeItem(View &view);
+
+public:
+ /**
+ * Sets the time of the marker.
+ */
+ virtual void set_time(const pv::util::Timestamp& time) = 0;
+
+ virtual float get_x() const = 0;
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
+
+protected:
+ View &view_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <cmath>
+
+#include <extdef.h>
+
+#include "timemarker.hpp"
+
+#include "pv/widgets/timestampspinbox.hpp"
+#include "view.hpp"
+
+#include <QApplication>
+#include <QFontMetrics>
+#include <QFormLayout>
+#include <QPainter>
+
+#include <pv/widgets/popup.hpp>
+
+using std::max;
+using std::min;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const int TimeMarker::ArrowSize = 4;
+
+TimeMarker::TimeMarker(
+ View &view, const QColor &colour, const pv::util::Timestamp& time) :
+ TimeItem(view),
+ colour_(colour),
+ time_(time),
+ value_action_(nullptr),
+ value_widget_(nullptr),
+ updating_value_widget_(false)
+{
+}
+
+const pv::util::Timestamp& TimeMarker::time() const
+{
+ return time_;
+}
+
+void TimeMarker::set_time(const pv::util::Timestamp& time)
+{
+ time_ = time;
+
+ if (value_widget_) {
+ updating_value_widget_ = true;
+ value_widget_->setValue(time);
+ updating_value_widget_ = false;
+ }
+
+ view_.time_item_appearance_changed(true, true);
+}
+
+float TimeMarker::get_x() const
+{
+ // Use roundf() from cmath, std::roundf() causes Android issues (see #945).
+ return roundf(((time_ - view_.offset()) / view_.scale()).convert_to<float>()) + 0.5f;
+}
+
+QPoint TimeMarker::point(const QRect &rect) const
+{
+ return QPoint(get_x(), rect.bottom());
+}
+
+QRectF TimeMarker::label_rect(const QRectF &rect) const
+{
+ QFontMetrics m(QApplication::font());
+ const QSizeF text_size(
+ max(m.boundingRect(get_text()).size().width(), ArrowSize),
+ m.height());
+ const QSizeF label_size(text_size + LabelPadding * 2);
+ const float top = rect.height() - label_size.height() -
+ TimeMarker::ArrowSize - 0.5f;
+ const float x = get_x();
+
+ return QRectF(QPointF(x - label_size.width() / 2, top), label_size);
+}
+
+QRectF TimeMarker::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ const float x = get_x();
+ const float h = QFontMetrics(QApplication::font()).height();
+ return QRectF(x - h / 2.0f, pp.top(), h, pp.height());
+}
+
+void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ if (!enabled())
+ return;
+
+ const qreal x = get_x();
+ const QRectF r(label_rect(rect));
+
+ const QPointF points[] = {
+ r.topLeft(),
+ r.bottomLeft(),
+ QPointF(max(r.left(), x - ArrowSize), r.bottom()),
+ QPointF(x, rect.bottom()),
+ QPointF(min(r.right(), x + ArrowSize), r.bottom()),
+ r.bottomRight(),
+ r.topRight()
+ };
+
+ const QPointF highlight_points[] = {
+ QPointF(r.left() + 1, r.top() + 1),
+ QPointF(r.left() + 1, r.bottom() - 1),
+ QPointF(max(r.left() + 1, x - ArrowSize), r.bottom() - 1),
+ QPointF(min(max(r.left() + 1, x), r.right() - 1),
+ rect.bottom() - 1),
+ QPointF(min(r.right() - 1, x + ArrowSize), r.bottom() - 1),
+ QPointF(r.right() - 1, r.bottom() - 1),
+ QPointF(r.right() - 1, r.top() + 1),
+ };
+
+ if (selected()) {
+ p.setPen(highlight_pen());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
+ }
+
+ p.setPen(Qt::transparent);
+ p.setBrush(hover ? colour_.lighter() : colour_);
+ p.drawPolygon(points, countof(points));
+
+ p.setPen(colour_.lighter());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(highlight_points, countof(highlight_points));
+
+ p.setPen(colour_.darker());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
+
+ p.setPen(select_text_colour(colour_));
+ p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_text());
+}
+
+void TimeMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ const float x = get_x();
+ p.setPen(colour_.darker());
+ p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
+}
+
+pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
+{
+ using pv::widgets::Popup;
+
+ Popup *const popup = new Popup(parent);
+ popup->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Bottom);
+
+ QFormLayout *const form = new QFormLayout(popup);
+ popup->setLayout(form);
+
+ value_widget_ = new pv::widgets::TimestampSpinBox(parent);
+ value_widget_->setValue(time_);
+
+ connect(value_widget_, SIGNAL(valueChanged(const pv::util::Timestamp&)),
+ this, SLOT(on_value_changed(const pv::util::Timestamp&)));
+
+ form->addRow(tr("Time"), value_widget_);
+
+ return popup;
+}
+
+void TimeMarker::on_value_changed(const pv::util::Timestamp& value)
+{
+ if (!updating_value_widget_)
+ set_time(value);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
+
+#include <QColor>
+#include <QDoubleSpinBox>
+#include <QObject>
+#include <QRectF>
+#include <QWidgetAction>
+
+#include "timeitem.hpp"
+
+class QPainter;
+class QRect;
+
+namespace pv {
+namespace widgets {
+ class TimestampSpinBox;
+}
+
+namespace views {
+namespace trace {
+
+class View;
+
+class TimeMarker : public TimeItem
+{
+ Q_OBJECT
+
+public:
+ static const int ArrowSize;
+
+protected:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ * @param colour A reference to the colour of this cursor.
+ * @param time The time to set the flag to.
+ */
+ TimeMarker(View &view, const QColor &colour, const pv::util::Timestamp& time);
+
+public:
+ /**
+ * Gets the time of the marker.
+ */
+ const pv::util::Timestamp& time() const;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const override;
+
+ /**
+ * Gets the arrow-tip point of the time marker.
+ * @param rect the rectangle of the ruler area.
+ */
+ QPoint point(const QRect &rect) const override;
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const override;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ */
+ QRectF hit_box_rect(const ViewItemPaintParams &pp) const override;
+
+ /**
+ * Gets the text to show in the marker.
+ */
+ virtual QString get_text() const = 0;
+
+ /**
+ * Paints the marker's label to the ruler.
+ * @param p The painter to draw with.
+ * @param rect The rectangle of the ruler client area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover) override;
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, ViewItemPaintParams &pp) override;
+
+ virtual pv::widgets::Popup* create_popup(QWidget *parent) override;
+
+private Q_SLOTS:
+ void on_value_changed(const pv::util::Timestamp& value);
+
+protected:
+ const QColor &colour_;
+
+ pv::util::Timestamp time_;
+
+ QSizeF text_size_;
+
+ QWidgetAction *value_action_;
+ pv::widgets::TimestampSpinBox *value_widget_;
+ bool updating_value_widget_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <cassert>
+#include <cmath>
+
+#include <QApplication>
+#include <QFormLayout>
+#include <QKeyEvent>
+#include <QLineEdit>
+
+#include "trace.hpp"
+#include "tracepalette.hpp"
+#include "view.hpp"
+
+#include "pv/globalsettings.hpp"
+#include "pv/widgets/colourbutton.hpp"
+#include "pv/widgets/popup.hpp"
+
+using std::pair;
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QPen Trace::AxisPen(QColor(0, 0, 0, 30 * 256 / 100));
+const int Trace::LabelHitPadding = 2;
+
+const QColor Trace::BrightGrayBGColour = QColor(0, 0, 0, 10 * 255 / 100);
+const QColor Trace::DarkGrayBGColour = QColor(0, 0, 0, 15 * 255 / 100);
+
+Trace::Trace(shared_ptr<data::SignalBase> channel) :
+ base_(channel),
+ popup_(nullptr),
+ popup_form_(nullptr)
+{
+ connect(channel.get(), SIGNAL(name_changed(const QString&)),
+ this, SLOT(on_name_changed(const QString&)));
+ connect(channel.get(), SIGNAL(colour_changed(const QColor&)),
+ this, SLOT(on_colour_changed(const QColor&)));
+}
+
+shared_ptr<data::SignalBase> Trace::base() const
+{
+ return base_;
+}
+
+void Trace::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ const int y = get_visual_y();
+
+ p.setBrush(base_->colour());
+
+ if (!enabled())
+ return;
+
+ const QRectF r = label_rect(rect);
+
+ // When selected, move the arrow to the left so that the border can show
+ const QPointF offs = (selected()) ? QPointF(-2, 0) : QPointF(0, 0);
+
+ // Paint the label
+ const float label_arrow_length = r.height() / 2;
+ QPointF points[] = {
+ offs + r.topLeft(),
+ offs + QPointF(r.right() - label_arrow_length, r.top()),
+ offs + QPointF(r.right(), y),
+ offs + QPointF(r.right() - label_arrow_length, r.bottom()),
+ offs + r.bottomLeft()
+ };
+ QPointF highlight_points[] = {
+ offs + QPointF(r.left() + 1, r.top() + 1),
+ offs + QPointF(r.right() - label_arrow_length, r.top() + 1),
+ offs + QPointF(r.right() - 1, y),
+ offs + QPointF(r.right() - label_arrow_length, r.bottom() - 1),
+ offs + QPointF(r.left() + 1, r.bottom() - 1)
+ };
+
+ if (selected()) {
+ p.setPen(highlight_pen());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
+ }
+
+ p.setPen(Qt::transparent);
+ p.setBrush(hover ? base_->colour().lighter() : base_->colour());
+ p.drawPolygon(points, countof(points));
+
+ p.setPen(base_->colour().lighter());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(highlight_points, countof(highlight_points));
+
+ p.setPen(base_->colour().darker());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
+
+ // Paint the text
+ p.setPen(select_text_colour(base_->colour()));
+ p.setFont(QApplication::font());
+ p.drawText(QRectF(r.x(), r.y(),
+ r.width() - label_arrow_length, r.height()),
+ Qt::AlignCenter | Qt::AlignVCenter, base_->name());
+}
+
+QMenu* Trace::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = ViewItem::create_context_menu(parent);
+
+ return menu;
+}
+
+pv::widgets::Popup* Trace::create_popup(QWidget *parent)
+{
+ using pv::widgets::Popup;
+
+ popup_ = new Popup(parent);
+ popup_->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Right);
+
+ create_popup_form();
+
+ connect(popup_, SIGNAL(closed()), this, SLOT(on_popup_closed()));
+
+ return popup_;
+}
+
+QRectF Trace::label_rect(const QRectF &rect) const
+{
+ QFontMetrics m(QApplication::font());
+ const QSize text_size(
+ m.boundingRect(QRect(), 0, base_->name()).width(), m.height());
+ const QSizeF label_size(
+ text_size.width() + LabelPadding.width() * 2,
+ ceilf((text_size.height() + LabelPadding.height() * 2) / 2) * 2);
+ const float half_height = label_size.height() / 2;
+ return QRectF(
+ rect.right() - half_height - label_size.width() - 0.5,
+ get_visual_y() + 0.5f - half_height,
+ label_size.width() + half_height,
+ label_size.height());
+}
+
+void Trace::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ const View *view = owner_->view();
+ assert(view);
+
+ if (view->coloured_bg())
+ p.setBrush(base_->bgcolour());
+ else
+ p.setBrush(pp.next_bg_colour_state() ? BrightGrayBGColour : DarkGrayBGColour);
+
+ p.setPen(QPen(Qt::NoPen));
+
+ const pair<int, int> extents = v_extents();
+ p.drawRect(pp.left(), get_visual_y() + extents.first,
+ pp.width(), extents.second - extents.first);
+}
+
+void Trace::paint_axis(QPainter &p, ViewItemPaintParams &pp, int y)
+{
+ p.setRenderHint(QPainter::Antialiasing, false);
+
+ p.setPen(AxisPen);
+ p.drawLine(QPointF(pp.left(), y), QPointF(pp.right(), y));
+
+ p.setRenderHint(QPainter::Antialiasing, true);
+}
+
+void Trace::add_colour_option(QWidget *parent, QFormLayout *form)
+{
+ using pv::widgets::ColourButton;
+
+ ColourButton *const colour_button = new ColourButton(
+ TracePalette::Rows, TracePalette::Cols, parent);
+ colour_button->set_palette(TracePalette::Colours);
+ colour_button->set_colour(base_->colour());
+ connect(colour_button, SIGNAL(selected(const QColor&)),
+ this, SLOT(on_colouredit_changed(const QColor&)));
+
+ form->addRow(tr("Colour"), colour_button);
+}
+
+void Trace::create_popup_form()
+{
+ // Clear the layout
+
+ // Transfer the layout and the child widgets to a temporary widget
+ // which we delete after the event was handled. This way, the layout
+ // and all widgets contained therein are deleted after the event was
+ // handled, leaving the parent popup_ time to handle the change.
+ if (popup_form_) {
+ QWidget *suicidal = new QWidget();
+ suicidal->setLayout(popup_form_);
+ suicidal->deleteLater();
+ }
+
+ // Repopulate the popup
+ popup_form_ = new QFormLayout(popup_);
+ popup_->setLayout(popup_form_);
+ populate_popup_form(popup_, popup_form_);
+}
+
+void Trace::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+ QLineEdit *const name_edit = new QLineEdit(parent);
+ name_edit->setText(base_->name());
+ connect(name_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(on_nameedit_changed(const QString&)));
+ form->addRow(tr("Name"), name_edit);
+
+ add_colour_option(parent, form);
+}
+
+void Trace::set_name(QString name)
+{
+ base_->set_name(name);
+}
+
+void Trace::set_colour(QColor colour)
+{
+ base_->set_colour(colour);
+}
+
+void Trace::on_name_changed(const QString &text)
+{
+ /* This event handler is called by SignalBase when the name was changed there */
+ (void)text;
+
+ if (owner_) {
+ owner_->extents_changed(true, false);
+ owner_->row_item_appearance_changed(true, false);
+ }
+}
+
+void Trace::on_colour_changed(const QColor &colour)
+{
+ /* This event handler is called by SignalBase when the colour was changed there */
+ (void)colour;
+
+ if (owner_)
+ owner_->row_item_appearance_changed(true, true);
+}
+
+void Trace::on_popup_closed()
+{
+ popup_ = nullptr;
+ popup_form_ = nullptr;
+}
+
+void Trace::on_nameedit_changed(const QString &name)
+{
+ /* This event handler notifies SignalBase that the name changed */
+ set_name(name);
+}
+
+void Trace::on_colouredit_changed(const QColor &colour)
+{
+ /* This event handler notifies SignalBase that the colour changed */
+ set_colour(colour);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
+
+#include <QColor>
+#include <QPainter>
+#include <QPen>
+#include <QRect>
+#include <QString>
+
+#include <cstdint>
+
+#include "tracetreeitem.hpp"
+
+#include "pv/data/signalbase.hpp"
+
+using std::shared_ptr;
+
+class QFormLayout;
+
+namespace pv {
+
+namespace data {
+class SignalBase;
+}
+
+namespace widgets {
+class Popup;
+}
+
+namespace views {
+namespace trace {
+
+class Trace : public TraceTreeItem
+{
+ Q_OBJECT
+
+private:
+ static const QPen AxisPen;
+ static const int LabelHitPadding;
+
+ static const QColor BrightGrayBGColour;
+ static const QColor DarkGrayBGColour;
+
+protected:
+ Trace(shared_ptr<data::SignalBase> channel);
+
+public:
+ /**
+ * Returns the underlying SignalBase instance.
+ */
+ shared_ptr<data::SignalBase> base() const;
+
+ /**
+ * Sets the name of the signal.
+ */
+ virtual void set_name(QString name);
+
+ /**
+ * Set the colour of the signal.
+ */
+ virtual void set_colour(QColor colour);
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param rect the rectangle of the header area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ virtual QMenu* create_context_menu(QWidget *parent);
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+protected:
+ /**
+ * Paints the background layer of the signal with a QPainter.
+ * @param p The QPainter to paint into.
+ * @param pp The painting parameters object to paint with.
+ */
+ virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints a zero axis across the viewport.
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ * @param y the y-offset of the axis.
+ */
+ void paint_axis(QPainter &p, ViewItemPaintParams &pp, int y);
+
+ void add_colour_option(QWidget *parent, QFormLayout *form);
+
+ void create_popup_form();
+
+ virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+protected Q_SLOTS:
+ virtual void on_name_changed(const QString &text);
+
+ virtual void on_colour_changed(const QColor &colour);
+
+ void on_popup_closed();
+
+private Q_SLOTS:
+ void on_nameedit_changed(const QString &name);
+
+ void on_colouredit_changed(const QColor &colour);
+
+protected:
+ shared_ptr<data::SignalBase> base_;
+
+private:
+ pv::widgets::Popup *popup_;
+ QFormLayout *popup_form_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <algorithm>
+#include <cassert>
+
+#include <QMenu>
+#include <QPainter>
+
+#include "tracegroup.hpp"
+
+using std::any_of;
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const int TraceGroup::Padding = 8;
+const int TraceGroup::Width = 12;
+const int TraceGroup::LineThickness = 5;
+const QColor TraceGroup::LineColour(QColor(0x55, 0x57, 0x53));
+
+TraceGroup::~TraceGroup()
+{
+ owner_ = nullptr;
+ clear_child_items();
+}
+
+bool TraceGroup::enabled() const
+{
+ return any_of(child_items().begin(), child_items().end(),
+ [](const shared_ptr<ViewItem> &r) { return r->enabled(); });
+}
+
+pv::Session& TraceGroup::session()
+{
+ assert(owner_);
+ return owner_->session();
+}
+
+const pv::Session& TraceGroup::session() const
+{
+ assert(owner_);
+ return owner_->session();
+}
+
+View* TraceGroup::view()
+{
+ assert(owner_);
+ return owner_->view();
+}
+
+const View* TraceGroup::view() const
+{
+ assert(owner_);
+ return owner_->view();
+}
+
+pair<int, int> TraceGroup::v_extents() const
+{
+ return TraceTreeItemOwner::v_extents();
+}
+
+void TraceGroup::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ const QRectF r = label_rect(rect).adjusted(
+ LineThickness / 2, LineThickness / 2,
+ -LineThickness / 2, -LineThickness / 2);
+
+ // Paint the label
+ const QPointF points[] = {
+ r.topRight(),
+ r.topLeft(),
+ r.bottomLeft(),
+ r.bottomRight()
+ };
+
+ if (selected()) {
+ const QPen pen(highlight_pen());
+ p.setPen(QPen(pen.brush(), pen.width() + LineThickness,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ p.setBrush(Qt::transparent);
+ p.drawPolyline(points, countof(points));
+ }
+
+ p.setPen(QPen(QBrush(LineColour.darker()), LineThickness,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ p.drawPolyline(points, countof(points));
+ p.setPen(QPen(QBrush(hover ? LineColour.lighter() : LineColour),
+ LineThickness - 2, Qt::SolidLine, Qt::SquareCap,
+ Qt::RoundJoin));
+ p.drawPolyline(points, countof(points));
+}
+
+QRectF TraceGroup::label_rect(const QRectF &rect) const
+{
+ QRectF child_rect;
+ for (const shared_ptr<ViewItem> r : child_items())
+ if (r && r->enabled())
+ child_rect = child_rect.united(r->label_rect(rect));
+
+ return QRectF(child_rect.x() - Width - Padding, child_rect.y(),
+ Width, child_rect.height());
+}
+
+bool TraceGroup::pt_in_label_rect(int left, int right, const QPoint &point)
+{
+ (void)left;
+ (void)right;
+ (void)point;
+
+ return false;
+}
+
+QMenu* TraceGroup::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = new QMenu(parent);
+
+ QAction *const ungroup = new QAction(tr("Ungroup"), this);
+ ungroup->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
+ connect(ungroup, SIGNAL(triggered()), this, SLOT(on_ungroup()));
+ menu->addAction(ungroup);
+
+ return menu;
+}
+
+pv::widgets::Popup* TraceGroup::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+int TraceGroup::owner_visual_v_offset() const
+{
+ return owner_ ? visual_v_offset() + owner_->owner_visual_v_offset() : 0;
+}
+
+void TraceGroup::restack_items()
+{
+ vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+
+ // Sort by the centre line of the extents
+ stable_sort(items.begin(), items.end(),
+ [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
+ const auto aext = a->v_extents();
+ const auto bext = b->v_extents();
+ return a->layout_v_offset() +
+ (aext.first + aext.second) / 2 <
+ b->layout_v_offset() +
+ (bext.first + bext.second) / 2;
+ });
+
+ int total_offset = 0;
+ for (shared_ptr<TraceTreeItem> r : items) {
+ const pair<int, int> extents = r->v_extents();
+ if (extents.first == 0 && extents.second == 0)
+ continue;
+
+ // We position disabled traces, so that they are close to the
+ // animation target positon should they be re-enabled
+ if (r->enabled())
+ total_offset += -extents.first;
+
+ if (!r->dragging())
+ r->set_layout_v_offset(total_offset);
+
+ if (r->enabled())
+ total_offset += extents.second;
+ }
+}
+
+unsigned int TraceGroup::depth() const
+{
+ return owner_ ? owner_->depth() + 1 : 0;
+}
+
+void TraceGroup::ungroup()
+{
+ const vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+ clear_child_items();
+
+ for (shared_ptr<TraceTreeItem> r : items)
+ owner_->add_child_item(r);
+
+ owner_->remove_child_item(shared_from_this());
+}
+
+void TraceGroup::on_ungroup()
+{
+ ungroup();
+}
+
+void TraceGroup::row_item_appearance_changed(bool label, bool content)
+{
+ if (owner_)
+ owner_->row_item_appearance_changed(label, content);
+}
+
+void TraceGroup::extents_changed(bool horz, bool vert)
+{
+ if (owner_)
+ owner_->extents_changed(horz, vert);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
+
+#include "tracetreeitem.hpp"
+#include "tracetreeitemowner.hpp"
+
+using std::pair;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TraceGroup : public TraceTreeItem, public TraceTreeItemOwner
+{
+ Q_OBJECT
+
+private:
+ static const int Padding;
+ static const int Width;
+ static const int LineThickness;
+ static const QColor LineColour;
+
+public:
+ /**
+ * Virtual destructor
+ */
+ virtual ~TraceGroup();
+
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Returns the session of the onwer.
+ */
+ pv::Session& session();
+
+ /**
+ * Returns the session of the onwer.
+ */
+ const pv::Session& session() const;
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual View* view();
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const View* view() const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ pair<int, int> v_extents() const;
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param right the x-coordinate of the right edge of the header
+ * area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Determines if a point is in the header label rect.
+ * @param left the x-coordinate of the left edge of the header
+ * area.
+ * @param right the x-coordinate of the right edge of the header
+ * area.
+ * @param point the point to test.
+ */
+ bool pt_in_label_rect(int left, int right, const QPoint &point);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+ /**
+ * Returns the total vertical offset of this trace and all it's owners
+ */
+ int owner_visual_v_offset() const;
+
+ void restack_items();
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ unsigned int depth() const;
+
+ void ungroup();
+
+public:
+ void row_item_appearance_changed(bool label, bool content);
+
+ void extents_changed(bool horz, bool vert);
+
+private Q_SLOTS:
+ void on_ungroup();
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tracepalette.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor TracePalette::Colours[Cols * Rows] = {
+
+ // Light Colours
+ QColor(0xFC, 0xE9, 0x4F), // Butter
+ QColor(0xFC, 0xAF, 0x3E), // Orange
+ QColor(0xE9, 0xB9, 0x6E), // Chocolate
+ QColor(0x8A, 0xE2, 0x34), // Chameleon
+ QColor(0x72, 0x9F, 0xCF), // Sky Blue
+ QColor(0xAD, 0x7F, 0xA8), // Plum
+ QColor(0xCF, 0x72, 0xC3), // Magenta
+ QColor(0xEF, 0x29, 0x29), // Scarlet Red
+
+ // Mid Colours
+ QColor(0xED, 0xD4, 0x00), // Butter
+ QColor(0xF5, 0x79, 0x00), // Orange
+ QColor(0xC1, 0x7D, 0x11), // Chocolate
+ QColor(0x73, 0xD2, 0x16), // Chameleon
+ QColor(0x34, 0x65, 0xA4), // Sky Blue
+ QColor(0x75, 0x50, 0x7B), // Plum
+ QColor(0xA3, 0x34, 0x96), // Magenta
+ QColor(0xCC, 0x00, 0x00), // Scarlet Red
+
+ // Dark Colours
+ QColor(0xC4, 0xA0, 0x00), // Butter
+ QColor(0xCE, 0x5C, 0x00), // Orange
+ QColor(0x8F, 0x59, 0x02), // Chocolate
+ QColor(0x4E, 0x9A, 0x06), // Chameleon
+ QColor(0x20, 0x4A, 0x87), // Sky Blue
+ QColor(0x5C, 0x35, 0x66), // Plum
+ QColor(0x87, 0x20, 0x7A), // Magenta
+ QColor(0xA4, 0x00, 0x00), // Scarlet Red
+
+ // Greys
+ QColor(0x16, 0x19, 0x1A), // Black
+ QColor(0x2E, 0x34, 0x36), // Grey 1
+ QColor(0x55, 0x57, 0x53), // Grey 2
+ QColor(0x88, 0x8A, 0x8F), // Grey 3
+ QColor(0xBA, 0xBD, 0xB6), // Grey 4
+ QColor(0xD3, 0xD7, 0xCF), // Grey 5
+ QColor(0xEE, 0xEE, 0xEC), // Grey 6
+ QColor(0xFF, 0xFF, 0xFF), // White
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
+
+#include <QColor>
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TracePalette
+{
+public:
+ static const unsigned int Cols = 8;
+ static const unsigned int Rows = 4;
+ static const QColor Colours[Cols * Rows];
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+
+#include "view.hpp"
+
+#include "tracetreeitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+TraceTreeItem::TraceTreeItem() :
+ owner_(nullptr),
+ layout_v_offset_(0),
+ visual_v_offset_(0),
+ v_offset_animation_(this, "visual_v_offset")
+{
+}
+
+void TraceTreeItem::select(bool select)
+{
+ ViewItem::select(select);
+ owner_->row_item_appearance_changed(true, true);
+}
+
+int TraceTreeItem::layout_v_offset() const
+{
+ return layout_v_offset_;
+}
+
+void TraceTreeItem::set_layout_v_offset(int v_offset)
+{
+ if (layout_v_offset_ == v_offset)
+ return;
+
+ layout_v_offset_ = v_offset;
+
+ if (owner_)
+ owner_->extents_changed(false, true);
+}
+
+int TraceTreeItem::visual_v_offset() const
+{
+ return visual_v_offset_;
+}
+
+void TraceTreeItem::set_visual_v_offset(int v_offset)
+{
+ visual_v_offset_ = v_offset;
+
+ if (owner_)
+ owner_->row_item_appearance_changed(true, true);
+}
+
+void TraceTreeItem::force_to_v_offset(int v_offset)
+{
+ v_offset_animation_.stop();
+ layout_v_offset_ = visual_v_offset_ = v_offset;
+
+ if (owner_) {
+ owner_->row_item_appearance_changed(true, true);
+ owner_->extents_changed(false, true);
+ }
+}
+
+void TraceTreeItem::animate_to_layout_v_offset()
+{
+ if (visual_v_offset_ == layout_v_offset_ ||
+ (v_offset_animation_.endValue() == layout_v_offset_ &&
+ v_offset_animation_.state() == QAbstractAnimation::Running))
+ return;
+
+ v_offset_animation_.setDuration(100);
+ v_offset_animation_.setStartValue(visual_v_offset_);
+ v_offset_animation_.setEndValue(layout_v_offset_);
+ v_offset_animation_.setEasingCurve(QEasingCurve::OutQuad);
+ v_offset_animation_.start();
+}
+
+TraceTreeItemOwner* TraceTreeItem::owner() const
+{
+ return owner_;
+}
+
+void TraceTreeItem::set_owner(TraceTreeItemOwner *owner)
+{
+ assert(owner_ || owner);
+ v_offset_animation_.stop();
+
+ if (owner_) {
+ const int owner_offset = owner_->owner_visual_v_offset();
+ layout_v_offset_ += owner_offset;
+ visual_v_offset_ += owner_offset;
+ }
+
+ owner_ = owner;
+
+ if (owner_) {
+ const int owner_offset = owner_->owner_visual_v_offset();
+ layout_v_offset_ -= owner_offset;
+ visual_v_offset_ -= owner_offset;
+ }
+}
+
+int TraceTreeItem::get_visual_y() const
+{
+ assert(owner_);
+ return visual_v_offset_ + owner_->owner_visual_v_offset();
+}
+
+void TraceTreeItem::drag_by(const QPoint &delta)
+{
+ assert(owner_);
+ force_to_v_offset(drag_point_.y() + delta.y() -
+ owner_->owner_visual_v_offset());
+}
+
+QPoint TraceTreeItem::point(const QRect &rect) const
+{
+ return QPoint(rect.right(), get_visual_y());
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
+
+#include <memory>
+
+#include <QPropertyAnimation>
+
+#include "rowitem.hpp"
+
+using std::enable_shared_from_this;
+using std::pair;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TraceTreeItemOwner;
+
+class TraceTreeItem : public RowItem,
+ public enable_shared_from_this<TraceTreeItem>
+{
+ Q_OBJECT
+ Q_PROPERTY(int visual_v_offset
+ READ visual_v_offset
+ WRITE set_visual_v_offset)
+
+public:
+ /**
+ * Constructor.
+ */
+ TraceTreeItem();
+
+ /**
+ * Gets the owner of this item in the view item hierachy.
+ */
+ TraceTreeItemOwner* owner() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ void select(bool select = true);
+
+ /**
+ * Gets the vertical layout offset of this signal.
+ */
+ int layout_v_offset() const;
+
+ /**
+ * Sets the vertical layout offset of this signal.
+ */
+ void set_layout_v_offset(int v_offset);
+
+ /**
+ * Gets the vertical visual offset of this signal.
+ */
+ int visual_v_offset() const;
+
+ /**
+ * Sets the vertical visual offset of this signal.
+ */
+ void set_visual_v_offset(int v_offset);
+
+ /**
+ * Sets the visual and layout offset of this signal.
+ */
+ void force_to_v_offset(int v_offset);
+
+ /**
+ * Begins an animation that will animate the visual offset toward
+ * the layout offset.
+ */
+ void animate_to_layout_v_offset();
+
+ /**
+ * Sets the owner this trace in the view trace hierachy.
+ * @param The new owner of the trace.
+ */
+ void set_owner(TraceTreeItemOwner *owner);
+
+ /**
+ * Gets the visual y-offset of the axis.
+ */
+ int get_visual_y() const;
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Gets the arrow-tip point of the row item marker.
+ * @param rect the rectangle of the header area.
+ */
+ QPoint point(const QRect &rect) const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ virtual pair<int, int> v_extents() const = 0;
+
+protected:
+ TraceTreeItemOwner *owner_;
+
+ int layout_v_offset_;
+ int visual_v_offset_;
+
+private:
+ QPropertyAnimation v_offset_animation_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+
+#include "tracetreeitem.hpp"
+#include "trace.hpp"
+#include "tracetreeitemowner.hpp"
+
+using std::find;
+using std::make_pair;
+using std::max;
+using std::min;
+using std::pair;
+using std::shared_ptr;
+using std::static_pointer_cast;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const ViewItemOwner::item_list& TraceTreeItemOwner::child_items() const
+{
+ return items_;
+}
+
+vector< shared_ptr<TraceTreeItem> >
+TraceTreeItemOwner::trace_tree_child_items() const
+{
+ vector< shared_ptr<TraceTreeItem> > items;
+ for (auto &i : items_) {
+ assert(dynamic_pointer_cast<TraceTreeItem>(i));
+ const shared_ptr<TraceTreeItem> t(
+ static_pointer_cast<TraceTreeItem>(i));
+ items.push_back(t);
+ }
+
+ return items;
+}
+
+void TraceTreeItemOwner::clear_child_items()
+{
+ for (auto &t : trace_tree_child_items()) {
+ assert(t->owner() == this);
+ t->set_owner(nullptr);
+ }
+ items_.clear();
+}
+
+void TraceTreeItemOwner::add_child_item(shared_ptr<TraceTreeItem> item)
+{
+ assert(!item->owner());
+ item->set_owner(this);
+ items_.push_back(item);
+
+ extents_changed(true, true);
+}
+
+void TraceTreeItemOwner::remove_child_item(shared_ptr<TraceTreeItem> item)
+{
+ assert(item->owner() == this);
+ item->set_owner(nullptr);
+ auto iter = find(items_.begin(), items_.end(), item);
+ assert(iter != items_.end());
+ items_.erase(iter);
+
+ extents_changed(true, true);
+}
+
+pair<int, int> TraceTreeItemOwner::v_extents() const
+{
+ bool has_children = false;
+
+ pair<int, int> extents(INT_MAX, INT_MIN);
+ for (const shared_ptr<TraceTreeItem> t : trace_tree_child_items()) {
+ assert(t);
+ if (!t->enabled())
+ continue;
+
+ has_children = true;
+
+ const int child_offset = t->layout_v_offset();
+ const pair<int, int> child_extents = t->v_extents();
+ extents.first = min(child_extents.first + child_offset,
+ extents.first);
+ extents.second = max(child_extents.second + child_offset,
+ extents.second);
+ }
+
+ if (!has_children)
+ extents = make_pair(0, 0);
+
+ return extents;
+}
+
+void TraceTreeItemOwner::restack_items()
+{
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
+
+#include "viewitemowner.hpp"
+#include "tracetreeitem.hpp"
+
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+
+class Session;
+
+namespace views {
+namespace trace {
+
+class TraceTreeItem;
+class View;
+
+class TraceTreeItemOwner : public ViewItemOwner
+{
+public:
+ /**
+ * Returns the view of the owner.
+ */
+ virtual View* view() = 0;
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const View* view() const = 0;
+
+ virtual int owner_visual_v_offset() const = 0;
+
+ /**
+ * Returns the session of the owner.
+ */
+ virtual Session& session() = 0;
+
+ /**
+ * Returns the session of the owner.
+ */
+ virtual const Session& session() const = 0;
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ virtual unsigned int depth() const = 0;
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ virtual const item_list& child_items() const;
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ vector< shared_ptr<TraceTreeItem> > trace_tree_child_items() const;
+
+ /**
+ * Clears the list of child items.
+ */
+ void clear_child_items();
+
+ /**
+ * Adds a child item to this object.
+ */
+ void add_child_item(shared_ptr<TraceTreeItem> item);
+
+ /**
+ * Removes a child item from this object.
+ */
+ void remove_child_item(shared_ptr<TraceTreeItem> item);
+
+ virtual void restack_items();
+
+ /**
+ * Computes the vertical extents of the contents of this row item owner.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ pair<int, int> v_extents() const;
+
+public:
+ virtual void row_item_appearance_changed(bool label, bool content) = 0;
+
+ virtual void extents_changed(bool horz, bool vert) = 0;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "triggermarker.hpp"
+#include "view.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QColor TriggerMarker::Colour(0x00, 0x00, 0xB0);
+
+TriggerMarker::TriggerMarker(View &view, const pv::util::Timestamp& time) :
+ TimeItem(view),
+ time_(time)
+{
+}
+
+TriggerMarker::TriggerMarker(const TriggerMarker &marker) :
+ TimeItem(marker.view_),
+ time_(marker.time_)
+{
+}
+
+bool TriggerMarker::enabled() const
+{
+ return true;
+}
+
+bool TriggerMarker::is_draggable() const
+{
+ return false;
+}
+
+void TriggerMarker::set_time(const pv::util::Timestamp& time)
+{
+ time_ = time;
+
+ view_.time_item_appearance_changed(true, true);
+}
+
+float TriggerMarker::get_x() const
+{
+ return ((time_ - view_.offset()) / view_.scale()).convert_to<float>();
+}
+
+QPoint TriggerMarker::point(const QRect &rect) const
+{
+ return QPoint(get_x(), rect.bottom());
+}
+
+void TriggerMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ QPen pen(Colour);
+ pen.setStyle(Qt::DashLine);
+
+ const float x = get_x();
+ p.setPen(pen);
+ p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
+
+#include "timeitem.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class TriggerMarker : public TimeItem
+{
+ Q_OBJECT
+
+public:
+ static const QColor Colour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ * @param time The time to set the marker to.
+ */
+ TriggerMarker(View &view, const pv::util::Timestamp& time);
+
+ /**
+ * Copy constructor.
+ */
+ TriggerMarker(const TriggerMarker &marker);
+
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const override;
+
+ /**
+ Returns true if the item may be dragged/moved.
+ */
+ bool is_draggable() const override;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const override;
+
+ /**
+ * Gets the arrow-tip point of the time marker.
+ * @param rect the rectangle of the ruler area.
+ */
+ QPoint point(const QRect &rect) const override;
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, ViewItemPaintParams &pp) override;
+
+private:
+ pv::util::Timestamp time_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ENABLE_DECODE
+#include <libsigrokdecode/libsigrokdecode.h>
+#endif
+
+#include <extdef.h>
+
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <iostream>
+#include <iterator>
+#include <unordered_set>
+
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/serialization/serialization.hpp>
+
+#include <QApplication>
+#include <QEvent>
+#include <QFontMetrics>
+#include <QMouseEvent>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "analogsignal.hpp"
+#include "header.hpp"
+#include "logicsignal.hpp"
+#include "ruler.hpp"
+#include "signal.hpp"
+#include "tracegroup.hpp"
+#include "triggermarker.hpp"
+#include "view.hpp"
+#include "viewport.hpp"
+
+#include "pv/data/logic.hpp"
+#include "pv/data/logicsegment.hpp"
+#include "pv/devices/device.hpp"
+#include "pv/globalsettings.hpp"
+#include "pv/session.hpp"
+#include "pv/util.hpp"
+
+#ifdef ENABLE_DECODE
+#include "decodetrace.hpp"
+#endif
+
+using pv::data::SignalData;
+using pv::data::Segment;
+using pv::util::TimeUnit;
+using pv::util::Timestamp;
+
+using std::back_inserter;
+using std::copy_if;
+using std::count_if;
+using std::dynamic_pointer_cast;
+using std::inserter;
+using std::max;
+using std::make_pair;
+using std::make_shared;
+using std::min;
+using std::pair;
+using std::set;
+using std::set_difference;
+using std::shared_ptr;
+using std::stringstream;
+using std::unordered_map;
+using std::unordered_set;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const Timestamp View::MaxScale("1e9");
+const Timestamp View::MinScale("1e-12");
+
+const int View::MaxScrollValue = INT_MAX / 2;
+
+const int View::ScaleUnits[3] = {1, 2, 5};
+
+
+CustomScrollArea::CustomScrollArea(QWidget *parent) :
+ QAbstractScrollArea(parent)
+{
+}
+
+bool CustomScrollArea::viewportEvent(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::Paint:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::Wheel:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ return false;
+ default:
+ return QAbstractScrollArea::viewportEvent(event);
+ }
+}
+
+View::View(Session &session, bool is_main_view, QWidget *parent) :
+ ViewBase(session, is_main_view, parent),
+ splitter_(new QSplitter()),
+ scale_(1e-3),
+ offset_(0),
+ updating_scroll_(false),
+ settings_restored_(false),
+ sticky_scrolling_(false), // Default setting is set in MainWindow::setup_ui()
+ always_zoom_to_fit_(false),
+ tick_period_(0),
+ tick_prefix_(pv::util::SIPrefix::yocto),
+ tick_precision_(0),
+ time_unit_(util::TimeUnit::Time),
+ show_cursors_(false),
+ cursors_(new CursorPair(*this)),
+ next_flag_text_('A'),
+ trigger_markers_(),
+ hover_point_(-1, -1),
+ scroll_needs_defaults_(true),
+ saved_v_offset_(0)
+{
+ QVBoxLayout *root_layout = new QVBoxLayout(this);
+ root_layout->setContentsMargins(0, 0, 0, 0);
+ root_layout->addWidget(splitter_);
+
+ viewport_ = new Viewport(*this);
+ scrollarea_ = new CustomScrollArea(splitter_);
+ scrollarea_->setViewport(viewport_);
+ scrollarea_->setFrameShape(QFrame::NoFrame);
+
+ ruler_ = new Ruler(*this);
+
+ header_ = new Header(*this);
+ header_->setMinimumWidth(10); // So that the arrow tips show at least
+
+ // We put the header into a simple layout so that we can add the top margin,
+ // allowing us to make it line up with the bottom of the ruler
+ QWidget *header_container = new QWidget();
+ header_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ QVBoxLayout *header_layout = new QVBoxLayout(header_container);
+ header_layout->setContentsMargins(0, ruler_->sizeHint().height(), 0, 0);
+ header_layout->addWidget(header_);
+
+ // To let the ruler and scrollarea be on the same split pane, we need a layout
+ QWidget *trace_container = new QWidget();
+ trace_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ QVBoxLayout *trace_layout = new QVBoxLayout(trace_container);
+ trace_layout->setSpacing(0); // We don't want space between the ruler and scrollarea
+ trace_layout->setContentsMargins(0, 0, 0, 0);
+ trace_layout->addWidget(ruler_);
+ trace_layout->addWidget(scrollarea_);
+
+ splitter_->addWidget(header_container);
+ splitter_->addWidget(trace_container);
+ splitter_->setHandleWidth(1); // Don't show a visible rubber band
+ splitter_->setCollapsible(0, false); // Prevent the header from collapsing
+ splitter_->setCollapsible(1, false); // Prevent the traces from collapsing
+ splitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+ viewport_->installEventFilter(this);
+ ruler_->installEventFilter(this);
+ header_->installEventFilter(this);
+
+ // Set up settings and event handlers
+ GlobalSettings settings;
+ coloured_bg_ = settings.value(GlobalSettings::Key_View_ColouredBG).toBool();
+
+ connect(scrollarea_->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(h_scroll_value_changed(int)));
+ connect(scrollarea_->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(v_scroll_value_changed()));
+
+ connect(header_, SIGNAL(selection_changed()),
+ ruler_, SLOT(clear_selection()));
+ connect(ruler_, SIGNAL(selection_changed()),
+ header_, SLOT(clear_selection()));
+
+ connect(header_, SIGNAL(selection_changed()),
+ this, SIGNAL(selection_changed()));
+ connect(ruler_, SIGNAL(selection_changed()),
+ this, SIGNAL(selection_changed()));
+
+ connect(splitter_, SIGNAL(splitterMoved(int, int)),
+ this, SLOT(on_splitter_moved()));
+
+ connect(this, SIGNAL(hover_point_changed()),
+ this, SLOT(on_hover_point_changed()));
+
+ connect(&lazy_event_handler_, SIGNAL(timeout()),
+ this, SLOT(process_sticky_events()));
+ lazy_event_handler_.setSingleShot(true);
+
+ // Trigger the initial event manually. The default device has signals
+ // which were created before this object came into being
+ signals_changed();
+
+ // make sure the transparent widgets are on the top
+ ruler_->raise();
+ header_->raise();
+
+ // Update the zoom state
+ calculate_tick_spacing();
+}
+
+Session& View::session()
+{
+ return session_;
+}
+
+const Session& View::session() const
+{
+ return session_;
+}
+
+unordered_set< shared_ptr<Signal> > View::signals() const
+{
+ return signals_;
+}
+
+void View::clear_signals()
+{
+ ViewBase::clear_signalbases();
+ signals_.clear();
+}
+
+void View::add_signal(const shared_ptr<Signal> signal)
+{
+ ViewBase::add_signalbase(signal->base());
+ signals_.insert(signal);
+}
+
+#ifdef ENABLE_DECODE
+void View::clear_decode_signals()
+{
+ decode_traces_.clear();
+}
+
+void View::add_decode_signal(shared_ptr<data::SignalBase> signalbase)
+{
+ shared_ptr<DecodeTrace> d(
+ new DecodeTrace(session_, signalbase, decode_traces_.size()));
+ decode_traces_.push_back(d);
+}
+
+void View::remove_decode_signal(shared_ptr<data::SignalBase> signalbase)
+{
+ for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++)
+ if ((*i)->base() == signalbase) {
+ decode_traces_.erase(i);
+ signals_changed();
+ return;
+ }
+}
+#endif
+
+View* View::view()
+{
+ return this;
+}
+
+const View* View::view() const
+{
+ return this;
+}
+
+Viewport* View::viewport()
+{
+ return viewport_;
+}
+
+const Viewport* View::viewport() const
+{
+ return viewport_;
+}
+
+void View::save_settings(QSettings &settings) const
+{
+ settings.setValue("scale", scale_);
+ settings.setValue("v_offset",
+ scrollarea_->verticalScrollBar()->sliderPosition());
+
+ settings.setValue("splitter_state", splitter_->saveState());
+
+ stringstream ss;
+ boost::archive::text_oarchive oa(ss);
+ oa << boost::serialization::make_nvp("offset", offset_);
+ settings.setValue("offset", QString::fromStdString(ss.str()));
+
+ for (shared_ptr<Signal> signal : signals_) {
+ settings.beginGroup(signal->base()->internal_name());
+ signal->save_settings(settings);
+ settings.endGroup();
+ }
+}
+
+void View::restore_settings(QSettings &settings)
+{
+ // Note: It is assumed that this function is only called once,
+ // immediately after restoring a previous session.
+
+ if (settings.contains("scale"))
+ set_scale(settings.value("scale").toDouble());
+
+ if (settings.contains("offset")) {
+ util::Timestamp offset;
+ stringstream ss;
+ ss << settings.value("offset").toString().toStdString();
+
+ boost::archive::text_iarchive ia(ss);
+ ia >> boost::serialization::make_nvp("offset", offset);
+
+ set_offset(offset);
+ }
+
+ if (settings.contains("splitter_state"))
+ splitter_->restoreState(settings.value("splitter_state").toByteArray());
+
+ for (shared_ptr<Signal> signal : signals_) {
+ settings.beginGroup(signal->base()->internal_name());
+ signal->restore_settings(settings);
+ settings.endGroup();
+ }
+
+ if (settings.contains("v_offset")) {
+ saved_v_offset_ = settings.value("v_offset").toInt();
+ set_v_offset(saved_v_offset_);
+ scroll_needs_defaults_ = false;
+ // Note: see eventFilter() for additional information
+ }
+
+ settings_restored_ = true;
+}
+
+vector< shared_ptr<TimeItem> > View::time_items() const
+{
+ const vector<shared_ptr<Flag>> f(flags());
+ vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
+ items.push_back(cursors_);
+ items.push_back(cursors_->first());
+ items.push_back(cursors_->second());
+
+ for (auto trigger_marker : trigger_markers_)
+ items.push_back(trigger_marker);
+
+ return items;
+}
+
+double View::scale() const
+{
+ return scale_;
+}
+
+void View::set_scale(double scale)
+{
+ if (scale_ != scale) {
+ scale_ = scale;
+ scale_changed();
+ }
+}
+
+const Timestamp& View::offset() const
+{
+ return offset_;
+}
+
+void View::set_offset(const pv::util::Timestamp& offset)
+{
+ if (offset_ != offset) {
+ offset_ = offset;
+ offset_changed();
+ }
+}
+
+int View::owner_visual_v_offset() const
+{
+ return -scrollarea_->verticalScrollBar()->sliderPosition();
+}
+
+void View::set_v_offset(int offset)
+{
+ scrollarea_->verticalScrollBar()->setSliderPosition(offset);
+ header_->update();
+ viewport_->update();
+}
+
+unsigned int View::depth() const
+{
+ return 0;
+}
+
+pv::util::SIPrefix View::tick_prefix() const
+{
+ return tick_prefix_;
+}
+
+void View::set_tick_prefix(pv::util::SIPrefix tick_prefix)
+{
+ if (tick_prefix_ != tick_prefix) {
+ tick_prefix_ = tick_prefix;
+ tick_prefix_changed();
+ }
+}
+
+unsigned int View::tick_precision() const
+{
+ return tick_precision_;
+}
+
+void View::set_tick_precision(unsigned tick_precision)
+{
+ if (tick_precision_ != tick_precision) {
+ tick_precision_ = tick_precision;
+ tick_precision_changed();
+ }
+}
+
+const pv::util::Timestamp& View::tick_period() const
+{
+ return tick_period_;
+}
+
+void View::set_tick_period(const pv::util::Timestamp& tick_period)
+{
+ if (tick_period_ != tick_period) {
+ tick_period_ = tick_period;
+ tick_period_changed();
+ }
+}
+
+TimeUnit View::time_unit() const
+{
+ return time_unit_;
+}
+
+void View::set_time_unit(pv::util::TimeUnit time_unit)
+{
+ if (time_unit_ != time_unit) {
+ time_unit_ = time_unit;
+ time_unit_changed();
+ }
+}
+
+void View::zoom(double steps)
+{
+ zoom(steps, viewport_->width() / 2);
+}
+
+void View::zoom(double steps, int offset)
+{
+ set_zoom(scale_ * pow(3.0 / 2.0, -steps), offset);
+}
+
+void View::zoom_fit(bool gui_state)
+{
+ // Act as one-shot when stopped, toggle along with the GUI otherwise
+ if (session_.get_capture_state() == Session::Stopped) {
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+ } else {
+ always_zoom_to_fit_ = gui_state;
+ always_zoom_to_fit_changed(gui_state);
+ }
+
+ const pair<Timestamp, Timestamp> extents = get_time_extents();
+ const Timestamp delta = extents.second - extents.first;
+ if (delta < Timestamp("1e-12"))
+ return;
+
+ assert(viewport_);
+ const int w = viewport_->width();
+ if (w <= 0)
+ return;
+
+ const Timestamp scale = max(min(delta / w, MaxScale), MinScale);
+ set_scale_offset(scale.convert_to<double>(), extents.first);
+}
+
+void View::zoom_one_to_one()
+{
+ using pv::data::SignalData;
+
+ // Make a set of all the visible data objects
+ set< shared_ptr<SignalData> > visible_data = get_visible_data();
+ if (visible_data.empty())
+ return;
+
+ assert(viewport_);
+ const int w = viewport_->width();
+ if (w <= 0)
+ return;
+
+ set_zoom(1.0 / session_.get_samplerate(), w / 2);
+}
+
+void View::set_scale_offset(double scale, const Timestamp& offset)
+{
+ // Disable sticky scrolling / always zoom to fit when acquisition runs
+ // and user drags the viewport
+ if ((scale_ == scale) && (offset_ != offset) &&
+ (session_.get_capture_state() == Session::Running)) {
+
+ if (sticky_scrolling_) {
+ sticky_scrolling_ = false;
+ sticky_scrolling_changed(false);
+ }
+
+ if (always_zoom_to_fit_) {
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+ }
+ }
+
+ set_scale(scale);
+ set_offset(offset);
+
+ calculate_tick_spacing();
+
+ update_scroll();
+ ruler_->update();
+ viewport_->update();
+}
+
+set< shared_ptr<SignalData> > View::get_visible_data() const
+{
+ // Make a set of all the visible data objects
+ set< shared_ptr<SignalData> > visible_data;
+ for (const shared_ptr<Signal> sig : signals_)
+ if (sig->enabled())
+ visible_data.insert(sig->data());
+
+ return visible_data;
+}
+
+pair<Timestamp, Timestamp> View::get_time_extents() const
+{
+ boost::optional<Timestamp> left_time, right_time;
+ const set< shared_ptr<SignalData> > visible_data = get_visible_data();
+ for (const shared_ptr<SignalData> d : visible_data) {
+ const vector< shared_ptr<Segment> > segments = d->segments();
+ for (const shared_ptr<Segment> &s : segments) {
+ double samplerate = s->samplerate();
+ samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
+
+ const Timestamp start_time = s->start_time();
+ left_time = left_time ?
+ min(*left_time, start_time) :
+ start_time;
+ right_time = right_time ?
+ max(*right_time, start_time + d->max_sample_count() / samplerate) :
+ start_time + d->max_sample_count() / samplerate;
+ }
+ }
+
+ if (!left_time || !right_time)
+ return make_pair(0, 0);
+
+ assert(*left_time < *right_time);
+ return make_pair(*left_time, *right_time);
+}
+
+void View::enable_show_sampling_points(bool state)
+{
+ (void)state;
+
+ viewport_->update();
+}
+
+void View::enable_show_analog_minor_grid(bool state)
+{
+ (void)state;
+
+ viewport_->update();
+}
+
+void View::enable_coloured_bg(bool state)
+{
+ coloured_bg_ = state;
+ viewport_->update();
+}
+
+bool View::coloured_bg() const
+{
+ return coloured_bg_;
+}
+
+bool View::cursors_shown() const
+{
+ return show_cursors_;
+}
+
+void View::show_cursors(bool show)
+{
+ show_cursors_ = show;
+ ruler_->update();
+ viewport_->update();
+}
+
+void View::centre_cursors()
+{
+ const double time_width = scale_ * viewport_->width();
+ cursors_->first()->set_time(offset_ + time_width * 0.4);
+ cursors_->second()->set_time(offset_ + time_width * 0.6);
+ ruler_->update();
+ viewport_->update();
+}
+
+shared_ptr<CursorPair> View::cursors() const
+{
+ return cursors_;
+}
+
+void View::add_flag(const Timestamp& time)
+{
+ flags_.push_back(make_shared<Flag>(*this, time,
+ QString("%1").arg(next_flag_text_)));
+
+ next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
+ (next_flag_text_ + 1);
+
+ time_item_appearance_changed(true, true);
+}
+
+void View::remove_flag(shared_ptr<Flag> flag)
+{
+ flags_.remove(flag);
+ time_item_appearance_changed(true, true);
+}
+
+vector< shared_ptr<Flag> > View::flags() const
+{
+ vector< shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
+ stable_sort(flags.begin(), flags.end(),
+ [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
+ return a->time() < b->time();
+ });
+
+ return flags;
+}
+
+const QPoint& View::hover_point() const
+{
+ return hover_point_;
+}
+
+void View::restack_all_trace_tree_items()
+{
+ // Make a list of owners that is sorted from deepest first
+ const vector<shared_ptr<TraceTreeItem>> items(
+ list_by_type<TraceTreeItem>());
+ set< TraceTreeItemOwner* > owners;
+ for (const auto &r : items)
+ owners.insert(r->owner());
+ vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end());
+ sort(sorted_owners.begin(), sorted_owners.end(),
+ [](const TraceTreeItemOwner* a, const TraceTreeItemOwner *b) {
+ return a->depth() > b->depth(); });
+
+ // Restack the items recursively
+ for (auto &o : sorted_owners)
+ o->restack_items();
+
+ // Animate the items to their destination
+ for (const auto &i : items)
+ i->animate_to_layout_v_offset();
+}
+
+void View::trigger_event(util::Timestamp location)
+{
+ trigger_markers_.push_back(make_shared<TriggerMarker>(*this, location));
+}
+
+void View::get_scroll_layout(double &length, Timestamp &offset) const
+{
+ const pair<Timestamp, Timestamp> extents = get_time_extents();
+ length = ((extents.second - extents.first) / scale_).convert_to<double>();
+ offset = offset_ / scale_;
+}
+
+void View::set_zoom(double scale, int offset)
+{
+ // Reset the "always zoom to fit" feature as the user changed the zoom
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+
+ const Timestamp cursor_offset = offset_ + scale_ * offset;
+ const Timestamp new_scale = max(min(Timestamp(scale), MaxScale), MinScale);
+ const Timestamp new_offset = cursor_offset - new_scale * offset;
+ set_scale_offset(new_scale.convert_to<double>(), new_offset);
+}
+
+void View::calculate_tick_spacing()
+{
+ const double SpacingIncrement = 10.0f;
+ const double MinValueSpacing = 40.0f;
+
+ // Figure out the highest numeric value visible on a label
+ const QSize areaSize = viewport_->size();
+ const Timestamp max_time = max(fabs(offset_),
+ fabs(offset_ + scale_ * areaSize.width()));
+
+ double min_width = SpacingIncrement;
+ double label_width, tick_period_width;
+
+ QFontMetrics m(QApplication::font());
+
+ // Copies of the member variables with the same name, used in the calculation
+ // and written back afterwards, so that we don't emit signals all the time
+ // during the calculation.
+ pv::util::Timestamp tick_period = tick_period_;
+ pv::util::SIPrefix tick_prefix = tick_prefix_;
+ unsigned tick_precision = tick_precision_;
+
+ do {
+ const double min_period = scale_ * min_width;
+
+ const int order = (int)floorf(log10f(min_period));
+ const pv::util::Timestamp order_decimal =
+ pow(pv::util::Timestamp(10), order);
+
+ // Allow for a margin of error so that a scale unit of 1 can be used.
+ // Otherwise, for a SU of 1 the tick period will almost always be below
+ // the min_period by a small amount - and thus skipped in favor of 2.
+ // Note: margin assumes that SU[0] and SU[1] contain the smallest values
+ double tp_margin = (ScaleUnits[0] + ScaleUnits[1]) / 2.0;
+ double tp_with_margin;
+ unsigned int unit = 0;
+
+ do {
+ tp_with_margin = order_decimal.convert_to<double>() *
+ (ScaleUnits[unit++] + tp_margin);
+ } while (tp_with_margin < min_period && unit < countof(ScaleUnits));
+
+ tick_period = order_decimal * ScaleUnits[unit - 1];
+ tick_prefix = static_cast<pv::util::SIPrefix>(
+ (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3);
+
+ // Precision is the number of fractional digits required, not
+ // taking the prefix into account (and it must never be negative)
+ tick_precision = max(ceil(log10(1 / tick_period)).convert_to<int>(), 0);
+
+ tick_period_width = (tick_period / scale_).convert_to<double>();
+
+ const QString label_text = Ruler::format_time_with_distance(
+ tick_period, max_time, tick_prefix, time_unit_, tick_precision);
+
+ label_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
+ Qt::AlignLeft | Qt::AlignTop, label_text).width() +
+ MinValueSpacing;
+
+ min_width += SpacingIncrement;
+ } while (tick_period_width < label_width);
+
+ set_tick_period(tick_period);
+ set_tick_prefix(tick_prefix);
+ set_tick_precision(tick_precision);
+}
+
+void View::adjust_top_margin()
+{
+ assert(viewport_);
+
+ const QSize areaSize = viewport_->size();
+
+ const pair<int, int> extents = v_extents();
+ const int top_margin = owner_visual_v_offset() + extents.first;
+ const int trace_bottom = owner_visual_v_offset() + extents.first + extents.second;
+
+ // Do we have empty space at the top while the last trace goes out of screen?
+ if ((top_margin > 0) && (trace_bottom > areaSize.height())) {
+ const int trace_height = extents.second - extents.first;
+
+ // Center everything vertically if there is enough space
+ if (areaSize.height() >= trace_height)
+ set_v_offset(extents.first -
+ ((areaSize.height() - trace_height) / 2));
+ else
+ // Remove the top margin to make as many traces fit on screen as possible
+ set_v_offset(extents.first);
+ }
+}
+
+void View::update_scroll()
+{
+ assert(viewport_);
+ QScrollBar *hscrollbar = scrollarea_->horizontalScrollBar();
+ QScrollBar *vscrollbar = scrollarea_->verticalScrollBar();
+
+ const QSize areaSize = viewport_->size();
+
+ // Set the horizontal scroll bar
+ double length = 0;
+ Timestamp offset;
+ get_scroll_layout(length, offset);
+ length = max(length - areaSize.width(), 0.0);
+
+ int major_tick_distance = (tick_period_ / scale_).convert_to<int>();
+
+ hscrollbar->setPageStep(areaSize.width() / 2);
+ hscrollbar->setSingleStep(major_tick_distance);
+
+ updating_scroll_ = true;
+
+ if (length < MaxScrollValue) {
+ hscrollbar->setRange(0, length);
+ hscrollbar->setSliderPosition(offset.convert_to<double>());
+ } else {
+ hscrollbar->setRange(0, MaxScrollValue);
+ hscrollbar->setSliderPosition(
+ (offset_ * MaxScrollValue / (scale_ * length)).convert_to<double>());
+ }
+
+ updating_scroll_ = false;
+
+ // Set the vertical scrollbar
+ vscrollbar->setPageStep(areaSize.height());
+ vscrollbar->setSingleStep(areaSize.height() / 8);
+
+ const pair<int, int> extents = v_extents();
+
+ // Don't change the scrollbar range if there are no traces
+ if (extents.first != extents.second)
+ vscrollbar->setRange(extents.first - areaSize.height(),
+ extents.second);
+
+ if (scroll_needs_defaults_)
+ set_scroll_default();
+}
+
+void View::reset_scroll()
+{
+ scrollarea_->verticalScrollBar()->setRange(0, 0);
+}
+
+void View::set_scroll_default()
+{
+ assert(viewport_);
+
+ const QSize areaSize = viewport_->size();
+
+ const pair<int, int> extents = v_extents();
+ const int trace_height = extents.second - extents.first;
+
+ // Do all traces fit in the view?
+ if (areaSize.height() >= trace_height)
+ // Center all traces vertically
+ set_v_offset(extents.first -
+ ((areaSize.height() - trace_height) / 2));
+ else
+ // Put the first trace at the top, letting the bottom ones overflow
+ set_v_offset(extents.first);
+}
+
+bool View::header_was_shrunk() const
+{
+ const int header_pane_width = splitter_->sizes().front();
+ const int header_width = header_->extended_size_hint().width();
+
+ // Allow for a slight margin of error so that we also accept
+ // slight differences when e.g. a label name change increased
+ // the overall width
+ return (header_pane_width < (header_width - 10));
+}
+
+void View::expand_header_to_fit()
+{
+ int splitter_area_width = 0;
+ for (int w : splitter_->sizes())
+ splitter_area_width += w;
+
+ // Make sure the header has enough horizontal space to show all labels fully
+ QList<int> pane_sizes;
+ pane_sizes.push_back(header_->extended_size_hint().width());
+ pane_sizes.push_back(splitter_area_width - header_->extended_size_hint().width());
+ splitter_->setSizes(pane_sizes);
+}
+
+void View::update_layout()
+{
+ update_scroll();
+}
+
+TraceTreeItemOwner* View::find_prevalent_trace_group(
+ const shared_ptr<sigrok::ChannelGroup> &group,
+ const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
+ &signal_map)
+{
+ assert(group);
+
+ unordered_set<TraceTreeItemOwner*> owners;
+ vector<TraceTreeItemOwner*> owner_list;
+
+ // Make a set and a list of all the owners
+ for (const auto &channel : group->channels()) {
+ for (auto entry : signal_map) {
+ if (entry.first->channel() == channel) {
+ TraceTreeItemOwner *const o = (entry.second)->owner();
+ owner_list.push_back(o);
+ owners.insert(o);
+ }
+ }
+ }
+
+ // Iterate through the list of owners, and find the most prevalent
+ size_t max_prevalence = 0;
+ TraceTreeItemOwner *prevalent_owner = nullptr;
+ for (TraceTreeItemOwner *owner : owners) {
+ const size_t prevalence = count_if(
+ owner_list.begin(), owner_list.end(),
+ [&](TraceTreeItemOwner *o) { return o == owner; });
+ if (prevalence > max_prevalence) {
+ max_prevalence = prevalence;
+ prevalent_owner = owner;
+ }
+ }
+
+ return prevalent_owner;
+}
+
+vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
+ const vector< shared_ptr<sigrok::Channel> > &channels,
+ const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
+ &signal_map,
+ set< shared_ptr<Trace> > &add_list)
+{
+ vector< shared_ptr<Trace> > filtered_traces;
+
+ for (const auto &channel : channels) {
+ for (auto entry : signal_map) {
+ if (entry.first->channel() == channel) {
+ shared_ptr<Trace> trace = entry.second;
+ const auto list_iter = add_list.find(trace);
+ if (list_iter == add_list.end())
+ continue;
+
+ filtered_traces.push_back(trace);
+ add_list.erase(list_iter);
+ }
+ }
+ }
+
+ return filtered_traces;
+}
+
+void View::determine_time_unit()
+{
+ // Check whether we know the sample rate and hence can use time as the unit
+ if (time_unit_ == util::TimeUnit::Samples) {
+ // Check all signals but...
+ for (const shared_ptr<Signal> signal : signals_) {
+ const shared_ptr<SignalData> data = signal->data();
+
+ // ...only check first segment of each
+ const vector< shared_ptr<Segment> > segments = data->segments();
+ if (!segments.empty())
+ if (segments[0]->samplerate()) {
+ set_time_unit(util::TimeUnit::Time);
+ break;
+ }
+ }
+ }
+}
+
+bool View::eventFilter(QObject *object, QEvent *event)
+{
+ const QEvent::Type type = event->type();
+ if (type == QEvent::MouseMove) {
+
+ const QMouseEvent *const mouse_event = (QMouseEvent*)event;
+ if (object == viewport_)
+ hover_point_ = mouse_event->pos();
+ else if (object == ruler_)
+ hover_point_ = QPoint(mouse_event->x(), 0);
+ else if (object == header_)
+ hover_point_ = QPoint(0, mouse_event->y());
+ else
+ hover_point_ = QPoint(-1, -1);
+
+ hover_point_changed();
+
+ } else if (type == QEvent::Leave) {
+ hover_point_ = QPoint(-1, -1);
+ hover_point_changed();
+ } else if (type == QEvent::Show) {
+
+ // This is somewhat of a hack, unfortunately. We cannot use
+ // set_v_offset() from within restore_settings() as the view
+ // at that point is neither visible nor properly sized.
+ // This is the least intrusive workaround I could come up
+ // with: set the vertical offset (or scroll defaults) when
+ // the view is shown, which happens after all widgets were
+ // resized to their final sizes.
+ update_layout();
+
+ if (!settings_restored_)
+ expand_header_to_fit();
+
+ if (scroll_needs_defaults_) {
+ set_scroll_default();
+ scroll_needs_defaults_ = false;
+ }
+
+ if (saved_v_offset_) {
+ set_v_offset(saved_v_offset_);
+ saved_v_offset_ = 0;
+ }
+ }
+
+ return QObject::eventFilter(object, event);
+}
+
+void View::resizeEvent(QResizeEvent* event)
+{
+ // Only adjust the top margin if we shrunk vertically
+ if (event->size().height() < event->oldSize().height())
+ adjust_top_margin();
+
+ update_layout();
+}
+
+void View::row_item_appearance_changed(bool label, bool content)
+{
+ if (label)
+ header_->update();
+ if (content)
+ viewport_->update();
+}
+
+void View::time_item_appearance_changed(bool label, bool content)
+{
+ if (label) {
+ ruler_->update();
+
+ // Make sure the header pane width is updated, too
+ update_layout();
+ }
+
+ if (content)
+ viewport_->update();
+}
+
+void View::extents_changed(bool horz, bool vert)
+{
+ sticky_events_ |=
+ (horz ? TraceTreeItemHExtentsChanged : 0) |
+ (vert ? TraceTreeItemVExtentsChanged : 0);
+
+ lazy_event_handler_.start();
+}
+
+void View::on_splitter_moved()
+{
+ // Setting the maximum width of the header widget doesn't work as
+ // expected because the splitter would allow the user to make the
+ // pane wider than that, creating empty space as a result.
+ // To make this work, we stricly enforce the maximum width by
+ // expanding the header unless the user shrunk it on purpose.
+ // As we're then setting the width of the header pane, we set the
+ // splitter to the maximum allowed position.
+ if (!header_was_shrunk())
+ expand_header_to_fit();
+}
+
+void View::h_scroll_value_changed(int value)
+{
+ if (updating_scroll_)
+ return;
+
+ // Disable sticky scrolling when user moves the horizontal scroll bar
+ // during a running acquisition
+ if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) {
+ sticky_scrolling_ = false;
+ sticky_scrolling_changed(false);
+ }
+
+ const int range = scrollarea_->horizontalScrollBar()->maximum();
+ if (range < MaxScrollValue)
+ set_offset(scale_ * value);
+ else {
+ double length = 0;
+ Timestamp offset;
+ get_scroll_layout(length, offset);
+ set_offset(scale_ * length * value / MaxScrollValue);
+ }
+
+ ruler_->update();
+ viewport_->update();
+}
+
+void View::v_scroll_value_changed()
+{
+ header_->update();
+ viewport_->update();
+}
+
+void View::signals_changed()
+{
+ using sigrok::Channel;
+
+ vector< shared_ptr<Channel> > channels;
+ shared_ptr<sigrok::Device> sr_dev;
+
+ // Do we need to set the vertical scrollbar to its default position later?
+ // We do if there are no traces, i.e. the scroll bar has no range set
+ bool reset_scrollbar =
+ (scrollarea_->verticalScrollBar()->minimum() ==
+ scrollarea_->verticalScrollBar()->maximum());
+
+ if (!session_.device()) {
+ reset_scroll();
+ signals_.clear();
+ } else {
+ sr_dev = session_.device()->device();
+ assert(sr_dev);
+ channels = sr_dev->channels();
+ }
+
+ vector< shared_ptr<TraceTreeItem> > new_top_level_items;
+
+ // Make a list of traces that are being added, and a list of traces
+ // that are being removed
+ const vector<shared_ptr<Trace>> prev_trace_list = list_by_type<Trace>();
+ const set<shared_ptr<Trace>> prev_traces(
+ prev_trace_list.begin(), prev_trace_list.end());
+
+ set< shared_ptr<Trace> > traces(signals_.begin(), signals_.end());
+
+#ifdef ENABLE_DECODE
+ traces.insert(decode_traces_.begin(), decode_traces_.end());
+#endif
+
+ set< shared_ptr<Trace> > add_traces;
+ set_difference(traces.begin(), traces.end(),
+ prev_traces.begin(), prev_traces.end(),
+ inserter(add_traces, add_traces.begin()));
+
+ set< shared_ptr<Trace> > remove_traces;
+ set_difference(prev_traces.begin(), prev_traces.end(),
+ traces.begin(), traces.end(),
+ inserter(remove_traces, remove_traces.begin()));
+
+ // Make a look-up table of sigrok Channels to pulseview Signals
+ unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
+ signal_map;
+ for (const shared_ptr<Signal> &sig : signals_)
+ signal_map[sig->base()] = sig;
+
+ // Populate channel groups
+ if (sr_dev)
+ for (auto entry : sr_dev->channel_groups()) {
+ const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
+
+ if (group->channels().size() <= 1)
+ continue;
+
+ // Find best trace group to add to
+ TraceTreeItemOwner *owner = find_prevalent_trace_group(
+ group, signal_map);
+
+ // If there is no trace group, create one
+ shared_ptr<TraceGroup> new_trace_group;
+ if (!owner) {
+ new_trace_group.reset(new TraceGroup());
+ owner = new_trace_group.get();
+ }
+
+ // Extract traces for the trace group, removing them from
+ // the add list
+ const vector< shared_ptr<Trace> > new_traces_in_group =
+ extract_new_traces_for_channels(group->channels(),
+ signal_map, add_traces);
+
+ // Add the traces to the group
+ const pair<int, int> prev_v_extents = owner->v_extents();
+ int offset = prev_v_extents.second - prev_v_extents.first;
+ for (shared_ptr<Trace> trace : new_traces_in_group) {
+ assert(trace);
+ owner->add_child_item(trace);
+
+ const pair<int, int> extents = trace->v_extents();
+ if (trace->enabled())
+ offset += -extents.first;
+ trace->force_to_v_offset(offset);
+ if (trace->enabled())
+ offset += extents.second;
+ }
+
+ if (new_trace_group) {
+ // Assign proper vertical offsets to each channel in the group
+ new_trace_group->restack_items();
+
+ // If this is a new group, enqueue it in the new top level
+ // items list
+ if (!new_traces_in_group.empty())
+ new_top_level_items.push_back(new_trace_group);
+ }
+ }
+
+ // Enqueue the remaining logic channels in a group
+ vector< shared_ptr<Channel> > logic_channels;
+ copy_if(channels.begin(), channels.end(), back_inserter(logic_channels),
+ [](const shared_ptr<Channel>& c) {
+ return c->type() == sigrok::ChannelType::LOGIC; });
+
+ const vector< shared_ptr<Trace> > non_grouped_logic_signals =
+ extract_new_traces_for_channels(logic_channels, signal_map, add_traces);
+
+ if (non_grouped_logic_signals.size() > 0) {
+ const shared_ptr<TraceGroup> non_grouped_trace_group(
+ make_shared<TraceGroup>());
+ for (shared_ptr<Trace> trace : non_grouped_logic_signals)
+ non_grouped_trace_group->add_child_item(trace);
+
+ non_grouped_trace_group->restack_items();
+ new_top_level_items.push_back(non_grouped_trace_group);
+ }
+
+ // Enqueue the remaining channels as free ungrouped traces
+ const vector< shared_ptr<Trace> > new_top_level_signals =
+ extract_new_traces_for_channels(channels, signal_map, add_traces);
+ new_top_level_items.insert(new_top_level_items.end(),
+ new_top_level_signals.begin(), new_top_level_signals.end());
+
+ // Enqueue any remaining traces i.e. decode traces
+ new_top_level_items.insert(new_top_level_items.end(),
+ add_traces.begin(), add_traces.end());
+
+ // Remove any removed traces
+ for (shared_ptr<Trace> trace : remove_traces) {
+ TraceTreeItemOwner *const owner = trace->owner();
+ assert(owner);
+ owner->remove_child_item(trace);
+ }
+
+ // Remove any empty trace groups
+ for (shared_ptr<TraceGroup> group : list_by_type<TraceGroup>())
+ if (group->child_items().size() == 0) {
+ remove_child_item(group);
+ group.reset();
+ }
+
+ // Add and position the pending top levels items
+ int offset = v_extents().second;
+ for (auto item : new_top_level_items) {
+ add_child_item(item);
+
+ // Position the item after the last item or at the top if there is none
+ const pair<int, int> extents = item->v_extents();
+
+ if (item->enabled())
+ offset += -extents.first;
+
+ item->force_to_v_offset(offset);
+
+ if (item->enabled())
+ offset += extents.second;
+ }
+
+
+ if (!new_top_level_items.empty())
+ // Expand the header pane because the header should become fully
+ // visible when new signals are added
+ expand_header_to_fit();
+
+ update_layout();
+
+ header_->update();
+ viewport_->update();
+
+ if (reset_scrollbar)
+ set_scroll_default();
+}
+
+void View::capture_state_updated(int state)
+{
+ if (state == Session::Running) {
+ set_time_unit(util::TimeUnit::Samples);
+
+ trigger_markers_.clear();
+
+ // Activate "always zoom to fit" if the setting is enabled and we're
+ // the main view of this session (other trace views may be used for
+ // zooming and we don't want to mess them up)
+ GlobalSettings settings;
+ bool state = settings.value(GlobalSettings::Key_View_AlwaysZoomToFit).toBool();
+ if (is_main_view_ && state) {
+ always_zoom_to_fit_ = true;
+ always_zoom_to_fit_changed(always_zoom_to_fit_);
+ }
+
+ // Enable sticky scrolling if the setting is enabled
+ sticky_scrolling_ = settings.value(GlobalSettings::Key_View_StickyScrolling).toBool();
+ }
+
+ if (state == Session::Stopped) {
+ // After acquisition has stopped we need to re-calculate the ticks once
+ // as it's otherwise done when the user pans or zooms, which is too late
+ calculate_tick_spacing();
+
+ // Reset "always zoom to fit", the acquisition has stopped
+ if (always_zoom_to_fit_) {
+ // Perform a final zoom-to-fit before disabling
+ zoom_fit(always_zoom_to_fit_);
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(always_zoom_to_fit_);
+ }
+ }
+}
+
+void View::perform_delayed_view_update()
+{
+ if (always_zoom_to_fit_) {
+ zoom_fit(true);
+ } else if (sticky_scrolling_) {
+ // Make right side of the view sticky
+ double length = 0;
+ Timestamp offset;
+ get_scroll_layout(length, offset);
+
+ const QSize areaSize = viewport_->size();
+ length = max(length - areaSize.width(), 0.0);
+
+ set_offset(scale_ * length);
+ }
+
+ determine_time_unit();
+ update_scroll();
+ ruler_->update();
+ viewport_->update();
+}
+
+void View::process_sticky_events()
+{
+ if (sticky_events_ & TraceTreeItemHExtentsChanged)
+ update_layout();
+ if (sticky_events_ & TraceTreeItemVExtentsChanged) {
+ restack_all_trace_tree_items();
+ update_scroll();
+ }
+
+ // Clear the sticky events
+ sticky_events_ = 0;
+}
+
+void View::on_hover_point_changed()
+{
+ const vector<shared_ptr<TraceTreeItem>> trace_tree_items(
+ list_by_type<TraceTreeItem>());
+ for (shared_ptr<TraceTreeItem> r : trace_tree_items)
+ r->hover_point_changed();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
+
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include <QAbstractScrollArea>
+#include <QSizeF>
+#include <QSplitter>
+
+#include <pv/data/signaldata.hpp>
+#include <pv/util.hpp>
+#include <pv/views/viewbase.hpp>
+
+#include "cursorpair.hpp"
+#include "flag.hpp"
+#include "tracetreeitemowner.hpp"
+
+using std::list;
+using std::unordered_map;
+using std::unordered_set;
+using std::set;
+using std::shared_ptr;
+using std::vector;
+
+namespace sigrok {
+class ChannelGroup;
+}
+
+namespace pv {
+
+class Session;
+
+namespace data {
+class Logic;
+}
+
+namespace views {
+
+namespace trace {
+
+class CursorHeader;
+class DecodeTrace;
+class Header;
+class Ruler;
+class Signal;
+class Trace;
+class Viewport;
+class TriggerMarker;
+
+class CustomScrollArea : public QAbstractScrollArea
+{
+ Q_OBJECT
+
+public:
+ CustomScrollArea(QWidget *parent = nullptr);
+ bool viewportEvent(QEvent *event);
+};
+
+class View : public ViewBase, public TraceTreeItemOwner
+{
+ Q_OBJECT
+
+private:
+ enum StickyEvents {
+ TraceTreeItemHExtentsChanged = 1,
+ TraceTreeItemVExtentsChanged = 2
+ };
+
+private:
+ static const pv::util::Timestamp MaxScale;
+ static const pv::util::Timestamp MinScale;
+
+ static const int MaxScrollValue;
+
+ static const int ScaleUnits[3];
+
+public:
+ explicit View(Session &session, bool is_main_view=false, QWidget *parent = nullptr);
+
+ Session& session();
+ const Session& session() const;
+
+ /**
+ * Returns the signals contained in this view.
+ */
+ unordered_set< shared_ptr<Signal> > signals() const;
+
+ virtual void clear_signals();
+
+ void add_signal(const shared_ptr<Signal> signal);
+
+#ifdef ENABLE_DECODE
+ virtual void clear_decode_signals();
+
+ virtual void add_decode_signal(shared_ptr<data::SignalBase> signalbase);
+
+ virtual void remove_decode_signal(shared_ptr<data::SignalBase> signalbase);
+#endif
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual View* view();
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const View* view() const;
+
+ Viewport* viewport();
+
+ const Viewport* viewport() const;
+
+ virtual void save_settings(QSettings &settings) const;
+
+ virtual void restore_settings(QSettings &settings);
+
+ /**
+ * Gets a list of time markers.
+ */
+ vector< shared_ptr<TimeItem> > time_items() const;
+
+ /**
+ * Returns the view time scale in seconds per pixel.
+ */
+ double scale() const;
+
+ /**
+ * Returns the time offset of the left edge of the view in
+ * seconds.
+ */
+ const pv::util::Timestamp& offset() const;
+
+ /**
+ * Returns the vertical scroll offset.
+ */
+ int owner_visual_v_offset() const;
+
+ /**
+ * Sets the visual v-offset.
+ */
+ void set_v_offset(int offset);
+
+ /**
+ * Returns the SI prefix to apply to the graticule time markings.
+ */
+ pv::util::SIPrefix tick_prefix() const;
+
+ /**
+ * Returns the number of fractional digits shown for the time markings.
+ */
+ unsigned int tick_precision() const;
+
+ /**
+ * Returns period of the graticule time markings.
+ */
+ const pv::util::Timestamp& tick_period() const;
+
+ /**
+ * Returns the unit of time currently used.
+ */
+ util::TimeUnit time_unit() const;
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ unsigned int depth() const;
+
+ void zoom(double steps);
+ void zoom(double steps, int offset);
+
+ void zoom_fit(bool gui_state);
+
+ void zoom_one_to_one();
+
+ /**
+ * Sets the scale and offset.
+ * @param scale The new view scale in seconds per pixel.
+ * @param offset The view time offset in seconds.
+ */
+ void set_scale_offset(double scale, const pv::util::Timestamp& offset);
+
+ set< shared_ptr<pv::data::SignalData> > get_visible_data() const;
+
+ pair<pv::util::Timestamp, pv::util::Timestamp> get_time_extents() const;
+
+ /**
+ * Enables or disables coloured trace backgrounds. If they're not
+ * coloured then they will use alternating colors.
+ */
+ void enable_coloured_bg(bool state);
+
+ /**
+ * Returns true if the trace background should be drawn with a coloured background.
+ */
+ bool coloured_bg() const;
+
+ /**
+ * Enable or disable showing sampling points.
+ */
+ void enable_show_sampling_points(bool state);
+
+ /**
+ * Enable or disable showing the analog minor grid.
+ */
+ void enable_show_analog_minor_grid(bool state);
+
+ /**
+ * Returns true if cursors are displayed. false otherwise.
+ */
+ bool cursors_shown() const;
+
+ /**
+ * Shows or hides the cursors.
+ */
+ void show_cursors(bool show = true);
+
+ /**
+ * Moves the cursors to a convenient position in the view.
+ */
+ void centre_cursors();
+
+ /**
+ * Returns a reference to the pair of cursors.
+ */
+ shared_ptr<CursorPair> cursors() const;
+
+ /**
+ * Adds a new flag at a specified time.
+ */
+ void add_flag(const pv::util::Timestamp& time);
+
+ /**
+ * Removes a flag from the list.
+ */
+ void remove_flag(shared_ptr<Flag> flag);
+
+ /**
+ * Gets the list of flags.
+ */
+ vector< shared_ptr<Flag> > flags() const;
+
+ const QPoint& hover_point() const;
+
+ void restack_all_trace_tree_items();
+
+Q_SIGNALS:
+ void hover_point_changed();
+
+ void selection_changed();
+
+ /// Emitted when the offset changed.
+ void offset_changed();
+
+ /// Emitted when the scale changed.
+ void scale_changed();
+
+ void sticky_scrolling_changed(bool state);
+
+ void always_zoom_to_fit_changed(bool state);
+
+ /// Emitted when the tick_prefix changed.
+ void tick_prefix_changed();
+
+ /// Emitted when the tick_precision changed.
+ void tick_precision_changed();
+
+ /// Emitted when the tick_period changed.
+ void tick_period_changed();
+
+ /// Emitted when the time_unit changed.
+ void time_unit_changed();
+
+public Q_SLOTS:
+ void trigger_event(util::Timestamp location);
+
+private:
+ void get_scroll_layout(double &length, pv::util::Timestamp &offset) const;
+
+ /**
+ * Simultaneously sets the zoom and offset.
+ * @param scale The scale to set the view to in seconds per pixel. This
+ * value is clamped between MinScale and MaxScale.
+ * @param offset The offset of the left edge of the view in seconds.
+ */
+ void set_zoom(double scale, int offset);
+
+ /**
+ * Find a tick spacing and number formatting that does not cause
+ * the values to collide.
+ */
+ void calculate_tick_spacing();
+
+ void adjust_top_margin();
+
+ void update_scroll();
+
+ void reset_scroll();
+
+ void set_scroll_default();
+
+ bool header_was_shrunk() const;
+
+ void expand_header_to_fit();
+
+ void update_layout();
+
+ TraceTreeItemOwner* find_prevalent_trace_group(
+ const shared_ptr<sigrok::ChannelGroup> &group,
+ const unordered_map<shared_ptr<data::SignalBase>,
+ shared_ptr<Signal> > &signal_map);
+
+ static vector< shared_ptr<Trace> >
+ extract_new_traces_for_channels(
+ const vector< shared_ptr<sigrok::Channel> > &channels,
+ const unordered_map<shared_ptr<data::SignalBase>,
+ shared_ptr<Signal> > &signal_map,
+ set< shared_ptr<Trace> > &add_list);
+
+ void determine_time_unit();
+
+ bool eventFilter(QObject *object, QEvent *event);
+
+ void resizeEvent(QResizeEvent *event);
+
+public:
+ void row_item_appearance_changed(bool label, bool content);
+ void time_item_appearance_changed(bool label, bool content);
+
+ void extents_changed(bool horz, bool vert);
+
+private Q_SLOTS:
+
+ void on_splitter_moved();
+
+ void h_scroll_value_changed(int value);
+ void v_scroll_value_changed();
+
+ void signals_changed();
+ void capture_state_updated(int state);
+
+ virtual void perform_delayed_view_update();
+
+ void process_sticky_events();
+
+ void on_hover_point_changed();
+
+ /**
+ * Sets the 'offset_' member and emits the 'offset_changed'
+ * signal if needed.
+ */
+ void set_offset(const pv::util::Timestamp& offset);
+
+ /**
+ * Sets the 'scale_' member and emits the 'scale_changed'
+ * signal if needed.
+ */
+ void set_scale(double scale);
+
+ /**
+ * Sets the 'tick_prefix_' member and emits the 'tick_prefix_changed'
+ * signal if needed.
+ */
+ void set_tick_prefix(pv::util::SIPrefix tick_prefix);
+
+ /**
+ * Sets the 'tick_precision_' member and emits the 'tick_precision_changed'
+ * signal if needed.
+ */
+ void set_tick_precision(unsigned tick_precision);
+
+ /**
+ * Sets the 'tick_period_' member and emits the 'tick_period_changed'
+ * signal if needed.
+ */
+ void set_tick_period(const pv::util::Timestamp& tick_period);
+
+ /**
+ * Sets the 'time_unit' member and emits the 'time_unit_changed'
+ * signal if needed.
+ */
+ void set_time_unit(pv::util::TimeUnit time_unit);
+
+private:
+ CustomScrollArea *scrollarea_;
+ Viewport *viewport_;
+ Ruler *ruler_;
+ Header *header_;
+ QSplitter *splitter_;
+
+ unordered_set< shared_ptr<Signal> > signals_;
+
+#ifdef ENABLE_DECODE
+ vector< shared_ptr<DecodeTrace> > decode_traces_;
+#endif
+
+ /// The view time scale in seconds per pixel.
+ double scale_;
+
+ /// The view time offset in seconds.
+ pv::util::Timestamp offset_;
+
+ bool updating_scroll_;
+ bool settings_restored_;
+
+ bool sticky_scrolling_;
+ bool coloured_bg_;
+ bool always_zoom_to_fit_;
+
+ pv::util::Timestamp tick_period_;
+ pv::util::SIPrefix tick_prefix_;
+ unsigned int tick_precision_;
+ util::TimeUnit time_unit_;
+
+ bool show_cursors_;
+ shared_ptr<CursorPair> cursors_;
+
+ list< shared_ptr<Flag> > flags_;
+ char next_flag_text_;
+
+ vector< shared_ptr<TriggerMarker> > trigger_markers_;
+
+ QPoint hover_point_;
+
+ unsigned int sticky_events_;
+ QTimer lazy_event_handler_;
+
+ // This is true when the defaults couldn't be set due to insufficient info
+ bool scroll_needs_defaults_;
+
+ // A nonzero value indicates the v offset to restore. See View::resizeEvent()
+ int saved_v_offset_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "viewitem.hpp"
+
+#include <climits>
+
+#include <QApplication>
+#include <QMenu>
+#include <QPalette>
+
+namespace pv {
+namespace views {
+namespace trace {
+
+const QSizeF ViewItem::LabelPadding(4, 0);
+const int ViewItem::HighlightRadius = 3;
+
+ViewItem::ViewItem() :
+ context_parent_(nullptr),
+ drag_point_(INT_MIN, INT_MIN),
+ selected_(false)
+{
+}
+
+bool ViewItem::selected() const
+{
+ return selected_;
+}
+
+void ViewItem::select(bool select)
+{
+ selected_ = select;
+}
+
+bool ViewItem::is_draggable() const
+{
+ return true;
+}
+
+bool ViewItem::dragging() const
+{
+ return drag_point_.x() != INT_MIN && drag_point_.y() != INT_MIN;
+}
+
+void ViewItem::drag()
+{
+ if (is_draggable())
+ drag_point_ = point(QRect());
+}
+
+void ViewItem::drag_release()
+{
+ drag_point_ = QPoint(INT_MIN, INT_MIN);
+}
+
+QRectF ViewItem::label_rect(const QRectF &rect) const
+{
+ (void)rect;
+ return QRectF();
+}
+
+QRectF ViewItem::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ (void)pp;
+ return QRectF();
+}
+
+QMenu* ViewItem::create_context_menu(QWidget *parent)
+{
+ context_parent_ = parent;
+ return new QMenu(parent);
+}
+
+widgets::Popup* ViewItem::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+void ViewItem::delete_pressed()
+{
+}
+
+QPen ViewItem::highlight_pen()
+{
+ return QPen(QApplication::palette().brush(
+ QPalette::Highlight), HighlightRadius * 2,
+ Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
+}
+
+void ViewItem::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ (void)p;
+ (void)rect;
+ (void)hover;
+}
+
+void ViewItem::paint_back(QPainter &p, ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+void ViewItem::paint_mid(QPainter &p, ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+void ViewItem::paint_fore(QPainter &p, ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+QColor ViewItem::select_text_colour(QColor background)
+{
+ return (background.lightness() > 110) ? Qt::black : Qt::white;
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWITEM_HPP
+#define PULSEVIEW_PV_VIEWITEM_HPP
+
+#include <list>
+
+#include <QPen>
+
+#include "viewitempaintparams.hpp"
+
+class QAction;
+class QMenu;
+class QWidget;
+
+namespace pv {
+
+namespace widgets {
+class Popup;
+}
+
+namespace views {
+namespace trace {
+
+class ViewItemOwner;
+
+class ViewItem : public QObject
+{
+ Q_OBJECT
+
+public:
+ static const QSizeF LabelPadding;
+ static const int HighlightRadius;
+
+public:
+ ViewItem();
+
+public:
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ virtual bool enabled() const = 0;
+
+ /**
+ * Returns true if the item has been selected by the user.
+ */
+ bool selected() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ virtual void select(bool select = true);
+
+ /**
+ * Returns true if the item may be dragged/moved.
+ */
+ virtual bool is_draggable() const;
+
+ /**
+ * Returns true if the item is being dragged.
+ */
+ bool dragging() const;
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ void drag();
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ virtual void drag_release();
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ virtual void drag_by(const QPoint &delta) = 0;
+
+ /**
+ * Get the drag point.
+ * @param rect the rectangle of the widget area.
+ */
+ virtual QPoint point(const QRect &rect) const = 0;
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ * @remarks The default implementation returns an empty rectangle.
+ */
+ virtual QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ * @remarks The default implementation returns an empty hit-box.
+ */
+ virtual QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param rect the rectangle of the header area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Paints the background layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the mid-layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+
+public:
+ /**
+ * Gets the text colour.
+ * @remarks This colour is computed by comparing the lightness
+ * of the trace colour against a threshold to determine whether
+ * white or black would be more visible.
+ */
+ static QColor select_text_colour(QColor background);
+
+public:
+ virtual QMenu* create_context_menu(QWidget *parent);
+
+ virtual pv::widgets::Popup* create_popup(QWidget *parent);
+
+ virtual void delete_pressed();
+
+protected:
+ static QPen highlight_pen();
+
+protected:
+ QWidget *context_parent_;
+ QPoint drag_point_;
+
+private:
+ bool selected_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWITEM_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <stack>
+#include <type_traits>
+#include <vector>
+
+#include <pv/session.hpp>
+
+using std::dynamic_pointer_cast;
+using std::forward_iterator_tag;
+using std::shared_ptr;
+using std::stack;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+template<class Owner, class Item> class ViewItemIterator
+{
+public:
+ typedef typename Owner::item_list::const_iterator child_iterator;
+ typedef shared_ptr<Item> value_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type pointer;
+ typedef const value_type& reference;
+ typedef forward_iterator_tag iterator_category;
+
+public:
+ ViewItemIterator(Owner *owner) :
+ owner_stack_({owner}) {}
+
+ ViewItemIterator(Owner *owner, child_iterator iter) :
+ owner_stack_({owner}) {
+ assert(owner);
+ if (iter != owner->child_items().end())
+ iter_stack_.push(iter);
+ }
+
+ ViewItemIterator(const ViewItemIterator<Owner, Item> &o) :
+ owner_stack_(o.owner_stack_),
+ iter_stack_(o.iter_stack_) {}
+
+ reference operator*() const {
+ return *iter_stack_.top();
+ }
+
+ reference operator->() const {
+ return *this;
+ }
+
+ ViewItemIterator<Owner, Item>& operator++() {
+ assert(!owner_stack_.empty());
+ assert(!iter_stack_.empty());
+
+ shared_ptr<Owner> owner(dynamic_pointer_cast<Owner>(
+ *iter_stack_.top()));
+ if (owner && !owner->child_items().empty()) {
+ owner_stack_.push(owner.get());
+ iter_stack_.push(owner->child_items().begin());
+ } else {
+ while (!iter_stack_.empty() && (++iter_stack_.top()) ==
+ owner_stack_.top()->child_items().end()) {
+ owner_stack_.pop();
+ iter_stack_.pop();
+ }
+ }
+
+ return *this;
+ }
+
+ ViewItemIterator<Owner, Item> operator++(int) {
+ ViewItemIterator<Owner, Item> pre = *this;
+ ++*this;
+ return pre;
+ }
+
+ bool operator==(const ViewItemIterator &o) const {
+ return (iter_stack_.empty() && o.iter_stack_.empty()) || (
+ iter_stack_.size() == o.iter_stack_.size() &&
+ owner_stack_.top() == o.owner_stack_.top() &&
+ iter_stack_.top() == o.iter_stack_.top());
+ }
+
+ bool operator!=(const ViewItemIterator &o) const {
+ return !((const ViewItemIterator&)*this == o);
+ }
+
+ void swap(ViewItemIterator<Owner, Item>& other) {
+ swap(owner_stack_, other.owner_stack_);
+ swap(iter_stack_, other.iter_stack_);
+ }
+
+private:
+ stack<Owner*> owner_stack_;
+ stack<child_iterator> iter_stack_;
+};
+
+template<class Owner, class Item>
+void swap(ViewItemIterator<Owner, Item>& a, ViewItemIterator<Owner, Item>& b)
+{
+ a.swap(b);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+
+#include "tracetreeitem.hpp"
+#include "trace.hpp"
+#include "tracetreeitemowner.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+ViewItemOwner::iterator ViewItemOwner::begin()
+{
+ return iterator(this, items_.begin());
+}
+
+ViewItemOwner::iterator ViewItemOwner::end()
+{
+ return iterator(this);
+}
+
+ViewItemOwner::const_iterator ViewItemOwner::begin() const
+{
+ return const_iterator(this, items_.cbegin());
+}
+
+ViewItemOwner::const_iterator ViewItemOwner::end() const
+{
+ return const_iterator(this);
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
+
+#include <memory>
+#include <vector>
+
+#include "viewitemiterator.hpp"
+
+using std::dynamic_pointer_cast;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+
+class Session;
+
+namespace views {
+namespace trace {
+
+class ViewItem;
+class View;
+
+class ViewItemOwner
+{
+public:
+ typedef vector< shared_ptr<ViewItem> > item_list;
+ typedef ViewItemIterator<ViewItemOwner, ViewItem> iterator;
+ typedef ViewItemIterator<const ViewItemOwner, ViewItem> const_iterator;
+
+public:
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ virtual const item_list& child_items() const = 0;
+
+ /**
+ * Returns a depth-first iterator at the beginning of the child ViewItem
+ * tree.
+ */
+ iterator begin();
+
+ /**
+ * Returns a depth-first iterator at the end of the child ViewItem tree.
+ */
+ iterator end();
+
+ /**
+ * Returns a constant depth-first iterator at the beginning of the
+ * child ViewItem tree.
+ */
+ const_iterator begin() const;
+
+ /**
+ * Returns a constant depth-first iterator at the end of the child
+ * ViewItem tree.
+ */
+ const_iterator end() const;
+
+ /**
+ * Creates a list of descendant signals filtered by type.
+ */
+ template<class T>
+ vector< shared_ptr<T> > list_by_type() {
+ vector< shared_ptr<T> > items;
+ for (const auto &r : *this) {
+ shared_ptr<T> p = dynamic_pointer_cast<T>(r);
+ if (p)
+ items.push_back(p);
+ }
+
+ return items;
+ }
+
+protected:
+ item_list items_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+
+#include <QApplication>
+#include <QFontMetrics>
+
+#include "viewitempaintparams.hpp"
+
+namespace pv {
+namespace views {
+namespace trace {
+
+ViewItemPaintParams::ViewItemPaintParams(
+ const QRect &rect, double scale, const pv::util::Timestamp& offset) :
+ rect_(rect),
+ scale_(scale),
+ offset_(offset),
+ bg_colour_state_(false)
+{
+ assert(scale > 0.0);
+}
+
+QFont ViewItemPaintParams::font()
+{
+ return QApplication::font();
+}
+
+int ViewItemPaintParams::text_height()
+{
+ return QFontMetrics(font()).height();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
+
+#include "pv/util.hpp"
+
+#include <QFont>
+#include <QRect>
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class ViewItemPaintParams
+{
+public:
+ ViewItemPaintParams(
+ const QRect &rect, double scale, const pv::util::Timestamp& offset);
+
+ QRect rect() const {
+ return rect_;
+ }
+
+ double scale() const {
+ return scale_;
+ }
+
+ const pv::util::Timestamp& offset() const {
+ return offset_;
+ }
+
+ int left() const {
+ return rect_.left();
+ }
+
+ int right() const {
+ return rect_.right();
+ }
+
+ int top() const {
+ return rect_.top();
+ }
+
+ int bottom() const {
+ return rect_.bottom();
+ }
+
+ int width() const {
+ return rect_.width();
+ }
+
+ int height() const {
+ return rect_.height();
+ }
+
+ double pixels_offset() const {
+ return (offset_ / scale_).convert_to<double>();
+ }
+
+ bool next_bg_colour_state() {
+ const bool state = bg_colour_state_;
+ bg_colour_state_ = !bg_colour_state_;
+ return state;
+ }
+
+public:
+ static QFont font();
+
+ static int text_height();
+
+private:
+ QRect rect_;
+ double scale_;
+ pv::util::Timestamp offset_;
+ bool bg_colour_state_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <limits>
+
+#include "signal.hpp"
+#include "view.hpp"
+#include "viewitempaintparams.hpp"
+#include "viewport.hpp"
+
+#include <pv/session.hpp>
+
+#include <QMouseEvent>
+
+#include <QDebug>
+
+using std::abs;
+using std::back_inserter;
+using std::copy;
+using std::dynamic_pointer_cast;
+using std::none_of; // Used in assert()s.
+using std::shared_ptr;
+using std::stable_sort;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+Viewport::Viewport(View &parent) :
+ ViewWidget(parent),
+ pinch_zoom_active_(false)
+{
+ setAutoFillBackground(true);
+ setBackgroundRole(QPalette::Base);
+}
+
+shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
+{
+ const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
+ const vector< shared_ptr<ViewItem> > items(this->items());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() && (*i)->hit_box_rect(pp).contains(pt))
+ return *i;
+ return nullptr;
+}
+
+void Viewport::item_hover(const shared_ptr<ViewItem> &item)
+{
+ if (item && item->is_draggable())
+ setCursor(dynamic_pointer_cast<RowItem>(item) ?
+ Qt::SizeVerCursor : Qt::SizeHorCursor);
+ else
+ unsetCursor();
+}
+
+void Viewport::drag()
+{
+ drag_offset_ = view_.offset();
+ drag_v_offset_ = view_.owner_visual_v_offset();
+}
+
+void Viewport::drag_by(const QPoint &delta)
+{
+ if (drag_offset_ == boost::none)
+ return;
+
+ view_.set_scale_offset(view_.scale(),
+ (*drag_offset_ - delta.x() * view_.scale()));
+
+ view_.set_v_offset(-drag_v_offset_ - delta.y());
+}
+
+void Viewport::drag_release()
+{
+ drag_offset_ = boost::none;
+}
+
+vector< shared_ptr<ViewItem> > Viewport::items()
+{
+ vector< shared_ptr<ViewItem> > items;
+ const vector< shared_ptr<ViewItem> > view_items(
+ view_.list_by_type<ViewItem>());
+ copy(view_items.begin(), view_items.end(), back_inserter(items));
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ copy(time_items.begin(), time_items.end(), back_inserter(items));
+ return items;
+}
+
+bool Viewport::touch_event(QTouchEvent *event)
+{
+ QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
+
+ if (touchPoints.count() != 2) {
+ pinch_zoom_active_ = false;
+ return false;
+ }
+
+ const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
+ const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
+
+ if (!pinch_zoom_active_ ||
+ (event->touchPointStates() & Qt::TouchPointPressed)) {
+ pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.pos().x()).convert_to<double>();
+ pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.pos().x()).convert_to<double>();
+ pinch_zoom_active_ = true;
+ }
+
+ double w = touchPoint1.pos().x() - touchPoint0.pos().x();
+ if (abs(w) >= 1.0) {
+ const double scale =
+ fabs((pinch_offset1_ - pinch_offset0_) / w);
+ double offset = pinch_offset0_ - touchPoint0.pos().x() * scale;
+ if (scale > 0)
+ view_.set_scale_offset(scale, offset);
+ }
+
+ if (event->touchPointStates() & Qt::TouchPointReleased) {
+ pinch_zoom_active_ = false;
+
+ if (touchPoint0.state() & Qt::TouchPointReleased) {
+ // Primary touch released
+ drag_release();
+ } else {
+ // Update the mouse down fields so that continued
+ // dragging with the primary touch will work correctly
+ mouse_down_point_ = touchPoint0.pos().toPoint();
+ drag();
+ }
+ }
+
+ return true;
+}
+
+void Viewport::paintEvent(QPaintEvent*)
+{
+ typedef void (ViewItem::*LayerPaintFunc)(
+ QPainter &p, ViewItemPaintParams &pp);
+ LayerPaintFunc layer_paint_funcs[] = {
+ &ViewItem::paint_back, &ViewItem::paint_mid,
+ &ViewItem::paint_fore, nullptr};
+
+ vector< shared_ptr<RowItem> > row_items(view_.list_by_type<RowItem>());
+ assert(none_of(row_items.begin(), row_items.end(),
+ [](const shared_ptr<RowItem> &r) { return !r; }));
+
+ stable_sort(row_items.begin(), row_items.end(),
+ [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
+ return a->point(QRect()).y() < b->point(QRect()).y(); });
+
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ assert(none_of(time_items.begin(), time_items.end(),
+ [](const shared_ptr<TimeItem> &t) { return !t; }));
+
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ for (LayerPaintFunc *paint_func = layer_paint_funcs;
+ *paint_func; paint_func++) {
+ ViewItemPaintParams time_pp(rect(), view_.scale(), view_.offset());
+ for (const shared_ptr<TimeItem> t : time_items)
+ (t.get()->*(*paint_func))(p, time_pp);
+
+ ViewItemPaintParams row_pp(rect(), view_.scale(), view_.offset());
+ for (const shared_ptr<RowItem> r : row_items)
+ (r.get()->*(*paint_func))(p, row_pp);
+ }
+
+ p.end();
+}
+
+void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ assert(event);
+
+ if (event->buttons() & Qt::LeftButton)
+ view_.zoom(2.0, event->x());
+ else if (event->buttons() & Qt::RightButton)
+ view_.zoom(-2.0, event->x());
+}
+
+void Viewport::wheelEvent(QWheelEvent *event)
+{
+ assert(event);
+
+ if (event->orientation() == Qt::Vertical) {
+ if (event->modifiers() & Qt::ControlModifier) {
+ // Vertical scrolling with the control key pressed
+ // is intrepretted as vertical scrolling
+ view_.set_v_offset(-view_.owner_visual_v_offset() -
+ (event->delta() * height()) / (8 * 120));
+ } else {
+ // Vertical scrolling is interpreted as zooming in/out
+ view_.zoom(event->delta() / 120.0, event->x());
+ }
+ } else if (event->orientation() == Qt::Horizontal) {
+ // Horizontal scrolling is interpreted as moving left/right
+ view_.set_scale_offset(view_.scale(),
+ event->delta() * view_.scale() + view_.offset());
+ }
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
+#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
+
+#include <boost/optional.hpp>
+
+#include <QTimer>
+#include <QTouchEvent>
+
+#include "pv/util.hpp"
+#include "viewwidget.hpp"
+
+using std::shared_ptr;
+using std::vector;
+
+class QPainter;
+class QPaintEvent;
+class Session;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class View;
+
+class Viewport : public ViewWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Viewport(View &parent);
+
+private:
+ /**
+ * Indicates when a view item is being hovered over.
+ * @param item The item that is being hovered over, or @c nullptr
+ * if no view item is being hovered over.
+ */
+ void item_hover(const shared_ptr<ViewItem> &item);
+
+ /**
+ * Gets the first view item which has a hit-box that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt);
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ void drag();
+
+ /**
+ * Drag the background by the delta offset.
+ * @param delta the drag offset in pixels.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ void drag_release();
+
+ /**
+ * Gets the items in the view widget.
+ */
+ vector< shared_ptr<ViewItem> > items();
+
+ /**
+ * Handles touch begin update and end events.
+ * @param e the event that triggered this handler.
+ */
+ bool touch_event(QTouchEvent *event);
+
+private:
+ void paintEvent(QPaintEvent *event);
+
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void wheelEvent(QWheelEvent *event);
+
+private:
+ boost::optional<pv::util::Timestamp> drag_offset_;
+ int drag_v_offset_;
+
+ double pinch_offset0_;
+ double pinch_offset1_;
+ bool pinch_zoom_active_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QApplication>
+#include <QMouseEvent>
+#include <QTouchEvent>
+
+#include "tracetreeitem.hpp"
+#include "view.hpp"
+#include "viewwidget.hpp"
+
+using std::any_of;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+ViewWidget::ViewWidget(View &parent) :
+ QWidget(&parent),
+ view_(parent),
+ item_dragging_(false)
+{
+ setFocusPolicy(Qt::ClickFocus);
+ setAttribute(Qt::WA_AcceptTouchEvents, true);
+ setMouseTracking(true);
+}
+
+void ViewWidget::clear_selection()
+{
+ const auto items = this->items();
+ for (auto &i : items)
+ i->select(false);
+}
+
+void ViewWidget::item_hover(const shared_ptr<ViewItem> &item)
+{
+ (void)item;
+}
+
+void ViewWidget::item_clicked(const shared_ptr<ViewItem> &item)
+{
+ (void)item;
+}
+
+bool ViewWidget::accept_drag() const
+{
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
+ view_.list_by_type<TraceTreeItem>());
+
+ const bool any_row_items_selected = any_of(
+ trace_tree_items.begin(), trace_tree_items.end(),
+ [](const shared_ptr<TraceTreeItem> &r) { return r->selected(); });
+
+ const bool any_time_items_selected = any_of(items.begin(), items.end(),
+ [](const shared_ptr<TimeItem> &i) { return i->selected(); });
+
+ if (any_row_items_selected && !any_time_items_selected) {
+ // Check all the drag items share a common owner
+ TraceTreeItemOwner *item_owner = nullptr;
+ for (shared_ptr<TraceTreeItem> r : trace_tree_items)
+ if (r->dragging()) {
+ if (!item_owner)
+ item_owner = r->owner();
+ else if (item_owner != r->owner())
+ return false;
+ }
+
+ return true;
+ } else if (any_time_items_selected && !any_row_items_selected) {
+ return true;
+ }
+
+ // A background drag is beginning
+ return true;
+}
+
+bool ViewWidget::mouse_down() const
+{
+ return mouse_down_point_.x() != INT_MIN &&
+ mouse_down_point_.y() != INT_MIN;
+}
+
+void ViewWidget::drag_items(const QPoint &delta)
+{
+ bool item_dragged = false;
+
+ // Drag the row items
+ const vector< shared_ptr<RowItem> > row_items(
+ view_.list_by_type<RowItem>());
+ for (shared_ptr<RowItem> r : row_items)
+ if (r->dragging()) {
+ r->drag_by(delta);
+
+ // Ensure the trace is selected
+ r->select();
+
+ item_dragged = true;
+ }
+
+ // If an item is being dragged, update the stacking
+ TraceTreeItemOwner *item_owner = nullptr;
+ const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
+ view_.list_by_type<TraceTreeItem>());
+ for (shared_ptr<TraceTreeItem> i : trace_tree_items)
+ if (i->dragging())
+ item_owner = i->owner();
+
+ if (item_owner) {
+ item_owner->restack_items();
+ for (shared_ptr<TraceTreeItem> i : trace_tree_items)
+ i->animate_to_layout_v_offset();
+ }
+
+ // Drag the time items
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items)
+ if (i->dragging()) {
+ i->drag_by(delta);
+ item_dragged = true;
+ }
+
+ // Do the background drag
+ if (!item_dragged)
+ drag_by(delta);
+}
+
+void ViewWidget::drag()
+{
+}
+
+void ViewWidget::drag_by(const QPoint &delta)
+{
+ (void)delta;
+}
+
+void ViewWidget::drag_release()
+{
+}
+
+void ViewWidget::mouse_left_press_event(QMouseEvent *event)
+{
+ (void)event;
+
+ const bool ctrl_pressed =
+ QApplication::keyboardModifiers() & Qt::ControlModifier;
+
+ // Clear selection if control is not pressed and this item is unselected
+ if ((!mouse_down_item_ || !mouse_down_item_->selected()) &&
+ !ctrl_pressed)
+ clear_selection();
+
+ // Set the signal selection state if the item has been clicked
+ if (mouse_down_item_) {
+ if (ctrl_pressed)
+ mouse_down_item_->select(!mouse_down_item_->selected());
+ else
+ mouse_down_item_->select(true);
+ }
+
+ // Save the offsets of any signals which will be dragged
+ bool item_dragged = false;
+ const auto items = this->items();
+ for (auto &i : items)
+ if (i->selected()) {
+ item_dragged = true;
+ i->drag();
+ }
+
+ // Do the background drag
+ if (!item_dragged)
+ drag();
+
+ selection_changed();
+}
+
+void ViewWidget::mouse_left_release_event(QMouseEvent *event)
+{
+ assert(event);
+
+ auto items = this->items();
+ const bool ctrl_pressed =
+ QApplication::keyboardModifiers() & Qt::ControlModifier;
+
+ // Unselect everything if control is not pressed
+ const shared_ptr<ViewItem> mouse_over =
+ get_mouse_over_item(event->pos());
+
+ for (auto &i : items)
+ i->drag_release();
+
+ if (item_dragging_)
+ view_.restack_all_trace_tree_items();
+ else {
+ if (!ctrl_pressed) {
+ for (shared_ptr<ViewItem> i : items)
+ if (mouse_down_item_ != i)
+ i->select(false);
+
+ if (mouse_down_item_)
+ item_clicked(mouse_down_item_);
+ }
+ }
+
+ item_dragging_ = false;
+}
+
+bool ViewWidget::touch_event(QTouchEvent *event)
+{
+ (void)event;
+
+ return false;
+}
+
+bool ViewWidget::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ if (touch_event(static_cast<QTouchEvent *>(event)))
+ return true;
+ break;
+
+ default:
+ break;
+ }
+
+ return QWidget::event(event);
+}
+
+void ViewWidget::mousePressEvent(QMouseEvent *event)
+{
+ assert(event);
+
+ /* Ignore right click events as they will open context menus when
+ * used on trace labels. Those menus prevent ViewWidget::mouseReleaseEvent()
+ * to be triggered upon button release, making mouse_down_item_
+ * hold the last reference to a view item that might have been deleted
+ * from the context menu, preventing it from being freed as intended.
+ */
+ if (event->button() & Qt::LeftButton) {
+ mouse_down_point_ = event->pos();
+ mouse_down_item_ = get_mouse_over_item(event->pos());
+ mouse_left_press_event(event);
+ }
+}
+
+void ViewWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ assert(event);
+
+ if (event->button() & Qt::LeftButton)
+ mouse_left_release_event(event);
+
+ mouse_down_point_ = QPoint(INT_MIN, INT_MIN);
+ mouse_down_item_ = nullptr;
+}
+
+void ViewWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ assert(event);
+ mouse_point_ = event->pos();
+
+ if (!event->buttons())
+ item_hover(get_mouse_over_item(event->pos()));
+ else if (event->buttons() & Qt::LeftButton) {
+ if (!item_dragging_) {
+ if ((event->pos() - mouse_down_point_).manhattanLength() <
+ QApplication::startDragDistance())
+ return;
+
+ if (!accept_drag())
+ return;
+
+ item_dragging_ = true;
+ }
+
+ // Do the drag
+ drag_items(event->pos() - mouse_down_point_);
+ }
+}
+
+void ViewWidget::leaveEvent(QEvent*)
+{
+ mouse_point_ = QPoint(-1, -1);
+ update();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
--- /dev/null
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
+ *
+ * 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 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWWIDGET_HPP
+#define PULSEVIEW_PV_VIEWWIDGET_HPP
+
+#include <memory>
+
+#include <QWidget>
+
+using std::shared_ptr;
+using std::vector;
+
+class QTouchEvent;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class View;
+class ViewItem;
+
+class ViewWidget : public QWidget
+{
+ Q_OBJECT
+
+protected:
+ ViewWidget(View &parent);
+
+ /**
+ * Indicates when a view item is being hovered over.
+ * @param item The item that is being hovered over, or @c nullptr
+ * if no view item is being hovered over.
+ * @remarks the default implementation does nothing.
+ */
+ virtual void item_hover(const shared_ptr<ViewItem> &item);
+
+ /**
+ * Indicates the event an a view item has been clicked.
+ * @param item the view item that has been clicked.
+ * @remarks the default implementation does nothing.
+ */
+ virtual void item_clicked(const shared_ptr<ViewItem> &item);
+
+ /**
+ * Returns true if the selection of row items allows dragging.
+ * @return Returns true if the drag is acceptable.
+ */
+ bool accept_drag() const;
+
+ /**
+ * Returns true if the mouse button is down.
+ */
+ bool mouse_down() const;
+
+ /**
+ * Drag the dragging items by the delta offset.
+ * @param delta the drag offset in pixels.
+ */
+ void drag_items(const QPoint &delta);
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ virtual void drag();
+
+ /**
+ * Drag the background by the delta offset.
+ * @param delta the drag offset in pixels.
+ * @remarks The default implementation does nothing.
+ */
+ virtual void drag_by(const QPoint &delta);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ virtual void drag_release();
+
+ /**
+ * Gets the items in the view widget.
+ */
+ virtual vector< shared_ptr<ViewItem> > items() = 0;
+
+ /**
+ * Gets the first view item which has a hit-box that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ virtual shared_ptr<ViewItem> get_mouse_over_item(const QPoint &pt) = 0;
+
+ /**
+ * Handles left mouse button press events.
+ * @param event the mouse event that triggered this handler.
+ */
+ void mouse_left_press_event(QMouseEvent *event);
+
+ /**
+ * Handles left mouse button release events.
+ * @param event the mouse event that triggered this handler.
+ */
+ void mouse_left_release_event(QMouseEvent *event);
+
+ /**
+ * Handles touch begin update and end events.
+ * @param e the event that triggered this handler.
+ */
+ virtual bool touch_event(QTouchEvent *event);
+
+protected:
+ bool event(QEvent *event);
+
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+
+ void leaveEvent(QEvent *event);
+
+public Q_SLOTS:
+ void clear_selection();
+
+Q_SIGNALS:
+ void selection_changed();
+
+protected:
+ pv::views::trace::View &view_;
+ QPoint mouse_point_;
+ QPoint mouse_down_point_;
+ shared_ptr<ViewItem> mouse_down_item_;
+ bool item_dragging_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWWIDGET_HPP
${PROJECT_SOURCE_DIR}/pv/popups/channels.cpp
${PROJECT_SOURCE_DIR}/pv/popups/deviceoptions.cpp
${PROJECT_SOURCE_DIR}/pv/toolbars/mainbar.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/analogsignal.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/cursor.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/cursorpair.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/flag.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/header.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/rowitem.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/ruler.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/signal.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/signalscalehandle.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/timeitem.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/timemarker.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/trace.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracegroup.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracepalette.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitem.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitemowner.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/triggermarker.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/view.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewitem.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewitemowner.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewitempaintparams.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewport.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewwidget.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/analogsignal.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/cursor.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/cursorpair.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/flag.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/header.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/marginwidget.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/logicsignal.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/rowitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/ruler.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/signal.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/signalscalehandle.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/timeitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/timemarker.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/trace.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracegroup.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracepalette.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracetreeitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracetreeitemowner.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/triggermarker.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/view.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewitemowner.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewitempaintparams.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewport.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewwidget.cpp
${PROJECT_SOURCE_DIR}/pv/views/viewbase.cpp
${PROJECT_SOURCE_DIR}/pv/views/trace/standardbar.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.cpp
${PROJECT_SOURCE_DIR}/pv/prop/property.hpp
${PROJECT_SOURCE_DIR}/pv/prop/string.hpp
${PROJECT_SOURCE_DIR}/pv/toolbars/mainbar.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/analogsignal.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/cursor.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/flag.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/header.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/rowitem.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/ruler.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/signal.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/signalscalehandle.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/timeitem.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/timemarker.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/trace.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracegroup.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitem.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/triggermarker.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/view.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewitem.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewport.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/viewwidget.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/analogsignal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/cursor.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/flag.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/header.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/logicsignal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/marginwidget.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/rowitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/ruler.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/signal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/signalscalehandle.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/timeitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/timemarker.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/trace.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracegroup.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/tracetreeitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/triggermarker.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/view.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewport.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/viewwidget.hpp
${PROJECT_SOURCE_DIR}/pv/views/viewbase.hpp
${PROJECT_SOURCE_DIR}/pv/views/trace/standardbar.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.hpp
${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/row.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/rowdata.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.cpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
data/decoderstack.cpp
list(APPEND pulseview_TEST_HEADERS
${PROJECT_SOURCE_DIR}/pv/data/decoderstack.hpp
- ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.hpp
+ ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp
)
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/unit_test.hpp>
-#include "pv/view/ruler.hpp"
+#include "pv/views/trace/ruler.hpp"
#include "test/test.hpp"
using namespace pv::views::TraceView;