]>
vaikene.ee Git - evaf/blob - src/plugins/LogView/logview.cpp
2 * @file LogView/logview.cpp
3 * @brief Implementation of the LogView module
6 * Copyright (c) 2011-2019 Enar Vaikene
8 * This file is part of the eVaf C++ cross-platform application development framework.
10 * This file can be used under the terms of the GNU General Public License
11 * version 3.0 as published by the Free Software Foundation and appearing in
12 * the file LICENSE included in the packaging of this file. Please review the
13 * the following information to ensure the GNU General Public License version
14 * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
16 * Alternatively, this file may be used in accordance with the Commercial License
17 * Agreement provided with the Software.
23 #include <Common/Globals>
24 #include <Common/iLogger>
25 #include <Common/iApp>
26 #include <Common/iRegistry>
27 #include <SdiWindow/iSdiWindow>
30 #include <QXmlStreamReader>
34 using namespace eVaf::LogView::Internal
;
37 //-------------------------------------------------------------------
39 int const Model::MaxLines
= 1000;
41 char const * const Model::SeverityText
[Common::iLogger::Count
] = {
42 QT_TR_NOOP("[NONE] "),
43 QT_TR_NOOP("[FATAL] "),
44 QT_TR_NOOP("[ERROR] "),
45 QT_TR_NOOP("[WARNING]"),
46 QT_TR_NOOP("[INFO] "),
47 QT_TR_NOOP("[DEBUG] ")
50 Model::Model(QObject
* parent
)
51 : QAbstractListModel(parent
)
55 QVariant
Model::data(QModelIndex
const & index
, int role
) const
57 if (!index
.isValid() || index
.row() < 0 || index
.row() >= mData
.size() || index
.column() != 0)
62 // Return the message for the display role
63 case Qt::DisplayRole
: {
64 return mData
.at(index
.row()).simplified
;
67 // Change color for different message types
68 case Qt::ForegroundRole
: {
69 Common::iLogger::Severity s
= mData
.at(index
.row()).severity
;
71 case Common::iLogger::Info
:
72 return QBrush(QColor(Qt::blue
));
73 case Common::iLogger::Warning
:
74 return QBrush(QColor(Qt::black
));
75 case Common::iLogger::Error
:
76 case Common::iLogger::Fatal
:
77 return QBrush(QColor(Qt::red
));
87 void Model::addMessage(Common::iLogger::Severity severity
, QString
const & text
, QString
const & where
)
89 // Add the message to the end of the queue
90 beginInsertRows(QModelIndex(), mData
.size(), mData
.size());
91 mData
.enqueue(Message(severity
, text
, where
));
94 // Remove oldest messages if the list is full
95 if (mData
.size() > MaxLines
) {
96 beginRemoveRows(QModelIndex(), 0, 0);
101 emit
messageAdded(index(mData
.size() - 1, 0));
104 QString
Model::details(QModelIndex
const & index
) const
106 Message
const & m
= mData
.at(index
.row());
107 return tr("%1 %2 %3 : %4\nOccurred in %5")
108 .arg(m
.dt
.date().toString(Qt::DefaultLocaleShortDate
))
109 .arg(m
.dt
.time().toString("HH:mm:ss.zzz"))
110 .arg(tr(severityText(m
.severity
)))
115 bool Model::copyToClipboard(QModelIndex
const & index
)
117 mErrorString
.clear();
119 QClipboard
* cb
= QApplication::clipboard();
121 cb
->setText(details(index
));
125 mErrorString
= tr("The global clipboard is not available");
130 bool Model::saveToFile(QString
const & fileName
)
132 mErrorString
.clear();
135 if (!f
.open(QFile::WriteOnly
)) {
136 mErrorString
= tr("Failed to open the file '%1' for writing : %2").arg(fileName
).arg(f
.errorString());
142 for (int i
= 0; i
< mData
.size(); ++i
) {
143 Message
const & m
= mData
.at(i
);
144 out
<< tr("%1 %2 %3 : %4 (occurred in %5)\n")
145 .arg(m
.dt
.date().toString(Qt::DefaultLocaleShortDate
))
146 .arg(m
.dt
.time().toString("HH:mm:ss.zzz"))
147 .arg(tr(severityText(m
.severity
)))
156 char const * Model::severityText(Common::iLogger::Severity s
) const
158 if (s
>= Common::iLogger::None
&& s
< Common::iLogger::Count
)
159 return SeverityText
[s
];
161 return SeverityText
[Common::iLogger::None
];
165 //-------------------------------------------------------------------
167 Widget::Widget(QString
const & source
, QWidget
* parent
)
172 QVBoxLayout
* w
= new QVBoxLayout
;
175 mModel
= new Model(this);
176 connect(mModel
, SIGNAL(messageAdded(QModelIndex
)), this, SLOT(messageAdded(QModelIndex
)));
178 wList
= new QListView
;
179 wList
->setModel(mModel
);
180 wList
->setUniformItemSizes(true);
181 connect(wList
->selectionModel(), SIGNAL(currentChanged(QModelIndex
,QModelIndex
)), this, SLOT(currentChanged(QModelIndex
,QModelIndex
)));
184 wList
->setContextMenuPolicy(Qt::ActionsContextMenu
);
186 QAction
* a
= new QAction(tr("&Copy", VER_MODULE_NAME_STR
), this);
187 a
->setStatusTip(tr("Copies the selected message to the clipboard for pasting into other applications", VER_MODULE_NAME_STR
));
188 connect(a
, SIGNAL(triggered()), this, SLOT(copyToClipboard()));
191 a
= new QAction(tr("&Save to ...", VER_MODULE_NAME_STR
), this);
192 a
->setStatusTip(tr("Saves all the messages to a file", VER_MODULE_NAME_STR
));
193 connect(a
, SIGNAL(triggered()), this, SLOT(saveToFile()));
196 wDetails
= new QLabel
;
197 wDetails
->setWordWrap(true);
198 w
->addWidget(wDetails
);
201 void Widget::messageAdded(QModelIndex
const & index
)
204 wList
->selectionModel()->setCurrentIndex(index
, QItemSelectionModel::ClearAndSelect
);
207 void Widget::currentChanged(QModelIndex
const & current
, QModelIndex
const & previous
)
211 if (!current
.isValid() || current
.row() < 0 || current
.row() > mModel
->rowCount()) {
216 mAutoScroll
= current
.row() == (mModel
->rowCount() - 1);
218 wDetails
->setText(mModel
->details(current
));
221 void Widget::copyToClipboard()
223 QModelIndex idx
= wList
->selectionModel()->currentIndex();
225 mModel
->copyToClipboard(idx
);
228 void Widget::saveToFile()
230 QString fileName
= QFileDialog::getSaveFileName(this,
231 tr("Save to file", VER_MODULE_NAME_STR
),
232 Common::iApp::instance()->dataRootDir() + QString("%1_%2.txt").arg(mSource
).arg(QDate::currentDate().toString("yyyyMMdd")),
233 tr("Text files (*.txt);;All files (*)", VER_MODULE_NAME_STR
));
234 if (fileName
.isEmpty())
237 if (!mModel
->saveToFile(fileName
))
238 QMessageBox::critical(this, tr("Error", VER_MODULE_NAME_STR
), mModel
->errorString());
242 //-------------------------------------------------------------------
244 Window::Window(QString
const & args
, QWidget
* parent
, Qt::WindowFlags flags
)
245 : Gui::Panel(parent
, flags
)
247 setObjectName(QString("%1-Window").arg(VER_MODULE_NAME_STR
));
249 SdiWindow::iSdiWindow
* win
= evafQueryInterface
<SdiWindow::iSdiWindow
>("iSdiWindow");
250 win
->addPanel(getPanelName(args
), this);
252 setWindowTitle(tr("Messages"));
254 Common::iLogger
* logger
= Common::iLogger::instance();
255 EVAF_TEST_X(logger
, "No iLogger interface");
257 mDefSource
= logger
->defaultSource();
258 if (mDefSource
.isEmpty())
259 mDefSource
= "Common";
261 QVBoxLayout
* w
= new QVBoxLayout
;
265 wTabs
= new QTabWidget
;
268 // Add the default source
269 Widget
* s
= new Widget(mDefSource
);
270 mLogViews
.insert(s
->source(), s
);
271 wTabs
->addTab(s
, s
->source());
273 wStatusBar
= new QStatusBar
;
274 w
->addWidget(wStatusBar
);
276 QAction
* a
= new QAction(this);
277 a
->setShortcut(Qt::Key_Escape
);
278 connect(a
, SIGNAL(triggered()), this, SLOT(close()));
283 connect(logger
, SIGNAL(loggerEvent(Common::iLogger::Severity
,QString
,QString
,QString
)), this, SLOT(loggerEvent(Common::iLogger::Severity
,QString
,QString
,QString
)));
287 EVAF_INFO("%s created", qPrintable(objectName()));
296 EVAF_INFO("%s destroyed", qPrintable(objectName()));
299 QString
Window::getPanelName(QString
const & args
) const
301 QString panelName
= "LogView";
303 QXmlStreamReader
xml(args
);
304 while (!xml
.atEnd()) {
306 if (xml
.isStartElement() && xml
.name() == "attributes") {
307 if (xml
.attributes().hasAttribute("panelName")) {
308 QString s
= xml
.attributes().value("panelName").toString();
318 bool Window::event(QEvent
* e
)
320 if (e
->type() == QEvent::StatusTip
) {
321 QStatusTipEvent
* event
= static_cast<QStatusTipEvent
*>(e
);
322 wStatusBar
->showMessage(event
->tip());
325 return QWidget::event(e
);
328 void Window::saveSettings()
330 static int ver
[4] = {VER_FILE_VERSION
};
331 QSettings
settings(VER_COMPANY_NAME_STR
, Common::iApp::instance()->name());
332 settings
.setValue(QString("%1/version/major").arg(objectName()), ver
[0]);
333 settings
.setValue(QString("%1/version/minor").arg(objectName()), ver
[1]);
334 settings
.setValue(QString("%1/geometry").arg(objectName()), saveGeometry());
337 void Window::restoreSettings()
339 static int ver
[4] = {VER_FILE_VERSION
};
340 QSettings
settings(VER_COMPANY_NAME_STR
, Common::iApp::instance()->name());
342 // Ignore saved settings if the version number is not the same
343 // More intelligent checks can be implemented to allow upgrading from previous versions
344 QVariant v
= settings
.value(QString("%1/version/major").arg(objectName()));
345 if (!v
.isValid() || v
.toInt() != ver
[0])
347 v
= settings
.value(QString("%1/version/minor").arg(objectName()));
348 if (!v
.isValid() || v
.toInt() != ver
[1])
351 // Restore the geometry
352 restoreGeometry(settings
.value(QString("%1/geometry").arg(objectName())).toByteArray());
355 void Window::loggerEvent(Common::iLogger::Severity severity
, QString
const & text
, QString
const & source
, QString
const & where
)
357 // Ignore messages with >=DEBUG severity level
358 if (severity
>= Common::iLogger::Debug
)
361 // Find or create the log view widget for this source
363 QString s
= source
.isEmpty() ? mDefSource
: source
;
364 QHash
<QString
, Widget
*>::const_iterator it
= mLogViews
.constFind(s
);
365 if (it
== mLogViews
.constEnd()) {
367 mLogViews
.insert(w
->source(), w
);
368 wTabs
->addTab(w
, w
->source());
373 w
->addMessage(severity
, text
, where
);
377 //-------------------------------------------------------------------
383 setObjectName(QString("%1-Module").arg(VER_MODULE_NAME_STR
));
384 EVAF_INFO("%s created", qPrintable(objectName()));
389 EVAF_INFO("%s destroyed", qPrintable(objectName()));
392 bool Module::init(QString
const & args
)
394 wWindow
= new Window(args
);
396 EVAF_INFO("%s initialized", qPrintable(objectName()));
408 EVAF_INFO("%s finalized", qPrintable(objectName()));