• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kleopatra

  • sources
  • kde-4.12
  • kdepim
  • kleopatra
  • utils
kdlogtextwidget.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Tools library.
5 **
6 ** Licensees holding valid commercial KD Tools licenses may use this file in
7 ** accordance with the KD Tools Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU Lesser General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.LGPL included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include <config-kleopatra.h>
24 
25 #include "kdlogtextwidget.h"
26 
27 #include <QBasicTimer>
28 #include <QHash>
29 #include <QPainter>
30 #include <QPaintEvent>
31 #include <QScrollBar>
32 
33 #include <cassert>
34 #include <algorithm>
35 #include <iterator>
36 
55 class KDLogTextWidget::Private {
56  friend class ::KDLogTextWidget;
57  KDLogTextWidget * const q;
58 public:
59  explicit Private( KDLogTextWidget * qq );
60  ~Private();
61 
62  void updateCache() const;
63 
64  void triggerTimer() {
65  if ( !timer.isActive() )
66  timer.start( 500, q );
67  }
68 
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 ) ) );
75  }
76  int lineByYCoordinate( int x ) const;
77 
78  QPoint scrollOffset() const;
79 
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 );
83  }
84 
85  struct Style {
86  QColor color;
87 
88  friend inline uint qHash( const Style & style ) {
89  return qHash( style.color.rgba() );
90  }
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(); }
93  };
94 
95  struct LineItem {
96  QString text;
97  unsigned int styleID;
98  };
99 
100  unsigned int findOrAddStyle( const Style & style );
101 
102 private:
103  QHash<unsigned int, Style> styleByID;
104  QHash<Style,unsigned int> idByStyle;
105 
106  QVector<LineItem> lines, pendingLines;
107 
108  unsigned int historySize;
109  unsigned int minimumVisibleLines;
110  unsigned int minimumVisibleColumns;
111 
112  bool alternatingRowColors;
113 
114  QBasicTimer timer;
115 
116  mutable struct Cache {
117  enum { Dimensions = 1, FontMetrics = 2, All = FontMetrics|Dimensions };
118  Cache() : dirty( All ) {}
119  int dirty;
120 
121  struct {
122  int lineSpacing;
123  int ascent;
124  int averageCharWidth;
125  QVector<int> lineWidths;
126  } fontMetrics;
127 
128  struct {
129  int indexOfLongestLine;
130  int longestLineLength;
131  } dimensions;
132  } cache;
133 };
134 
138 KDLogTextWidget::KDLogTextWidget( QWidget * parent_ )
139  : QAbstractScrollArea( parent_ ), d( new Private( this ) )
140 {
141 
142 }
143 
147 KDLogTextWidget::~KDLogTextWidget() {}
148 
149 
159 void KDLogTextWidget::setHistorySize( unsigned int hs ) {
160  if ( hs == d->historySize )
161  return;
162  d->historySize = hs;
163  d->enforceHistorySize();
164  d->updateScrollRanges();
165  viewport()->update();
166 }
167 
168 unsigned int KDLogTextWidget::historySize() const {
169  return d->historySize;
170 }
171 
180 QString KDLogTextWidget::text() const {
181  return lines().join( QLatin1String( "\n" ) );
182 }
183 
192 void KDLogTextWidget::setLines( const QStringList & l ) {
193  clear();
194  Q_FOREACH( const QString & s, l )
195  message( s );
196 }
197 
198 QStringList KDLogTextWidget::lines() const {
199  QStringList result;
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 );
204  return result;
205 }
206 
216 void KDLogTextWidget::setMinimumVisibleLines( unsigned int num ) {
217  if ( num == d->minimumVisibleLines )
218  return;
219  d->minimumVisibleLines = num;
220  updateGeometry();
221 }
222 
223 unsigned int KDLogTextWidget::minimumVisibleLines() const {
224  return d->minimumVisibleLines;
225 }
226 
238 void KDLogTextWidget::setMinimumVisibleColumns( unsigned int num ) {
239  if ( num == d->minimumVisibleColumns )
240  return;
241  d->minimumVisibleColumns = num;
242  updateGeometry();
243 }
244 
245 unsigned int KDLogTextWidget::minimumVisibleColumns() const {
246  return d->minimumVisibleColumns;
247 }
248 
258 void KDLogTextWidget::setAlternatingRowColors( bool on ) {
259  if ( on == d->alternatingRowColors )
260  return;
261  d->alternatingRowColors = on;
262  update();
263 }
264 
265 bool KDLogTextWidget::alternatingRowColors() const {
266  return d->alternatingRowColors;
267 }
268 
269 QSize KDLogTextWidget::minimumSizeHint() const {
270  d->updateCache();
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;
277 }
278 
279 QSize KDLogTextWidget::sizeHint() const {
280  if ( d->minimumVisibleLines > 1 || d->minimumVisibleColumns > 1 )
281  return minimumSizeHint();
282  else
283  return 2 * minimumSizeHint();
284 }
285 
291 void KDLogTextWidget::clear() {
292  d->timer.stop();
293  d->lines.clear();
294  d->pendingLines.clear();
295  d->styleByID.clear();
296  d->idByStyle.clear();
297  d->cache.dirty = Private::Cache::All;
298  viewport()->update();
299 }
300 
306 void KDLogTextWidget::message( const QString & str, const QColor & color ) {
307  const Private::Style s = { color };
308  const Private::LineItem li = { str, d->findOrAddStyle( s ) };
309  d->pendingLines.push_back( li );
310  d->triggerTimer();
311 }
312 
318 void KDLogTextWidget::message( const QString & str ) {
319  const Private::LineItem li = { str, 0 };
320  d->pendingLines.push_back( li );
321  d->triggerTimer();
322 }
323 
324 void KDLogTextWidget::paintEvent( QPaintEvent * e ) {
325 
326  d->updateCache();
327 
328  QPainter p( viewport() );
329 
330  p.translate( -d->scrollOffset() );
331 
332  const QRect visible = p.matrix().inverted().mapRect( e->rect() );
333 
334  const QPair<int,int> visibleLines
335  = d->visibleLines( visible.top(), visible.bottom() );
336 
337  assert( visibleLines.first <= visibleLines.second );
338 
339  const Private::Style defaultStyle = { p.pen().color() };
340 
341  const Private::Cache & cache = d->cache;
342 
343  p.setPen( Qt::NoPen );
344 
345  p.setBrush( palette().base() );
346 
347  if ( d->alternatingRowColors ) {
348 
349  p.drawRect( visible );
350 
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 ) );
354 
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 );
359  }
360 #endif
361 
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 ) );
365 
366  } else {
367 
368  p.drawRect( visible );
369 
370  }
371 
372  // ### unused optimization: paint lines by styles to minimise pen changes.
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 ;
377 
378  p.setPen( st.color );
379  p.drawText( 0, i * cache.fontMetrics.lineSpacing + cache.fontMetrics.ascent, li.text );
380  }
381 
382 }
383 
384 void KDLogTextWidget::timerEvent( QTimerEvent * e ) {
385  if ( e->timerId() == d->timer.timerId() ) {
386  d->addPendingLines();
387  d->timer.stop();
388  } else {
389  QAbstractScrollArea::timerEvent( e );
390  }
391 }
392 
393 void KDLogTextWidget::changeEvent( QEvent * e ) {
394  QAbstractScrollArea::changeEvent( e );
395  d->cache.dirty |= Private::Cache::FontMetrics;
396 }
397 
398 void KDLogTextWidget::resizeEvent( QResizeEvent * ) {
399  d->updateScrollRanges();
400 }
401 
402 KDLogTextWidget::Private::Private( KDLogTextWidget * qq )
403  : q( qq ),
404  styleByID(),
405  idByStyle(),
406  lines(),
407  pendingLines(),
408  historySize( 0xFFFFFFFF ),
409  minimumVisibleLines( 1 ),
410  minimumVisibleColumns( 1 ),
411  alternatingRowColors( false ),
412  timer(),
413  cache()
414 {
415  // PENDING(marc) find all the magic flags we need here...
416  QWidget * const vp = qq->viewport();
417  vp->setBackgroundRole( QPalette::Base );
418  vp->setAttribute( Qt::WA_StaticContents );
419  vp->setAttribute( Qt::WA_NoSystemBackground );
420 #ifndef QT_NO_CURSOR
421  vp->setCursor(Qt::IBeamCursor);
422 #endif
423 }
424 
425 KDLogTextWidget::Private::~Private() {}
426 
427 
428 void KDLogTextWidget::Private::updateCache() const {
429 
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();
435 
436  QVector<int> & lw = cache.fontMetrics.lineWidths;
437  lw.clear();
438  lw.reserve( lines.size() );
439  Q_FOREACH( const LineItem & li, lines )
440  lw.push_back( fm.width( li.text ) );
441  }
442 
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;
450  } else {
451  cache.dimensions.indexOfLongestLine = it - lw.begin();
452  cache.dimensions.longestLineLength = *it;
453  }
454  }
455 
456  cache.dirty = false;
457 }
458 
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 );
464  return id;
465  } else {
466  static unsigned int nextID = 0; // remember, 0 is reserved
467  const unsigned int id = ++nextID;
468  idByStyle.insert( s, id );
469  styleByID.insert( id, s );
470  return id;
471  }
472 }
473 
474 void KDLogTextWidget::Private::enforceHistorySize() {
475  const size_t numLimes = lines.size();
476  if ( numLimes <= historySize )
477  return;
478  const int remove = numLimes - historySize ;
479  lines.erase( lines.begin(), lines.begin() + remove );
480 
481  // can't quickly update the dimensions if the fontMetrics aren't uptodate.
482  if ( cache.dirty & Cache::FontMetrics ) {
483  cache.dirty |= Cache::Dimensions;
484  return;
485  }
486 
487  QVector<int> & lw = cache.fontMetrics.lineWidths;
488 
489  assert( lw.size() > remove );
490  lw.erase( lw.begin(), lw.begin() + remove );
491 
492  if ( cache.dirty & Cache::Dimensions )
493  return;
494 
495  if ( cache.dimensions.indexOfLongestLine >= remove )
496  cache.dimensions.indexOfLongestLine -= remove;
497  else
498  cache.dirty |= Cache::Dimensions;
499 }
500 
501 static void set_scrollbar_properties( QScrollBar & sb, int document, int viewport, int singleStep, Qt::Orientation o ) {
502  const int min = 0;
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 );
510 }
511 
512 void KDLogTextWidget::Private::updateScrollRanges() {
513 
514  updateCache();
515 
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;
520  set_scrollbar_properties( *sb, document, viewport, singleStep, Qt::Vertical );
521  }
522 
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; // rather randomly chosen
527  set_scrollbar_properties( *sb, document, viewport, singleStep, Qt::Horizontal );
528  }
529 }
530 
531 void KDLogTextWidget::Private::addPendingLines() {
532  if ( pendingLines.empty() )
533  return;
534 
535  const unsigned int oldNumLines = lines.size();
536 
537  lines += pendingLines;
538 
539  // if the cache isn't dirty, we can quickly update it without
540  // invalidation:
541 
542  if ( !cache.dirty ) {
543 
544  // update fontMetrics:
545  const QFontMetrics & fm = q->fontMetrics();
546  QVector<int> plw;
547  plw.reserve( pendingLines.size() );
548  Q_FOREACH( const LineItem & li, pendingLines )
549  plw.push_back( fm.width( li.text ) );
550 
551  // update dimensions:
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() );
557  }
558  }
559 
560  pendingLines.clear();
561 
562  enforceHistorySize();
563  updateScrollRanges();
564  q->viewport()->update();
565 }
566 
567 int KDLogTextWidget::Private::lineByYCoordinate( int y ) const {
568  updateCache();
569  if ( cache.fontMetrics.lineSpacing == 0 )
570  return -1;
571  const int raw = y / cache.fontMetrics.lineSpacing ;
572  if ( raw < 0 )
573  return -1;
574  if ( raw >= lines.size() )
575  return lines.size() - 1;
576  return raw;
577 }
578 
579 static int get_scrollbar_offset( const QScrollBar * sb ) {
580  return sb ? sb->value() : 0 ;
581 }
582 
583 QPoint KDLogTextWidget::Private::scrollOffset() const {
584  return QPoint( get_scrollbar_offset( q->horizontalScrollBar() ),
585  get_scrollbar_offset( q->verticalScrollBar() ) );
586 }
587 
588 #include "moc_kdlogtextwidget.cpp"
qHash
static uint qHash(const char *data)
Definition: keylistmodel.cpp:147
KDLogTextWidget::paintEvent
void paintEvent(QPaintEvent *)
Definition: kdlogtextwidget.cpp:324
KDLogTextWidget::minimumSizeHint
QSize minimumSizeHint() const
Definition: kdlogtextwidget.cpp:269
KDLogTextWidget::changeEvent
void changeEvent(QEvent *)
Definition: kdlogtextwidget.cpp:393
KDLogTextWidget::~KDLogTextWidget
~KDLogTextWidget()
Definition: kdlogtextwidget.cpp:147
KDLogTextWidget
A high-speed text display widget.
Definition: kdlogtextwidget.h:34
KDLogTextWidget::KDLogTextWidget
KDLogTextWidget(QWidget *parent=0)
Definition: kdlogtextwidget.cpp:138
KDLogTextWidget::setMinimumVisibleColumns
void setMinimumVisibleColumns(unsigned int num)
Definition: kdlogtextwidget.cpp:238
KDLogTextWidget::alternatingRowColors
bool alternatingRowColors() const
KDLogTextWidget::setMinimumVisibleLines
void setMinimumVisibleLines(unsigned int num)
Definition: kdlogtextwidget.cpp:216
QWidget
KDLogTextWidget::text
QString text() const
QAbstractScrollArea
d
#define d
Definition: adduseridcommand.cpp:90
KDLogTextWidget::sizeHint
QSize sizeHint() const
Definition: kdlogtextwidget.cpp:279
KDLogTextWidget::setLines
void setLines(const QStringList &list)
Definition: kdlogtextwidget.cpp:192
KDLogTextWidget::minimumVisibleLines
unsigned int minimumVisibleLines() const
set_scrollbar_properties
static void set_scrollbar_properties(QScrollBar &sb, int document, int viewport, int singleStep, Qt::Orientation o)
Definition: kdlogtextwidget.cpp:501
KMime::Types::operator==
static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
Definition: recipient.cpp:55
kdlogtextwidget.h
KDLogTextWidget::timerEvent
void timerEvent(QTimerEvent *)
Definition: kdlogtextwidget.cpp:384
update
static void update(const QString &fname, const QString &id)
Definition: filedialog.cpp:61
q
#define q
Definition: adduseridcommand.cpp:91
KDLogTextWidget::setHistorySize
void setHistorySize(unsigned int size)
Definition: kdlogtextwidget.cpp:159
KDLogTextWidget::clear
void clear()
Definition: kdlogtextwidget.cpp:291
KDLogTextWidget::historySize
unsigned int historySize() const
KDLogTextWidget::resizeEvent
void resizeEvent(QResizeEvent *)
Definition: kdlogtextwidget.cpp:398
KDLogTextWidget::minimumVisibleColumns
unsigned int minimumVisibleColumns() const
KDLogTextWidget::setAlternatingRowColors
void setAlternatingRowColors(bool on)
Definition: kdlogtextwidget.cpp:258
KDLogTextWidget::message
void message(const QString &msg, const QColor &color)
Definition: kdlogtextwidget.cpp:306
KDLogTextWidget::lines
QStringList lines() const
get_scrollbar_offset
static int get_scrollbar_offset(const QScrollBar *sb)
Definition: kdlogtextwidget.cpp:579
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:56:41 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kleopatra

Skip menu "kleopatra"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal