Vidalia  0.3.1
StatusEventItemDelegate.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If you
4 ** did not receive the LICENSE file with this file, you may obtain it from the
5 ** Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file StatusEventItemDelegate.cpp
13 ** \brief Handles custom painting of items in a StatusEventWidget
14 */
15 
17 #include "StatusEventItem.h"
18 
19 #include "Vidalia.h"
20 
21 #include <QPainter>
22 #include <QTextLine>
23 #include <QTextLayout>
24 
26  : QItemDelegate(parent)
27 {
28  _helpIcon = QPixmap(":/images/16x16/system-help.png");
29 }
30 
31 void
33  const QStyleOptionViewItem &option,
34  const QModelIndex &index) const
35 {
36  QItemDelegate::paint(painter, option, index);
37 
38  painter->save();
39  if (option.state & QStyle::State_Selected)
40  painter->setPen(option.palette.highlightedText().color());
41 
42  QPixmap icon = index.data(StatusEventItem::IconRole).value<QPixmap>();
43  QTime tstamp = index.data(StatusEventItem::TimestampRole).toTime();
44  QString title = index.data(StatusEventItem::TitleRole).toString();
45  QString text = index.data(StatusEventItem::DescriptionRole).toString();
46  QFont font = option.font;
47  QFontMetrics fm = option.fontMetrics;
48 
49  /* XXX: Handle right-to-left layouts here. */
50  QRect iconRect(option.rect.x(),
51  option.rect.y(),
52  qMax(fm.width(tstamp.toString()), icon.width()) + 16,
53  option.rect.height());
54  QRect textRect(iconRect.topRight(), option.rect.bottomRight());
55 
56  // Draw the status icon
57  QPoint center = iconRect.center();
58  int x = center.x() - qRound(icon.width() / 2.0);
59  int y = center.y() - qRound((icon.height() + fm.lineSpacing()) / 2.0);
60  painter->drawPixmap(x, y, icon);
61 
62  // Draw the timestamp text underneath the status icon
63  x = iconRect.x();
64  y = y + icon.height();
65  painter->drawText(x, y,
66  iconRect.width(),
67  fm.lineSpacing(),
68  Qt::AlignCenter,
69  tstamp.toString());
70 
71  // Draw the event's title in a bold font. If the current item has an
72  // associated help URL, draw the little "?" icon to the right of the
73  // title text
74  font.setBold(true);
75  painter->setFont(font);
76  if (! index.data(StatusEventItem::HelpUrlRole).isNull()) {
77  // Draw the little "?" icon in the corner of the list item and
78  // account for it when eliding the title
79  title = fm.elidedText(title,
80  Qt::ElideRight,
81  textRect.width() - _helpIcon.width() - 24);
82 
83  x = textRect.topRight().x() - _helpIcon.width() - 8;
84  y = textRect.y() + 8;
85  painter->drawPixmap(x, y, _helpIcon);
86  } else {
87  title = fm.elidedText(title, Qt::ElideRight, textRect.width() - 16);
88  }
89  painter->drawText(textRect.x(),
90  textRect.y() + 8,
91  textRect.width(),
92  fm.lineSpacing(),
93  Qt::AlignVCenter | Qt::AlignLeft, title);
94 
95  // Draw the rest of the event text, up to a maximum of 2 lines for
96  // unselected items or 5 lines for selected items. Any extra text will
97  // be elided.
98  font.setBold(false);
99  painter->setFont(font);
100  if (option.state & QStyle::State_Selected)
101  text = layoutText(text, font, textRect.width(), 6).join("\n");
102  else
103  text = layoutText(text, font, textRect.width(), 3).join("\n");
104 
105  x = textRect.x();
106  y = textRect.y() + 8 + fm.leading() + fm.lineSpacing();
107  painter->drawText(x, y,
108  textRect.width(),
109  textRect.height() - (y - textRect.y()),
110  Qt::AlignTop | Qt::AlignLeft, text);
111 
112  painter->restore();
113 }
114 
115 QSize
116 StatusEventItemDelegate::sizeHint(const QStyleOptionViewItem &option,
117  const QModelIndex &index) const
118 {
119  int iconHeight, iconWidth;
120  int textWidth, textHeight;
121  QFontMetrics fontMetrics = option.fontMetrics;
122 
123  QPixmap icon = index.data(StatusEventItem::IconRole).value<QPixmap>();
124  QString text = index.data(StatusEventItem::DescriptionRole).toString();
125  QTime tstamp = index.data(StatusEventItem::TimestampRole).toTime();
126 
127  iconHeight = icon.height() + fontMetrics.lineSpacing() + 16;
128  iconWidth = qMax(fontMetrics.width(tstamp.toString()), icon.width()) + 16;
129  textWidth = option.rect.width() - iconWidth;
130 
131  if (option.state & QStyle::State_Selected)
132  layoutText(text, option.font, textWidth, 6, &textHeight);
133  else
134  layoutText(text, option.font, textWidth, 3, &textHeight);
135  textHeight += 8 + fontMetrics.leading() + fontMetrics.lineSpacing();
136 
137  return QSize(option.rect.width(), qMax(iconHeight, textHeight));
138 }
139 
140 QStringList
142  const QFont &font,
143  int maxLineWidth,
144  int maxLines,
145  int *textHeight)
146 {
147  QTextLayout textLayout(text, font);
148  QFontMetrics fontMetrics(font);
149  QStringList lines;
150  qreal height = 0.0;
151 
152  textLayout.beginLayout();
153  while (lines.size() < maxLines) {
154  QTextLine line = textLayout.createLine();
155  if (! line.isValid())
156  break;
157  if (maxLines <= 0 || lines.size() < maxLines-1) {
158  // Wrap the current line at or below the maximum line width
159  line.setLineWidth(maxLineWidth);
160  lines.append(text.mid(line.textStart(), line.textLength()));
161  } else {
162  // Set the line width beyond the max line width, and then elide it
163  // so the user has a visible indication that the full message is
164  // longer than what is visible.
165  line.setLineWidth(2 * maxLineWidth);
166  lines.append(fontMetrics.elidedText(text.mid(line.textStart()),
167  Qt::ElideRight,
168  maxLineWidth));
169  }
170  height += fontMetrics.leading() + line.height();
171  }
172  textLayout.endLayout();
173 
174  if (textHeight)
175  *textHeight = qRound(height);
176 
177  return lines;
178 }
179