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();
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 );
116 mutable struct Cache {
117 enum { Dimensions = 1, FontMetrics = 2, All = FontMetrics|Dimensions };
118 Cache() : dirty( All ) {}
124 int averageCharWidth;
129 int indexOfLongestLine;
130 int longestLineLength;
160 if ( hs == d->historySize )
163 d->enforceHistorySize();
164 d->updateScrollRanges();
169 return d->historySize;
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;
272 const QSize view( d->minimumVisibleColumns * d->cache.fontMetrics.averageCharWidth,
273 d->minimumVisibleLines * d->cache.fontMetrics.lineSpacing );
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;
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 );
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;
347 if ( d->alternatingRowColors ) {
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 )
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 );
363 for (
unsigned int i = visibleLines.first % 2 ? visibleLines.first : visibleLines.first+1, end = visibleLines.second ; i < end ; i+=2 )
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 ;
379 p.
drawText( 0, i * cache.fontMetrics.lineSpacing + cache.fontMetrics.ascent, li.text );
385 if ( e->
timerId() == d->timer.timerId() ) {
386 d->addPendingLines();
395 d->cache.dirty |= Private::Cache::FontMetrics;
399 d->updateScrollRanges();
425 KDLogTextWidget::Private::~Private() {}
428 void KDLogTextWidget::Private::updateCache()
const {
430 if ( cache.dirty >= Cache::FontMetrics ) {
433 cache.fontMetrics.ascent = fm.
ascent();
439 Q_FOREACH(
const LineItem & li, lines )
440 lw.push_back( fm.width( li.text ) );
443 if ( cache.dirty >= Cache::Dimensions ) {
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;
489 assert( lw.
size() > 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();
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 ) {
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 {
static uint qHash(const char *data)
void paintEvent(QPaintEvent *)
QSize minimumSizeHint() const
void changeEvent(QEvent *)
void setRange(int min, int max)
A high-speed text display widget.
KDLogTextWidget(QWidget *parent=0)
void setMinimumVisibleColumns(unsigned int num)
bool alternatingRowColors() const
void setMinimumVisibleLines(unsigned int num)
iterator erase(iterator begin, iterator end)
QString join(const QString &separator) const
virtual void timerEvent(QTimerEvent *event)
const QRect & rect() const
void drawRect(const QRectF &rectangle)
void setPen(const QColor &color)
virtual void changeEvent(QEvent *ev)
void setLines(const QStringList &list)
void setBrush(const QBrush &brush)
unsigned int minimumVisibleLines() const
void drawText(const QPointF &position, const QString &text)
static void set_scrollbar_properties(QScrollBar &sb, int document, int viewport, int singleStep, Qt::Orientation o)
QRect mapRect(const QRect &rectangle) const
static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
const_iterator constBegin() const
void timerEvent(QTimerEvent *)
void setHistorySize(unsigned int size)
void translate(const QPointF &offset)
unsigned int historySize() const
void resizeEvent(QResizeEvent *)
int averageCharWidth() const
unsigned int minimumVisibleColumns() const
void setAlternatingRowColors(bool on)
void message(const QString &msg, const QColor &color)
QMatrix inverted(bool *invertible) const
QStringList lines() const
const QMatrix & matrix() const
static int get_scrollbar_offset(const QScrollBar *sb)