#include <climits>
#include <cmath>
#include <mutex>
+#include <unordered_set>
#include <QEvent>
#include <QMouseEvent>
#include <QScrollBar>
+#include <libsigrok/libsigrok.hpp>
+
#include "cursorheader.h"
#include "decodetrace.h"
#include "header.h"
+#include "logicsignal.h"
#include "ruler.h"
#include "signal.h"
+#include "tracegroup.h"
#include "view.h"
#include "viewport.h"
#include "pv/data/logic.h"
#include "pv/data/logicsnapshot.h"
+using boost::shared_lock;
+using boost::shared_mutex;
using pv::data::SignalData;
using std::back_inserter;
using std::deque;
+using std::dynamic_pointer_cast;
using std::list;
using std::lock_guard;
using std::max;
using std::make_pair;
using std::min;
-using std::mutex;
using std::pair;
using std::set;
using std::shared_ptr;
+using std::unordered_map;
+using std::unordered_set;
using std::vector;
using std::weak_ptr;
const int View::MaxScrollValue = INT_MAX / 2;
-const int View::SignalHeight = 30;
-const int View::SignalMargin = 10;
-const int View::SignalSnapGridSize = 10;
-
const QColor View::CursorAreaColour(220, 231, 243);
const QSizeF View::LabelPadding(4, 0);
connect(_cursors.second().get(), SIGNAL(time_changed()),
this, SLOT(marker_time_changed()));
- connect(_header, SIGNAL(geometry_updated()),
- this, SLOT(on_geometry_updated()));
connect(_header, SIGNAL(signals_moved()),
this, SLOT(on_signals_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);
+
setViewport(_viewport);
_viewport->installEventFilter(this);
return _offset;
}
-int View::owner_v_offset() const
+int View::owner_visual_v_offset() const
{
return -_v_offset;
}
+unsigned int View::depth() const
+{
+ return 0;
+}
+
void View::zoom(double steps)
{
zoom(steps, _viewport->width() / 2);
scale_offset_changed();
}
-list<weak_ptr<SelectableItem> > View::selected_items() const
-{
- list<weak_ptr<SelectableItem> > items;
-
- // List the selected signals
- const vector< shared_ptr<RowItem> > row_items(child_items());
- for (shared_ptr<RowItem> r : row_items) {
- if (r && r->selected())
- items.push_back(r);
- }
-
- // List the selected cursors
- if (_cursors.first()->selected())
- items.push_back(_cursors.first());
- if (_cursors.second()->selected())
- items.push_back(_cursors.second());
-
- return items;
-}
-
set< shared_ptr<SignalData> > View::get_visible_data() const
{
- lock_guard<mutex> lock(session().signals_mutex());
+ shared_lock<shared_mutex> lock(session().signals_mutex());
const vector< shared_ptr<Signal> > &sigs(session().signals());
// Make a set of all the visible data objects
return _hover_point;
}
-void View::normalize_layout()
+void View::update_viewport()
{
- const vector< shared_ptr<RowItem> > row_items(child_items());
+ assert(_viewport);
+ _viewport->update();
+ _header->update();
+}
- int v_min = INT_MAX;
- for (const shared_ptr<RowItem> r : row_items)
- v_min = min(r->v_offset(), v_min);
+void View::restack_all_row_items()
+{
+ // Make a set of owners
+ unordered_set< RowItemOwner* > owners;
+ for (const auto &r : *this)
+ owners.insert(r->owner());
- const int delta = -min(v_min, 0);
- for (shared_ptr<RowItem> r : row_items)
- r->set_v_offset(r->v_offset() + delta);
+ // Make a list that is sorted from deepest first
+ vector< RowItemOwner* > sorted_owners(owners.begin(), owners.end());
+ sort(sorted_owners.begin(), sorted_owners.end(),
+ [](const RowItemOwner* a, const RowItemOwner *b) {
+ return a->depth() > b->depth(); });
- verticalScrollBar()->setSliderPosition(_v_offset + delta);
- v_scroll_value_changed(verticalScrollBar()->sliderPosition());
-}
+ // Restack the items recursively
+ for (auto &o : sorted_owners)
+ o->restack_items();
-void View::update_viewport()
-{
- assert(_viewport);
- _viewport->update();
+ // Animate the items to their destination
+ for (const auto &r : *this)
+ r->animate_to_layout_v_offset();
}
void View::get_scroll_layout(double &length, double &offset) const
// Set the vertical scrollbar
verticalScrollBar()->setPageStep(areaSize.height());
- verticalScrollBar()->setRange(0,
- _viewport->get_total_height() + SignalMargin -
- areaSize.height());
+
+ const pair<int, int> extents = v_extents();
+ const int extra_scroll_height = (extents.second - extents.first) / 4;
+ verticalScrollBar()->setRange(extents.first - extra_scroll_height,
+ extents.first + extra_scroll_height);
}
void View::update_layout()
return QRectF();
}
+bool View::add_channels_to_owner(
+ const vector< shared_ptr<sigrok::Channel> > &channels,
+ RowItemOwner *owner, int &offset,
+ unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+ &signal_map,
+ std::function<bool (shared_ptr<RowItem>)> filter_func)
+{
+ bool any_added = false;
+
+ assert(owner);
+
+ for (const auto &channel : channels)
+ {
+ const auto iter = signal_map.find(channel);
+ if (iter == signal_map.end() ||
+ (filter_func && !filter_func((*iter).second)))
+ continue;
+
+ shared_ptr<RowItem> row_item = (*iter).second;
+ owner->add_child_item(row_item);
+ apply_offset(row_item, offset);
+ signal_map.erase(iter);
+
+ any_added = true;
+ }
+
+ return any_added;
+}
+
+void View::apply_offset(shared_ptr<RowItem> row_item, int &offset) {
+ assert(row_item);
+ const pair<int, int> extents = row_item->v_extents();
+ if (row_item->enabled())
+ offset += -extents.first;
+ row_item->force_to_v_offset(offset);
+ if (row_item->enabled())
+ offset += extents.second;
+}
+
bool View::eventFilter(QObject *object, QEvent *event)
{
const QEvent::Type type = event->type();
update_layout();
}
+void View::appearance_changed(bool label, bool content)
+{
+ if (label)
+ _header->update();
+ if (content)
+ _viewport->update();
+}
+
+void View::extents_changed(bool horz, bool vert)
+{
+ _sticky_events |=
+ (horz ? SelectableItemHExtentsChanged : 0) |
+ (vert ? SelectableItemVExtentsChanged : 0);
+ _lazy_event_handler.start();
+}
+
void View::h_scroll_value_changed(int value)
{
if (_updating_scroll)
void View::signals_changed()
{
+ int offset = 0;
+
// Populate the traces
clear_child_items();
- lock_guard<mutex> lock(session().signals_mutex());
- const vector< shared_ptr<Signal> > &sigs(session().signals());
- for (auto s : sigs)
- add_child_item(s);
+ shared_ptr<sigrok::Device> device = _session.device();
+ assert(device);
+
+ // Collect a set of signals
+ unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+ signal_map;
+
+ shared_lock<shared_mutex> lock(_session.signals_mutex());
+ const vector< shared_ptr<Signal> > &sigs(_session.signals());
+ for (const shared_ptr<Signal> &sig : sigs)
+ signal_map[sig->channel()] = sig;
+
+ // Populate channel groups
+ for (auto entry : device->channel_groups())
+ {
+ const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
+
+ if (group->channels().size() <= 1)
+ continue;
+
+ shared_ptr<TraceGroup> trace_group(new TraceGroup());
+ int child_offset = 0;
+ if (add_channels_to_owner(group->channels(),
+ trace_group.get(), child_offset, signal_map))
+ {
+ add_child_item(trace_group);
+ apply_offset(trace_group, offset);
+ }
+ }
+
+ // Add the remaining logic channels
+ shared_ptr<TraceGroup> logic_trace_group(new TraceGroup());
+ int child_offset = 0;
+
+ if (add_channels_to_owner(device->channels(),
+ logic_trace_group.get(), child_offset, signal_map,
+ [](shared_ptr<RowItem> r) -> bool {
+ return dynamic_pointer_cast<LogicSignal>(r) != nullptr;
+ }))
+
+ {
+ add_child_item(logic_trace_group);
+ apply_offset(logic_trace_group, offset);
+ }
+
+ // Add the remaining channels
+ add_channels_to_owner(device->channels(), this, offset, signal_map);
+ assert(signal_map.empty());
+
+ // Add decode signals
#ifdef ENABLE_DECODE
const vector< shared_ptr<DecodeTrace> > decode_sigs(
session().get_decode_signals());
- for (auto s : decode_sigs)
+ for (auto s : decode_sigs) {
add_child_item(s);
-#endif
-
- // Create the initial layout
- int offset = SignalMargin + SignalHeight;
- for (shared_ptr<RowItem> r : child_items()) {
- r->set_v_offset(offset);
- offset += SignalHeight + 2 * SignalMargin;
+ apply_offset(s, offset);
}
+#endif
update_layout();
- normalize_layout();
}
void View::data_updated()
signals_moved();
}
-void View::on_geometry_updated()
+void View::process_sticky_events()
{
- update_layout();
+ if (_sticky_events & SelectableItemHExtentsChanged)
+ update_layout();
+ if (_sticky_events & SelectableItemVExtentsChanged)
+ restack_all_row_items();
+
+ // Clear the sticky events
+ _sticky_events = 0;
}
void View::on_hover_point_changed()
{
- const vector< shared_ptr<RowItem> > row_items(child_items());
- for (shared_ptr<RowItem> r : row_items)
+ for (shared_ptr<RowItem> r : *this)
r->hover_point_changed();
}