23 #include <config-kleopatra.h>
27 #include <QBasicTimer>
30 #include <QPaintEvent>
55 class KDLogTextWidget::Private {
56 friend class ::KDLogTextWidget;
62 void updateCache()
const;
65 if ( !timer.isActive() )
66 timer.start( 500,
q );
69 void addPendingLines();
70 void enforceHistorySize();
71 void updateScrollRanges();
72 QPair<int,int> visibleLines(
int top,
int bottom ) {
73 return qMakePair( qMax( 0, lineByYCoordinate( top ) ),
74 qMax( 0, 1 + lineByYCoordinate( bottom ) ) );
76 int lineByYCoordinate(
int x )
const;
78 QPoint scrollOffset()
const;
80 QRect lineRect(
int idx )
const {
81 assert( !cache.dirty );
82 return QRect( 0, idx * cache.fontMetrics.lineSpacing, cache.dimensions.longestLineLength, cache.fontMetrics.lineSpacing-1 );
88 friend inline uint
qHash(
const Style & style ) {
89 return qHash( style.color.rgba() );
91 bool operator==(
const Style & other )
const {
return this->color.rgba() == other.color.rgba(); }
92 bool operator<(
const Style & other )
const {
return this->color.rgba() < other.color.rgba(); }
100 unsigned int findOrAddStyle(
const Style & style );
103 QHash<unsigned int, Style> styleByID;
104 QHash<Style,unsigned int> idByStyle;
106 QVector<LineItem>
lines, pendingLines;
116 mutable struct Cache {
117 enum { Dimensions = 1, FontMetrics = 2, All = FontMetrics|Dimensions };
118 Cache() : dirty( All ) {}
124 int averageCharWidth;
125 QVector<int> lineWidths;
129 int indexOfLongestLine;
130 int longestLineLength;
160 if ( hs == d->historySize )
163 d->enforceHistorySize();
164 d->updateScrollRanges();
165 viewport()->update();
169 return d->historySize;
181 return lines().join( QLatin1String(
"\n" ) );
194 Q_FOREACH(
const QString & s, l )
200 Q_FOREACH(
const Private::LineItem & li, d->lines )
201 result.push_back( li.
text );
202 Q_FOREACH( const Private::LineItem & li, d->pendingLines )
203 result.push_back( li.text );
217 if ( num == d->minimumVisibleLines )
219 d->minimumVisibleLines = num;
224 return d->minimumVisibleLines;
239 if ( num == d->minimumVisibleColumns )
241 d->minimumVisibleColumns = num;
246 return d->minimumVisibleColumns;
259 if ( on == d->alternatingRowColors )
261 d->alternatingRowColors = on;
266 return d->alternatingRowColors;
271 const QSize base = QAbstractScrollArea::minimumSizeHint();
272 const QSize view( d->minimumVisibleColumns * d->cache.fontMetrics.averageCharWidth,
273 d->minimumVisibleLines * d->cache.fontMetrics.lineSpacing );
274 const QSize scrollbars( verticalScrollBar() ? verticalScrollBar()->
minimumSizeHint().width() : 0,
275 horizontalScrollBar() ? horizontalScrollBar()->
minimumSizeHint().height() : 0 );
276 return base + view + scrollbars;
280 if ( d->minimumVisibleLines > 1 || d->minimumVisibleColumns > 1 )
294 d->pendingLines.clear();
295 d->styleByID.clear();
296 d->idByStyle.clear();
297 d->cache.dirty = Private::Cache::All;
298 viewport()->update();
307 const Private::Style s = { color };
308 const Private::LineItem li = { str, d->findOrAddStyle( s ) };
309 d->pendingLines.push_back( li );
319 const Private::LineItem li = { str, 0 };
320 d->pendingLines.push_back( li );
328 QPainter p( viewport() );
330 p.translate( -d->scrollOffset() );
332 const QRect visible = p.matrix().inverted().mapRect( e->rect() );
334 const QPair<int,int> visibleLines
335 = d->visibleLines( visible.top(), visible.bottom() );
337 assert( visibleLines.first <= visibleLines.second );
339 const Private::Style defaultStyle = { p.pen().color() };
341 const Private::Cache & cache = d->cache;
343 p.setPen( Qt::NoPen );
345 p.setBrush( palette().base() );
347 if ( d->alternatingRowColors ) {
349 p.drawRect( visible );
351 #if 0 // leaves garbage
352 for (
unsigned int i = visibleLines.first % 2 ? visibleLines.first+1 : visibleLines.first, end = visibleLines.second ; i < end ; i+=2 )
353 p.drawRect( d->lineRect( i ) );
355 if ( visibleLines.second >= 0 ) {
356 const int lastY = d->lineRect( visibleLines.second-1 ).y();
357 if ( lastY < visible.bottom() )
358 p.drawRect( 0, lastY+1, cache.dimensions.longestLineLength, visible.bottom() - lastY );
362 p.setBrush( palette().alternateBase() );
363 for (
unsigned int i = visibleLines.first % 2 ? visibleLines.first : visibleLines.first+1, end = visibleLines.second ; i < end ; i+=2 )
364 p.drawRect( d->lineRect( i ) );
368 p.drawRect( visible );
373 for (
unsigned int i = visibleLines.first, end = visibleLines.second ; i != end ; ++i ) {
374 const Private::LineItem & li = d->lines[i];
375 assert( !li.styleID || d->styleByID.contains( li.styleID ) );
376 const Private::Style & st = li.styleID ? d->styleByID[li.styleID] : defaultStyle ;
378 p.setPen( st.color );
379 p.drawText( 0, i * cache.fontMetrics.lineSpacing + cache.fontMetrics.ascent, li.text );
385 if ( e->timerId() == d->timer.timerId() ) {
386 d->addPendingLines();
389 QAbstractScrollArea::timerEvent( e );
394 QAbstractScrollArea::changeEvent( e );
395 d->cache.dirty |= Private::Cache::FontMetrics;
399 d->updateScrollRanges();
416 QWidget *
const vp = qq->viewport();
417 vp->setBackgroundRole( QPalette::Base );
418 vp->setAttribute( Qt::WA_StaticContents );
419 vp->setAttribute( Qt::WA_NoSystemBackground );
421 vp->setCursor(Qt::IBeamCursor);
425 KDLogTextWidget::Private::~Private() {}
428 void KDLogTextWidget::Private::updateCache()
const {
430 if ( cache.dirty >= Cache::FontMetrics ) {
431 const QFontMetrics & fm =
q->fontMetrics();
432 cache.fontMetrics.lineSpacing = fm.lineSpacing();
433 cache.fontMetrics.ascent = fm.ascent();
434 cache.fontMetrics.averageCharWidth = fm.averageCharWidth();
436 QVector<int> & lw = cache.fontMetrics.lineWidths;
438 lw.reserve( lines.size() );
439 Q_FOREACH(
const LineItem & li, lines )
440 lw.push_back( fm.width( li.text ) );
443 if ( cache.dirty >= Cache::Dimensions ) {
444 const QVector<int> & lw = cache.fontMetrics.lineWidths;
445 const QVector<int>::const_iterator it =
446 std::max_element( lw.begin(), lw.end() );
447 if ( it == lw.end() ) {
448 cache.dimensions.indexOfLongestLine = -1;
449 cache.dimensions.longestLineLength = 0;
451 cache.dimensions.indexOfLongestLine = it - lw.begin();
452 cache.dimensions.longestLineLength = *it;
459 unsigned int KDLogTextWidget::Private::findOrAddStyle(
const Style & s ) {
460 if ( idByStyle.contains( s ) ) {
461 const unsigned int id = idByStyle[s];
462 assert( styleByID.contains(
id ) );
463 assert( styleByID[
id] == s );
466 static unsigned int nextID = 0;
467 const unsigned int id = ++nextID;
468 idByStyle.insert( s,
id );
469 styleByID.insert(
id, s );
474 void KDLogTextWidget::Private::enforceHistorySize() {
475 const size_t numLimes = lines.size();
476 if ( numLimes <= historySize )
478 const int remove = numLimes - historySize ;
479 lines.erase( lines.begin(), lines.begin() + remove );
482 if ( cache.dirty & Cache::FontMetrics ) {
483 cache.dirty |= Cache::Dimensions;
487 QVector<int> & lw = cache.fontMetrics.lineWidths;
489 assert( lw.size() > remove );
490 lw.erase( lw.begin(), lw.begin() + remove );
492 if ( cache.dirty & Cache::Dimensions )
495 if ( cache.dimensions.indexOfLongestLine >=
remove )
496 cache.dimensions.indexOfLongestLine -=
remove;
498 cache.dirty |= Cache::Dimensions;
503 const int max = std::max( 0, document - viewport );
504 const int value = sb.value();
505 const bool wasAtEnd = value == sb.maximum();
506 sb.setRange( min, max );
507 sb.setPageStep( viewport );
508 sb.setSingleStep( singleStep );
509 sb.setValue( o == Qt::Vertical && wasAtEnd ? sb.maximum() : value );
512 void KDLogTextWidget::Private::updateScrollRanges() {
516 if ( QScrollBar *
const sb =
q->verticalScrollBar() ) {
517 const int document = lines.size() * cache.fontMetrics.lineSpacing ;
518 const int viewport =
q->viewport()->height();
519 const int singleStep = cache.fontMetrics.lineSpacing;
523 if ( QScrollBar *
const sb =
q->horizontalScrollBar() ) {
524 const int document = cache.dimensions.longestLineLength;
525 const int viewport =
q->viewport()->width();
526 const int singleStep = cache.fontMetrics.lineSpacing;
531 void KDLogTextWidget::Private::addPendingLines() {
532 if ( pendingLines.empty() )
535 const unsigned int oldNumLines = lines.size();
537 lines += pendingLines;
542 if ( !cache.dirty ) {
545 const QFontMetrics & fm =
q->fontMetrics();
547 plw.reserve( pendingLines.size() );
548 Q_FOREACH(
const LineItem & li, pendingLines )
549 plw.push_back( fm.width( li.text ) );
552 const QVector<
int>::const_iterator it =
553 std::max_element( plw.constBegin(), plw.constEnd() );
554 if ( *it >= cache.dimensions.longestLineLength ) {
555 cache.dimensions.longestLineLength = *it;
556 cache.dimensions.indexOfLongestLine = oldNumLines + ( it - plw.constBegin() );
560 pendingLines.clear();
562 enforceHistorySize();
563 updateScrollRanges();
564 q->viewport()->update();
567 int KDLogTextWidget::Private::lineByYCoordinate(
int y )
const {
569 if ( cache.fontMetrics.lineSpacing == 0 )
571 const int raw = y / cache.fontMetrics.lineSpacing ;
574 if ( raw >= lines.size() )
575 return lines.size() - 1;
580 return sb ? sb->value() : 0 ;
583 QPoint KDLogTextWidget::Private::scrollOffset()
const {
588 #include "moc_kdlogtextwidget.cpp"
static uint qHash(const char *data)
void paintEvent(QPaintEvent *)
QSize minimumSizeHint() const
void changeEvent(QEvent *)
A high-speed text display widget.
KDLogTextWidget(QWidget *parent=0)
void setMinimumVisibleColumns(unsigned int num)
bool alternatingRowColors() const
void setMinimumVisibleLines(unsigned int num)
void setLines(const QStringList &list)
unsigned int minimumVisibleLines() const
static void set_scrollbar_properties(QScrollBar &sb, int document, int viewport, int singleStep, Qt::Orientation o)
static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
void timerEvent(QTimerEvent *)
static void update(const QString &fname, const QString &id)
void setHistorySize(unsigned int size)
unsigned int historySize() const
void resizeEvent(QResizeEvent *)
unsigned int minimumVisibleColumns() const
void setAlternatingRowColors(bool on)
void message(const QString &msg, const QColor &color)
QStringList lines() const
static int get_scrollbar_offset(const QScrollBar *sb)