LogTable.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* ArmarX is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License version 2 as
6* published by the Free Software Foundation.
7*
8* ArmarX is distributed in the hope that it will be useful, but
9* WITHOUT ANY WARRANTY; without even the implied warranty of
10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11* GNU General Public License for more details.
12*
13* You should have received a copy of the GNU General Public License
14* along with this program. If not, see <http://www.gnu.org/licenses/>.
15*
16* @package ArmarX::
17* @author Mirko Waechter ( mirko.waechter at kit dot edu)
18* @date 2012
19* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20* GNU General Public License
21*/
22#include "LogTable.h"
23
24#include <filesystem>
25
26#include <QApplication>
27#include <QHeaderView>
28#include <QMenu>
29#include <QScrollBar>
30#include <QTimer>
31
32#include "LogMessageDelegate.h"
33#include "LogTableModel.h"
34
35namespace armarx
36{
37 LogTable::LogTable(QWidget* parent) :
38 QTableView(parent), newMessageCount(0), maxNewLogLevelType(eUNDEFINED)
39 {
40 autoscrollActive = true;
42
43 this->setVerticalScrollMode(ScrollMode::ScrollPerPixel);
44
45 setObjectName(QString::fromUtf8("tableLog"));
46 QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Expanding);
47 sizePolicy2.setHorizontalStretch(10);
48 sizePolicy2.setVerticalStretch(10);
49 sizePolicy2.setHeightForWidth(sizePolicy().hasHeightForWidth());
50 setSizePolicy(sizePolicy2);
51 setSizeIncrement(QSize(1, 0));
52 setEditTriggers(QAbstractItemView::DoubleClicked); //QAbstractItemView::NoEditTriggers
53 setAlternatingRowColors(true);
54 setShowGrid(true);
55 setGridStyle(Qt::SolidLine);
56 setSortingEnabled(false);
57 setWordWrap(true);
58 setCornerButtonEnabled(true);
59
60 setSelectionMode(QAbstractItemView::SingleSelection);
61 setSelectionBehavior(QAbstractItemView::SelectRows);
62
63 horizontalHeader()->setVisible(true);
64 horizontalHeader()->setCascadingSectionResizes(true);
65 horizontalHeader()->setDefaultSectionSize(100);
66 horizontalHeader()->setMinimumSectionSize(20);
67 horizontalHeader()->setStretchLastSection(true);
68 verticalHeader()->setVisible(false);
69 verticalHeader()->setDefaultSectionSize(20);
70
71 // verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
72
73 setModel(new LogTableModel(this));
74 //connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
75 setColumnWidth(0, 90);
76 setColumnWidth(1, 90);
77 setColumnWidth(2, 90);
78 setColumnWidth(3, 60);
79 setColumnWidth(4, 700);
80 setColumnWidth(5, 150);
81 setColumnWidth(6, 200);
82 hideColumn(7);
83
84 QFont font;
85 font.setPointSize(8);
86 setFont(font);
87
88
89 setItemDelegateForColumn(getModel()->getColumn(ARMARX_LOG_MESSAGESTR),
90 new LogMessageDelegate());
91
92 QAbstractItemModel* absmodel = qobject_cast<QAbstractItemModel*>(model());
93 connect(absmodel,
94 SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
95 this,
96 SLOT(checkAutoScroll(QModelIndex, int, int)));
97 connect(
98 this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(doubleClickOnCell(QModelIndex)));
99
100 setContextMenuPolicy(Qt::CustomContextMenu);
101 connect(this,
102 SIGNAL(customContextMenuRequested(QPoint)),
103 this,
104 SLOT(showContextMenu(QPoint)));
105 connect(absmodel,
106 SIGNAL(dataChanged(QModelIndex, QModelIndex)),
107 this,
108 SLOT(itemsAdded(QModelIndex, QModelIndex)));
109 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(checkAutoScroll()));
110
111 // connect(this, SIGNAL(scrollToEnd()), this, SLOT(scrollToBottom()));
112 }
113
115 {
116 }
117
118 void
119 LogTable::showContextMenu(const QPoint& pos)
120 {
121 const QModelIndex index = indexAt(pos);
122 if (!index.isValid())
123 {
124 return;
125 }
126
127 const int row = index.row();
128 // Copy the message: the underlying entry may move as the log grows/trims.
129 const LogMessage message = getModel()->getLogEntry(row);
130
131 QMenu menu(this);
132 if (getModel()->isRowMarked(row))
133 {
134 QAction* removeAction = menu.addAction(tr("Remove label"));
135 connect(removeAction,
136 &QAction::triggered,
137 this,
138 [this, message] { emit removeLabelRequested(message); });
139 }
140 else
141 {
142 QAction* addAction = menu.addAction(tr("Add label"));
143 connect(addAction,
144 &QAction::triggered,
145 this,
146 [this, message] { emit addLabelRequested(message); });
147 }
148 menu.exec(viewport()->mapToGlobal(pos));
149 }
150
151 QString
153 {
154 return currentLiveFilter;
155 }
156
157 void
158 LogTable::liveFilterRow(const QString& filterStr, int row)
159 {
160 LogTableModel* logModel = getModel();
161 bool contains = logModel->rowContainsString(row, filterStr);
162 if (!isRowHidden(row) && !contains)
163 {
164 setRowHidden(row, true);
165 }
166 else if (isRowHidden(row) && contains)
167 {
168 setRowHidden(row, false);
169 }
170 }
171
172 void
173 LogTable::liveFilter(const QString& filterStr, int startRow)
174 {
175 LogTableModel* logModel = getModel();
176 currentLiveFilter = filterStr;
177 if (filterStr.length() && filterStr.contains(lastLiveFilter))
178 {
179 // incremental filter - Only remove not fitting entries
180 int rowCount = model()->rowCount();
181
182 for (int i = startRow; i < rowCount; i++)
183 {
184 if (!isRowHidden(i) && !logModel->rowContainsString(i, filterStr))
185 {
186 setRowHidden(i, true);
187 }
188 if (i % 1000 == 0)
189 {
190 qApp->processEvents();
191 }
192 if (filterStr != currentLiveFilter)
193 {
194 break; // filterstring already changed again -> cancel
195 }
196 }
197 }
198 else
199 {
200 // filter from scratch
201 // clear();
202 // setRowCount(0);
203 // QList<QTableWidgetItem*>results = findItems(filterStr,Qt::MatchContains);
204 // foreach(QTableWidgetItem*item, results)
205 // {
206 // if(applyFilters(item))
207 // item->
208 // }
209 int rowCount = model()->rowCount();
210
211 for (int i = startRow; i < rowCount; i++)
212 {
213 if (isRowHidden(i))
214 {
215 if (logModel->rowContainsString(i, filterStr))
216 {
217 setRowHidden(i, false);
218 }
219 }
220 else if (!logModel->rowContainsString(i, filterStr))
221 {
222 setRowHidden(i, true);
223 }
224 if (i % 1000 == 0)
225 {
226 qApp->processEvents();
227 }
228 if (filterStr != currentLiveFilter)
229 {
230 break; // filterstring already changed again -> cancel
231 }
232 }
233
234 // std::cout << "logBuffer: " << logBuffer.size() << std::endl;
235 // for(unsigned int i=0; i < logBuffer.size(); i++)
236 // {
237 // if(msgContainsString(logBuffer[i], filterStr) && applyFilters(logBuffer[i]) )
238 // addEntry(logBuffer[i], true);
239 // }
240 }
241
242 lastLiveFilter = filterStr;
243 }
244
245 bool
246 LogTable::liveSearch(const QString& search)
247 {
248 getModel()->search(search);
249 // scrollToBottom();
250 clearSelection();
251 return selectNextSearchResult(true, true);
252 }
253
254 void
256 {
257 // ARMARX_WARNING_S << "LiveFilterReseted";
259 lastLiveFilter = "";
260 liveFilter("");
261 }
262
263 void
265 {
266 getModel()->search("");
267 // repaint();
268 }
269
272 {
273 return dynamic_cast<LogTableModel*>(model());
274 }
275
276 bool
277 LogTable::checkAutoScroll(const QModelIndex& parent, int start, int end)
278 {
279 return checkAutoScroll();
280 }
281
282 bool
284 {
285 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
286 {
287 autoscrollActive = true;
288 }
289 else
290 {
291 autoscrollActive = false;
292 }
293
294
295 return autoscrollActive;
296 }
297
298 void
299 LogTable::itemsAdded(QModelIndex leftTop, QModelIndex bottomRight)
300 {
301 }
302
303 void
304 LogTable::showEvent(QShowEvent* event)
305 {
307
308 // verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
309 // resizeRowsToContents();
310 // verticalHeader()->setResizeMode(QHeaderView::Fixed);
312 {
313 QTimer::singleShot(
314 50,
315 this,
316 SLOT(
317 scrollToBottom())); // delayed because something is inserting one line after this function or something
318 }
319 }
320
321 void
323 {
324 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
325 {
326 autoscrollActive = true;
327 }
328 else
329 {
330 autoscrollActive = false;
331 }
332 }
333
334 void
335 LogTable::rowsInserted(const QModelIndex& parent, int start, int end)
336 {
337 auto logModel = getModel();
338 auto fontHeight = QFontMetrics(font()).height();
339 // ARMARX_INFO << "Adjusting height for " << start << " to " << end << " count: " << logModel->rowCount();
340 for (int i = start; i <= end && i < logModel->rowCount(); ++i)
341 {
342 // Use the precomputed line count instead of re-fetching and splitting the
343 // message string (which data() already built once at insert time).
344 const int stringRows = logModel->lineCountAt(i);
345 if (stringRows > 1)
346 {
347 setRowHeight(i, fontHeight * stringRows + 1);
348 }
349 }
350
351 if (!isVisible())
352 {
353 newMessageCount += end - start + 1;
354
356 {
358 }
359
360 // LogTableModel * logModel = getModel();
361 // if(start> end)
362 // std::swap(start, end);
363 // if(lastLiveFilter.length() > 0)
364 // {
365 // for (int i = start; i <= end; ++i) {
366 // if(!logModel->rowContainsString(i, lastLiveFilter))
367 // setRowHidden(i, true);
368 // }
369 // }
370 }
371 // resizeRowsToContents();
372 else
373 {
374 // verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
375
376 // for(int i = start; i < end; i++)
377 // resizeRowToContents(i);
378 // verticalHeader()->setResizeMode(QHeaderView::Fixed);
379 }
380
382 {
383 scrollToBottom();
384 }
385 }
386
387 void
388 LogTable::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
389 {
390 if (end - start >= model()->rowCount() - 1)
391 {
393 }
394 }
395
396 bool
397 LogTable::selectNextSearchResult(bool backwards, bool keepSelectionIfPossible)
398 {
399
400 int checkCounter = 0; // just a counter for avoiding inifite loops
401 int tempSelectedSearchIndex = indexAt(QPoint(10, 10)).row();
402
403 if (selectedIndexes().size() > 0)
404 {
405 tempSelectedSearchIndex = selectedIndexes()[0].row();
406 }
407
408 int oldSelectedSearchIndex = tempSelectedSearchIndex;
409
410 do // search until we reach old line again
411 {
412
413 if ((tempSelectedSearchIndex != oldSelectedSearchIndex || keepSelectionIfPossible) &&
414 getModel()->rowContainsString(tempSelectedSearchIndex,
415 getModel()->getCurrentSearchStr()))
416 {
417 selectRow(tempSelectedSearchIndex);
418 return true;
419 }
420
421 if (backwards)
422 {
423 tempSelectedSearchIndex--;
424 }
425 else
426 {
427 tempSelectedSearchIndex++;
428 }
429
430 if (tempSelectedSearchIndex >= model()->rowCount())
431 {
432 tempSelectedSearchIndex = 0;
433 }
434
435 if (tempSelectedSearchIndex < 0)
436 {
437 tempSelectedSearchIndex = model()->rowCount() - 1;
438 }
439
440
441 checkCounter++;
442
443 if (checkCounter > model()->rowCount())
444 {
445 break;
446 }
447 } while (tempSelectedSearchIndex != oldSelectedSearchIndex);
448
449 return false;
450 }
451
452 void
454 {
455 if (index.column() != getModel()->getColumn(ARMARX_LOG_FILESTR))
456 {
457 return;
458 }
459
460 std::string fileWithLineNumber = model()->data(index).toString().toStdString();
461 const auto colonPos = fileWithLineNumber.rfind(':');
462 if (colonPos == std::string::npos)
463 {
464 // No "file:line" content (e.g. an entry without a source location).
465 return;
466 }
467 std::string file = fileWithLineNumber.substr(0, colonPos);
468 std::string line = fileWithLineNumber.substr(colonPos + 1);
469
470 if (!std::filesystem::exists(file))
471 {
472 ARMARX_INFO << "File '" << file << "' does not exists - cannot open it.";
473 return;
474 }
475
476 fileOpener.openFileWithDefaultEditor(file, atoi(line.c_str()));
477 // std::string command = "qtcreator -client " + fileWithLineNumber.toStdString() + "&";
478 // if(system(command.c_str())){}
479 }
480} // namespace armarx
uint8_t index
#define ARMARX_LOG_FILESTR
Definition LogTable.h:45
#define ARMARX_LOG_MESSAGESTR
Definition LogTable.h:44
MessageType getMaxNewLogLevelType()
const LogMessage & getLogEntry(size_t row) const
bool rowContainsString(int row, const QString &searchStr) const
void search(const QString &searchStr)
MessageType getMaxNewLogLevelType()
Definition LogTable.h:80
void liveFilter(const QString &search, int startRow=0)
Definition LogTable.cpp:173
void doubleClickOnCell(const QModelIndex &index)
Definition LogTable.cpp:453
void showContextMenu(const QPoint &pos)
Build the right-click menu ("Add label" / "Remove label") for the row under pos.
Definition LogTable.cpp:119
bool selectNextSearchResult(bool backwards=true, bool keepSelectionIfPossible=false)
Definition LogTable.cpp:397
void showEvent(QShowEvent *event) override
Definition LogTable.cpp:304
bool autoscrollActive
Definition LogTable.h:118
QString lastLiveFilter
Definition LogTable.h:121
QString currentLiveFilter
Definition LogTable.h:121
void liveFilterRow(const QString &filterStr, int row)
Definition LogTable.cpp:158
bool liveSearch(const QString &search)
Definition LogTable.cpp:246
LogTableModel * getModel()
Definition LogTable.cpp:271
~LogTable() override
Definition LogTable.cpp:114
void resetLiveFilter()
Definition LogTable.cpp:255
MessageType maxNewLogLevelType
Definition LogTable.h:120
void hideEvent(QHideEvent *) override
Definition LogTable.cpp:322
QString getCurrentLiveFilter() const
Definition LogTable.cpp:152
LogTable(QWidget *parent=0)
Definition LogTable.cpp:37
bool checkAutoScroll(const QModelIndex &parent, int start, int end)
Definition LogTable.cpp:277
void itemsAdded(QModelIndex leftTop, QModelIndex bottomRight)
Definition LogTable.cpp:299
void removeLabelRequested(const LogMessage &message)
Emitted from the context menu to remove the pin on the given line.
void resetLiveSearch()
Definition LogTable.cpp:264
void resetNewMessageCount()
Definition LogTable.h:60
int selectedSearchIndex
Definition LogTable.h:122
EditorFileOpener fileOpener
Definition LogTable.h:123
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override
Definition LogTable.cpp:388
void rowsInserted(const QModelIndex &parent, int start, int end) override
Definition LogTable.cpp:335
void addLabelRequested(const LogMessage &message)
Emitted from the context menu to pin the given line (assign a label).
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
This file offers overloads of toIce() and fromIce() functions for STL container types.