# include "tools/cabana/binaryview.h"
# include <QApplication>
# include <QHeaderView>
# include <QPainter>
# include "tools/cabana/canmessages.h"
// BinaryView
const int CELL_HEIGHT = 30 ;
BinaryView : : BinaryView ( QWidget * parent ) : QTableView ( parent ) {
model = new BinaryViewModel ( this ) ;
setModel ( model ) ;
setItemDelegate ( new BinaryItemDelegate ( this ) ) ;
horizontalHeader ( ) - > setSectionResizeMode ( QHeaderView : : Stretch ) ;
horizontalHeader ( ) - > hide ( ) ;
verticalHeader ( ) - > setSectionResizeMode ( QHeaderView : : Stretch ) ;
setHorizontalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
// 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 ) ;
} ) ;
}
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 ) ;
}
}
void BinaryView : : setMessage ( const QString & message_id ) {
msg_id = message_id ;
model - > setMessage ( message_id ) ;
resizeRowsToContents ( ) ;
clearSelection ( ) ;
updateState ( ) ;
}
void BinaryView : : updateState ( ) {
model - > updateState ( ) ;
}
// BinaryViewModel
void BinaryViewModel : : setMessage ( const QString & message_id ) {
msg_id = message_id ;
beginResetModel ( ) ;
items . clear ( ) ;
row_count = 0 ;
dbc_msg = dbc ( ) - > msg ( msg_id ) ;
if ( dbc_msg ) {
row_count = dbc_msg - > size ;
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 ;
for ( int j = start ; j < = end ; + + j ) {
int idx = column_count * ( j / 8 ) + j % 8 ;
if ( idx > = items . size ( ) ) {
qWarning ( ) < < " signal " < < sig . name . c_str ( ) < < " out of bounds.start_bit: " < < sig . start_bit < < " size: " < < sig . size ;
break ;
}
if ( j = = start ) {
sig . is_little_endian ? items [ idx ] . is_lsb = true : items [ idx ] . is_msb = true ;
} else if ( j = = end ) {
sig . is_little_endian ? items [ idx ] . is_msb = true : items [ idx ] . is_lsb = true ;
}
items [ idx ] . bg_color = QColor ( getColor ( i ) ) ;
}
}
}
endResetModel ( ) ;
}
QModelIndex BinaryViewModel : : index ( int row , int column , const QModelIndex & parent ) const {
return createIndex ( row , column , ( void * ) & items [ row * column_count + column ] ) ;
}
Qt : : ItemFlags BinaryViewModel : : flags ( const QModelIndex & index ) const {
return ( index . column ( ) = = column_count - 1 ) ? Qt : : ItemIsEnabled : Qt : : ItemIsEnabled | Qt : : ItemIsSelectable ;
}
void BinaryViewModel : : updateState ( ) {
auto prev_items = items ;
const auto & binary = can - > lastMessage ( msg_id ) . dat ;
// data size may changed.
if ( ! dbc_msg & & binary . size ( ) ! = row_count ) {
beginResetModel ( ) ;
row_count = binary . size ( ) ;
items . clear ( ) ;
items . resize ( row_count * column_count ) ;
endResetModel ( ) ;
}
char hex [ 3 ] = { ' \0 ' } ;
for ( int i = 0 ; i < std : : min ( binary . size ( ) , row_count ) ; + + i ) {
for ( int j = 0 ; j < column_count - 1 ; + + j ) {
items [ i * column_count + j ] . val = QChar ( ( binary [ i ] > > ( 7 - j ) ) & 1 ? ' 1 ' : ' 0 ' ) ;
}
hex [ 0 ] = toHex ( binary [ i ] > > 4 ) ;
hex [ 1 ] = toHex ( binary [ i ] & 0xf ) ;
items [ i * column_count + 8 ] . val = hex ;
}
for ( int i = 0 ; i < items . size ( ) ; + + i ) {
if ( i > = prev_items . size ( ) | | prev_items [ i ] . val ! = items [ i ] . val ) {
auto idx = index ( i / column_count , i % column_count ) ;
emit dataChanged ( idx , idx ) ;
}
}
}
QVariant BinaryViewModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const {
if ( orientation = = Qt : : Vertical ) {
switch ( role ) {
case Qt : : DisplayRole : return section + 1 ;
case Qt : : SizeHintRole : return QSize ( 30 , CELL_HEIGHT ) ;
case Qt : : TextAlignmentRole : return Qt : : AlignCenter ;
}
}
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 ) {
// cache fonts and color
small_font . setPointSize ( 6 ) ;
bold_font . setBold ( true ) ;
highlight_color = QApplication : : style ( ) - > standardPalette ( ) . color ( QPalette : : Active , QPalette : : Highlight ) ;
}
QSize BinaryItemDelegate : : sizeHint ( const QStyleOptionViewItem & option , const QModelIndex & index ) const {
QSize sz = QStyledItemDelegate : : sizeHint ( option , index ) ;
return { sz . width ( ) , CELL_HEIGHT } ;
}
void BinaryItemDelegate : : paint ( QPainter * painter , const QStyleOptionViewItem & option , const QModelIndex & index ) const {
auto item = ( const BinaryViewModel : : Item * ) index . internalPointer ( ) ;
painter - > save ( ) ;
// TODO: highlight signal cells on mouse over
painter - > fillRect ( option . rect , option . state & QStyle : : State_Selected ? highlight_color : item - > bg_color ) ;
if ( index . column ( ) = = 8 ) {
painter - > setFont ( bold_font ) ;
}
painter - > drawText ( option . rect , Qt : : AlignCenter , item - > val ) ;
if ( item - > is_msb | | item - > is_lsb ) {
painter - > setFont ( small_font ) ;
painter - > drawText ( option . rect , Qt : : AlignHCenter | Qt : : AlignBottom , item - > is_msb ? " MSB " : " LSB " ) ;
}
painter - > restore ( ) ;
}