/[svn]/qsampler/trunk/src/qsamplerMessages.cpp
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerMessages.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3861 - (show annotations) (download)
Mon Mar 1 15:38:42 2021 UTC (3 years, 1 month ago) by capela
File size: 9777 byte(s)
- Fixed messages stdout-buffer flush.
1 // qsamplerMessages.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2021, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007, Christian Schoenebeck
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 *****************************************************************************/
22
23 #include "qsamplerAbout.h"
24 #include "qsamplerMessages.h"
25
26 #include <QSocketNotifier>
27
28 #include <QFile>
29 #include <QTextBrowser>
30 #include <QTextCursor>
31 #include <QTextStream>
32 #include <QTextBlock>
33 #include <QDateTime>
34 #include <QIcon>
35
36 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
37 #include <unistd.h>
38 #include <fcntl.h>
39 #endif
40
41
42 // Deprecated QTextStreamFunctions/Qt namespaces workaround.
43 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
44 #define endl Qt::endl
45 #endif
46
47
48 namespace QSampler {
49
50 // The default maximum number of message lines.
51 #define QSAMPLER_MESSAGES_MAXLINES 1000
52
53 // Notification pipe descriptors
54 #define QSAMPLER_MESSAGES_FDNIL -1
55 #define QSAMPLER_MESSAGES_FDREAD 0
56 #define QSAMPLER_MESSAGES_FDWRITE 1
57
58
59 //-------------------------------------------------------------------------
60 // QSampler::Messages - Messages log dockable window.
61 //
62
63 // Constructor.
64 Messages::Messages ( QWidget *pParent )
65 : QDockWidget(pParent)
66 {
67 // Surely a name is crucial (e.g.for storing geometry settings)
68 QDockWidget::setObjectName("qsamplerMessages");
69
70 // Intialize stdout capture stuff.
71 m_pStdoutNotifier = nullptr;
72 m_fdStdout[QSAMPLER_MESSAGES_FDREAD] = QSAMPLER_MESSAGES_FDNIL;
73 m_fdStdout[QSAMPLER_MESSAGES_FDWRITE] = QSAMPLER_MESSAGES_FDNIL;
74
75 // Create local text view widget.
76 m_pMessagesTextView = new QTextBrowser(this);
77 // QFont font(m_pMessagesTextView->font());
78 // font.setFamily("Fixed");
79 // m_pMessagesTextView->setFont(font);
80 m_pMessagesTextView->setLineWrapMode(QTextEdit::NoWrap);
81 // m_pMessagesTextView->setReadOnly(true);
82 // m_pMessagesTextView->setUndoRedoEnabled(false);
83 // m_pMessagesTextView->setTextFormat(Qt::LogText);
84
85 // Initialize default message limit.
86 m_iMessagesLines = 0;
87 setMessagesLimit(QSAMPLER_MESSAGES_MAXLINES);
88
89 m_pMessagesLog = nullptr;
90
91 // Prepare the dockable window stuff.
92 QDockWidget::setWidget(m_pMessagesTextView);
93 // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures);
94 QDockWidget::setAllowedAreas(
95 Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
96 // Some specialties to this kind of dock window...
97 QDockWidget::setMinimumHeight(120);
98
99 // Finally set the default caption and tooltip.
100 const QString& sCaption = tr("Messages");
101 QDockWidget::setWindowTitle(sCaption);
102 // QDockWidget::setWindowIcon(QIcon(":/icons/qsamplerMessages.png"));
103 QDockWidget::setToolTip(sCaption);
104 }
105
106
107 // Destructor.
108 Messages::~Messages (void)
109 {
110 // Turn off and close logging.
111 setLogging(false);
112
113 // No more notifications.
114 if (m_pStdoutNotifier)
115 delete m_pStdoutNotifier;
116
117 // No need to delete child widgets, Qt does it all for us.
118 }
119
120
121 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
122 #pragma GCC diagnostic push
123 #pragma GCC diagnostic ignored "-Wunused-parameter"
124 #endif
125
126 // Set stdout/stderr blocking mode.
127 bool Messages::stdoutBlock ( int fd, bool bBlock ) const
128 {
129 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
130 const int iFlags = ::fcntl(fd, F_GETFL, 0);
131 const bool bNonBlock = bool(iFlags & O_NONBLOCK);
132 if (bBlock && bNonBlock)
133 bBlock = (::fcntl(fd, F_SETFL, iFlags & ~O_NONBLOCK) == 0);
134 else
135 if (!bBlock && !bNonBlock)
136 bBlock = (::fcntl(fd, F_SETFL, iFlags | O_NONBLOCK) != 0);
137 #endif
138 return bBlock;
139 }
140
141
142 // Own stdout/stderr socket notifier slot.
143 void Messages::stdoutNotify ( int fd )
144 {
145 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
146 // Set non-blocking reads, if not already...
147 const bool bBlock = stdoutBlock(fd, false);
148 // Read as much as is available...
149 QString sTemp;
150 char achBuffer[1024];
151 const int cchBuffer = sizeof(achBuffer) - 1;
152 int cchRead = ::read(fd, achBuffer, cchBuffer);
153 while (cchRead > 0) {
154 achBuffer[cchRead] = (char) 0;
155 sTemp.append(achBuffer);
156 cchRead = (bBlock ? 0 : ::read(fd, achBuffer, cchBuffer));
157 }
158 // Needs to be non-empty...
159 if (!sTemp.isEmpty())
160 appendStdoutBuffer(sTemp);
161 #endif
162 }
163
164 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
165 #pragma GCC diagnostic pop
166 #endif
167
168
169 // Stdout buffer handler -- now splitted by complete new-lines...
170 void Messages::appendStdoutBuffer ( const QString& s )
171 {
172 m_sStdoutBuffer.append(s);
173
174 processStdoutBuffer();
175 }
176
177 void Messages::processStdoutBuffer (void)
178 {
179 const int iLength = m_sStdoutBuffer.lastIndexOf('\n');
180 if (iLength > 0) {
181 QStringListIterator iter(m_sStdoutBuffer.left(iLength).split('\n'));
182 while (iter.hasNext()) {
183 const QString& sTemp = iter.next();
184 if (!sTemp.isEmpty())
185 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
186 appendMessagesText(sTemp.trimmed());
187 #else
188 appendMessagesText(sTemp);
189 #endif
190 }
191 m_sStdoutBuffer.remove(0, iLength + 1);
192 }
193 }
194
195
196 // Stdout flusher -- show up any unfinished line...
197 void Messages::flushStdoutBuffer (void)
198 {
199 processStdoutBuffer();
200
201 if (!m_sStdoutBuffer.isEmpty()) {
202 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
203 appendMessagesText(m_sStdoutBuffer.trimmed());
204 #else
205 appendMessagesText(m_sStdoutBuffer);
206 #endif
207 m_sStdoutBuffer.clear();
208 }
209 }
210
211
212 // Stdout capture accessors.
213 bool Messages::isCaptureEnabled (void)
214 {
215 return (m_pStdoutNotifier != nullptr);
216 }
217
218 void Messages::setCaptureEnabled ( bool bCapture )
219 {
220 // Flush current buffer.
221 flushStdoutBuffer();
222
223 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
224 // Destroy if already enabled.
225 if (!bCapture && m_pStdoutNotifier) {
226 delete m_pStdoutNotifier;
227 m_pStdoutNotifier = nullptr;
228 // Close the notification pipes.
229 if (m_fdStdout[QSAMPLER_MESSAGES_FDREAD] != QSAMPLER_MESSAGES_FDNIL) {
230 ::close(m_fdStdout[QSAMPLER_MESSAGES_FDREAD]);
231 m_fdStdout[QSAMPLER_MESSAGES_FDREAD] = QSAMPLER_MESSAGES_FDNIL;
232 }
233 }
234 // Are we going to make up the capture?
235 if (bCapture && m_pStdoutNotifier == nullptr && ::pipe(m_fdStdout) == 0) {
236 ::dup2(m_fdStdout[QSAMPLER_MESSAGES_FDWRITE], STDOUT_FILENO);
237 ::dup2(m_fdStdout[QSAMPLER_MESSAGES_FDWRITE], STDERR_FILENO);
238 m_pStdoutNotifier = new QSocketNotifier(
239 m_fdStdout[QSAMPLER_MESSAGES_FDREAD], QSocketNotifier::Read, this);
240 QObject::connect(m_pStdoutNotifier,
241 SIGNAL(activated(int)),
242 SLOT(stdoutNotify(int)));
243 }
244 #endif
245 }
246
247
248 // Message font accessors.
249 QFont Messages::messagesFont (void)
250 {
251 return m_pMessagesTextView->font();
252 }
253
254 void Messages::setMessagesFont ( const QFont& font )
255 {
256 m_pMessagesTextView->setFont(font);
257 }
258
259
260 // Maximum number of message lines accessors.
261 int Messages::messagesLimit (void)
262 {
263 return m_iMessagesLimit;
264 }
265
266 void Messages::setMessagesLimit ( int iMessagesLimit )
267 {
268 m_iMessagesLimit = iMessagesLimit;
269 m_iMessagesHigh = iMessagesLimit + (iMessagesLimit / 3);
270 }
271
272 // Messages logging stuff.
273 bool Messages::isLogging (void) const
274 {
275 return (m_pMessagesLog != nullptr);
276 }
277
278 void Messages::setLogging ( bool bEnabled, const QString& sFilename )
279 {
280 if (m_pMessagesLog) {
281 appendMessages(tr("Logging stopped --- %1 ---")
282 .arg(QDateTime::currentDateTime().toString()));
283 m_pMessagesLog->close();
284 delete m_pMessagesLog;
285 m_pMessagesLog = nullptr;
286 }
287
288 if (bEnabled) {
289 m_pMessagesLog = new QFile(sFilename);
290 if (m_pMessagesLog->open(QIODevice::Text | QIODevice::Append)) {
291 appendMessages(tr("Logging started --- %1 ---")
292 .arg(QDateTime::currentDateTime().toString()));
293 } else {
294 delete m_pMessagesLog;
295 m_pMessagesLog = nullptr;
296 }
297 }
298 }
299
300
301 // Messages log output method.
302 void Messages::appendMessagesLog ( const QString& s )
303 {
304 if (m_pMessagesLog) {
305 QTextStream(m_pMessagesLog)
306 << QTime::currentTime().toString("hh:mm:ss.zzz")
307 << ' ' << s << endl;
308 m_pMessagesLog->flush();
309 }
310 }
311
312 // Messages widget output method.
313 void Messages::appendMessagesLine ( const QString& s )
314 {
315 // Check for message line limit...
316 if (m_iMessagesLines > m_iMessagesHigh) {
317 m_pMessagesTextView->setUpdatesEnabled(false);
318 QTextCursor textCursor(m_pMessagesTextView->document()->begin());
319 while (m_iMessagesLines > m_iMessagesLimit) {
320 // Move cursor extending selection
321 // from start to next line-block...
322 textCursor.movePosition(
323 QTextCursor::NextBlock, QTextCursor::KeepAnchor);
324 m_iMessagesLines--;
325 }
326 // Remove the excessive line-blocks...
327 textCursor.removeSelectedText();
328 m_pMessagesTextView->setUpdatesEnabled(true);
329 }
330
331 m_pMessagesTextView->append(s);
332 m_iMessagesLines++;
333 }
334
335
336 // The main utility methods.
337 void Messages::appendMessages ( const QString& s )
338 {
339 appendMessagesColor(s, Qt::gray);
340 }
341
342 void Messages::appendMessagesColor ( const QString& s, const QColor& rgb )
343 {
344 appendMessagesLine("<font color=\"" + rgb.name() + "\">" + s + "</font>");
345 appendMessagesLog(s);
346 }
347
348 void Messages::appendMessagesText ( const QString& s )
349 {
350 appendMessagesLine(s);
351 appendMessagesLog(s);
352 }
353
354
355 // History reset.
356 void Messages::clear (void)
357 {
358 m_iMessagesLines = 0;
359 m_pMessagesTextView->clear();
360 }
361
362 } // namespace QSampler
363
364
365 // end of qsamplerMessages.cpp

  ViewVC Help
Powered by ViewVC