Move trace view files
authorSoeren Apel <soeren@apelpie.net>
Sat, 10 Jun 2017 13:37:28 +0000 (15:37 +0200)
committerSoeren Apel <soeren@apelpie.net>
Sat, 10 Jun 2017 13:37:28 +0000 (15:37 +0200)
115 files changed:
CMakeLists.txt
pv/data/decoderstack.cpp
pv/mainwindow.cpp
pv/popups/channels.cpp
pv/session.cpp
pv/toolbars/mainbar.cpp
pv/view/analogsignal.cpp [deleted file]
pv/view/analogsignal.hpp [deleted file]
pv/view/cursor.cpp [deleted file]
pv/view/cursor.hpp [deleted file]
pv/view/cursorpair.cpp [deleted file]
pv/view/cursorpair.hpp [deleted file]
pv/view/decodetrace.cpp [deleted file]
pv/view/decodetrace.hpp [deleted file]
pv/view/flag.cpp [deleted file]
pv/view/flag.hpp [deleted file]
pv/view/header.cpp [deleted file]
pv/view/header.hpp [deleted file]
pv/view/logicsignal.cpp [deleted file]
pv/view/logicsignal.hpp [deleted file]
pv/view/marginwidget.cpp [deleted file]
pv/view/marginwidget.hpp [deleted file]
pv/view/rowitem.cpp [deleted file]
pv/view/rowitem.hpp [deleted file]
pv/view/ruler.cpp [deleted file]
pv/view/ruler.hpp [deleted file]
pv/view/signal.cpp [deleted file]
pv/view/signal.hpp [deleted file]
pv/view/signalscalehandle.cpp [deleted file]
pv/view/signalscalehandle.hpp [deleted file]
pv/view/timeitem.cpp [deleted file]
pv/view/timeitem.hpp [deleted file]
pv/view/timemarker.cpp [deleted file]
pv/view/timemarker.hpp [deleted file]
pv/view/trace.cpp [deleted file]
pv/view/trace.hpp [deleted file]
pv/view/tracegroup.cpp [deleted file]
pv/view/tracegroup.hpp [deleted file]
pv/view/tracepalette.cpp [deleted file]
pv/view/tracepalette.hpp [deleted file]
pv/view/tracetreeitem.cpp [deleted file]
pv/view/tracetreeitem.hpp [deleted file]
pv/view/tracetreeitemowner.cpp [deleted file]
pv/view/tracetreeitemowner.hpp [deleted file]
pv/view/triggermarker.cpp [deleted file]
pv/view/triggermarker.hpp [deleted file]
pv/view/view.cpp [deleted file]
pv/view/view.hpp [deleted file]
pv/view/viewitem.cpp [deleted file]
pv/view/viewitem.hpp [deleted file]
pv/view/viewitemiterator.hpp [deleted file]
pv/view/viewitemowner.cpp [deleted file]
pv/view/viewitemowner.hpp [deleted file]
pv/view/viewitempaintparams.cpp [deleted file]
pv/view/viewitempaintparams.hpp [deleted file]
pv/view/viewport.cpp [deleted file]
pv/view/viewport.hpp [deleted file]
pv/view/viewwidget.cpp [deleted file]
pv/view/viewwidget.hpp [deleted file]
pv/views/trace/analogsignal.cpp [new file with mode: 0644]
pv/views/trace/analogsignal.hpp [new file with mode: 0644]
pv/views/trace/cursor.cpp [new file with mode: 0644]
pv/views/trace/cursor.hpp [new file with mode: 0644]
pv/views/trace/cursorpair.cpp [new file with mode: 0644]
pv/views/trace/cursorpair.hpp [new file with mode: 0644]
pv/views/trace/decodetrace.cpp [new file with mode: 0644]
pv/views/trace/decodetrace.hpp [new file with mode: 0644]
pv/views/trace/flag.cpp [new file with mode: 0644]
pv/views/trace/flag.hpp [new file with mode: 0644]
pv/views/trace/header.cpp [new file with mode: 0644]
pv/views/trace/header.hpp [new file with mode: 0644]
pv/views/trace/logicsignal.cpp [new file with mode: 0644]
pv/views/trace/logicsignal.hpp [new file with mode: 0644]
pv/views/trace/marginwidget.cpp [new file with mode: 0644]
pv/views/trace/marginwidget.hpp [new file with mode: 0644]
pv/views/trace/rowitem.cpp [new file with mode: 0644]
pv/views/trace/rowitem.hpp [new file with mode: 0644]
pv/views/trace/ruler.cpp [new file with mode: 0644]
pv/views/trace/ruler.hpp [new file with mode: 0644]
pv/views/trace/signal.cpp [new file with mode: 0644]
pv/views/trace/signal.hpp [new file with mode: 0644]
pv/views/trace/signalscalehandle.cpp [new file with mode: 0644]
pv/views/trace/signalscalehandle.hpp [new file with mode: 0644]
pv/views/trace/standardbar.cpp
pv/views/trace/timeitem.cpp [new file with mode: 0644]
pv/views/trace/timeitem.hpp [new file with mode: 0644]
pv/views/trace/timemarker.cpp [new file with mode: 0644]
pv/views/trace/timemarker.hpp [new file with mode: 0644]
pv/views/trace/trace.cpp [new file with mode: 0644]
pv/views/trace/trace.hpp [new file with mode: 0644]
pv/views/trace/tracegroup.cpp [new file with mode: 0644]
pv/views/trace/tracegroup.hpp [new file with mode: 0644]
pv/views/trace/tracepalette.cpp [new file with mode: 0644]
pv/views/trace/tracepalette.hpp [new file with mode: 0644]
pv/views/trace/tracetreeitem.cpp [new file with mode: 0644]
pv/views/trace/tracetreeitem.hpp [new file with mode: 0644]
pv/views/trace/tracetreeitemowner.cpp [new file with mode: 0644]
pv/views/trace/tracetreeitemowner.hpp [new file with mode: 0644]
pv/views/trace/triggermarker.cpp [new file with mode: 0644]
pv/views/trace/triggermarker.hpp [new file with mode: 0644]
pv/views/trace/view.cpp [new file with mode: 0644]
pv/views/trace/view.hpp [new file with mode: 0644]
pv/views/trace/viewitem.cpp [new file with mode: 0644]
pv/views/trace/viewitem.hpp [new file with mode: 0644]
pv/views/trace/viewitemiterator.hpp [new file with mode: 0644]
pv/views/trace/viewitemowner.cpp [new file with mode: 0644]
pv/views/trace/viewitemowner.hpp [new file with mode: 0644]
pv/views/trace/viewitempaintparams.cpp [new file with mode: 0644]
pv/views/trace/viewitempaintparams.hpp [new file with mode: 0644]
pv/views/trace/viewport.cpp [new file with mode: 0644]
pv/views/trace/viewport.hpp [new file with mode: 0644]
pv/views/trace/viewwidget.cpp [new file with mode: 0644]
pv/views/trace/viewwidget.hpp [new file with mode: 0644]
test/CMakeLists.txt
test/view/ruler.cpp

index 983dc537c3de4ba87cf718d52a589cd565060f75..f7c0eb39234f6b346028ef7c79ea14a1a2348a9e 100644 (file)
@@ -224,31 +224,31 @@ set(pulseview_SOURCES
        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
@@ -288,26 +288,26 @@ set(pulseview_HEADERS
        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
@@ -339,14 +339,14 @@ if(ENABLE_DECODE)
                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
        )
index 561e229dda8ec647f13238978b0c212cac3af480..1e1d600d329d5a010a9231aa047ebfcbef885f9d 100644 (file)
@@ -30,7 +30,7 @@
 #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;
index e6686fed08def913cbb3b40d301838198a531d8f..e925bd63e8d8ee06a070debc1b7e013ea17211af 100644 (file)
@@ -45,7 +45,7 @@
 #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>
index 49784bd7ae40630f3f5b3ccc4e125a9f228053e4..ca142d68582a433eb0626661a459e8000653a5c6 100644 (file)
@@ -30,7 +30,7 @@
 #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>
 
index c18f809446a037475f4106e10576f3a89d5c1688..66d80f1a4bde58b649cc571d1194431824f6f516 100644 (file)
 
 #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>
 
index 31035436df547125ab8d786e3a9c7cb172e12758..76cd8ea17632d689a675d1d089d26549e30a0028 100644 (file)
@@ -46,7 +46,7 @@
 #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
diff --git a/pv/view/analogsignal.cpp b/pv/view/analogsignal.cpp
deleted file mode 100644 (file)
index e0e0b16..0000000
+++ /dev/null
@@ -1,857 +0,0 @@
-/*
- * 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
diff --git a/pv/view/analogsignal.hpp b/pv/view/analogsignal.hpp
deleted file mode 100644 (file)
index 109cdd3..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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
diff --git a/pv/view/cursor.cpp b/pv/view/cursor.cpp
deleted file mode 100644 (file)
index f22680b..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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
diff --git a/pv/view/cursor.hpp b/pv/view/cursor.hpp
deleted file mode 100644 (file)
index e78920e..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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
diff --git a/pv/view/cursorpair.cpp b/pv/view/cursorpair.cpp
deleted file mode 100644 (file)
index 05b9dfc..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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
diff --git a/pv/view/cursorpair.hpp b/pv/view/cursorpair.hpp
deleted file mode 100644 (file)
index ee91ff4..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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
diff --git a/pv/view/decodetrace.cpp b/pv/view/decodetrace.cpp
deleted file mode 100644 (file)
index 89d335d..0000000
+++ /dev/null
@@ -1,1066 +0,0 @@
-/*
- * 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
diff --git a/pv/view/decodetrace.hpp b/pv/view/decodetrace.hpp
deleted file mode 100644 (file)
index 619c732..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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
diff --git a/pv/view/flag.cpp b/pv/view/flag.cpp
deleted file mode 100644 (file)
index 662a6d0..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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
diff --git a/pv/view/flag.hpp b/pv/view/flag.hpp
deleted file mode 100644 (file)
index 5dec187..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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
diff --git a/pv/view/header.cpp b/pv/view/header.cpp
deleted file mode 100644 (file)
index 7be804d..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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
diff --git a/pv/view/header.hpp b/pv/view/header.hpp
deleted file mode 100644 (file)
index 6a4c9a3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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
diff --git a/pv/view/logicsignal.cpp b/pv/view/logicsignal.cpp
deleted file mode 100644 (file)
index 8a36afa..0000000
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- * 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
diff --git a/pv/view/logicsignal.hpp b/pv/view/logicsignal.hpp
deleted file mode 100644 (file)
index cc62d91..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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
diff --git a/pv/view/marginwidget.cpp b/pv/view/marginwidget.cpp
deleted file mode 100644 (file)
index 0d35c5f..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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
diff --git a/pv/view/marginwidget.hpp b/pv/view/marginwidget.hpp
deleted file mode 100644 (file)
index ebe8cfc..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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
diff --git a/pv/view/rowitem.cpp b/pv/view/rowitem.cpp
deleted file mode 100644 (file)
index 2353dd9..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
diff --git a/pv/view/rowitem.hpp b/pv/view/rowitem.hpp
deleted file mode 100644 (file)
index 79811e7..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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
diff --git a/pv/view/ruler.cpp b/pv/view/ruler.cpp
deleted file mode 100644 (file)
index 712244a..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * 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
diff --git a/pv/view/ruler.hpp b/pv/view/ruler.hpp
deleted file mode 100644 (file)
index 1efd873..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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
diff --git a/pv/view/signal.cpp b/pv/view/signal.cpp
deleted file mode 100644 (file)
index 960cefa..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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
diff --git a/pv/view/signal.hpp b/pv/view/signal.hpp
deleted file mode 100644 (file)
index 40ba52e..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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
diff --git a/pv/view/signalscalehandle.cpp b/pv/view/signalscalehandle.cpp
deleted file mode 100644 (file)
index 20a4cad..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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
diff --git a/pv/view/signalscalehandle.hpp b/pv/view/signalscalehandle.hpp
deleted file mode 100644 (file)
index 193d352..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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
diff --git a/pv/view/timeitem.cpp b/pv/view/timeitem.cpp
deleted file mode 100644 (file)
index 33fe731..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
diff --git a/pv/view/timeitem.hpp b/pv/view/timeitem.hpp
deleted file mode 100644 (file)
index c5af3b8..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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
diff --git a/pv/view/timemarker.cpp b/pv/view/timemarker.cpp
deleted file mode 100644 (file)
index 853f7c0..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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
diff --git a/pv/view/timemarker.hpp b/pv/view/timemarker.hpp
deleted file mode 100644 (file)
index dba52fa..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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
diff --git a/pv/view/trace.cpp b/pv/view/trace.cpp
deleted file mode 100644 (file)
index e573337..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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
diff --git a/pv/view/trace.hpp b/pv/view/trace.hpp
deleted file mode 100644 (file)
index b6b1519..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracegroup.cpp b/pv/view/tracegroup.cpp
deleted file mode 100644 (file)
index 265bc27..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracegroup.hpp b/pv/view/tracegroup.hpp
deleted file mode 100644 (file)
index b00d5bf..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracepalette.cpp b/pv/view/tracepalette.cpp
deleted file mode 100644 (file)
index d4cfdd7..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracepalette.hpp b/pv/view/tracepalette.hpp
deleted file mode 100644 (file)
index 86536fe..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracetreeitem.cpp b/pv/view/tracetreeitem.cpp
deleted file mode 100644 (file)
index a70ca32..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracetreeitem.hpp b/pv/view/tracetreeitem.hpp
deleted file mode 100644 (file)
index 5b0bc61..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracetreeitemowner.cpp b/pv/view/tracetreeitemowner.cpp
deleted file mode 100644 (file)
index bff63c7..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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
diff --git a/pv/view/tracetreeitemowner.hpp b/pv/view/tracetreeitemowner.hpp
deleted file mode 100644 (file)
index cfe7bf7..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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
diff --git a/pv/view/triggermarker.cpp b/pv/view/triggermarker.cpp
deleted file mode 100644 (file)
index bac1e82..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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
diff --git a/pv/view/triggermarker.hpp b/pv/view/triggermarker.hpp
deleted file mode 100644 (file)
index b1610a5..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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
diff --git a/pv/view/view.cpp b/pv/view/view.cpp
deleted file mode 100644 (file)
index eeda37c..0000000
+++ /dev/null
@@ -1,1379 +0,0 @@
-/*
- * 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
diff --git a/pv/view/view.hpp b/pv/view/view.hpp
deleted file mode 100644 (file)
index 2fff8d2..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitem.cpp b/pv/view/viewitem.cpp
deleted file mode 100644 (file)
index 7213f96..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitem.hpp b/pv/view/viewitem.hpp
deleted file mode 100644 (file)
index 978cb91..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitemiterator.hpp b/pv/view/viewitemiterator.hpp
deleted file mode 100644 (file)
index 91ace4e..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitemowner.cpp b/pv/view/viewitemowner.cpp
deleted file mode 100644 (file)
index 9487659..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitemowner.hpp b/pv/view/viewitemowner.hpp
deleted file mode 100644 (file)
index dcec370..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitempaintparams.cpp b/pv/view/viewitempaintparams.cpp
deleted file mode 100644 (file)
index 5a0b815..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewitempaintparams.hpp b/pv/view/viewitempaintparams.hpp
deleted file mode 100644 (file)
index 9f2125c..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewport.cpp b/pv/view/viewport.cpp
deleted file mode 100644 (file)
index 70282d2..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewport.hpp b/pv/view/viewport.hpp
deleted file mode 100644 (file)
index de3e681..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewwidget.cpp b/pv/view/viewwidget.cpp
deleted file mode 100644 (file)
index ce5c27a..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * 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
diff --git a/pv/view/viewwidget.hpp b/pv/view/viewwidget.hpp
deleted file mode 100644 (file)
index 02c842a..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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
diff --git a/pv/views/trace/analogsignal.cpp b/pv/views/trace/analogsignal.cpp
new file mode 100644 (file)
index 0000000..c523795
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ * 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
diff --git a/pv/views/trace/analogsignal.hpp b/pv/views/trace/analogsignal.hpp
new file mode 100644 (file)
index 0000000..a1b3eee
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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
diff --git a/pv/views/trace/cursor.cpp b/pv/views/trace/cursor.cpp
new file mode 100644 (file)
index 0000000..4fb4b65
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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
diff --git a/pv/views/trace/cursor.hpp b/pv/views/trace/cursor.hpp
new file mode 100644 (file)
index 0000000..ad91b05
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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
diff --git a/pv/views/trace/cursorpair.cpp b/pv/views/trace/cursorpair.cpp
new file mode 100644 (file)
index 0000000..9a87440
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * 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
diff --git a/pv/views/trace/cursorpair.hpp b/pv/views/trace/cursorpair.hpp
new file mode 100644 (file)
index 0000000..7a73e19
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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
diff --git a/pv/views/trace/decodetrace.cpp b/pv/views/trace/decodetrace.cpp
new file mode 100644 (file)
index 0000000..6ccd3fb
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * 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
diff --git a/pv/views/trace/decodetrace.hpp b/pv/views/trace/decodetrace.hpp
new file mode 100644 (file)
index 0000000..e49d122
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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
diff --git a/pv/views/trace/flag.cpp b/pv/views/trace/flag.cpp
new file mode 100644 (file)
index 0000000..1db843e
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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
diff --git a/pv/views/trace/flag.hpp b/pv/views/trace/flag.hpp
new file mode 100644 (file)
index 0000000..4f707bd
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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
diff --git a/pv/views/trace/header.cpp b/pv/views/trace/header.cpp
new file mode 100644 (file)
index 0000000..58096dc
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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
diff --git a/pv/views/trace/header.hpp b/pv/views/trace/header.hpp
new file mode 100644 (file)
index 0000000..0038abb
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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
diff --git a/pv/views/trace/logicsignal.cpp b/pv/views/trace/logicsignal.cpp
new file mode 100644 (file)
index 0000000..9913d68
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ * 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
diff --git a/pv/views/trace/logicsignal.hpp b/pv/views/trace/logicsignal.hpp
new file mode 100644 (file)
index 0000000..2f5c66b
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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
diff --git a/pv/views/trace/marginwidget.cpp b/pv/views/trace/marginwidget.cpp
new file mode 100644 (file)
index 0000000..fa12c2b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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
diff --git a/pv/views/trace/marginwidget.hpp b/pv/views/trace/marginwidget.hpp
new file mode 100644 (file)
index 0000000..9a0686a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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
diff --git a/pv/views/trace/rowitem.cpp b/pv/views/trace/rowitem.cpp
new file mode 100644 (file)
index 0000000..11a02e9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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
diff --git a/pv/views/trace/rowitem.hpp b/pv/views/trace/rowitem.hpp
new file mode 100644 (file)
index 0000000..ae91d4b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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
diff --git a/pv/views/trace/ruler.cpp b/pv/views/trace/ruler.cpp
new file mode 100644 (file)
index 0000000..5455b20
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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
diff --git a/pv/views/trace/ruler.hpp b/pv/views/trace/ruler.hpp
new file mode 100644 (file)
index 0000000..40aae48
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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
diff --git a/pv/views/trace/signal.cpp b/pv/views/trace/signal.cpp
new file mode 100644 (file)
index 0000000..a55598c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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
diff --git a/pv/views/trace/signal.hpp b/pv/views/trace/signal.hpp
new file mode 100644 (file)
index 0000000..8347281
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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
diff --git a/pv/views/trace/signalscalehandle.cpp b/pv/views/trace/signalscalehandle.cpp
new file mode 100644 (file)
index 0000000..11f9bde
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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
diff --git a/pv/views/trace/signalscalehandle.hpp b/pv/views/trace/signalscalehandle.hpp
new file mode 100644 (file)
index 0000000..54eb59e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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
index 6d8f97e6f266dc9f7eaabf4cf317c969fcd22aad..fa02d1677d7cad4de0473571ddb20058d4bcc9c1 100644 (file)
@@ -22,9 +22,9 @@
 #include <QMessageBox>
 
 #include "standardbar.hpp"
+#include "view.hpp"
 
 #include <pv/mainwindow.hpp>
-#include <pv/view/view.hpp>
 
 using pv::views::TraceView::View;
 
diff --git a/pv/views/trace/timeitem.cpp b/pv/views/trace/timeitem.cpp
new file mode 100644 (file)
index 0000000..bd07e9b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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
diff --git a/pv/views/trace/timeitem.hpp b/pv/views/trace/timeitem.hpp
new file mode 100644 (file)
index 0000000..dd8d3c4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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
diff --git a/pv/views/trace/timemarker.cpp b/pv/views/trace/timemarker.cpp
new file mode 100644 (file)
index 0000000..487496d
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * 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
diff --git a/pv/views/trace/timemarker.hpp b/pv/views/trace/timemarker.hpp
new file mode 100644 (file)
index 0000000..7c49948
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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
diff --git a/pv/views/trace/trace.cpp b/pv/views/trace/trace.cpp
new file mode 100644 (file)
index 0000000..46d9edb
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * 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
diff --git a/pv/views/trace/trace.hpp b/pv/views/trace/trace.hpp
new file mode 100644 (file)
index 0000000..88b4ea6
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracegroup.cpp b/pv/views/trace/tracegroup.cpp
new file mode 100644 (file)
index 0000000..9bcc29e
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracegroup.hpp b/pv/views/trace/tracegroup.hpp
new file mode 100644 (file)
index 0000000..6a72318
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracepalette.cpp b/pv/views/trace/tracepalette.cpp
new file mode 100644 (file)
index 0000000..762eafc
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracepalette.hpp b/pv/views/trace/tracepalette.hpp
new file mode 100644 (file)
index 0000000..c3a8701
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracetreeitem.cpp b/pv/views/trace/tracetreeitem.cpp
new file mode 100644 (file)
index 0000000..470b6ad
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracetreeitem.hpp b/pv/views/trace/tracetreeitem.hpp
new file mode 100644 (file)
index 0000000..7a4bf46
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracetreeitemowner.cpp b/pv/views/trace/tracetreeitemowner.cpp
new file mode 100644 (file)
index 0000000..a65c621
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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
diff --git a/pv/views/trace/tracetreeitemowner.hpp b/pv/views/trace/tracetreeitemowner.hpp
new file mode 100644 (file)
index 0000000..5710900
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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
diff --git a/pv/views/trace/triggermarker.cpp b/pv/views/trace/triggermarker.cpp
new file mode 100644 (file)
index 0000000..31afba1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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
diff --git a/pv/views/trace/triggermarker.hpp b/pv/views/trace/triggermarker.hpp
new file mode 100644 (file)
index 0000000..09017c0
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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
diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp
new file mode 100644 (file)
index 0000000..dbc0d20
--- /dev/null
@@ -0,0 +1,1379 @@
+/*
+ * 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
diff --git a/pv/views/trace/view.hpp b/pv/views/trace/view.hpp
new file mode 100644 (file)
index 0000000..bfa7ed3
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitem.cpp b/pv/views/trace/viewitem.cpp
new file mode 100644 (file)
index 0000000..61bc99f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitem.hpp b/pv/views/trace/viewitem.hpp
new file mode 100644 (file)
index 0000000..e1cc200
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitemiterator.hpp b/pv/views/trace/viewitemiterator.hpp
new file mode 100644 (file)
index 0000000..88600d0
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitemowner.cpp b/pv/views/trace/viewitemowner.cpp
new file mode 100644 (file)
index 0000000..855cb1a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitemowner.hpp b/pv/views/trace/viewitemowner.hpp
new file mode 100644 (file)
index 0000000..b8dbe20
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitempaintparams.cpp b/pv/views/trace/viewitempaintparams.cpp
new file mode 100644 (file)
index 0000000..2d9cfbb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewitempaintparams.hpp b/pv/views/trace/viewitempaintparams.hpp
new file mode 100644 (file)
index 0000000..f39085c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewport.cpp b/pv/views/trace/viewport.cpp
new file mode 100644 (file)
index 0000000..d426a13
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewport.hpp b/pv/views/trace/viewport.hpp
new file mode 100644 (file)
index 0000000..ab67f69
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewwidget.cpp b/pv/views/trace/viewwidget.cpp
new file mode 100644 (file)
index 0000000..5a1aa3c
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * 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
diff --git a/pv/views/trace/viewwidget.hpp b/pv/views/trace/viewwidget.hpp
new file mode 100644 (file)
index 0000000..e4fb73c
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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
index 09678e84e87ae87788fbeb1c41ba9a31638a5ec4..8cab18feb3db969f59d5a53813f9b07885a2c04c 100644 (file)
@@ -51,31 +51,31 @@ set(pulseview_TEST_SOURCES
        ${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
@@ -120,26 +120,26 @@ set(pulseview_TEST_HEADERS
        ${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
@@ -162,7 +162,7 @@ if(ENABLE_DECODE)
                ${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
@@ -170,7 +170,7 @@ if(ENABLE_DECODE)
 
        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
        )
index 5230e240dd9450f16757480e9e8de9b7cd89eb63..9287975cae82cd6243fa26c9605258c9a66a836c 100644 (file)
@@ -20,7 +20,7 @@
 #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;