42 iEquals(
const std::string& a,
const std::string& b)
44 return a.size() == b.size() &&
48 [](
unsigned char x,
unsigned char y)
49 { return std::tolower(x) == std::tolower(y); });
56 columnFromName(
const std::string& name)
96 maxNewLogLevelType = eUNDEFINED;
118 LogTableModel::entryAt(
int row)
const
121 if (!store || row < 0 || row >= (
signed int)rows.size())
125 const LogSeq seq = rows[row].seq;
130 return &store->at(seq);
136 const int& row =
index.row();
137 const int& column =
index.column();
143 std::unique_lock lock(rowsMutex);
147 return QString(
"n/A");
150 const LogMessage& entry = stored->
message;
156 if (role == Qt::ToolTipRole)
163 if (!entry.backtrace.empty())
165 whatStr = QString::fromStdString(entry.what +
"\nBacktrace:\n" +
170 whatStr = QString::fromStdString(entry.what);
179 case Qt::DisplayRole:
180 case Qt::ToolTipRole:
182 std::unique_lock lock(rowsMutex);
187 return QString(
"n/A");
190 const LogMessage& entry = stored->
message;
201 return QString::fromStdString(entry.who);
206 return QString::fromStdString(entry.tag);
211 if (entry.type != eUNDEFINED)
213 return QString::fromStdString(
224 if (role == Qt::ToolTipRole)
233 if (role == Qt::ToolTipRole)
235 return QString::fromStdString(
236 "Double click to open file in editor: " + entry.file +
":" +
237 QString::number(entry.line).toStdString());
239 else if (!entry.file.empty())
241 return QString::fromStdString(
242 entry.file +
":" + QString::number(entry.line).toStdString());
252 return QString::fromStdString(entry.function);
257 return QString::fromStdString(entry.group);
265 case Qt::DecorationRole:
269 return QIcon(
":icons/document-open-4.ico");
279 std::unique_lock lock(rowsMutex);
280 if (row >= 0 && row < (
signed int)rows.size() && rows[row].injected)
283 font.setItalic(
true);
289 case Qt::BackgroundColorRole:
291 std::unique_lock lock(rowsMutex);
293 if (row < 0 || row >= (
signed int)rows.size())
300 if (rows[row].markerColor.isValid())
302 return rows[row].markerColor;
316 return QColor(200, 200, 250);
319 return QColor(50, 255, 50);
322 return QColor(216, 88, 0);
325 return QColor(255, 64, 64);
328 return QColor(176, 0, 0);
335 if (activeSearchStr.length() == 0)
340 if (rows[row].matchesSearch)
342 return QColor(255, 244, 127);
354 if (searchStr.length() == 0)
361 if (
data(createIndex(row, i), Qt::DisplayRole)
363 .contains(searchStr, Qt::CaseInsensitive))
375 if (QString(logMsg.who.c_str()).contains(searchStr, Qt::CaseInsensitive))
380 if (QString(logMsg.tag.c_str()).contains(searchStr, Qt::CaseInsensitive))
385 if (QString(logMsg.what.c_str()).contains(searchStr, Qt::CaseInsensitive))
390 if (QString(logMsg.backtrace.c_str()).contains(searchStr, Qt::CaseInsensitive))
395 if (QString(logMsg.file.c_str()).contains(searchStr, Qt::CaseInsensitive))
400 if (QString(logMsg.function.c_str()).contains(searchStr, Qt::CaseInsensitive))
405 if (QString::number(logMsg.line).contains(searchStr, Qt::CaseInsensitive))
416 activeSearchStr = searchStr;
422 std::unique_lock lock(rowsMutex);
423 const bool hasSearch = searchStr.length() > 0;
424 for (std::size_t i = 0; i < rows.size(); ++i)
427 rows[i].matchesSearch =
442 if (role == Qt::DisplayRole)
444 if (orientation == Qt::Horizontal)
477 else if (role == Qt::ToolTipRole)
479 if (orientation == Qt::Horizontal)
499 return QString::fromStdString(
501 std::string(
": Double click cell to open Qtcreator at that location"));
526 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
530 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
536 const std::string& filter,
540 entry.
column = columnFromName(columnName);
541 entry.
value = filter;
543 entry.
qValue = QString::fromStdString(filter);
546 activeFilters.push_back(std::move(entry));
554 std::unique_lock lock(rowsMutex);
557 rebuildRowsFromStore();
558 applyMarkersLocked();
567 maxNewLogLevelType = eUNDEFINED;
574 activeFilters.clear();
580 if (markerRegistry == registry)
586 disconnect(markerRegistry,
nullptr,
this,
nullptr);
588 markerRegistry = registry;
591 connect(markerRegistry,
610 std::unique_lock lock(rowsMutex);
611 applyMarkersLocked();
617 LogTableModel::applyMarkersLocked()
622 rows.erase(std::remove_if(rows.begin(),
624 [](
const Row& r) { return r.injected; }),
627 for (Row& row : rows)
629 row.markerColor = QColor();
632 if (!markerRegistry || !store)
637 const std::vector<LogMarker> markers = markerRegistry->markers();
644 for (Row& row : rows)
646 for (
const LogMarker& marker : markers)
648 if (marker.seq == row.seq)
650 row.markerColor = marker.color;
658 for (
const LogMarker& marker : markers)
660 if (!store->contains(marker.seq) || marker.seq < clearedBeforeSeq)
664 const bool present = std::any_of(rows.begin(),
666 [&marker](
const Row& r)
667 { return !r.injected && r.seq == marker.seq; });
674 injected.seq = marker.seq;
675 injected.injected =
true;
676 injected.markerColor = marker.color;
677 injected.matchesSearch =
678 !activeSearchStr.isEmpty() &&
683 const auto pos = std::lower_bound(rows.begin(),
686 [](
const Row& r,
LogSeq seq)
687 { return r.seq < seq; });
688 rows.insert(pos, std::move(injected));
693 LogTableModel::rebuildRowsFromStore()
705 const LogSeq begin = std::max(store->firstSeq(), clearedBeforeSeq);
706 std::vector<Row> collected;
707 for (
LogSeq seq = store->endSeq(); seq > begin;)
710 if (maxEntries != 0 && collected.size() >= maxEntries)
714 const StoredLogEntry& stored = store->at(seq);
721 row.matchesSearch = !activeSearchStr.isEmpty() &&
723 collected.push_back(std::move(row));
726 rows.assign(collected.rbegin(), collected.rend());
732 if (this->materialised == materialised)
736 this->materialised = materialised;
740 std::unique_lock lock(rowsMutex);
743 rebuildRowsFromStore();
744 applyMarkersLocked();
751 rows.shrink_to_fit();
760 if (batchMaxLevelOut)
762 *batchMaxLevelOut = eUNDEFINED;
764 if (seqs.empty() || !store)
770 std::vector<LogMarker> markers;
773 markers = markerRegistry->markers();
776 std::vector<Row> accepted;
777 accepted.reserve(seqs.size());
778 MessageType batchMaxLevel = eUNDEFINED;
779 for (
const LogSeq seq : seqs)
781 if (!store->contains(seq))
785 const LogMessage& message = store->at(seq).message;
790 if (message.type > batchMaxLevel)
792 batchMaxLevel = message.type;
801 if (marker.seq == seq)
803 row.markerColor = marker.color;
807 accepted.push_back(std::move(row));
810 if (accepted.empty())
815 const int addedCount =
static_cast<int>(accepted.size());
817 if (batchMaxLevel > maxNewLogLevelType)
819 maxNewLogLevelType = batchMaxLevel;
821 if (batchMaxLevelOut)
823 *batchMaxLevelOut = maxNewLogLevelType;
832 maxNewLogLevelType = eUNDEFINED;
839 beginInsertRows(QModelIndex(), firstRow, firstRow + addedCount - 1);
841 std::unique_lock lock(rowsMutex);
842 rows.insert(rows.end(),
843 std::make_move_iterator(accepted.begin()),
844 std::make_move_iterator(accepted.end()));
851 maxNewLogLevelType = eUNDEFINED;
867 if (maxEntries == 0 || !materialised)
875 constexpr double headroomFactor = 1.3;
877 std::size_t currentSize;
879 std::unique_lock lock(rowsMutex);
880 currentSize = rows.size();
882 if (currentSize <=
static_cast<std::size_t
>(maxEntries * headroomFactor))
887 const int removeCount =
static_cast<int>(currentSize - maxEntries);
890 beginRemoveRows(QModelIndex(), 0, removeCount - 1);
892 std::unique_lock lock(rowsMutex);
893 rows.erase(rows.begin(), rows.begin() + removeCount);
908 std::unique_lock lock(rowsMutex);
909 const auto end = std::lower_bound(rows.begin(),
912 [](
const Row& r,
LogSeq seq)
913 { return r.seq < seq; });
914 removeCount =
static_cast<int>(std::distance(rows.begin(), end));
916 if (removeCount <= 0)
921 beginRemoveRows(QModelIndex(), 0, removeCount - 1);
923 std::unique_lock lock(rowsMutex);
924 rows.erase(rows.begin(), rows.begin() + removeCount);
932 std::unique_lock lock(rowsMutex);
933 if (row < 0 || row >= (
signed int)rows.size())
937 return rows[row].seq;
943 std::unique_lock lock(rowsMutex);
944 for (std::size_t row = 0; row < rows.size(); ++row)
946 if (rows[row].seq == seq)
948 return static_cast<int>(row);
957 std::unique_lock lock(rowsMutex);
958 if (row < 0 || row >= (
signed int)rows.size())
962 return rows[row].markerColor.isValid();
968 if (columnName.empty())
973 QString qcolumnName = QString::fromStdString(columnName);
975 for (
int i = 0; i <
columnCount(QModelIndex()); i++)
977 if (
headerData(i, Qt::Horizontal, Qt::DisplayRole).toString().compare(qcolumnName) == 0)
994 clearedBeforeSeq = store->endSeq();
997 int size = (int)rows.size();
1003 beginRemoveRows(QModelIndex(), 0, size - 1);
1005 std::unique_lock lock(rowsMutex);
1009 maxNewLogLevelType = eUNDEFINED;
1016 std::unique_lock lock(rowsMutex);
1020 static const LogMessage empty{};
1029 std::unique_lock lock(rowsMutex);
1041 const auto matches = [&filter](
const std::string& field, Qt::CaseSensitivity cs)
1045 return cs == Qt::CaseSensitive ? field == filter.
value
1046 : iEquals(field, filter.
value);
1048 return QString::fromStdString(field).contains(filter.
qValue, cs);
1054 return matches(logMsg.group, Qt::CaseSensitive);
1056 return matches(logMsg.who, Qt::CaseInsensitive);
1058 return matches(logMsg.tag, Qt::CaseInsensitive);
1060 return !(logMsg.type < filter.
intValue);
1062 return matches(logMsg.what, Qt::CaseInsensitive);
1064 return matches(logMsg.file, Qt::CaseInsensitive) ||
1067 return matches(logMsg.function, Qt::CaseInsensitive);
1079 for (
unsigned int i = 0; i < activeFilters.size(); i++)
#define ARMARX_LOG_VERBOSITYSTR
#define ARMARX_LOG_COMPONENTSTR
#define ARMARX_LOG_FILESTR
#define ARMARX_LOG_TAGSTR
#define ARMARX_LOG_TIMESTR
#define ARMARX_LOG_MESSAGESTR
#define ARMARX_LOG_LOGGINGGROUPSTR
#define ARMARX_LOG_FUNCTIONSTR
Shared, session-only store of pinned ("labeled") log lines.
static std::string levelToString(MessageTypeT type)
The single bounded buffer holding every log line the viewer retains.
bool contains(LogSeq seq) const
void setMaterialised(bool materialised)
Build rows (materialised) or drop them and only keep counting (not).
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void setMarkerRegistry(LogMarkerRegistry *registry)
Attach the shared pin registry.
void dropRowsBefore(LogSeq firstSeq)
Discard rows whose store entry has been dropped. Called after the store trims.
void addFilter(const std::string &columnName, const std::string &filter, MatchMode mode=MatchMode::Contains)
bool applyFilters(const LogMessage &logMsg)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
LogTableModel(QObject *parent=0)
bool isRowMarked(int row) const
Whether the given display row is currently pinned (has a label color).
bool setData(const QModelIndex &index, const QVariant &value, int role) override
bool applyFilter(const LogFilter &filter, const LogMessage &logMsg)
int lineCountAt(int row) const
Number of display lines of the message column for the given row.
void setMaxEntries(std::size_t maxEntries)
Upper bound on displayed rows.
int rowForSeq(LogSeq seq) const
Display row showing seq, or -1 if it is not shown.
int addEntries(const std::vector< LogSeq > &seqs, MessageType *batchMaxLevelOut=nullptr)
Append those of seqs that pass this model's filters, in one insertion.
const LogMessage & getLogEntry(size_t row) const
The message shown in the given row. Only valid while the row exists.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
LogSeq seqAt(int row) const
Store sequence number of the given display row, or 0 if the row is invalid.
void enforceMaxEntries()
Trim the oldest rows once the display window is exceeded.
int getColumn(const std::string &columnName) const
bool msgContainsString(const LogMessage &logMsg, QString searchStr) const
QVariant data(const QModelIndex &index, int role) const override
bool rowContainsString(int row, const QString &searchStr) const
void refreshMarkers()
Rebuild the set of injected pin rows and re-color rows (full reset).
void setStore(const LogStore *store)
Attach the shared backing store. Must be called before any ingestion.
void search(const QString &searchStr)
double a(double t, double a0, double j)
This file offers overloads of toIce() and fromIce() functions for STL container types.
quint64 LogSeq
Identifies a stored log entry for as long as it is retained.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
int intValue
value as int, for the verbosity / file-line comparisons
QString qValue
value as QString, for substring (Contains) matching
A single pinned log line: the store entry it refers to plus the color assigned to it.
A retained log line: the message plus the display data derived from it.
QString displayMessage
truncated message column (4)
int lineCount
number of display lines in displayMessage
QString timeString
formatted time column (0)