Vidalia 0.2.10

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