This commit is contained in:
DavidXanatos 2023-01-12 23:10:50 +01:00
parent e5f40dd9d8
commit 7ce252fb36
18 changed files with 319 additions and 192 deletions

View File

@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Changed ## Changed
- improved trace log retrival greately improving performance - improved trace log retrival greately improving performance
- improved list/tree finder
- improved trace logging
### Fixed ### Fixed
- fixed potential BSOD issue in the driver - fixed potential BSOD issue in the driver

View File

@ -553,6 +553,8 @@ _FX void Session_MonitorPutEx(ULONG type, const WCHAR** strings, ULONG* lengths,
if (session->monitor_log) { if (session->monitor_log) {
LARGE_INTEGER timestamp = Util_GetTimestamp();
ULONG pid = (ULONG)hpid; ULONG pid = (ULONG)hpid;
ULONG tid = (ULONG)htid; ULONG tid = (ULONG)htid;
@ -561,12 +563,13 @@ _FX void Session_MonitorPutEx(ULONG type, const WCHAR** strings, ULONG* lengths,
data_len += ((lengths ? lengths [i] : wcslen(strings[i])) + 1) * sizeof(WCHAR); data_len += ((lengths ? lengths [i] : wcslen(strings[i])) + 1) * sizeof(WCHAR);
//[Type 4][PID 4][TID 4][Data n*2] //[Time 8][Type 4][PID 4][TID 4][Data n*2]
SIZE_T entry_size = 4 + 4 + 4 + data_len; SIZE_T entry_size = 8 + 4 + 4 + 4 + data_len;
CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, session->monitor_log, FALSE); CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, session->monitor_log, FALSE);
if (write_ptr) { if (write_ptr) {
WCHAR null_char = L'\0'; WCHAR null_char = L'\0';
log_buffer_push_bytes((CHAR*)&timestamp.QuadPart, 8, &write_ptr, session->monitor_log);
log_buffer_push_bytes((CHAR*)&type, 4, &write_ptr, session->monitor_log); log_buffer_push_bytes((CHAR*)&type, 4, &write_ptr, session->monitor_log);
log_buffer_push_bytes((CHAR*)&pid, 4, &write_ptr, session->monitor_log); log_buffer_push_bytes((CHAR*)&pid, 4, &write_ptr, session->monitor_log);
log_buffer_push_bytes((CHAR*)&tid, 4, &write_ptr, session->monitor_log); log_buffer_push_bytes((CHAR*)&tid, 4, &write_ptr, session->monitor_log);
@ -925,6 +928,7 @@ _FX NTSTATUS Session_Api_MonitorGetEx(PROCESS* proc, ULONG64* parms)
API_MONITOR_GET_EX_ARGS* args = (API_MONITOR_GET_EX_ARGS*)parms; API_MONITOR_GET_EX_ARGS* args = (API_MONITOR_GET_EX_ARGS*)parms;
NTSTATUS status; NTSTATUS status;
//ULONG* seq_num; //ULONG* seq_num;
LARGE_INTEGER timestamp;
ULONG* log_type; ULONG* log_type;
ULONG* log_pid; ULONG* log_pid;
ULONG* log_tid; ULONG* log_tid;
@ -1007,7 +1011,9 @@ _FX NTSTATUS Session_Api_MonitorGetEx(PROCESS* proc, ULONG64* parms)
// __leave; // __leave;
//} //}
//[Type 4][PID 4][TID 4][Data n*2] //[Time 8][Type 4][PID 4][TID 4][Data n*2]
log_buffer_get_bytes((CHAR*)&timestamp.QuadPart, 8, &read_ptr, session->monitor_log);
log_buffer_get_bytes((CHAR*)log_type, 4, &read_ptr, session->monitor_log); log_buffer_get_bytes((CHAR*)log_type, 4, &read_ptr, session->monitor_log);

View File

@ -1,6 +1,6 @@
/* /*
* Copyright 2004-2020 Sandboxie Holdings, LLC * Copyright 2004-2020 Sandboxie Holdings, LLC
* Copyright 2020-2021 David Xanatos, xanasoft.com * Copyright 2020-2023 David Xanatos, xanasoft.com
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -443,3 +443,30 @@ retry:
return pid; return pid;
} }
//---------------------------------------------------------------------------
// Util_GetTime
//---------------------------------------------------------------------------
_FX LARGE_INTEGER Util_GetTimestamp(void)
{
static LARGE_INTEGER gMonitorStartCounter;
static LARGE_INTEGER gPerformanceFrequency;
static LARGE_INTEGER gMonitorStartTime = { 0 };
if (gMonitorStartTime.QuadPart == 0) {
KeQuerySystemTime(&gMonitorStartTime);
gMonitorStartCounter = KeQueryPerformanceCounter(&gPerformanceFrequency);
}
LARGE_INTEGER Time;
LARGE_INTEGER CounterNow = KeQueryPerformanceCounter(NULL);
LONGLONG CounterOff = CounterNow.QuadPart - gMonitorStartCounter.QuadPart;
Time.QuadPart = gMonitorStartTime.QuadPart +
(10000000 * (CounterOff / gPerformanceFrequency.QuadPart)) +
((10000000 * (CounterOff % gPerformanceFrequency.QuadPart)) / gPerformanceFrequency.QuadPart);
return Time;
}

View File

@ -1,6 +1,6 @@
/* /*
* Copyright 2004-2020 Sandboxie Holdings, LLC * Copyright 2004-2020 Sandboxie Holdings, LLC
* Copyright 2020 David Xanatos, xanasoft.com * Copyright 2020-2023 David Xanatos, xanasoft.com
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -110,6 +110,8 @@ NTSTATUS MyValidateCertificate(void);
HANDLE Util_GetProcessPidByName(const WCHAR* name); HANDLE Util_GetProcessPidByName(const WCHAR* name);
LARGE_INTEGER Util_GetTimestamp(void);
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -3,15 +3,16 @@
bool CFinder::m_DarkMode = false; bool CFinder::m_DarkMode = false;
QWidget* CFinder::AddFinder(QWidget* pList, QObject* pFilterTarget, int iOptions, CFinder** ppFinder) QWidget* CFinder::AddFinder(QTreeView* pTree, QObject* pFilterTarget, int iOptions, CFinder** ppFinder)
{ {
QWidget* pWidget = new QWidget(); QWidget* pWidget = new QWidget();
QVBoxLayout* pLayout = new QVBoxLayout(); QVBoxLayout* pLayout = new QVBoxLayout();
pLayout->setContentsMargins(0,0,0,0); pLayout->setContentsMargins(0,0,0,0);
pWidget->setLayout(pLayout); pWidget->setLayout(pLayout);
pLayout->addWidget(pList); pLayout->addWidget(pTree);
CFinder* pFinder = new CFinder(pFilterTarget, pWidget, iOptions); CFinder* pFinder = new CFinder(pFilterTarget, pWidget, iOptions);
pFinder->SetTree(pTree);
pLayout->addWidget(pFinder); pLayout->addWidget(pFinder);
if (ppFinder) if (ppFinder)
@ -52,9 +53,10 @@ CFinder::CFinder(QObject* pFilterTarget, QWidget *parent, int iOptions)
else else
m_pRegExp = NULL; m_pRegExp = NULL;
m_pSortProxy = qobject_cast<QSortFilterProxyModel*>(pFilterTarget); m_pTree = NULL;
m_pModel = qobject_cast<QAbstractProxyModel*>(pFilterTarget);
if (m_pSortProxy) { if (m_pModel) {
m_pColumn = new QComboBox(); m_pColumn = new QComboBox();
m_pSearchLayout->addWidget(m_pColumn); m_pSearchLayout->addWidget(m_pColumn);
connect(m_pColumn, SIGNAL(currentIndexChanged(int)), this, SLOT(OnUpdate())); connect(m_pColumn, SIGNAL(currentIndexChanged(int)), this, SLOT(OnUpdate()));
@ -66,7 +68,8 @@ CFinder::CFinder(QObject* pFilterTarget, QWidget *parent, int iOptions)
if ((iOptions & eHighLight) != 0) if ((iOptions & eHighLight) != 0)
{ {
m_pHighLight = new QCheckBox(tr("Highlight")); m_pHighLight = new QCheckBox(tr("Highlight"));
//m_pHighLight->setChecked(true); if ((iOptions & eHighLightDefault) == eHighLightDefault)
m_pHighLight->setChecked(true);
m_pSearchLayout->addWidget(m_pHighLight); m_pSearchLayout->addWidget(m_pHighLight);
connect(m_pHighLight, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate())); connect(m_pHighLight, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate()));
} }
@ -103,7 +106,7 @@ CFinder::CFinder(QObject* pFilterTarget, QWidget *parent, int iOptions)
if (pFilterTarget) { if (pFilterTarget) {
QObject::connect(this, SIGNAL(SetFilter(const QString&, int, int)), pFilterTarget, SLOT(SetFilter(const QString&, int, int))); QObject::connect(this, SIGNAL(SetFilter(const QString&, int, int)), pFilterTarget, SLOT(SetFilter(const QString&, int, int)));
QObject::connect(this, SIGNAL(SelectNext()), pFilterTarget, SLOT(SelectNext())); //QObject::connect(this, SIGNAL(SelectNext()), pFilterTarget, SLOT(SelectNext()));
} }
m_pTimer = new QTimer(this); m_pTimer = new QTimer(this);
@ -118,6 +121,12 @@ CFinder::~CFinder()
{ {
} }
void CFinder::SetTree(QTreeView* pTree)
{
m_pTree = pTree;
QObject::connect(this, SIGNAL(SelectNext()), this, SLOT(OnSelectNext()));
}
bool CFinder::eventFilter(QObject* source, QEvent* event) bool CFinder::eventFilter(QObject* source, QEvent* event)
{ {
if (event->type() == QEvent::KeyPress && ((QKeyEvent*)event)->key() == Qt::Key_Escape if (event->type() == QEvent::KeyPress && ((QKeyEvent*)event)->key() == Qt::Key_Escape
@ -132,11 +141,11 @@ bool CFinder::eventFilter(QObject* source, QEvent* event)
void CFinder::Open() void CFinder::Open()
{ {
if (m_pSortProxy && m_pColumn->count() == 0) if (m_pModel && m_pColumn->count() == 0)
{ {
m_pColumn->addItem(tr("All columns"), -1); m_pColumn->addItem(tr("All columns"), -1);
for (int i = 0; i < m_pSortProxy->columnCount(); i++) for (int i = 0; i < m_pModel->columnCount(); i++)
m_pColumn->addItem(m_pSortProxy->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), i); m_pColumn->addItem(m_pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), i);
m_pColumn->setVisible(true); m_pColumn->setVisible(true);
} }
@ -158,7 +167,12 @@ void CFinder::OnUpdate()
iOptions |= eCaseSens; iOptions |= eCaseSens;
if (GetHighLight()) if (GetHighLight())
iOptions |= eHighLight; iOptions |= eHighLight;
SetFilter(m_pSearch->text(), iOptions, GetColumn()); QString Exp = m_pSearch->text();
QString ExpStr = ((iOptions & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*");
m_RegExp = QRegularExpression(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
SetFilter(Exp, iOptions, GetColumn());
} }
void CFinder::OnText() void CFinder::OnText()
@ -179,3 +193,125 @@ void CFinder::Close()
emit SetFilter(QString()); emit SetFilter(QString());
hide(); hide();
} }
////////////////////////////////////////////////////////////////
//
bool CFinder::MatchString(const QString& value)
{
return value.contains(m_RegExp);
}
bool CFinder::MatchCell(QModelIndex idx, int column)
{
QModelIndex tmp = idx.sibling(idx.row(), column);
QString str = m_pModel->data(tmp, Qt::DisplayRole).toString();
return MatchString(str);
}
bool CFinder::MatchRow(QModelIndex idx)
{
int iColumn = GetColumn();
if (iColumn != -1)
return MatchCell(idx, iColumn);
for(int col = 0; col < m_pModel->columnCount(idx); col++) {
if (MatchCell(idx, col))
return true;
}
return false;
}
QModelIndex CFinder::FindNext(QModelIndex idx, bool next, int depth)
{
if (MatchRow(idx) && !next)
return idx;
//Q_ASSERT(depth < 100);
if (m_pModel->hasChildren(idx))
{
int numRows = m_pModel->rowCount(idx);
for (int count = 0; count < numRows; count++) {
QModelIndex tmp = FindNext(m_pModel->index(count, 0, idx), false, depth + 1);
if (tmp.isValid())
return tmp;
}
}
for(;;)
{
QModelIndex par = m_pModel->parent(idx);
if (!par.isValid() && depth > 0)
break;
int numRows = m_pModel->rowCount(par);
for (int count = idx.row() + 1; count < numRows; count++) {
QModelIndex tmp = FindNext(m_pModel->index(count, 0, par), false, depth + 1);
if (tmp.isValid())
return tmp;
}
if (!par.isValid())
break;
idx = par;
}
return QModelIndex();
}
QModelIndex CFinder::FindPrev(QModelIndex idx, bool next, int depth)
{
if (MatchRow(idx) && !next)
return idx;
//Q_ASSERT(depth < 100);
if (m_pModel->hasChildren(idx))
{
int numRows = m_pModel->rowCount(idx);
for (int count = numRows-1; count >= 0; count++) {
QModelIndex tmp = FindNext(m_pModel->index(count, 0, idx), false, depth + 1);
if (tmp.isValid())
return tmp;
}
}
for(;;)
{
QModelIndex par = m_pModel->parent(idx);
if (!par.isValid() && depth > 0)
break;
int numRows = m_pModel->rowCount(par);
for (int count = idx.row() - 1; count >= 0; count--) {
QModelIndex tmp = FindNext(m_pModel->index(count, 0, par), false, depth + 1);
if (tmp.isValid())
return tmp;
}
if (!par.isValid())
break;
idx = par;
}
return QModelIndex();
}
void CFinder::OnSelectNext()
{
bool next = true;
QModelIndex idx = m_pTree->currentIndex();
if (!(next = idx.isValid()))
idx = m_pModel->index(0, 0);
//if (QApplication::keyboardModifiers() & Qt::ControlModifier)
if (QApplication::keyboardModifiers() & Qt::ShiftModifier)
idx = FindPrev(idx, next);
else
idx = FindNext(idx, next);
if (idx.isValid())
m_pTree->setCurrentIndex(idx);
else
QApplication::beep();
}

View File

@ -10,21 +10,21 @@ public:
CFinder(QObject* pFilterTarget, QWidget *parent = NULL, int iOptions = eRegExp | eCaseSens | eHighLight); CFinder(QObject* pFilterTarget, QWidget *parent = NULL, int iOptions = eRegExp | eCaseSens | eHighLight);
~CFinder(); ~CFinder();
void SetTree(QTreeView* pTree);
static void SetDarkMode(bool bDarkMode) { m_DarkMode = bDarkMode; } static void SetDarkMode(bool bDarkMode) { m_DarkMode = bDarkMode; }
static bool GetDarkMode() { return m_DarkMode; } static bool GetDarkMode() { return m_DarkMode; }
static QWidget* AddFinder(QWidget* pList, QObject* pFilterTarget, int iOptions = eRegExp | eCaseSens | eHighLight, CFinder** ppFinder = NULL); static QWidget* AddFinder(QTreeView* pTree, QObject* pFilterTarget, int iOptions = eRegExp | eCaseSens | eHighLight, CFinder** ppFinder = NULL);
bool GetCaseSensitive() const { return m_pCaseSensitive ? m_pCaseSensitive->isChecked() : false; } QRegularExpression GetSearchExp() const { return m_RegExp; }
bool GetRegExp() const { return m_pRegExp ? m_pRegExp->isChecked() : false; }
bool GetHighLight() const { return m_pHighLight ? m_pHighLight->isChecked() : false; }
int GetColumn() const { return m_pColumn ? m_pColumn->currentData().toInt() : -1; }
enum EOptions enum EOptions
{ {
eRegExp = 0x01, eRegExp = 0x01,
eCaseSens = 0x02, eCaseSens = 0x02,
eHighLight = 0x04, eHighLight = 0x04,
eHighLightDefault = eHighLight | 0x08,
}; };
signals: signals:
@ -40,9 +40,22 @@ private slots:
void OnText(); void OnText();
void OnReturn(); void OnReturn();
void OnSelectNext();
protected: protected:
bool GetCaseSensitive() const { return m_pCaseSensitive ? m_pCaseSensitive->isChecked() : false; }
bool GetRegExp() const { return m_pRegExp ? m_pRegExp->isChecked() : false; }
bool GetHighLight() const { return m_pHighLight ? m_pHighLight->isChecked() : false; }
int GetColumn() const { return m_pColumn ? m_pColumn->currentData().toInt() : -1; }
bool eventFilter(QObject* source, QEvent* event); bool eventFilter(QObject* source, QEvent* event);
virtual bool MatchString(const QString& value);
bool MatchCell(QModelIndex idx, int column);
bool MatchRow(QModelIndex idx);
QModelIndex FindNext(QModelIndex idx, bool next = false, int depth = 0);
QModelIndex FindPrev(QModelIndex idx, bool next = false, int depth = 0);
private: private:
QHBoxLayout* m_pSearchLayout; QHBoxLayout* m_pSearchLayout;
@ -53,7 +66,10 @@ private:
QComboBox* m_pColumn; QComboBox* m_pColumn;
QCheckBox* m_pHighLight; QCheckBox* m_pHighLight;
QSortFilterProxyModel* m_pSortProxy; QRegularExpression m_RegExp;
QTreeView* m_pTree;
QAbstractProxyModel*m_pModel;
QTimer* m_pTimer; QTimer* m_pTimer;

View File

@ -136,10 +136,10 @@ public:
} }
private slots: private slots:
void SetFilter(const QString& Exp, int iFormat, int Col = -1) // -1 = any void SetFilter(const QString& Exp, int iOptions, int Col = -1) // -1 = any
{ {
QString ExpStr = ((iFormat & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*"); QString ExpStr = ((iOptions & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*");
QRegularExpression RegExp(ExpStr, (iFormat & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
ApplyFilter(m_pTreeList, RegExp); ApplyFilter(m_pTreeList, RegExp);
} }
@ -164,8 +164,6 @@ public:
m_pSortProxy->setDynamicSortFilter(true); m_pSortProxy->setDynamicSortFilter(true);
m_pTreeList->setModel(m_pSortProxy); m_pTreeList->setModel(m_pSortProxy);
((CSortFilterProxyModel*)m_pSortProxy)->setView(m_pTreeList);
m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection); m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection);
#ifdef WIN32 #ifdef WIN32

View File

@ -14,16 +14,10 @@ public:
{ {
m_bHighLight = false; m_bHighLight = false;
m_iColumn = 0; m_iColumn = 0;
m_pView = NULL;
this->setSortCaseSensitivity(Qt::CaseInsensitive); this->setSortCaseSensitivity(Qt::CaseInsensitive);
} }
void setView(QTreeView* pView)
{
m_pView = pView;
}
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{ {
if (m_bHighLight) if (m_bHighLight)
@ -86,124 +80,16 @@ public slots:
QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
QModelIndex idx; QModelIndex idx;
//if (m_pView) idx = m_pView->currentIndex();
m_iColumn = Col; m_iColumn = Col;
m_bHighLight = (iOptions & CFinder::eHighLight) != 0; m_bHighLight = (iOptions & CFinder::eHighLight) != 0;
setFilterKeyColumn(Col); setFilterKeyColumn(Col);
setFilterRegularExpression(RegExp); setFilterRegularExpression(RegExp);
//if (m_pView) m_pView->setCurrentIndex(idx);
if (m_bHighLight) if (m_bHighLight)
emit layoutChanged(); emit layoutChanged();
} }
void SelectNext()
{
if (!m_pView)
return;
bool next = true;
QModelIndex idx = m_pView->currentIndex();
if (!(next = idx.isValid()))
idx = index(0, 0);
//if (QApplication::keyboardModifiers() & Qt::ControlModifier)
if (QApplication::keyboardModifiers() & Qt::ShiftModifier)
idx = FindPrev(idx, next);
else
idx = FindNext(idx, next);
if (idx.isValid())
m_pView->setCurrentIndex(idx);
else
QApplication::beep();
}
protected: protected:
bool m_bHighLight; bool m_bHighLight;
int m_iColumn; int m_iColumn;
QTreeView* m_pView;
bool MatchCell(QModelIndex idx, int column)
{
QModelIndex tmp = idx.sibling(idx.row(), column);
QString str = data(tmp, filterRole()).toString();
if (str.contains(filterRegularExpression()))
return true;
return false;
}
bool MatchRow(QModelIndex idx)
{
if (m_iColumn != -1)
return MatchCell(idx, m_iColumn);
for(int col = 0; col < columnCount(idx); col++) {
if (MatchCell(idx, col))
return true;
}
return false;
}
QModelIndex FindNext(QModelIndex idx, bool next = false)
{
if (MatchRow(idx) && !next)
return idx;
if (hasChildren(idx))
{
int numRows = rowCount(idx);
for (int count = 0; count < numRows; count++) {
QModelIndex tmp = FindNext(index(count, 0, idx));
if (tmp.isValid())
return tmp;
}
}
do {
QModelIndex par = parent(idx);
int numRows = rowCount(par);
for (int count = idx.row() + 1; count < numRows; count++) {
QModelIndex tmp = FindNext(index(count, 0, par));
if (tmp.isValid())
return tmp;
}
idx = par;
} while (idx.isValid());
return QModelIndex();
}
QModelIndex FindPrev(QModelIndex idx, bool next = false)
{
if (MatchRow(idx) && !next)
return idx;
if (hasChildren(idx))
{
int numRows = rowCount(idx);
for (int count = numRows-1; count >= 0; count++) {
QModelIndex tmp = FindNext(index(count, 0, idx));
if (tmp.isValid())
return tmp;
}
}
do {
QModelIndex par = parent(idx);
int numRows = rowCount(par);
for (int count = idx.row() - 1; count >= 0; count--) {
QModelIndex tmp = FindNext(index(count, 0, par));
if (tmp.isValid())
return tmp;
}
idx = par;
} while (idx.isValid());
return QModelIndex();
}
}; };

View File

@ -2533,7 +2533,7 @@ bool CSbieAPI::GetMonitor()
pos += (len + 1) * sizeof(WCHAR); pos += (len + 1) * sizeof(WCHAR);
} }
CTraceEntryPtr LogEntry = CTraceEntryPtr(new CTraceEntry(pid, tid, type, LogData)); CTraceEntryPtr LogEntry = CTraceEntryPtr(new CTraceEntry(0, pid, tid, type, LogData));
QMutexLocker Lock(&m_TraceMutex); QMutexLocker Lock(&m_TraceMutex);
m_TraceCache.append(LogEntry); m_TraceCache.append(LogEntry);
@ -2570,6 +2570,10 @@ bool CSbieAPI::GetMonitor()
ULONG uSize = *(ULONG*)ptr; ULONG uSize = *(ULONG*)ptr;
ptr += sizeof(ULONG); ptr += sizeof(ULONG);
LONGLONG uTimestamp = *(LONGLONG*)ptr;
ptr += sizeof(LONGLONG);
uSize -= sizeof(LONGLONG);
ULONG uType = *(ULONG*)ptr; ULONG uType = *(ULONG*)ptr;
ptr += sizeof(ULONG); ptr += sizeof(ULONG);
uSize -= sizeof(ULONG); uSize -= sizeof(ULONG);
@ -2590,7 +2594,7 @@ bool CSbieAPI::GetMonitor()
} }
ptr += uSize; ptr += uSize;
CTraceEntryPtr LogEntry = CTraceEntryPtr(new CTraceEntry(uPid, uTid, uType, LogData)); CTraceEntryPtr LogEntry = CTraceEntryPtr(new CTraceEntry(FILETIME2ms(uTimestamp), uPid, uTid, uType, LogData));
QMutexLocker Lock(&m_TraceMutex); QMutexLocker Lock(&m_TraceMutex);
m_TraceCache.append(LogEntry); m_TraceCache.append(LogEntry);

View File

@ -60,7 +60,7 @@ QString ErrorString(qint32 err)
return Error; return Error;
} }
CTraceEntry::CTraceEntry(quint32 ProcessId, quint32 ThreadId, quint32 Type, const QStringList& LogData) CTraceEntry::CTraceEntry(quint64 Timestamp, quint32 ProcessId, quint32 ThreadId, quint32 Type, const QStringList& LogData)
{ {
m_ProcessId = ProcessId; m_ProcessId = ProcessId;
m_ThreadId = ThreadId; m_ThreadId = ThreadId;
@ -69,7 +69,7 @@ CTraceEntry::CTraceEntry(quint32 ProcessId, quint32 ThreadId, quint32 Type, cons
m_SubType = LogData.length() > 2 ? LogData.at(2) : QString(); m_SubType = LogData.length() > 2 ? LogData.at(2) : QString();
m_Type.Flags = Type; m_Type.Flags = Type;
m_TimeStamp = QDateTime::currentDateTime(); // ms resolution m_TimeStamp = Timestamp ? Timestamp : QDateTime::currentDateTime().toMSecsSinceEpoch();
m_BoxPtr = 0; m_BoxPtr = 0;

View File

@ -28,13 +28,13 @@
class QSBIEAPI_EXPORT CTraceEntry : public QSharedData class QSBIEAPI_EXPORT CTraceEntry : public QSharedData
{ {
public: public:
CTraceEntry(quint32 ProcessId, quint32 ThreadId, quint32 Type, const QStringList& LogData); CTraceEntry(quint64 Timestamp, quint32 ProcessId, quint32 ThreadId, quint32 Type, const QStringList& LogData);
virtual QString GetName() const { return m_Name; } virtual QString GetName() const { return m_Name; }
virtual QString GetMessage() const { return m_Message; } virtual QString GetMessage() const { return m_Message; }
virtual quint32 GetProcessId() const { return m_ProcessId; } virtual quint32 GetProcessId() const { return m_ProcessId; }
virtual quint32 GetThreadId() const { return m_ThreadId; } virtual quint32 GetThreadId() const { return m_ThreadId; }
virtual QDateTime GetTimeStamp() const { return m_TimeStamp; } virtual quint64 GetTimeStamp() const { return m_TimeStamp; }
virtual quint8 GetType() const { return m_Type.Type; } virtual quint8 GetType() const { return m_Type.Type; }
static QList<quint32>AllTypes(); static QList<quint32>AllTypes();
@ -72,7 +72,7 @@ protected:
QString m_SubType; QString m_SubType;
quint32 m_ProcessId; quint32 m_ProcessId;
quint32 m_ThreadId; quint32 m_ThreadId;
QDateTime m_TimeStamp; quint64 m_TimeStamp;
QString m_ProcessName; QString m_ProcessName;
void* m_BoxPtr; void* m_BoxPtr;

View File

@ -144,6 +144,17 @@ void CTraceModel::FreeNode(STreeNode* pNode)
//delete pNode; //delete pNode;
} }
bool CTraceModel::TestHighLight(STreeNode* pNode) const
{
if (m_HighLightExp.isEmpty())
return false;
for (int i = 0; i < eCount; i++) {
if (NodeData(pNode, Qt::DisplayRole, i).toString().contains(m_HighLightExp))
return true;
}
return false;
}
QVariant CTraceModel::NodeData(STreeNode* pNode, int role, int section) const QVariant CTraceModel::NodeData(STreeNode* pNode, int role, int section) const
{ {
const CTraceEntryPtr& pEntry = pNode->pEntry; const CTraceEntryPtr& pEntry = pNode->pEntry;
@ -183,9 +194,10 @@ QVariant CTraceModel::NodeData(STreeNode* pNode, int role, int section) const
if(!m_bTree) { if(!m_bTree) {
QString Name = pEntry->GetProcessName(); QString Name = pEntry->GetProcessName();
return QString("%1 (%2, %3) - %4").arg(Name.isEmpty() ? tr("Unknown") : Name) return QString("%1 (%2, %3) - %4").arg(Name.isEmpty() ? tr("Unknown") : Name)
.arg(pEntry->GetProcessId()).arg(pEntry->GetThreadId()).arg(pEntry->GetTimeStamp().toString("hh:mm:ss.zzz")); .arg(pEntry->GetProcessId()).arg(pEntry->GetThreadId())
.arg(QDateTime::fromMSecsSinceEpoch(pEntry->GetTimeStamp()).toString("hh:mm:ss.zzz"));
} else } else
return pEntry->GetTimeStamp().toString("hh:mm:ss.zzz"); return QDateTime::fromMSecsSinceEpoch(pEntry->GetTimeStamp()).toString("hh:mm:ss.zzz");
} }
case eType: return pEntry->GetTypeStr(); case eType: return pEntry->GetTypeStr();
case eStatus: return pEntry->GetStautsStr(); case eStatus: return pEntry->GetStautsStr();
@ -198,6 +210,18 @@ QVariant CTraceModel::NodeData(STreeNode* pNode, int role, int section) const
} }
} }
} }
case Qt::BackgroundRole:
{
if(!CTreeItemModel::GetDarkMode())
return TestHighLight(pNode) ? QColor(Qt::yellow) : QVariant();
break;
}
case Qt::ForegroundRole:
{
if(CTreeItemModel::GetDarkMode())
return TestHighLight(pNode) ? QColor(Qt::yellow) : QVariant();
break;
}
} }
return QVariant(); return QVariant();

View File

@ -15,6 +15,8 @@ public:
void SetTree(bool bTree) { m_bTree = bTree; } void SetTree(bool bTree) { m_bTree = bTree; }
bool IsTree() const { return m_bTree; } bool IsTree() const { return m_bTree; }
void SetHighLight(const QString& Exp) { m_HighLightExp = Exp; }
QList<QModelIndex> Sync(const QVector<CTraceEntryPtr>& EntryList); QList<QModelIndex> Sync(const QVector<CTraceEntryPtr>& EntryList);
CTraceEntryPtr GetEntry(const QModelIndex& index) const; CTraceEntryPtr GetEntry(const QModelIndex& index) const;
@ -96,5 +98,9 @@ protected:
STreeNode* m_Root; STreeNode* m_Root;
QHash<quint64, STreeNode*> m_Branches; QHash<quint64, STreeNode*> m_Branches;
QString m_HighLightExp;
bool TestHighLight(STreeNode* pNode) const;
static PoolAllocator<sizeof(STreeNode)> m_NodeAllocator; static PoolAllocator<sizeof(STreeNode)> m_NodeAllocator;
}; };

View File

@ -25,7 +25,7 @@ CSbieView::CSbieView(QWidget* parent) : CPanelView(parent)
//m_UserConfigChanged = false; //m_UserConfigChanged = false;
m_pSbieModel = new CSbieModel(); m_pSbieModel = new CSbieModel(this);
m_pSbieModel->SetTree(true); m_pSbieModel->SetTree(true);
m_pSbieModel->SetUseIcons(true); m_pSbieModel->SetUseIcons(true);
@ -50,7 +50,6 @@ CSbieView::CSbieView(QWidget* parent) : CPanelView(parent)
m_pSbieModel->SetLargeIcons(); m_pSbieModel->SetLargeIcons();
m_pSbieTree->setIconSize(QSize(32, 32)); m_pSbieTree->setIconSize(QSize(32, 32));
} }
((CSortFilterProxyModel*)m_pSortProxy)->setView(m_pSbieTree);
m_pSbieTree->setDragDropMode(QAbstractItemView::InternalMove); m_pSbieTree->setDragDropMode(QAbstractItemView::InternalMove);
@ -82,7 +81,9 @@ CSbieView::CSbieView(QWidget* parent) : CPanelView(parent)
m_pMainLayout->addWidget(m_pSbieTree); m_pMainLayout->addWidget(m_pSbieTree);
// //
m_pMainLayout->addWidget(new CFinder(m_pSortProxy, this)); CFinder* pFinder = new CFinder(m_pSortProxy, this);
m_pMainLayout->addWidget(pFinder);
pFinder->SetTree(m_pSbieTree);
connect(m_pSbieModel, SIGNAL(ToolTipCallback(const QVariant&, QString&)), this, SLOT(OnToolTipCallback(const QVariant&, QString&)), Qt::DirectConnection); connect(m_pSbieModel, SIGNAL(ToolTipCallback(const QVariant&, QString&)), this, SLOT(OnToolTipCallback(const QVariant&, QString&)), Qt::DirectConnection);

View File

@ -51,6 +51,9 @@
CTraceTree::CTraceTree(QWidget* parent) CTraceTree::CTraceTree(QWidget* parent)
: CPanelWidget<QTreeViewEx>(parent) : CPanelWidget<QTreeViewEx>(parent)
{ {
m_bHighLight = false;
//m_FilterCol = -1;
m_pTreeList->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false)); m_pTreeList->setAlternatingRowColors(theConf->GetBool("Options/AltRowColors", false));
m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection); m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection);
@ -83,7 +86,8 @@ CTraceTree::CTraceTree(QWidget* parent)
//connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged())); //connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged()));
//m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, m_pSortProxy)); //m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, m_pSortProxy));
m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, this, CFinder::eHighLight)); m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, this, CFinder::eHighLightDefault));
//QObject::connect(pFinder, SIGNAL(SelectNext()), this, SLOT(SelectNext()));
QByteArray Columns = theConf->GetBlob("MainWindow/TraceLog_Columns"); QByteArray Columns = theConf->GetBlob("MainWindow/TraceLog_Columns");
@ -98,6 +102,23 @@ CTraceTree::~CTraceTree()
theConf->SetBlob("MainWindow/TraceLog_Columns", GetView()->header()->saveState()); theConf->SetBlob("MainWindow/TraceLog_Columns", GetView()->header()->saveState());
} }
void CTraceTree::SetFilter(const QString& Exp, int iOptions, int Column)
{
bool bReset = m_FilterExp != Exp || m_bHighLight != ((iOptions & CFinder::eHighLight) != 0);
//QString ExpStr = ((iOptions & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*");
//QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
//m_FilterExp = RegExp;
m_FilterExp = Exp;
m_bHighLight = (iOptions & CFinder::eHighLight) != 0;
//m_FilterCol = Col;
if(bReset)
emit FilterChanged();
}
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// CMonitorList // CMonitorList
@ -117,8 +138,6 @@ CMonitorList::CMonitorList(QWidget* parent)
m_pSortProxy->setDynamicSortFilter(true); m_pSortProxy->setDynamicSortFilter(true);
m_pTreeList->setModel(m_pSortProxy); m_pTreeList->setModel(m_pSortProxy);
m_pSortProxy->setView(m_pTreeList);
QStyle* pStyle = QStyleFactory::create("windows"); QStyle* pStyle = QStyleFactory::create("windows");
m_pTreeList->setStyle(pStyle); m_pTreeList->setStyle(pStyle);
@ -163,8 +182,6 @@ CTraceView::CTraceView(bool bStandAlone, QWidget* parent) : QWidget(parent)
m_LastCount = 0; m_LastCount = 0;
m_bUpdatePending = false; m_bUpdatePending = false;
m_bHighLight = false;
//m_FilterCol = -1;
m_FilterPid = 0; m_FilterPid = 0;
m_FilterTid = 0; m_FilterTid = 0;
m_FilterStatus = 0; m_FilterStatus = 0;
@ -177,7 +194,7 @@ CTraceView::CTraceView(bool bStandAlone, QWidget* parent) : QWidget(parent)
m_pMonitorMode = m_pTraceToolBar->addAction(CSandMan::GetIcon("Monitor"), tr("Monitor mode"), this, SLOT(OnSetMode())); m_pMonitorMode = m_pTraceToolBar->addAction(CSandMan::GetIcon("Monitor"), tr("Monitor mode"), this, SLOT(OnSetMode()));
m_pMonitorMode->setCheckable(true); m_pMonitorMode->setCheckable(true);
m_pMonitorMode->setChecked(theConf->GetBool("Options/UseMonitorMode")); m_pMonitorMode->setChecked(theConf->GetBool("Options/UseMonitorMode", true));
m_pTraceTree = m_pTraceToolBar->addAction(CSandMan::GetIcon("Tree"), tr("Show as task tree"), this, SLOT(OnSetTree())); m_pTraceTree = m_pTraceToolBar->addAction(CSandMan::GetIcon("Tree"), tr("Show as task tree"), this, SLOT(OnSetTree()));
m_pTraceTree->setCheckable(true); m_pTraceTree->setCheckable(true);
@ -258,7 +275,7 @@ CTraceView::CTraceView(bool bStandAlone, QWidget* parent) : QWidget(parent)
m_pLayout->addWidget(m_pTrace); m_pLayout->addWidget(m_pTrace);
QObject::connect(m_pTrace, SIGNAL(FilterSet(const QString&, int, int)), this, SLOT(SetFilter(const QString&, int, int))); QObject::connect(m_pTrace, SIGNAL(FilterChanged()), this, SLOT(OnFilterChanged()));
m_pMonitor = new CMonitorList(this); m_pMonitor = new CMonitorList(this);
m_pMonitor->m_pMonitorModel->SetObjTree(m_pObjectTree->isChecked()); m_pMonitor->m_pMonitorModel->SetObjTree(m_pObjectTree->isChecked());
@ -335,8 +352,8 @@ void CTraceView::Refresh()
if (m_LastCount == ResourceLog.count()) if (m_LastCount == ResourceLog.count())
return; return;
//bool bHasFilter = !m_FilterExp.pattern().isEmpty(); //bool bHasFilter = !m_pTrace->m_FilterExp.pattern().isEmpty();
bool bHasFilter = !m_FilterExp.isEmpty(); bool bHasFilter = !m_pTrace->m_FilterExp.isEmpty();
quint64 start = GetCurCycle(); quint64 start = GetCurCycle();
for (; i < ResourceLog.count(); i++) for (; i < ResourceLog.count(); i++)
@ -381,12 +398,12 @@ void CTraceView::Refresh()
} }
else else
{ {
if (bHasFilter && !m_bHighLight) { if (bHasFilter && !m_pTrace->m_bHighLight) {
if (!pEntry->GetName().contains(m_FilterExp) if (!pEntry->GetName().contains(m_pTrace->m_FilterExp)
&& !pEntry->GetMessage().contains(m_FilterExp) && !pEntry->GetMessage().contains(m_pTrace->m_FilterExp)
//&& !pEntry->GetTypeStr().contains(m_FilterExp) // dont filter on non static strings !!! //&& !pEntry->GetTypeStr().contains(m_pTrace->m_FilterExp) // dont filter on non static strings !!!
//&& !pEntry->GetStautsStr().contains(m_FilterExp) // dont filter on non static strings !!! //&& !pEntry->GetStautsStr().contains(m_pTrace->m_FilterExp) // dont filter on non static strings !!!
&& !pEntry->GetProcessName().contains(m_FilterExp)) && !pEntry->GetProcessName().contains(m_pTrace->m_FilterExp))
continue; continue;
} }
@ -434,6 +451,11 @@ void CTraceView::Refresh()
} }
else else
{ {
if (m_pTrace->m_bHighLight)
m_pTrace->m_pTraceModel->SetHighLight(m_pTrace->m_FilterExp);
else
m_pTrace->m_pTraceModel->SetHighLight(QString());
quint64 start = GetCurCycle(); quint64 start = GetCurCycle();
QList<QModelIndex> NewBranches = m_pTrace->m_pTraceModel->Sync(m_TraceList); QList<QModelIndex> NewBranches = m_pTrace->m_pTraceModel->Sync(m_TraceList);
qDebug() << "Sync took" << (GetCurCycle() - start) / 1000000.0 << "s"; qDebug() << "Sync took" << (GetCurCycle() - start) / 1000000.0 << "s";
@ -538,15 +560,8 @@ void CTraceView::UpdateFilters()
} }
} }
void CTraceView::SetFilter(const QString& Exp, int iOptions, int Col) void CTraceView::OnFilterChanged()
{ {
//QString ExpStr = ((iOptions & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*");
//QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
//m_FilterExp = RegExp;
m_FilterExp = Exp;
m_bHighLight = (iOptions & CFinder::eHighLight) != 0;
//m_FilterCol = Col;
m_FullRefresh = true; m_FullRefresh = true;
} }
@ -626,7 +641,7 @@ void CTraceView::SaveToFile()
const CTraceEntryPtr& pEntry = ResourceLog.at(i); const CTraceEntryPtr& pEntry = ResourceLog.at(i);
QStringList Line; QStringList Line;
Line.append(pEntry->GetTimeStamp().toString("hh:mm:ss.zzz")); Line.append(QDateTime::fromMSecsSinceEpoch(pEntry->GetTimeStamp()).toString("hh:mm:ss.zzz"));
QString Name = pEntry->GetProcessName(); QString Name = pEntry->GetProcessName();
Line.append(Name.isEmpty() ? tr("Unknown") : Name); Line.append(Name.isEmpty() ? tr("Unknown") : Name);
Line.append(QString("%1").arg(pEntry->GetProcessId())); Line.append(QString("%1").arg(pEntry->GetProcessId()));

View File

@ -5,8 +5,8 @@
#include "../Models/SbieModel.h" #include "../Models/SbieModel.h"
#include "../Models/TraceModel.h" #include "../Models/TraceModel.h"
#include "../Models/MonitorModel.h" #include "../Models/MonitorModel.h"
#include "../../MiscHelpers/Common/SortFilterProxyModel.h"
class CTraceFilterProxyModel;
class CTraceTree : public CPanelWidget<QTreeViewEx> class CTraceTree : public CPanelWidget<QTreeViewEx>
{ {
@ -19,13 +19,20 @@ public:
CTraceModel* m_pTraceModel; CTraceModel* m_pTraceModel;
public slots: public slots:
void SetFilter(const QString& Exp, int iOptions = 0, int Column = -1) { void SetFilter(const QString& Exp, int iOptions = 0, int Column = -1);
emit FilterSet(Exp, iOptions, Column);
}
void SelectNext() {}
signals: signals:
void FilterSet(const QString& Exp, int iOptions = 0, int Column = -1); void FilterChanged();
protected:
friend class CTraceView;
QString GetFilterExp() const { return m_FilterExp; }
//QRegularExpression m_FilterExp;
QString m_FilterExp;
bool m_bHighLight;
//int m_FilterCol;
}; };
class CMonitorList : public CPanelWidget<QTreeViewEx> class CMonitorList : public CPanelWidget<QTreeViewEx>
@ -61,7 +68,7 @@ public slots:
private slots: private slots:
void UpdateFilters(); void UpdateFilters();
void SetFilter(const QString& Exp, int iOptions = 0, int Col = -1); // -1 = any void OnFilterChanged();
void SaveToFile(); void SaveToFile();
@ -85,10 +92,6 @@ protected:
protected: protected:
bool m_FullRefresh; bool m_FullRefresh;
//QRegularExpression m_FilterExp;
QString m_FilterExp;
bool m_bHighLight;
//int m_FilterCol;
quint32 m_FilterPid; quint32 m_FilterPid;
quint32 m_FilterTid; quint32 m_FilterTid;
QList<quint32> m_FilterTypes; QList<quint32> m_FilterTypes;

View File

@ -65,7 +65,7 @@ CRecoveryWindow::CRecoveryWindow(const CSandBoxPtr& pBox, bool bImmediate, QWidg
ui.btnDeleteAll->setVisible(false); ui.btnDeleteAll->setVisible(false);
m_pFileModel = new CSimpleTreeModel(); m_pFileModel = new CSimpleTreeModel(this);
m_pFileModel->SetUseIcons(true); m_pFileModel->SetUseIcons(true);
m_pFileModel->AddColumn(tr("File Name"), "FileName"); m_pFileModel->AddColumn(tr("File Name"), "FileName");
m_pFileModel->AddColumn(tr("File Size"), "FileSize"); m_pFileModel->AddColumn(tr("File Size"), "FileSize");
@ -79,13 +79,14 @@ CRecoveryWindow::CRecoveryWindow(const CSandBoxPtr& pBox, bool bImmediate, QWidg
//ui.treeFiles->setItemDelegate(theGUI->GetItemDelegate()); //ui.treeFiles->setItemDelegate(theGUI->GetItemDelegate());
ui.treeFiles->setModel(m_pSortProxy); ui.treeFiles->setModel(m_pSortProxy);
((CSortFilterProxyModel*)m_pSortProxy)->setView(ui.treeFiles);
ui.treeFiles->setSelectionMode(QAbstractItemView::ExtendedSelection); ui.treeFiles->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui.treeFiles->setSortingEnabled(true); ui.treeFiles->setSortingEnabled(true);
//ui.treeFiles->setUniformRowHeights(true); //ui.treeFiles->setUniformRowHeights(true);
ui.gridLayout->addWidget(new CFinder(m_pSortProxy, this, true), 3, 0, 1, 5); CFinder* pFinder = new CFinder(m_pSortProxy, this);
ui.gridLayout->addWidget(pFinder, 3, 0, 1, 5);
pFinder->SetTree(ui.treeFiles);
ui.finder->deleteLater(); // remove place holder ui.finder->deleteLater(); // remove place holder
//connect(ui.treeFiles, SIGNAL(clicked(const QModelIndex&)), this, SLOT(UpdateSnapshot(const QModelIndex&))); //connect(ui.treeFiles, SIGNAL(clicked(const QModelIndex&)), this, SLOT(UpdateSnapshot(const QModelIndex&)));

View File

@ -34,7 +34,7 @@ CSnapshotsWindow::CSnapshotsWindow(const CSandBoxPtr& pBox, QWidget *parent)
ui.treeSnapshots->setItemDelegate(new CTreeItemDelegate()); ui.treeSnapshots->setItemDelegate(new CTreeItemDelegate());
ui.treeSnapshots->setExpandsOnDoubleClick(false); ui.treeSnapshots->setExpandsOnDoubleClick(false);
m_pSnapshotModel = new CSimpleTreeModel(); m_pSnapshotModel = new CSimpleTreeModel(this);
m_pSnapshotModel->AddColumn(tr("Snapshot"), "Name"); m_pSnapshotModel->AddColumn(tr("Snapshot"), "Name");
/*m_pSortProxy = new CSortFilterProxyModel(this); /*m_pSortProxy = new CSortFilterProxyModel(this);