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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3861 - (hide 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 capela 94 // qsamplerMessages.cpp
2     //
3     /****************************************************************************
4 capela 3853 Copyright (C) 2004-2021, rncbc aka Rui Nuno Capela. All rights reserved.
5 capela 1464 Copyright (C) 2007, Christian Schoenebeck
6 capela 94
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 capela 920 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 capela 94
21     *****************************************************************************/
22    
23 capela 1465 #include "qsamplerAbout.h"
24 capela 94 #include "qsamplerMessages.h"
25    
26 capela 1465 #include <QSocketNotifier>
27 capela 1738
28     #include <QFile>
29 capela 3788 #include <QTextBrowser>
30 capela 1465 #include <QTextCursor>
31 capela 1738 #include <QTextStream>
32 capela 1465 #include <QTextBlock>
33     #include <QDateTime>
34     #include <QIcon>
35 schoenebeck 1461
36 capela 3358 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
37 capela 115 #include <unistd.h>
38 capela 2669 #include <fcntl.h>
39 capela 115 #endif
40    
41 capela 1558
42 capela 3759 // 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 capela 1558 namespace QSampler {
49    
50 capela 94 // 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 capela 2582
59 capela 94 //-------------------------------------------------------------------------
60 capela 1558 // QSampler::Messages - Messages log dockable window.
61 capela 94 //
62    
63     // Constructor.
64 capela 1558 Messages::Messages ( QWidget *pParent )
65 capela 1739 : QDockWidget(pParent)
66 capela 94 {
67 capela 1465 // Surely a name is crucial (e.g.for storing geometry settings)
68     QDockWidget::setObjectName("qsamplerMessages");
69    
70 capela 1739 // Intialize stdout capture stuff.
71 capela 3555 m_pStdoutNotifier = nullptr;
72 capela 1739 m_fdStdout[QSAMPLER_MESSAGES_FDREAD] = QSAMPLER_MESSAGES_FDNIL;
73     m_fdStdout[QSAMPLER_MESSAGES_FDWRITE] = QSAMPLER_MESSAGES_FDNIL;
74 capela 94
75 capela 1465 // Create local text view widget.
76 capela 3788 m_pMessagesTextView = new QTextBrowser(this);
77 capela 1738 // QFont font(m_pMessagesTextView->font());
78 capela 94 // font.setFamily("Fixed");
79 capela 1738 // m_pMessagesTextView->setFont(font);
80     m_pMessagesTextView->setLineWrapMode(QTextEdit::NoWrap);
81 capela 3788 // m_pMessagesTextView->setReadOnly(true);
82     // m_pMessagesTextView->setUndoRedoEnabled(false);
83 capela 1738 // m_pMessagesTextView->setTextFormat(Qt::LogText);
84 capela 94
85 capela 1465 // Initialize default message limit.
86     m_iMessagesLines = 0;
87     setMessagesLimit(QSAMPLER_MESSAGES_MAXLINES);
88    
89 capela 3555 m_pMessagesLog = nullptr;
90 capela 1738
91 capela 1465 // Prepare the dockable window stuff.
92 capela 1738 QDockWidget::setWidget(m_pMessagesTextView);
93 capela 3793 // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures);
94 capela 1465 QDockWidget::setAllowedAreas(
95     Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
96 capela 772 // Some specialties to this kind of dock window...
97 capela 1465 QDockWidget::setMinimumHeight(120);
98 capela 94
99 capela 1465 // 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 capela 94 }
105    
106    
107     // Destructor.
108 capela 1558 Messages::~Messages (void)
109 capela 94 {
110 capela 1739 // Turn off and close logging.
111 capela 1738 setLogging(false);
112    
113 capela 1739 // No more notifications.
114     if (m_pStdoutNotifier)
115     delete m_pStdoutNotifier;
116 capela 94
117 capela 1739 // No need to delete child widgets, Qt does it all for us.
118 capela 94 }
119    
120    
121 capela 3855 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
122 capela 3853 #pragma GCC diagnostic push
123     #pragma GCC diagnostic ignored "-Wunused-parameter"
124     #endif
125    
126 capela 2848 // Set stdout/stderr blocking mode.
127     bool Messages::stdoutBlock ( int fd, bool bBlock ) const
128     {
129 capela 3358 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
130 capela 2848 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 capela 94 // Own stdout/stderr socket notifier slot.
143 capela 1558 void Messages::stdoutNotify ( int fd )
144 capela 94 {
145 capela 3358 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
146 capela 2669 // Set non-blocking reads, if not already...
147 capela 2848 const bool bBlock = stdoutBlock(fd, false);
148 capela 2669 // Read as much as is available...
149     QString sTemp;
150 capela 1739 char achBuffer[1024];
151 capela 2669 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 capela 2848 cchRead = (bBlock ? 0 : ::read(fd, achBuffer, cchBuffer));
157 capela 1739 }
158 capela 2669 // Needs to be non-empty...
159     if (!sTemp.isEmpty())
160     appendStdoutBuffer(sTemp);
161 capela 94 #endif
162     }
163    
164 capela 3855 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
165 capela 3853 #pragma GCC diagnostic pop
166     #endif
167 capela 94
168 capela 3853
169 capela 94 // Stdout buffer handler -- now splitted by complete new-lines...
170 capela 1558 void Messages::appendStdoutBuffer ( const QString& s )
171 capela 94 {
172 capela 1465 m_sStdoutBuffer.append(s);
173 capela 94
174 capela 3788 processStdoutBuffer();
175     }
176    
177     void Messages::processStdoutBuffer (void)
178     {
179 capela 2582 const int iLength = m_sStdoutBuffer.lastIndexOf('\n');
180 capela 1465 if (iLength > 0) {
181 capela 3861 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 schoenebeck 1474 m_sStdoutBuffer.remove(0, iLength + 1);
192 capela 1465 }
193 capela 94 }
194    
195    
196     // Stdout flusher -- show up any unfinished line...
197 capela 1558 void Messages::flushStdoutBuffer (void)
198 capela 94 {
199 capela 3861 processStdoutBuffer();
200    
201 capela 1739 if (!m_sStdoutBuffer.isEmpty()) {
202 capela 3861 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
203     appendMessagesText(m_sStdoutBuffer.trimmed());
204     #else
205     appendMessagesText(m_sStdoutBuffer);
206     #endif
207 capela 2669 m_sStdoutBuffer.clear();
208 capela 1739 }
209 capela 94 }
210    
211    
212     // Stdout capture accessors.
213 capela 1558 bool Messages::isCaptureEnabled (void)
214 capela 94 {
215 capela 3555 return (m_pStdoutNotifier != nullptr);
216 capela 94 }
217    
218 capela 1558 void Messages::setCaptureEnabled ( bool bCapture )
219 capela 94 {
220 capela 1739 // Flush current buffer.
221     flushStdoutBuffer();
222 capela 94
223 capela 3358 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32)
224 capela 1465 // Destroy if already enabled.
225     if (!bCapture && m_pStdoutNotifier) {
226     delete m_pStdoutNotifier;
227 capela 3555 m_pStdoutNotifier = nullptr;
228 capela 1465 // 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 capela 3555 if (bCapture && m_pStdoutNotifier == nullptr && ::pipe(m_fdStdout) == 0) {
236 capela 1465 ::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 capela 94 #endif
245     }
246    
247    
248     // Message font accessors.
249 capela 1558 QFont Messages::messagesFont (void)
250 capela 94 {
251 capela 1739 return m_pMessagesTextView->font();
252 capela 94 }
253    
254 capela 1738 void Messages::setMessagesFont ( const QFont& font )
255 capela 94 {
256 capela 1739 m_pMessagesTextView->setFont(font);
257 capela 94 }
258    
259    
260     // Maximum number of message lines accessors.
261 capela 1558 int Messages::messagesLimit (void)
262 capela 94 {
263 capela 1739 return m_iMessagesLimit;
264 capela 94 }
265    
266 capela 1558 void Messages::setMessagesLimit ( int iMessagesLimit )
267 capela 94 {
268 capela 1739 m_iMessagesLimit = iMessagesLimit;
269     m_iMessagesHigh = iMessagesLimit + (iMessagesLimit / 3);
270 capela 94 }
271    
272 capela 1738 // Messages logging stuff.
273     bool Messages::isLogging (void) const
274     {
275 capela 3555 return (m_pMessagesLog != nullptr);
276 capela 1738 }
277 capela 94
278 capela 1738 void Messages::setLogging ( bool bEnabled, const QString& sFilename )
279 capela 94 {
280 capela 1738 if (m_pMessagesLog) {
281     appendMessages(tr("Logging stopped --- %1 ---")
282     .arg(QDateTime::currentDateTime().toString()));
283     m_pMessagesLog->close();
284     delete m_pMessagesLog;
285 capela 3555 m_pMessagesLog = nullptr;
286 capela 1738 }
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 capela 3555 m_pMessagesLog = nullptr;
296 capela 1738 }
297     }
298 capela 94 }
299    
300 capela 1738
301     // Messages log output method.
302     void Messages::appendMessagesLog ( const QString& s )
303 capela 94 {
304 capela 1738 if (m_pMessagesLog) {
305 capela 3788 QTextStream(m_pMessagesLog)
306     << QTime::currentTime().toString("hh:mm:ss.zzz")
307     << ' ' << s << endl;
308 capela 1738 m_pMessagesLog->flush();
309     }
310 capela 94 }
311    
312 capela 1738 // Messages widget output method.
313     void Messages::appendMessagesLine ( const QString& s )
314 capela 94 {
315 capela 1738 // Check for message line limit...
316     if (m_iMessagesLines > m_iMessagesHigh) {
317     m_pMessagesTextView->setUpdatesEnabled(false);
318     QTextCursor textCursor(m_pMessagesTextView->document()->begin());
319 capela 1465 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 capela 1738 m_pMessagesTextView->setUpdatesEnabled(true);
329     }
330 capela 1465
331 capela 1738 m_pMessagesTextView->append(s);
332 capela 1465 m_iMessagesLines++;
333 capela 94 }
334    
335    
336 capela 1738 // The main utility methods.
337     void Messages::appendMessages ( const QString& s )
338     {
339 capela 3788 appendMessagesColor(s, Qt::gray);
340 capela 1738 }
341    
342 capela 3788 void Messages::appendMessagesColor ( const QString& s, const QColor& rgb )
343 capela 1738 {
344 capela 3788 appendMessagesLine("<font color=\"" + rgb.name() + "\">" + s + "</font>");
345     appendMessagesLog(s);
346 capela 1738 }
347    
348     void Messages::appendMessagesText ( const QString& s )
349     {
350     appendMessagesLine(s);
351     appendMessagesLog(s);
352     }
353    
354    
355 capela 1465 // History reset.
356 capela 1558 void Messages::clear (void)
357 capela 94 {
358 capela 1465 m_iMessagesLines = 0;
359 capela 1738 m_pMessagesTextView->clear();
360 capela 94 }
361    
362 capela 1558 } // namespace QSampler
363 capela 94
364 capela 1558
365 capela 94 // end of qsamplerMessages.cpp

  ViewVC Help
Powered by ViewVC