diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index c0ebfa9f0a..eee3f90986 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -13,21 +13,23 @@ const int CELL_HEIGHT = 30; +static std::pair getSignalRange(const Signal *s) { + int from = s->is_little_endian ? s->start_bit : bigEndianBitIndex(s->start_bit); + int to = from + s->size - 1; + return {from, to}; +} + BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { model = new BinaryViewModel(this); setModel(model); - setItemDelegate(new BinaryItemDelegate(this)); + delegate = new BinaryItemDelegate(this); + setItemDelegate(delegate); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); horizontalHeader()->hide(); verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setMouseTracking(true); - // replace selection model - auto old_model = selectionModel(); - setSelectionModel(new BinarySelectionModel(model)); - delete old_model; - QObject::connect(model, &QAbstractItemModel::modelReset, [this]() { setFixedHeight((CELL_HEIGHT + 1) * std::min(model->rowCount(), 8) + 2); }); @@ -41,12 +43,42 @@ void BinaryView::highlight(const Signal *sig) { } } +void BinaryView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) { + QModelIndex tl = indexAt({qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())}); + QModelIndex br = indexAt({qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())}); + if (!tl.isValid() || !br.isValid()) + return; + + if (tl < anchor_index) { + br = anchor_index; + } else if (anchor_index < br) { + tl = anchor_index; + } + QItemSelection selection; + for (int row = tl.row(); row <= br.row(); ++row) { + int left_col = (row == tl.row()) ? tl.column() : 0; + int right_col = (row == br.row()) ? br.column() : 7; + selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); + } + selectionModel()->select(selection, flags); +} + +void BinaryView::mousePressEvent(QMouseEvent *event) { + delegate->setSelectionColor(style()->standardPalette().color(QPalette::Active, QPalette::Highlight)); + anchor_index = indexAt(event->pos()); + if (getResizingSignal() != nullptr) { + auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); + delegate->setSelectionColor(item->bg_color); + } + QTableView::mousePressEvent(event); +} + void BinaryView::mouseMoveEvent(QMouseEvent *event) { if (auto index = indexAt(event->pos()); index.isValid()) { auto item = (BinaryViewModel::Item *)index.internalPointer(); highlight(item->sig); - if (item->sig) - QToolTip::showText(event->globalPos(), item->sig->name.c_str(), this, rect()); + item->sig ? QToolTip::showText(event->globalPos(), item->sig->name.c_str(), this, rect()) + : QToolTip::hideText(); } QTableView::mouseMoveEvent(event); } @@ -55,10 +87,21 @@ void BinaryView::mouseReleaseEvent(QMouseEvent *event) { QTableView::mouseReleaseEvent(event); if (auto indexes = selectedIndexes(); !indexes.isEmpty()) { - int start_bit = indexes.first().row() * 8 + indexes.first().column(); - int size = indexes.back().row() * 8 + indexes.back().column() - start_bit + 1; - emit cellsSelected(start_bit, size); + int from = indexes.first().row() * 8 + indexes.first().column(); + int to = indexes.back().row() * 8 + indexes.back().column(); + if (auto sig = getResizingSignal()) { + auto [sig_from, sig_to] = getSignalRange(sig); + if (from >= sig_from && to <= sig_to) { // reduce size + emit(from == sig_from ? resizeSignal(sig, to, sig_to) : resizeSignal(sig, sig_from, from)); + } else { // increase size + emit resizeSignal(sig, std::min(from, sig_from), std::max(to, sig_to)); + } + } else { + emit addSignal(from, to); + } + clearSelection(); } + anchor_index = QModelIndex(); } void BinaryView::leaveEvent(QEvent *event) { @@ -78,6 +121,19 @@ void BinaryView::updateState() { model->updateState(); } +const Signal *BinaryView::getResizingSignal() const { + if (anchor_index.isValid()) { + auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); + if (item && item->sig) { + int archor_pos = anchor_index.row() * 8 + anchor_index.column(); + auto [sig_from, sig_to] = getSignalRange(item->sig); + if (archor_pos == sig_from || archor_pos == sig_to) + return item->sig; + } + } + return nullptr; +} + // BinaryViewModel void BinaryViewModel::setMessage(const QString &message_id) { @@ -93,8 +149,7 @@ void BinaryViewModel::setMessage(const QString &message_id) { items.resize(row_count * column_count); for (int i = 0; i < dbc_msg->sigs.size(); ++i) { const auto &sig = dbc_msg->sigs[i]; - const int start = sig.is_little_endian ? sig.start_bit : bigEndianBitIndex(sig.start_bit); - const int end = start + sig.size - 1; + auto [start, end] = getSignalRange(&sig); for (int j = start; j <= end; ++j) { int idx = column_count * (j / 8) + j % 8; if (idx >= items.size()) { @@ -165,21 +220,6 @@ QVariant BinaryViewModel::headerData(int section, Qt::Orientation orientation, i return {}; } -// BinarySelectionModel - -void BinarySelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { - QItemSelection new_selection = selection; - if (auto indexes = selection.indexes(); !indexes.isEmpty()) { - auto [begin_idx, end_idx] = (QModelIndex[]){indexes.first(), indexes.back()}; - for (int row = begin_idx.row(); row <= end_idx.row(); ++row) { - int left_col = (row == begin_idx.row()) ? begin_idx.column() : 0; - int right_col = (row == end_idx.row()) ? end_idx.column() : 7; - new_selection.merge({model()->index(row, left_col), model()->index(row, right_col)}, command); - } - } - QItemSelectionModel::select(new_selection, command); -} - // BinaryItemDelegate BinaryItemDelegate::BinaryItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { @@ -187,7 +227,7 @@ BinaryItemDelegate::BinaryItemDelegate(QObject *parent) : QStyledItemDelegate(pa small_font.setPointSize(6); hex_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); hex_font.setBold(true); - highlight_color = QApplication::style()->standardPalette().color(QPalette::Active, QPalette::Highlight); + selection_color = QApplication::style()->standardPalette().color(QPalette::Active, QPalette::Highlight); } QSize BinaryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { @@ -204,7 +244,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op // background QColor bg_color = hover ? hoverColor(item->bg_color) : item->bg_color; if (option.state & QStyle::State_Selected) { - bg_color = highlight_color; + bg_color = selection_color; } painter->fillRect(option.rect, bg_color); diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 48eb5eff8a..8c1a582014 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -12,10 +12,11 @@ public: BinaryItemDelegate(QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setSelectionColor(const QColor &color) { selection_color = color; } private: QFont small_font, hex_font; - QColor highlight_color; + QColor selection_color; }; class BinaryViewModel : public QAbstractTableModel { @@ -48,13 +49,6 @@ private: std::vector items; }; -// the default QItemSelectionModel does not support our selection mode. -class BinarySelectionModel : public QItemSelectionModel { - public: - BinarySelectionModel(QAbstractItemModel *model = nullptr) : QItemSelectionModel(model) {} - void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override; -}; - class BinaryView : public QTableView { Q_OBJECT @@ -66,15 +60,21 @@ public: const Signal *hoveredSignal() const { return hovered_sig; } signals: - void cellsSelected(int start_bit, int size); void signalHovered(const Signal *sig); + void addSignal(int from, int size); + void resizeSignal(const Signal *sig, int from, int size); private: + void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override; + void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void leaveEvent(QEvent *event) override; + const Signal *getResizingSignal() const; QString msg_id; + QModelIndex anchor_index; BinaryViewModel *model; + BinaryItemDelegate *delegate; const Signal *hovered_sig = nullptr; }; diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index fdedb066fb..579bb1af75 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -73,7 +73,8 @@ DetailWidget::DetailWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(history_log); QObject::connect(edit_btn, &QPushButton::clicked, this, &DetailWidget::editMsg); - QObject::connect(binary_view, &BinaryView::cellsSelected, this, &DetailWidget::addSignal); + QObject::connect(binary_view, &BinaryView::resizeSignal, this, &DetailWidget::resizeSignal); + QObject::connect(binary_view, &BinaryView::addSignal, this, &DetailWidget::addSignal); QObject::connect(can, &CANMessages::updated, this, &DetailWidget::updateState); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &DetailWidget::dbcMsgChanged); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { setMessage(messages[index]); }); @@ -159,9 +160,9 @@ void DetailWidget::editMsg() { } } -void DetailWidget::addSignal(int start_bit, int size) { +void DetailWidget::addSignal(int start_bit, int to) { if (dbc()->msg(msg_id)) { - AddSignalDialog dlg(msg_id, start_bit, size, this); + AddSignalDialog dlg(msg_id, start_bit, to - start_bit + 1, this); if (dlg.exec()) { dbc()->addSignal(msg_id, dlg.form->getSignal()); dbcMsgChanged(); @@ -169,6 +170,22 @@ void DetailWidget::addSignal(int start_bit, int size) { } } +void DetailWidget::resizeSignal(const Signal *sig, int from, int to) { + assert(sig != nullptr); + Signal s = *sig; + s.start_bit = s.is_little_endian ? from : bigEndianBitIndex(from);; + s.size = to - from + 1; + if (s.is_little_endian) { + s.lsb = s.start_bit; + s.msb = s.start_bit + s.size - 1; + } else { + s.lsb = bigEndianStartBitsIndex(bigEndianBitIndex(s.start_bit) + s.size - 1); + s.msb = s.start_bit; + } + dbc()->updateSignal(msg_id, s.name.c_str(), s); + dbcMsgChanged(); +} + void DetailWidget::saveSignal() { SignalEdit *sig_form = qobject_cast(QObject::sender()); auto s = sig_form->form->getSignal(); diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index d935839dc4..02bbb2a9e8 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -39,7 +39,8 @@ signals: void removeChart(const Signal *sig); private: - void addSignal(int start_bit, int size); + void addSignal(int start_bit, int to); + void resizeSignal(const Signal *sig, int from, int to); void saveSignal(); void removeSignal(); void editMsg();