From 1e42aa0003876f5416f723d535ca27e7b2f6dc68 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Fri, 16 Apr 2010 12:25:42 +0100
Subject: Prettify the ChatList a bit.

Resolves: #322

diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp
index c8e958d..ac05b07 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.cpp
+++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp
@@ -4,4 +4,83 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+#include <QPen>
+#include <QPainter>
+
 #include "Swift/QtUI/ChatList/ChatListDelegate.h"
+#include "Swift/QtUI/Roster/GroupItemDelegate.h"
+#include "Swift/QtUI/ChatList/ChatListItem.h"
+#include "Swift/QtUI/ChatList/ChatListMUCItem.h"
+#include "Swift/QtUI/ChatList/ChatListGroupItem.h"
+
+namespace Swift {
+
+ChatListDelegate::ChatListDelegate() {
+	groupDelegate_ = new GroupItemDelegate();
+}
+
+ChatListDelegate::~ChatListDelegate() {
+	delete groupDelegate_;
+}
+
+QSize ChatListDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const {
+	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());
+	if (item && dynamic_cast<ChatListMUCItem*>(item)) {
+		return mucSizeHint(option, index);
+	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
+		return groupDelegate_->sizeHint(option, index);
+	} 
+	return QStyledItemDelegate::sizeHint(option, index);
+}
+
+QSize ChatListDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
+	QFontMetrics nameMetrics(common_.nameFont);
+	QFontMetrics statusMetrics(common_.detailFont);
+	int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height();
+	return QSize(150, sizeByText);
+}
+
+void ChatListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
+	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());
+	if (item && dynamic_cast<ChatListMUCItem*>(item)) {
+		paintMUC(painter, option, dynamic_cast<ChatListMUCItem*>(item));
+	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) {
+		ChatListGroupItem* group = dynamic_cast<ChatListGroupItem*>(item);
+		groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open); 
+	} else {
+		QStyledItemDelegate::paint(painter, option, index);
+	}
+}
+
+void ChatListDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const {
+	painter->save();
+	QRect fullRegion(option.rect);
+	if ( option.state & QStyle::State_Selected ) {
+		painter->fillRect(fullRegion, option.palette.highlight());
+		painter->setPen(option.palette.highlightedText().color());
+	} else {
+		QColor nameColor = item->data(Qt::TextColorRole).value<QColor>();
+		painter->setPen(QPen(nameColor));
+	}
+
+	QFontMetrics nameMetrics(common_.nameFont);
+	painter->setFont(common_.nameFont);
+	int extraFontWidth = nameMetrics.width("H");
+	int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2;
+	QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0));
+	
+	int nameHeight = nameMetrics.height() + common_.verticalMargin;
+	QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0));
+	
+	painter->drawText(nameRegion, Qt::AlignTop, item->data(Qt::DisplayRole).toString());
+	
+	painter->setFont(common_.detailFont);
+	painter->setPen(QPen(QColor(160,160,160)));
+	
+	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0));
+	painter->drawText(detailRegion, Qt::AlignTop, item->data(DetailTextRole).toString());
+	
+	painter->restore();
+}
+
+}
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.h b/Swift/QtUI/ChatList/ChatListDelegate.h
index ab7d43e..f6c6c40 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.h
+++ b/Swift/QtUI/ChatList/ChatListDelegate.h
@@ -8,10 +8,22 @@
 
 #include <QStyledItemDelegate>
 
-namespace Swift {
+#include "Swift/QtUI/Roster/GroupItemDelegate.h"
 
+namespace Swift {
+	class ChatListMUCItem;
 	class ChatListDelegate : public QStyledItemDelegate {
+		public:
+			ChatListDelegate();
+			~ChatListDelegate();
+			QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
+			void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+		private:
+			void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const;
+			QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const;
 
+			DelegateCommons common_;
+			GroupItemDelegate* groupDelegate_;
 	};
 
 }
diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.cpp b/Swift/QtUI/ChatList/ChatListMUCItem.cpp
index 91f64e1..ec643d1 100644
--- a/Swift/QtUI/ChatList/ChatListMUCItem.cpp
+++ b/Swift/QtUI/ChatList/ChatListMUCItem.cpp
@@ -20,6 +20,7 @@ boost::shared_ptr<MUCBookmark> ChatListMUCItem::getBookmark() {
 QVariant ChatListMUCItem::data(int role) { 
 	switch (role) {
 		case Qt::DisplayRole: return P2QSTRING(bookmark_->getName());
+		case DetailTextRole: return P2QSTRING(bookmark_->getRoom().toString());
 			/*case Qt::TextColorRole: return textColor_;
 		case Qt::BackgroundColorRole: return backgroundColor_;
 		case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant();
diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.h b/Swift/QtUI/ChatList/ChatListMUCItem.h
index 58abb55..4170c6f 100644
--- a/Swift/QtUI/ChatList/ChatListMUCItem.h
+++ b/Swift/QtUI/ChatList/ChatListMUCItem.h
@@ -15,6 +15,12 @@
 #include "Swift/QtUI/ChatList/ChatListItem.h"
 
 namespace Swift {
+	enum MUCItemRoles {
+		DetailTextRole = Qt::UserRole/*,
+		AvatarRole = Qt::UserRole + 1,
+		PresenceIconRole = Qt::UserRole + 2,
+		StatusShowTypeRole = Qt::UserRole + 3*/
+	};
 	class ChatListMUCItem : public ChatListItem {
 		public:
 			ChatListMUCItem(boost::shared_ptr<MUCBookmark> bookmark, ChatListGroupItem* parent);
diff --git a/Swift/QtUI/Roster/DelegateCommons.cpp b/Swift/QtUI/Roster/DelegateCommons.cpp
new file mode 100644
index 0000000..3cd3695
--- /dev/null
+++ b/Swift/QtUI/Roster/DelegateCommons.cpp
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "DelegateCommons.h"
+
+namespace Swift {
+const int DelegateCommons::horizontalMargin = 2;
+const int DelegateCommons::verticalMargin = 2;
+const int DelegateCommons::farLeftMargin = 2;
+}
diff --git a/Swift/QtUI/Roster/DelegateCommons.h b/Swift/QtUI/Roster/DelegateCommons.h
new file mode 100644
index 0000000..ac5efc4
--- /dev/null
+++ b/Swift/QtUI/Roster/DelegateCommons.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <QApplication>
+#include <QFont>
+
+namespace Swift {
+	class DelegateCommons {
+		public:
+			DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()) {
+				detailFontSizeDrop = nameFont.pointSize() >= 10 ? 2 : 0;
+				detailFont.setStyle(QFont::StyleItalic);
+				detailFont.setPointSize(nameFont.pointSize() - detailFontSizeDrop);
+			}
+
+			int detailFontSizeDrop;
+			QFont nameFont;
+			QFont detailFont;
+			static const int horizontalMargin;
+			static const int verticalMargin;
+			static const int farLeftMargin;			
+	};
+}
diff --git a/Swift/QtUI/Roster/GroupItemDelegate.cpp b/Swift/QtUI/Roster/GroupItemDelegate.cpp
new file mode 100644
index 0000000..6f13b40
--- /dev/null
+++ b/Swift/QtUI/Roster/GroupItemDelegate.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "GroupItemDelegate.h"
+
+#include <QPainter>
+#include <QPen>
+
+namespace Swift {
+
+GroupItemDelegate::GroupItemDelegate() : groupFont_(QApplication::font()) {
+	groupFont_.setPointSize(common_.nameFont.pointSize() - common_.detailFontSizeDrop);
+	groupFont_.setWeight(QFont::Bold);
+}
+
+QSize GroupItemDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const {
+	QFontMetrics groupMetrics(groupFont_);
+	return QSize(150, groupMetrics.height() + 4);
+}
+
+void GroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QString& name, int rowCount, bool expanded) const {
+	painter->save();		
+	painter->setPen(QPen(QColor(189, 189, 189)));
+	//FIXME: It looks like Qt is passing us a rectangle that's too small
+	//This deliberately draws outside the lines, and we need to find a better solution.
+	int correctionAmount = groupCornerRadius_ > 0 ? 0 : 1;
+	QRect region(QPoint(option.rect.left() - correctionAmount, option.rect.top()), QSize(option.rect.width() + correctionAmount, option.rect.height() - common_.verticalMargin));
+	QLinearGradient fillGradient(region.topLeft(), region.bottomLeft());
+	fillGradient.setColorAt(0, QColor(244, 244, 244));
+	fillGradient.setColorAt(0.1, QColor(231, 231, 231));
+	fillGradient.setColorAt(1, QColor(209, 209, 209));
+	
+	QBrush backgroundBrush = QBrush(fillGradient);
+	QPainterPath fillPath;
+	fillPath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_);
+	QPainterPath linePath;
+	linePath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_);
+	painter->fillPath(fillPath, backgroundBrush);
+	painter->drawPath(linePath);
+	
+	double triangleWidth = 9;
+	double triangleHeight = 5;
+	paintExpansionTriangle(painter, region.adjusted(common_.horizontalMargin +  1, 0, 0, 0), triangleWidth, triangleHeight, expanded);
+	
+	int textLeftOffset = 3 * common_.horizontalMargin + 1 + triangleWidth;
+	QFontMetrics fontMetrics(groupFont_);
+	int textTopOffset = (option.rect.height() - fontMetrics.height()) / 2;
+	painter->setFont(groupFont_);
+	int contactCountWidth = 0;
+	QRect textRect = region.adjusted(textLeftOffset, textTopOffset, -1 * textLeftOffset, -1 * textTopOffset);
+
+	if (!expanded) {
+		QFontMetrics groupMetrics(groupFont_);
+		int contactCount = rowCount;
+		QString countString = QString("%1").arg(contactCount);
+		contactCountWidth = groupMetrics.width(countString) + 2 * common_.horizontalMargin;
+		int offsetAmount = textRect.width() - contactCountWidth + common_.horizontalMargin;
+		QRect countRect = textRect.adjusted(offsetAmount, 0, 0/*-1 * offsetAmount*/, 0);
+		//qDebug() << "Painting count string " << countString << " at " << countRect << " from offset " << offsetAmount;
+		paintShadowText(painter, countRect, countString);
+	}
+	QRect nameTextRect = textRect.adjusted(0, 0, contactCountWidth, 0);
+	paintShadowText(painter, nameTextRect, name);
+	painter->restore();
+}
+
+void GroupItemDelegate::paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const {
+	QBrush triangleBrush(QColor(110, 110, 110));
+	QBrush triangleShadowBrush(QColor(47, 47, 47));
+	QPainterPath trianglePath;
+	QPainterPath triangleShadowPath;
+	QPolygonF triangle;
+	QPolygonF triangleShadow;
+	QPointF triangleTopLeft(region.left(), region.top() + region.height() / 2 - height / 2);
+	QPointF shadowOffset(0,-1);
+	QPointF trianglePoint2;
+	QPointF trianglePoint3;
+	
+	if (expanded) {
+		triangleTopLeft += QPoint(0, 1);
+		trianglePoint2 = triangleTopLeft + QPointF(width, 0);
+		trianglePoint3 = trianglePoint2 + QPointF(-1 * (width / 2), height);
+		//qDebug() << "Plotting expanded" << triangleTopLeft << ", " << trianglePoint2 << ", " << trianglePoint3;
+	} else {
+		trianglePoint2 = triangleTopLeft + QPointF(0, width);
+		trianglePoint3 = trianglePoint2 + QPointF(height, -1 * (width / 2));
+		//qDebug() << "Plotting collapsed" << triangleTopLeft << ", " << trianglePoint2 << ", " << trianglePoint3;
+	}
+	triangle << triangleTopLeft << trianglePoint2 << trianglePoint3 << triangleTopLeft;
+	triangleShadow << triangleTopLeft + shadowOffset << trianglePoint2 + shadowOffset << trianglePoint3 + shadowOffset << triangleTopLeft + shadowOffset;
+	
+	trianglePath.addPolygon(triangle);
+	triangleShadowPath.addPolygon(triangleShadow);
+	painter->fillPath(triangleShadowPath, triangleShadowBrush);
+	painter->fillPath(trianglePath, triangleBrush);
+}
+
+void GroupItemDelegate::paintShadowText(QPainter* painter, const QRect& region, const QString& text) const {
+	painter->setPen(QPen(QColor(254, 254, 254)));
+	painter->drawText(region.adjusted(0, 1, 0, 0), Qt::AlignTop, text);
+	painter->setPen(QPen(QColor(115, 115, 115)));
+	painter->drawText(region, Qt::AlignTop, text);
+}
+
+const int GroupItemDelegate::groupCornerRadius_ = 0;
+
+}
diff --git a/Swift/QtUI/Roster/GroupItemDelegate.h b/Swift/QtUI/Roster/GroupItemDelegate.h
new file mode 100644
index 0000000..1378e14
--- /dev/null
+++ b/Swift/QtUI/Roster/GroupItemDelegate.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <QStyledItemDelegate>
+#include <QColor>
+#include <QFont>
+
+#include "DelegateCommons.h"
+
+namespace Swift {
+	class QtTreeWidgetItem;
+	class GroupItemDelegate : public QStyledItemDelegate {
+	public:
+		GroupItemDelegate();
+		QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
+		void paint(QPainter* painter, const QStyleOptionViewItem& option, const QString& name, int rowCount, bool expanded) const;
+	private:
+		void paintShadowText(QPainter* painter, const QRect& region, const QString& text) const;
+		void paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const;
+		QFont groupFont_;
+		static const int groupCornerRadius_;
+		DelegateCommons common_;
+	};
+}
diff --git a/Swift/QtUI/Roster/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp
index b70faad..0c12d6e 100644
--- a/Swift/QtUI/Roster/RosterDelegate.cpp
+++ b/Swift/QtUI/Roster/RosterDelegate.cpp
@@ -19,36 +19,29 @@
 
 namespace Swift {
 
-RosterDelegate::RosterDelegate() : nameFont_(QApplication::font()), statusFont_(QApplication::font()), groupFont_(QApplication::font()) {
-	int statusFontSizeDrop = nameFont_.pointSize() >= 10 ? 2 : 0;
-	statusFont_.setStyle(QFont::StyleItalic);
-	statusFont_.setPointSize(nameFont_.pointSize() - statusFontSizeDrop);
-	groupFont_.setPointSize(nameFont_.pointSize() - statusFontSizeDrop);
-	groupFont_.setWeight(QFont::Bold);
+RosterDelegate::RosterDelegate() {
+	groupDelegate_ = new GroupItemDelegate();
+}
+
+RosterDelegate::~RosterDelegate() {
+	delete groupDelegate_;
 }
 	
 QSize RosterDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const {
 	QtTreeWidgetItem* item = static_cast<QtTreeWidgetItem*>(index.internalPointer());
 	if (!item || !item->isContact()) {
-		return groupSizeHint(option, index);
+		return groupDelegate_->sizeHint(option, index);
 	}
 	return contactSizeHint(option, index);
 }
 
-QSize RosterDelegate::groupSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const {
-	Q_UNUSED(option);
-	Q_UNUSED(index);
-	QFontMetrics groupMetrics(groupFont_);
-	return QSize(150, groupMetrics.height() + 4);
-}
-
 QSize RosterDelegate::contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const {
 	Q_UNUSED(option);
 	Q_UNUSED(index);
-	int heightByAvatar = avatarSize_ + verticalMargin_ * 2;
-	QFontMetrics nameMetrics(nameFont_);
-	QFontMetrics statusMetrics(statusFont_);
-	int sizeByText = 2 * verticalMargin_ + nameMetrics.height() + statusMetrics.height();
+	int heightByAvatar = avatarSize_ + common_.verticalMargin * 2;
+	QFontMetrics nameMetrics(common_.nameFont);
+	QFontMetrics statusMetrics(common_.detailFont);
+	int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height();
 	//Doesn't work, yay! FIXME: why?
 	//QSize size = (option.state & QStyle::State_Selected) ? QSize(150, 80) : QSize(150, avatarSize_ + margin_ * 2);
 	//qDebug() << "Returning size" << size;
@@ -65,91 +58,14 @@ void RosterDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option
 }
 
 void RosterDelegate::paintGroup(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
-	painter->save();		
-	painter->setPen(QPen(QColor(189, 189, 189)));
-	//FIXME: It looks like Qt is passing us a rectangle that's too small
-	//This deliberately draws outside the lines, and we need to find a better solution.
-	int correctionAmount = groupCornerRadius_ > 0 ? 0 : 1;
-	QRect region(QPoint(option.rect.left() - correctionAmount, option.rect.top()), QSize(option.rect.width() + correctionAmount, option.rect.height() - verticalMargin_));
-	QLinearGradient fillGradient(region.topLeft(), region.bottomLeft());
-	fillGradient.setColorAt(0, QColor(244, 244, 244));
-	fillGradient.setColorAt(0.1, QColor(231, 231, 231));
-	fillGradient.setColorAt(1, QColor(209, 209, 209));
-	
-	QBrush backgroundBrush = QBrush(fillGradient);
-	QPainterPath fillPath;
-	fillPath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_);
-	QPainterPath linePath;
-	linePath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_);
-	painter->fillPath(fillPath, backgroundBrush);
-	painter->drawPath(linePath);
-	
-	double triangleWidth = 9;
-	double triangleHeight = 5;
 	QtTreeWidgetItem* item = index.isValid() ? static_cast<QtTreeWidgetItem*>(index.internalPointer()) : NULL;
 	if (item) {
-		paintExpansionTriangle(painter, region.adjusted(horizontalMargin_ +  1, 0, 0, 0), triangleWidth, triangleHeight, item->isExpanded());
-	}
-	
-	int textLeftOffset = 3 * horizontalMargin_ + 1 + triangleWidth;
-	QFontMetrics fontMetrics(groupFont_);
-	int textTopOffset = (option.rect.height() - fontMetrics.height()) / 2;
-	painter->setFont(groupFont_);
-	int contactCountWidth = 0;
-	QRect textRect = region.adjusted(textLeftOffset, textTopOffset, -1 * textLeftOffset, -1 * textTopOffset);
-
-	if (!item->isExpanded()) {
-		QFontMetrics groupMetrics(groupFont_);
-		int contactCount = item->rowCount();
-		QString countString = QString("%1").arg(contactCount);
-		contactCountWidth = groupMetrics.width(countString) + 2 * horizontalMargin_;
-		int offsetAmount = textRect.width() - contactCountWidth + horizontalMargin_;
-		QRect countRect = textRect.adjusted(offsetAmount, 0, 0/*-1 * offsetAmount*/, 0);
-		//qDebug() << "Painting count string " << countString << " at " << countRect << " from offset " << offsetAmount;
-		paintShadowText(painter, countRect, countString);
+		groupDelegate_->paint(painter, option, index.data(Qt::DisplayRole).toString(), item->rowCount(), item->isExpanded());
 	}
-	QRect nameTextRect = textRect.adjusted(0, 0, contactCountWidth, 0);
-	paintShadowText(painter, nameTextRect, index.data(Qt::DisplayRole).toString());
-	painter->restore();
 }
 
-void RosterDelegate::paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const {
-	QBrush triangleBrush(QColor(110, 110, 110));
-	QBrush triangleShadowBrush(QColor(47, 47, 47));
-	QPainterPath trianglePath;
-	QPainterPath triangleShadowPath;
-	QPolygonF triangle;
-	QPolygonF triangleShadow;
-	QPointF triangleTopLeft(region.left(), region.top() + region.height() / 2 - height / 2);
-	QPointF shadowOffset(0,-1);
-	QPointF trianglePoint2;
-	QPointF trianglePoint3;
-	
-	if (expanded) {
-		triangleTopLeft += QPoint(0, 1);
-		trianglePoint2 = triangleTopLeft + QPointF(width, 0);
-		trianglePoint3 = trianglePoint2 + QPointF(-1 * (width / 2), height);
-		//qDebug() << "Plotting expanded" << triangleTopLeft << ", " << trianglePoint2 << ", " << trianglePoint3;
-	} else {
-		trianglePoint2 = triangleTopLeft + QPointF(0, width);
-		trianglePoint3 = trianglePoint2 + QPointF(height, -1 * (width / 2));
-		//qDebug() << "Plotting collapsed" << triangleTopLeft << ", " << trianglePoint2 << ", " << trianglePoint3;
-	}
-	triangle << triangleTopLeft << trianglePoint2 << trianglePoint3 << triangleTopLeft;
-	triangleShadow << triangleTopLeft + shadowOffset << trianglePoint2 + shadowOffset << trianglePoint3 + shadowOffset << triangleTopLeft + shadowOffset;
-	
-	trianglePath.addPolygon(triangle);
-	triangleShadowPath.addPolygon(triangleShadow);
-	painter->fillPath(triangleShadowPath, triangleShadowBrush);
-	painter->fillPath(trianglePath, triangleBrush);
-}
 
-void RosterDelegate::paintShadowText(QPainter* painter, const QRect& region, const QString& text) const {
-	painter->setPen(QPen(QColor(254, 254, 254)));
-	painter->drawText(region.adjusted(0, 1, 0, 0), Qt::AlignTop, text);
-	painter->setPen(QPen(QColor(115, 115, 115)));
-	painter->drawText(region, Qt::AlignTop, text);
-}
+
 
 void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
 	//qDebug() << "painting" << index.data(Qt::DisplayRole).toString();
@@ -165,7 +81,7 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem&
 		painter->setPen(QPen(nameColor));
 	}
 	
-	QRect presenceIconRegion(QPoint(farLeftMargin_, fullRegion.top()), QSize(presenceIconWidth_, fullRegion.height() - verticalMargin_));
+	QRect presenceIconRegion(QPoint(common_.farLeftMargin, fullRegion.top()), QSize(presenceIconWidth_, fullRegion.height() - common_.verticalMargin));
 	
 	int calculatedAvatarSize = presenceIconRegion.height();
 	//This overlaps the presenceIcon, so must be painted first
@@ -181,19 +97,19 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem&
 		: QIcon(":/icons/offline.png");
 	presenceIcon.paint(painter, presenceIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
 	
-	QFontMetrics nameMetrics(nameFont_);
-	painter->setFont(nameFont_);
+	QFontMetrics nameMetrics(common_.nameFont);
+	painter->setFont(common_.nameFont);
 	int extraFontWidth = nameMetrics.width("H");
-	int leftOffset = avatarRegion.right() + horizontalMargin_ * 2 + extraFontWidth / 2;
+	int leftOffset = avatarRegion.right() + common_.horizontalMargin * 2 + extraFontWidth / 2;
 	QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0/*-leftOffset*/, 0));
 	
-	int nameHeight = nameMetrics.height() + verticalMargin_;
-	QRect nameRegion(textRegion.adjusted(0, verticalMargin_, 0, 0));
+	int nameHeight = nameMetrics.height() + common_.verticalMargin;
+	QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0));
 	
 	painter->drawText(nameRegion, Qt::AlignTop, index.data(Qt::DisplayRole).toString());
 	
 	
-	painter->setFont(statusFont_);
+	painter->setFont(common_.detailFont);
 	painter->setPen(QPen(QColor(160,160,160)));
 	
 	QRect statusTextRegion(textRegion.adjusted(0, nameHeight, 0, 0));
@@ -205,9 +121,5 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem&
 const int RosterDelegate::avatarSize_ = 20;
 const int RosterDelegate::presenceIconHeight_ = 16;
 const int RosterDelegate::presenceIconWidth_ = 16;
-const int RosterDelegate::groupCornerRadius_ = 0;
-const int RosterDelegate::horizontalMargin_ = 2;
-const int RosterDelegate::verticalMargin_ = 2;
-const int RosterDelegate::farLeftMargin_ = 2;
 
 }
diff --git a/Swift/QtUI/Roster/RosterDelegate.h b/Swift/QtUI/Roster/RosterDelegate.h
index 7713968..696ea03 100644
--- a/Swift/QtUI/Roster/RosterDelegate.h
+++ b/Swift/QtUI/Roster/RosterDelegate.h
@@ -10,29 +10,26 @@
 #include <QColor>
 #include <QFont>
 
+#include "GroupItemDelegate.h"
+#include "DelegateCommons.h"
+
 namespace Swift {
 	class QtTreeWidgetItem;
 	class RosterDelegate : public QStyledItemDelegate {
 	public:
 		RosterDelegate();
+		~RosterDelegate();
 		QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
 		void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
 	private:
-		QSize groupSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
 		QSize contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
 		void paintGroup(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; 
 		void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; 
-		void paintShadowText(QPainter* painter, const QRect& region, const QString& text) const;
-		void paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const;
-		QFont nameFont_;
-		QFont statusFont_;
-		QFont groupFont_;
+		DelegateCommons common_;
+		GroupItemDelegate* groupDelegate_;
 		static const int avatarSize_;
 		static const int presenceIconHeight_;
 		static const int presenceIconWidth_;
-		static const int groupCornerRadius_;
-		static const int horizontalMargin_;
-		static const int verticalMargin_ ;
-		static const int farLeftMargin_;
+
 	};
 }
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 345e7c3..b88e217 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -80,6 +80,8 @@ sources = [
     "Roster/QtTreeWidget.cpp",
     "Roster/QtTreeWidgetItem.cpp",
     "Roster/RosterDelegate.cpp",
+    "Roster/GroupItemDelegate.cpp",
+    "Roster/DelegateCommons.cpp",
     "EventViewer/EventModel.cpp",
     "EventViewer/EventDelegate.cpp",
     "EventViewer/QtEventWindowFactory.cpp",
-- 
cgit v0.10.2-6-g49f6