|  |  |  | @ -6,7 +6,6 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #include <QHeaderView> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QLineEdit> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QPushButton> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QSortFilterProxyModel> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QTextEdit> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QVBoxLayout> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -49,27 +48,23 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { | 
			
		
	
		
			
				
					|  |  |  |  |   // message table
 | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget = new QTableView(this); | 
			
		
	
		
			
				
					|  |  |  |  |   model = new MessageListModel(this); | 
			
		
	
		
			
				
					|  |  |  |  |   QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this); | 
			
		
	
		
			
				
					|  |  |  |  |   proxy_model->setSourceModel(model); | 
			
		
	
		
			
				
					|  |  |  |  |   proxy_model->setFilterCaseSensitivity(Qt::CaseInsensitive); | 
			
		
	
		
			
				
					|  |  |  |  |   proxy_model->setDynamicSortFilter(false); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setModel(proxy_model); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setModel(model); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setSelectionMode(QAbstractItemView::SingleSelection); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setSortingEnabled(true); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->sortByColumn(0, Qt::AscendingOrder); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setColumnWidth(0, 250); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setColumnWidth(1, 80); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->setColumnWidth(2, 80); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->horizontalHeader()->setStretchLastSection(true); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->verticalHeader()->hide(); | 
			
		
	
		
			
				
					|  |  |  |  |   table_widget->sortByColumn(0, Qt::AscendingOrder); | 
			
		
	
		
			
				
					|  |  |  |  |   main_layout->addWidget(table_widget); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // signals/slots
 | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(filter, &QLineEdit::textChanged, proxy_model, &QSortFilterProxyModel::setFilterFixedString); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(filter, &QLineEdit::textChanged, model, &MessageListModel::setFilterString); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(can, &CANMessages::eventsMerged, this, &MessagesWidget::loadDBCFromFingerprint); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(can, &CANMessages::updated, model, &MessageListModel::updateState); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(can, &CANMessages::updated, [this]() { model->updateState(); }); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &))); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(load_from_paste, &QPushButton::clicked, this, &MessagesWidget::loadDBCFromPaste); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(save_btn, &QPushButton::clicked, [=]() { | 
			
		
	
	
		
			
				
					|  |  |  | @ -122,21 +117,15 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | QVariant MessageListModel::data(const QModelIndex &index, int role) const { | 
			
		
	
		
			
				
					|  |  |  |  |   if (role == Qt::DisplayRole) { | 
			
		
	
		
			
				
					|  |  |  |  |     auto it = std::next(can->can_msgs.begin(), index.row()); | 
			
		
	
		
			
				
					|  |  |  |  |     if (it != can->can_msgs.end() && !it.value().empty()) { | 
			
		
	
		
			
				
					|  |  |  |  |       const QString &msg_id = it.key(); | 
			
		
	
		
			
				
					|  |  |  |  |       switch (index.column()) { | 
			
		
	
		
			
				
					|  |  |  |  |         case 0: { | 
			
		
	
		
			
				
					|  |  |  |  |           auto msg = dbc()->msg(msg_id); | 
			
		
	
		
			
				
					|  |  |  |  |           return msg ? msg->name.c_str() : "untitled"; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         case 1: return msg_id; | 
			
		
	
		
			
				
					|  |  |  |  |         case 2: return can->counters[msg_id]; | 
			
		
	
		
			
				
					|  |  |  |  |         case 3: return toHex(it.value().front().dat); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |     const auto &m = msgs[index.row()]; | 
			
		
	
		
			
				
					|  |  |  |  |     switch (index.column()) { | 
			
		
	
		
			
				
					|  |  |  |  |       case 0: return m->name; | 
			
		
	
		
			
				
					|  |  |  |  |       case 1: return m->id; | 
			
		
	
		
			
				
					|  |  |  |  |       case 2: return can->counters[m->id]; | 
			
		
	
		
			
				
					|  |  |  |  |       case 3: return toHex(can->lastMessage(m->id).dat); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (role == Qt::UserRole) { | 
			
		
	
		
			
				
					|  |  |  |  |     return std::next(can->can_msgs.begin(), index.row()).key(); | 
			
		
	
		
			
				
					|  |  |  |  |     return msgs[index.row()]->id; | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (role == Qt::FontRole) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (index.column() == 3) { | 
			
		
	
		
			
				
					|  |  |  |  |       return QFontDatabase::systemFont(QFontDatabase::FixedFont); | 
			
		
	
	
		
			
				
					|  |  |  | @ -145,20 +134,77 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { | 
			
		
	
		
			
				
					|  |  |  |  |   return {}; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void MessageListModel::updateState() { | 
			
		
	
		
			
				
					|  |  |  |  |   int prev_row_count = row_count; | 
			
		
	
		
			
				
					|  |  |  |  |   row_count = can->can_msgs.size(); | 
			
		
	
		
			
				
					|  |  |  |  |   int delta = row_count - prev_row_count; | 
			
		
	
		
			
				
					|  |  |  |  | bool MessageListModel::updateMessages(bool sort) { | 
			
		
	
		
			
				
					|  |  |  |  |   if (msgs.size() == can->can_msgs.size() && filter_str.isEmpty() && !sort) | 
			
		
	
		
			
				
					|  |  |  |  |     return false; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // update message list
 | 
			
		
	
		
			
				
					|  |  |  |  |   int i = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   bool search_id = filter_str.contains(':'); | 
			
		
	
		
			
				
					|  |  |  |  |   for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { | 
			
		
	
		
			
				
					|  |  |  |  |     const Msg *msg = dbc()->msg(it.key()); | 
			
		
	
		
			
				
					|  |  |  |  |     QString msg_name = msg ? msg->name.c_str() : "untitled"; | 
			
		
	
		
			
				
					|  |  |  |  |     if (!filter_str.isEmpty() && !(search_id ? it.key() : msg_name).contains(filter_str, Qt::CaseInsensitive)) | 
			
		
	
		
			
				
					|  |  |  |  |       continue; | 
			
		
	
		
			
				
					|  |  |  |  |     auto &m = i < msgs.size() ? msgs[i] : msgs.emplace_back(new Message); | 
			
		
	
		
			
				
					|  |  |  |  |     m->id = it.key(); | 
			
		
	
		
			
				
					|  |  |  |  |     m->name = msg_name; | 
			
		
	
		
			
				
					|  |  |  |  |     ++i; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   msgs.resize(i); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (sort_column == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { | 
			
		
	
		
			
				
					|  |  |  |  |       bool ret = l->name < r->name || (l->name == r->name && l->id < r->id); | 
			
		
	
		
			
				
					|  |  |  |  |       return sort_order == Qt::AscendingOrder ? ret : !ret; | 
			
		
	
		
			
				
					|  |  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (sort_column == 1) { | 
			
		
	
		
			
				
					|  |  |  |  |     std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { | 
			
		
	
		
			
				
					|  |  |  |  |       return sort_order == Qt::AscendingOrder ? l->id < r->id : l->id > r->id; | 
			
		
	
		
			
				
					|  |  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (sort_column == 2) { | 
			
		
	
		
			
				
					|  |  |  |  |     std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { | 
			
		
	
		
			
				
					|  |  |  |  |       uint32_t lcount = can->counters[l->id], rcount = can->counters[r->id]; | 
			
		
	
		
			
				
					|  |  |  |  |       bool ret = lcount < rcount || (lcount == rcount && l->id < r->id); | 
			
		
	
		
			
				
					|  |  |  |  |       return sort_order == Qt::AscendingOrder ? ret : !ret; | 
			
		
	
		
			
				
					|  |  |  |  |     }); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   return true; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void MessageListModel::updateState(bool sort) { | 
			
		
	
		
			
				
					|  |  |  |  |   int prev_row_count = msgs.size(); | 
			
		
	
		
			
				
					|  |  |  |  |   auto prev_idx = persistentIndexList(); | 
			
		
	
		
			
				
					|  |  |  |  |   QString selected_msg_id = prev_idx.empty() ? "" : prev_idx[0].data(Qt::UserRole).toString(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   bool msg_updated = updateMessages(sort); | 
			
		
	
		
			
				
					|  |  |  |  |   int delta = msgs.size() - prev_row_count; | 
			
		
	
		
			
				
					|  |  |  |  |   if (delta > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     beginInsertRows({}, prev_row_count, row_count - 1); | 
			
		
	
		
			
				
					|  |  |  |  |     beginInsertRows({}, prev_row_count, msgs.size() - 1); | 
			
		
	
		
			
				
					|  |  |  |  |     endInsertRows(); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (delta < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     beginRemoveRows({}, row_count, prev_row_count - 1); | 
			
		
	
		
			
				
					|  |  |  |  |     beginRemoveRows({}, msgs.size(), prev_row_count - 1); | 
			
		
	
		
			
				
					|  |  |  |  |     endRemoveRows(); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (row_count > 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     emit dataChanged(index(0, 0), index(row_count - 1, 3), {Qt::DisplayRole}); | 
			
		
	
		
			
				
					|  |  |  |  |   if (!msgs.empty()) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (msg_updated && !prev_idx.isEmpty()) { | 
			
		
	
		
			
				
					|  |  |  |  |       // keep selection
 | 
			
		
	
		
			
				
					|  |  |  |  |       auto it = std::find_if(msgs.begin(), msgs.end(), [&](auto &m) { return m->id == selected_msg_id; }); | 
			
		
	
		
			
				
					|  |  |  |  |       if (it != msgs.end()) { | 
			
		
	
		
			
				
					|  |  |  |  |         for (auto &idx : prev_idx) | 
			
		
	
		
			
				
					|  |  |  |  |           changePersistentIndex(idx, index(std::distance(msgs.begin(), it), idx.column())); | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     emit dataChanged(index(0, 0), index(msgs.size() - 1, 3), {Qt::DisplayRole}); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void MessageListModel::sort(int column, Qt::SortOrder order) { | 
			
		
	
		
			
				
					|  |  |  |  |   if (column != columnCount() - 1) { | 
			
		
	
		
			
				
					|  |  |  |  |     sort_column = column; | 
			
		
	
		
			
				
					|  |  |  |  |     sort_order = order; | 
			
		
	
		
			
				
					|  |  |  |  |     updateState(true); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | 
 |