Vidalia 0.2.15
StatusEventItemDelegate.cpp
Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 
00007 **  including this file, may be copied, modified, propagated, or distributed 
00008 **  except according to the terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file StatusEventItemDelegate.cpp
00013 ** \brief Handles custom painting of items in a StatusEventWidget
00014 */
00015 
00016 #include "StatusEventItemDelegate.h"
00017 #include "StatusEventItem.h"
00018 
00019 #include "Vidalia.h"
00020 
00021 #include <QPainter>
00022 #include <QTextLine>
00023 #include <QTextLayout>
00024 
00025 StatusEventItemDelegate::StatusEventItemDelegate(QObject *parent)
00026   : QItemDelegate(parent)
00027 {
00028   _helpIcon = QPixmap(":/images/16x16/system-help.png");
00029 }
00030 
00031 void
00032 StatusEventItemDelegate::paint(QPainter *painter,
00033                                const QStyleOptionViewItem &option,
00034                                const QModelIndex &index) const
00035 {
00036   QItemDelegate::paint(painter, option, index);
00037 
00038   painter->save();
00039   if (option.state & QStyle::State_Selected)
00040     painter->setPen(option.palette.highlightedText().color());
00041 
00042   QPixmap icon  = index.data(StatusEventItem::IconRole).value<QPixmap>();
00043   QTime tstamp  = index.data(StatusEventItem::TimestampRole).toTime();
00044   QString title = index.data(StatusEventItem::TitleRole).toString();
00045   QString text  = index.data(StatusEventItem::DescriptionRole).toString();
00046   QFont font    = option.font;
00047   QFontMetrics fm = option.fontMetrics;
00048 
00049   /* XXX: Handle right-to-left layouts here. */
00050   QRect iconRect(option.rect.x(),
00051                  option.rect.y(),
00052                  qMax(fm.width(tstamp.toString()), icon.width()) + 16,
00053                  option.rect.height());
00054   QRect textRect(iconRect.topRight(), option.rect.bottomRight());
00055 
00056   // Draw the status icon
00057   QPoint center = iconRect.center();
00058   int x = center.x() - qRound(icon.width() / 2.0);
00059   int y = center.y() - qRound((icon.height() + fm.lineSpacing()) / 2.0);
00060   painter->drawPixmap(x, y, icon);
00061 
00062   // Draw the timestamp text underneath the status icon
00063   x = iconRect.x();
00064   y = y + icon.height();
00065   painter->drawText(x, y,
00066                     iconRect.width(),
00067                     fm.lineSpacing(),
00068                     Qt::AlignCenter,
00069                     tstamp.toString());
00070 
00071   // Draw the event's title in a bold font. If the current item has an
00072   // associated help URL, draw the little "?" icon to the right of the
00073   // title text
00074   font.setBold(true);
00075   painter->setFont(font);
00076   if (! index.data(StatusEventItem::HelpUrlRole).isNull()) {
00077     // Draw the little "?" icon in the corner of the list item and
00078     // account for it when eliding the title
00079     title = fm.elidedText(title,
00080                           Qt::ElideRight,
00081                           textRect.width() - _helpIcon.width() - 24);
00082 
00083     x = textRect.topRight().x() - _helpIcon.width() - 8;
00084     y = textRect.y() + 8;
00085     painter->drawPixmap(x, y, _helpIcon);
00086   } else {
00087     title = fm.elidedText(title, Qt::ElideRight, textRect.width() - 16);
00088   }
00089   painter->drawText(textRect.x(),
00090                     textRect.y() + 8,
00091                     textRect.width(),
00092                     fm.lineSpacing(),
00093                     Qt::AlignVCenter | Qt::AlignLeft, title);
00094 
00095   // Draw the rest of the event text, up to a maximum of 2 lines for
00096   // unselected items or 5 lines for selected items. Any extra text will
00097   // be elided.
00098   font.setBold(false);
00099   painter->setFont(font);
00100   if (option.state & QStyle::State_Selected)
00101     text = layoutText(text, font, textRect.width(), 6).join("\n");
00102   else
00103     text = layoutText(text, font, textRect.width(), 3).join("\n");
00104 
00105   x = textRect.x();
00106   y = textRect.y() + 8 + fm.leading() + fm.lineSpacing();
00107   painter->drawText(x, y,
00108                     textRect.width(),
00109                     textRect.height() - (y - textRect.y()),
00110                     Qt::AlignTop | Qt::AlignLeft, text);
00111 
00112   painter->restore();
00113 }
00114 
00115 QSize
00116 StatusEventItemDelegate::sizeHint(const QStyleOptionViewItem &option,
00117                                   const QModelIndex &index) const
00118 {
00119   int iconHeight, iconWidth;
00120   int textWidth, textHeight;
00121   QFontMetrics fontMetrics = option.fontMetrics;
00122 
00123   QPixmap icon = index.data(StatusEventItem::IconRole).value<QPixmap>();
00124   QString text = index.data(StatusEventItem::DescriptionRole).toString();
00125   QTime tstamp = index.data(StatusEventItem::TimestampRole).toTime();
00126 
00127   iconHeight = icon.height() + fontMetrics.lineSpacing() + 16;
00128   iconWidth  = qMax(fontMetrics.width(tstamp.toString()), icon.width()) + 16;
00129   textWidth  = option.rect.width() - iconWidth;
00130 
00131   if (option.state & QStyle::State_Selected)
00132     layoutText(text, option.font, textWidth, 6, &textHeight);
00133   else
00134     layoutText(text, option.font, textWidth, 3, &textHeight);
00135   textHeight += 8 + fontMetrics.leading() + fontMetrics.lineSpacing();
00136 
00137   return QSize(option.rect.width(), qMax(iconHeight, textHeight));
00138 }
00139 
00140 QStringList
00141 StatusEventItemDelegate::layoutText(const QString &text,
00142                                     const QFont &font,
00143                                     int maxLineWidth,
00144                                     int maxLines,
00145                                     int *textHeight)
00146 {
00147   QTextLayout textLayout(text, font);
00148   QFontMetrics fontMetrics(font);
00149   QStringList lines;
00150   qreal height = 0.0;
00151 
00152   textLayout.beginLayout();
00153   while (lines.size() < maxLines) {
00154     QTextLine line = textLayout.createLine();
00155     if (! line.isValid())
00156       break;
00157     if (maxLines <= 0 || lines.size() < maxLines-1) {
00158       // Wrap the current line at or below the maximum line width
00159       line.setLineWidth(maxLineWidth);
00160       lines.append(text.mid(line.textStart(), line.textLength()));
00161     } else {
00162       // Set the line width beyond the max line width, and then elide it
00163       // so the user has a visible indication that the full message is
00164       // longer than what is visible.
00165       line.setLineWidth(2 * maxLineWidth);
00166       lines.append(fontMetrics.elidedText(text.mid(line.textStart()),
00167                                           Qt::ElideRight,
00168                                           maxLineWidth));
00169     }
00170     height += fontMetrics.leading() + line.height();
00171   }
00172   textLayout.endLayout();
00173 
00174   if (textHeight)
00175     *textHeight = qRound(height);
00176 
00177   return lines;
00178 }
00179