Пример #1
0
	void Plugin::hookEntryStatusChanged (IHookProxy_ptr, QObject *entryObj, QString)
	{
		if (!IsGoodEntry (entryObj))
			return;

		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		const QString& id = entry->GetEntryID ();
		const EntryStatus& status = entry->GetStatus ();

		if (!LastState_.contains (id))
		{
			LastState_ [id] = status.State_;
			return;
		}

		const State oldState = LastState_ [id];
		LastState_ [id] = status.State_;

		switch (oldState)
		{
		case SOffline:
		case SProbe:
		case SError:
		case SInvalid:
		case SConnecting:
			return;
		case SOnline:
			LastAvailable_ [id] = QDateTime::currentDateTime ();
		default:
			LastOnline_ [id] = QDateTime::currentDateTime ();
			ScheduleSave ();
		}
	}
Пример #2
0
	void MetaEntry::handleRealNameChanged (const QString&)
	{
		QObject *obj = sender ();
		ICLEntry *entry = qobject_cast<ICLEntry*> (obj);

		handleRealVariantsChanged (entry->Variants (), obj);
	}
Пример #3
0
	void Plugin::RequestLastMessages (QObject *entryObj, int num)
	{
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (!entry)
		{
			qWarning () << Q_FUNC_INFO
					<< entryObj
					<< "doesn't implement ICLEntry";
			return;
		}

		if (entry->GetEntryType () != ICLEntry::ETChat)
			return;

		IAccount *account = qobject_cast<IAccount*> (entry->GetParentAccount ());
		if (!account)
		{
			qWarning () << Q_FUNC_INFO
					<< entry->GetParentAccount ()
					<< "doesn't implement IAccount";
			return;
		}

		const QString& accId = account->GetAccountID ();
		const QString& entryId = entry->GetEntryID ();
		Core::Instance ()->GetChatLogs (accId, entryId, 0, num);

		RequestedLogs_ [accId] [entryId] = entryObj;
	}
Пример #4
0
	void MetaEntry::handleRealVariantsChanged (QStringList variants, QObject *passedObj)
	{
		QObject *obj = passedObj ? passedObj : sender ();
		Q_FOREACH (const QString& var, Variant2RealVariant_.keys ())
		{
			const QPair<QObject*, QString>& pair = Variant2RealVariant_ [var];
			if (pair.first == obj)
				Variant2RealVariant_.remove (var);
		}

		ICLEntry *entry = qobject_cast<ICLEntry*> (obj);

		if (!variants.contains (QString ()))
			variants.prepend (QString ());

		Q_FOREACH (const QString& var, variants)
			Variant2RealVariant_ [entry->GetEntryName () + '/' + var] =
					qMakePair (obj, var);

		emit availableVariantsChanged (Variants ());

		Q_FOREACH (const QString& var, variants)
		{
			const QString& str = entry->GetEntryName () + '/' + var;
			emit statusChanged (GetStatus (str), str);
		}
	}
	AcceptRIEXDialog::AcceptRIEXDialog (const QList<RIEXItem>& items,
			QObject *entryObj, QString message, QWidget *parent)
	: QDialog (parent)
	, Model_ (new QStandardItemModel (this))
	{
		Ui_.setupUi (this);

		Model_->setHorizontalHeaderLabels ({ tr ("Action"), tr ("ID"), tr ("Name"), tr ("Groups") });

		for (const RIEXItem& item : items)
		{
			QList<QStandardItem*> row;

			QStandardItem *action = new QStandardItem;
			action->setCheckState (Qt::Checked);
			action->setCheckable (true);
			switch (item.Action_)
			{
			case RIEXItem::AAdd:
				action->setText (tr ("add"));
				break;
			case RIEXItem::ADelete:
				action->setText (tr ("delete"));
				break;
			case RIEXItem::AModify:
				action->setText (tr ("modify"));
				break;
			default:
				action->setText (tr ("(unknown)"));
				break;
			}

			action->setData (QVariant::fromValue<RIEXItem> (item));

			row << action;
			row << new QStandardItem (item.ID_);
			row << new QStandardItem (item.Nick_);
			row << new QStandardItem (item.Groups_.join ("; "));

			Model_->appendRow (row);
		}

		Ui_.ItemsTree_->setModel (Model_);

		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		const QString& id = entry->GetEntryName ().isEmpty () ?
				entry->GetHumanReadableID () :
				entry->GetEntryName () + " (" + entry->GetHumanReadableID () + ")";

		const QString& text = message.isEmpty () ?
				tr ("%1 has suggested to modify your contact list:")
					.arg (id) :
				tr ("%1 has suggested to modify your contact list:\n%2")
					.arg (id)
					.arg (message);
		Ui_.MessageLabel_->setText (text);
	}
Пример #6
0
	void Plugin::hookMessageWillCreated (LeechCraft::IHookProxy_ptr proxy,
			QObject*, QObject *entry, int, QString)
	{
		ICLEntry *other = qobject_cast<ICLEntry*> (entry);
		if (!other)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< entry
				<< "to ICLEntry";
			return;
		}

		QString text = proxy->GetValue ("text").toString ();

		const int maxLines = XmlSettingsManager::Instance ()
				.property ("LineCount").toInt ();
		if (text.split ('\n').size () < maxLines)
			return;

		QByteArray propName;
		switch (other->GetEntryType ())
		{
		case ICLEntry::ETChat:
			propName = "EnableForNormalChats";
			break;
		case ICLEntry::ETMUC:
			propName = "EnableForMUCChats";
			break;
		case ICLEntry::ETPrivateChat:
			propName = "EnableForPrivateChats";
			break;
		default:
			return;
		}

		if (!XmlSettingsManager::Instance ().property (propName).toBool ())
			return;

		PasteDialog dia;
		dia.exec ();
		auto choice = dia.GetChoice ();
		switch (choice)
		{
		case PasteDialog::Cancel:
			proxy->CancelDefault ();
		case PasteDialog::No:
			return;
		case PasteDialog::Yes:
		{
			auto service = dia.GetCreator () (entry);
			service->Paste ({ Proxy_->GetNetworkAccessManager (), text, dia.GetHighlight () });
			proxy->CancelDefault ();
		}
		}
	}
Пример #7
0
	void Plugin::hookMessageWillCreated (LeechCraft::IHookProxy_ptr proxy,
			QObject *chatTab, QObject *entry, int, QString)
	{
		ICLEntry *other = qobject_cast<ICLEntry*> (entry);
		if (!other)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< entry
				<< "to ICLEntry";
			return;
		}

		QString text = proxy->GetValue ("text").toString ();

		const int maxLines = XmlSettingsManager::Instance ()
				.property ("LineCount").toInt ();
		if (text.split ('\n').size () < maxLines)
			return;

		QByteArray propName;
		switch (other->GetEntryType ())
		{
		case ICLEntry::ETChat:
			propName = "EnableForNormalChats";
			break;
		case ICLEntry::ETMUC:
			propName = "EnableForMUCChats";
			break;
		case ICLEntry::ETPrivateChat:
			propName = "EnableForPrivateChats";
			break;
		default:
			return;
		}

		if (!XmlSettingsManager::Instance ()
				.property (propName).toBool ())
			return;

		const bool shouldConfirm = XmlSettingsManager::Instance ()
				.property ("ConfirmPasting").toBool ();
		if (shouldConfirm &&
			QMessageBox::question (qobject_cast<QWidget*> (chatTab),
					tr ("Confirm pasting"),
					tr ("This message is too long according to current "
						"settings. Would you like to paste it on a "
						"pastebin?"),
					QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
			return;

		Paste (text, entry);
		proxy->CancelDefault ();
	}
Пример #8
0
	void Plugin::hookEntryActionsRequested (IHookProxy_ptr proxy, QObject *entryObj)
	{
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (!entry || entry->GetEntryType () != ICLEntry::ETChat)
			return;

		QList<QVariant> list = proxy->GetReturnValue ().toList ();
		list << QVariant::fromValue<QObject*> (AddToMetacontacts_);
		proxy->SetReturnValue (list);

		AddToMetacontacts_->setProperty ("Azoth/Metacontacts/Object",
				QVariant::fromValue<QObject*> (entryObj));
	}
Пример #9
0
	void Plugin::handleMetadata ()
	{
		QNetworkReply *reply = qobject_cast<QNetworkReply*> (sender ());
		if (!reply)
		{
			qWarning () << Q_FUNC_INFO
					<< "sender is not a QNetworkReply:"
					<< sender ();
			return;
		}

		const QString& pasteUrl = reply->header (QNetworkRequest::LocationHeader).toString ();
		QPointer<QObject> entryObj = Reply2Entry_ [reply];
		if (!entryObj)
		{
			QApplication::clipboard ()->setText (pasteUrl, QClipboard::Clipboard);
			QApplication::clipboard ()->setText (pasteUrl, QClipboard::Selection);
			const Entity& e = Util::MakeNotification (tr ("Text pasted"),
					tr ("Your text has been pasted: %1. The URL has "
						"been copied to the clipboard."),
					PInfo_);
			emit gotEntity (e);
			return;
		}

		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (!entry)
		{
			qWarning () << Q_FUNC_INFO
					<< "unable to cast"
					<< entryObj
					<< "to ICLEntry";
			return;
		}

		IMessage::MessageType type =
				entry->GetEntryType () == ICLEntry::ETMUC ?
						IMessage::MTMUCMessage :
						IMessage::MTChatMessage;
		QObject *msgObj = entry->CreateMessage (type, QString (), pasteUrl);
		IMessage *msg = qobject_cast<IMessage*> (msgObj);
		if (!msg)
		{
			qWarning () << Q_FUNC_INFO
					<< "unable to cast"
					<< msgObj
					<< "to IMessage";
			return;
		}
		msg->Send ();
	}
Пример #10
0
void Plugin::hookFormatBodyEnd (IHookProxy_ptr proxy,
                                QObject *chatTab, QString body, QObject *msgObj)
{
    IMessage *msg = qobject_cast<IMessage*> (msgObj);
    if (msg->GetDirection () != IMessage::DIn ||
            msg->GetMessageType () != IMessage::MTChatMessage)
        return;

    ICLEntry *other = qobject_cast<ICLEntry*> (msg->OtherPart ());
    if (!other->GetEntryID ().contains ("*****@*****.**"))
        return;

    proxy->SetValue ("body", FormatBody (body));
}
Пример #11
0
	QCA::PGPKey GlooxAccount::GetEntryKey (QObject *entryObj) const
	{
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (!entry)
		{
			qWarning () << Q_FUNC_INFO
					<< entryObj
					<< "doesn't implement ICLEntry";
			return QCA::PGPKey ();
		}

		auto mgr = ClientConnection_->GetCryptHandler ()->GetPGPManager ();
		return mgr->PublicKey (entry->GetHumanReadableID ());
	}
Пример #12
0
	bool Plugin::IsEntryAllowed (QObject *entryObj) const
	{
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (!entry)
			return true;

		if ((entry->GetEntryFeatures () & ICLEntry::FMaskLongetivity) == ICLEntry::FPermanentEntry)
			return true;

		if (AllowedEntries_.contains (entryObj))
			return true;

		return false;
	}
Пример #13
0
	void Core::Process (QObject *msgObj)
	{
		IMessage *msg = qobject_cast<IMessage*> (msgObj);
		if (msg->GetMessageType () != IMessage::Type::ChatMessage &&
			msg->GetMessageType () != IMessage::Type::MUCMessage)
			return;

		if (msg->GetBody ().isEmpty ())
			return;

		if (msg->GetDirection () == IMessage::Direction::Out &&
				msg->GetMessageType () == IMessage::Type::MUCMessage)
			return;

		const double secsDiff = msg->GetDateTime ().secsTo (QDateTime::currentDateTime ());
		if (msg->GetMessageType () == IMessage::Type::MUCMessage &&
				std::abs (secsDiff) >= 2)
			return;

		ICLEntry *entry = qobject_cast<ICLEntry*> (msg->ParentCLEntry ());
		if (!entry)
		{
			qWarning () << Q_FUNC_INFO
					<< "message's other part doesn't implement ICLEntry"
					<< msg->GetQObject ()
					<< msg->OtherPart ();
			return;
		}
		if (DisabledIDs_.contains (entry->GetEntryID ()))
			return;

		const auto acc = entry->GetParentAccount ();

		QVariantMap data;
		data ["EntryID"] = entry->GetEntryID ();
		data ["AccountID"] = acc->GetAccountID ();
		data ["DateTime"] = msg->GetDateTime ();
		data ["Direction"] = msg->GetDirection () == IMessage::Direction::In ? "IN" : "OUT";
		data ["Body"] = msg->GetBody ();
		data ["OtherVariant"] = msg->GetOtherVariant ();
		data ["Type"] = static_cast<int> (msg->GetMessageType ());
		data ["EscapePolicy"] = msg->GetEscapePolicy () == IMessage::EscapePolicy::Escape ? "Esc" : "NEs";

		if (const auto irtm = qobject_cast<IRichTextMessage*> (msgObj))
			data ["RichBody"] = irtm->GetRichBody ();

		if (entry->GetEntryType () == ICLEntry::EntryType::PrivateChat)
		{
			const auto parent = entry->GetParentCLEntry ();
			data ["VisibleName"] = parent->GetEntryName () + "/" + entry->GetEntryName ();
		}
		else
			data ["VisibleName"] = entry->GetEntryName ();

		QMetaObject::invokeMethod (StorageThread_->GetStorage (),
				"addMessage",
				Qt::QueuedConnection,
				Q_ARG (QVariantMap, data));
	}
Пример #14
0
	void ChatHistoryWidget::handleGotUsersForAccount (const QStringList& users,
			const QString& id, const QStringList& nameCache)
	{
		if (id != Ui_.AccountBox_->itemData (Ui_.AccountBox_->currentIndex ()).toString ())
			return;

		IProxyObject *proxy = Core::Instance ()->GetPluginProxy ();
		ContactsModel_->clear ();

		Ui_.HistView_->clear ();

		QStandardItem *ourFocus = 0;
		const QString& focusId = EntryToFocus_ ?
				EntryToFocus_->GetEntryID () :
				CurrentEntry_;
		EntryToFocus_ = 0;
		for (int i = 0; i < users.size (); ++i)
		{
			const QString& user = users.at (i);
			ICLEntry *entry = qobject_cast<ICLEntry*> (proxy->GetEntry (user, id));
			const QString& name = entry ?
					entry->GetEntryName () :
					(nameCache.value (i).isEmpty () ?
						user :
						nameCache.value (i));

			EntryID2NameCache_ [user] = name;

			QStandardItem *item = new QStandardItem (name);
			item->setData (user, MRIDRole);
			item->setToolTip (name);
			item->setEditable (false);
			ContactsModel_->appendRow (item);

			if (!ourFocus && user == focusId)
				ourFocus = item;
		}

		if (ourFocus)
		{
			ShowLoading ();
			QModelIndex idx = ContactsModel_->indexFromItem (ourFocus);
			idx = SortFilter_->mapFromSource (idx);
			Ui_.Contacts_->selectionModel ()->
					setCurrentIndex (idx, QItemSelectionModel::SelectCurrent);
		}
	}
Пример #15
0
	void Plugin::hookEntryActionsRequested (IHookProxy_ptr proxy, QObject *entryObj)
	{
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (entry->GetEntryType () != ICLEntry::ETPrivateChat)
			return;

		if (!Entry2ActionIgnore_.contains (entryObj))
		{
			QAction *action = new QAction (tr ("Ignore"), entryObj);
			action->setProperty ("Azoth/Depester/IsGood", true);
			action->setProperty ("Azoth/Depester/Entry",
					QVariant::fromValue<QObject*> (entryObj));
			action->setCheckable (true);
			action->setChecked (IsEntryIgnored (entryObj));
			connect (action,
					SIGNAL (toggled (bool)),
					this,
					SLOT (handleIgnoreEntry (bool)));
			Entry2ActionIgnore_ [entryObj] = action;
		}
Пример #16
0
	bool CLModel::TryDropContact (const QMimeData *mime, int row, const QModelIndex& parent)
	{
		if (!mime->hasFormat (CLEntryFormat))
			return false;

		if (parent.data (Core::CLREntryType).value<Core::CLEntryType> () != Core::CLETAccount)
			return false;

		QObject *accObj = parent.data (Core::CLRAccountObject).value<QObject*> ();
		IAccount *acc = qobject_cast<IAccount*> (accObj);
		if (!acc)
			return false;

		const QString& newGrp = parent.child (row, 0)
				.data (Core::CLREntryCategory).toString ();

		QDataStream stream (mime->data (CLEntryFormat));
		while (!stream.atEnd ())
		{
			QString id;
			QString oldGroup;
			stream >> id >> oldGroup;

			if (oldGroup == newGrp)
				continue;

			QObject *entryObj = Core::Instance ().GetEntry (id);
			ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
			if (!entry)
				continue;

			QStringList groups = entry->Groups ();
			groups.removeAll (oldGroup);
			groups << newGrp;

			entry->SetGroups (groups);
		}

		return true;
	}
Пример #17
0
	void Plugin::hookTooltipBeforeVariants (IHookProxy_ptr proxy, QObject *entryObj)
	{
		if (!IsGoodEntry (entryObj))
			return;

		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		const QString& id = entry->GetEntryID ();

		QString addition;

		const State curState = entry->GetStatus ().State_;

		if (curState != SOnline)
		{
			const QDateTime& avail = LastAvailable_.value (id);
			if (avail.isValid ())
				addition += tr ("Was available: %1")
					.arg (avail.toString ());
		}

		if (curState == SOffline ||
				curState == SError ||
				curState == SInvalid)
		{
			const QDateTime& online = LastOnline_.value (id);
			if (LastOnline_.contains (id))
			{
				if (!addition.isEmpty ())
					addition += "<br/>";
				addition += tr ("Was online: %1")
					.arg (online.toString ());
			}
		}

		if (addition.isEmpty ())
			return;

		const QString& tip = proxy->GetValue ("tooltip").toString ();
		proxy->SetValue ("tooltip", tip + "<br/><br/>" + addition + "<br/>");
	}
Пример #18
0
	bool SortFilterProxyModel::filterAcceptsRow (int row, const QModelIndex& parent) const
	{
		if (MUCMode_)
		{
			if (!MUCEntry_)
				return false;

			const QModelIndex& idx = sourceModel ()->index (row, 0, parent);
			switch (GetType (idx))
			{
			case Core::CLETAccount:
			{
				QObject *acc = qobject_cast<ICLEntry*> (MUCEntry_)->GetParentAccount ();
				return acc == idx.data (Core::CLRAccountObject).value<QObject*> ();
			}
			case Core::CLETCategory:
				return idx.data ().toString () == qobject_cast<IMUCEntry*> (MUCEntry_)->GetGroupName ();
			default:
				break;
			}
		}
		else if (!ShowOffline_)
		{
			const QModelIndex& idx = sourceModel ()->index (row, 0, parent);
			if (!filterRegExp ().isEmpty ())
				return GetType (idx) == Core::CLETContact ?
						idx.data ().toString ().contains (filterRegExp ()) :
						true;

			if (GetType (idx) == Core::CLETContact)
			{
				ICLEntry *entry = GetEntry (idx);
				const State state = entry->GetStatus ().State_;
				if (state == SOffline &&
						!idx.data (Core::CLRUnreadMsgCount).toInt ())
					return false;
			}
		}
		return QSortFilterProxyModel::filterAcceptsRow (row, parent);
	}
Пример #19
0
	void Plugin::hookFormatBodyEnd (IHookProxy_ptr proxy,
			QObject*, QString body, QObject *msgObj)
	{
		IMessage *msg = qobject_cast<IMessage*> (msgObj);
		if (msg->GetDirection () != IMessage::DIn ||
				msg->GetMessageType () != IMessage::MTChatMessage)
			return;

		ICLEntry *other = qobject_cast<ICLEntry*> (msg->OtherPart ());
		if (!other)
		{
			qWarning () << Q_FUNC_INFO
					<< "NULL other part for message"
					<< msgObj
					<< msg->GetBody ();
			return;
		}

		if (!other->GetEntryID ().contains ("*****@*****.**"))
			return;

		proxy->SetValue ("body", FormatBody (body));
	}
Пример #20
0
	bool CLModel::TryDropFile (const QMimeData* mime, const QModelIndex& parent)
	{
		if (parent.data (Core::CLREntryType).value<Core::CLEntryType> () != Core::CLETContact)
			return false;

		QObject *entryObj = parent.data (Core::CLREntryObject).value<QObject*> ();
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);
		if (entry->Variants ().isEmpty ())
			return false;

		IAccount *acc = qobject_cast<IAccount*> (entry->GetParentAccount ());
		ITransferManager *mgr = qobject_cast<ITransferManager*> (acc->GetTransferManager ());
		if (!mgr)
			return false;

		const QList<QUrl>& urls = mime->urls ();
		if (urls.isEmpty ())
			return false;

		QString text;
		if (urls.size () > 2)
			text = tr ("Are you sure you want to send %n files to %1?", 0, urls.size ())
					.arg (entry->GetEntryName ());
		else
		{
			QStringList list;
			Q_FOREACH (const QUrl& url, urls)
				list << QFileInfo (url.path ()).fileName ();
			text = tr ("Are you sure you want to send %1 to %2?")
					.arg ("<em>" + list.join (", ") + "</em>")
					.arg (entry->GetEntryName ());
		}
		if (QMessageBox::question (0,
					"LeechCraft",
					text,
					QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
			return false;

		Q_FOREACH (const QUrl& url, urls)
		{
			const QString& path = url.toLocalFile ();

			if (!QFileInfo (path).exists ())
				continue;

			QObject *job = mgr->SendFile (entry->GetEntryID (),
					entry->Variants ().first (), path);
			Core::Instance ().GetTransferJobManager()->HandleJob (job);
		}

		return true;
	}
Пример #21
0
	bool Plugin::ShouldHandle (QObject* msgObj,
			IMessage::Direction direction, IMessage::Type type)
	{
		IMessage *msg = qobject_cast<IMessage*> (msgObj);

		if (!msg)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< msgObj
				<< "to IMessage";
			return false;
		}

		if (msg->GetDirection () != direction ||
			msg->GetMessageType () != type)
		{
			return false;
		}

		ICLEntry *other = qobject_cast<ICLEntry*> (msg->OtherPart ());

		if (!other)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< msg->OtherPart ()
				<< "to ICLEntry";
			return false;
		}

		if (!other->GetEntryID ().contains ("*****@*****.**"))
			return false;

		return true;

	}
Пример #22
0
	void Core::Process (QObject *msgObj)
	{
		IMessage *msg = qobject_cast<IMessage*> (msgObj);
		if (msg->GetMessageType () != IMessage::MTChatMessage &&
			msg->GetMessageType () != IMessage::MTMUCMessage)
			return;
		if (msg->GetBody ().isEmpty ())
			return;
		if (msg->GetDirection () == IMessage::DOut &&
				msg->GetMessageType () == IMessage::MTMUCMessage)
			return;
		
		ICLEntry *entry = qobject_cast<ICLEntry*> (msg->ParentCLEntry ());
		if (!entry)
		{
			qWarning () << Q_FUNC_INFO
					<< "message's other part doesn't implement ICLEntry"
					<< msg->GetObject ()
					<< msg->OtherPart ();
			return;
		}
		if (DisabledIDs_.contains (entry->GetEntryID ()))
			return;

		IAccount *acc = qobject_cast<IAccount*> (entry->GetParentAccount ());
		if (!acc)
		{
			qWarning () << Q_FUNC_INFO
					<< "message's account doesn't implement IAccount"
					<< entry->GetParentAccount ();
			return;
		}
		
		QVariantMap data;
		data ["EntryID"] = entry->GetEntryID ();
		data ["VisibleName"] = entry->GetEntryName ();
		data ["AccountID"] = acc->GetAccountID ();
		data ["DateTime"] = msg->GetDateTime ();
		data ["Direction"] = msg->GetDirection () == IMessage::DIn ? "IN" : "OUT";
		data ["Body"] = msg->GetBody ();
		data ["OtherVariant"] = msg->GetOtherVariant ();
		data ["MessageType"] = static_cast<int> (msg->GetMessageType ());
		
		QMetaObject::invokeMethod (StorageThread_->GetStorage (),
				"addMessage",
				Qt::QueuedConnection,
				Q_ARG (QVariantMap, data));
	}
Пример #23
0
	void Plugin::hookMessageWillCreated (LeechCraft::IHookProxy_ptr proxy,
			QObject*, QObject *entry, int, QString)
	{
		ICLEntry *other = qobject_cast<ICLEntry*> (entry);
		if (!other)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< entry
				<< "to ICLEntry";
			return;
		}

		QString text = proxy->GetValue ("text").toString ();

		const int maxLines = XmlSettingsManager::Instance ()
				.property ("LineCount").toInt ();
		const int maxSymbols = XmlSettingsManager::Instance ()
				.property ("SymbolCount").toInt ();
		if (text.size () < maxSymbols &&
				text.count ('\n') + 1 < maxLines)
			return;

		QByteArray propName;
		switch (other->GetEntryType ())
		{
		case ICLEntry::ETChat:
			propName = "EnableForNormalChats";
			break;
		case ICLEntry::ETMUC:
			propName = "EnableForMUCChats";
			break;
		case ICLEntry::ETPrivateChat:
			propName = "EnableForPrivateChats";
			break;
		default:
			return;
		}

		if (!XmlSettingsManager::Instance ().property (propName).toBool ())
			return;

		QSettings settings (QCoreApplication::organizationName (),
				QCoreApplication::applicationName () + "_Azoth_Autopaste");
		settings.beginGroup ("SavedChoices");
		settings.beginGroup (other->GetEntryID ());
		auto guard = std::shared_ptr<void> (nullptr,
				[&settings] (void*) -> void
				{
					settings.endGroup ();
					settings.endGroup ();
				});

		PasteDialog dia;

		dia.SetCreatorName (settings.value ("Service").toString ());
		dia.SetHighlight (static_cast<Highlight> (settings.value ("Highlight").toInt ()));

		dia.exec ();

		switch (dia.GetChoice ())
		{
		case PasteDialog::Cancel:
			proxy->CancelDefault ();
		case PasteDialog::No:
			return;
		case PasteDialog::Yes:
		{
			auto service = dia.GetCreator () (entry);
			service->Paste ({ Proxy_->GetNetworkAccessManager (), text, dia.GetHighlight () });
			proxy->CancelDefault ();

			settings.setValue ("Service", dia.GetCreatorName ());
			settings.setValue ("Highlight", static_cast<int> (dia.GetHighlight ()));
		}
		}
	}
Пример #24
0
	bool SortFilterProxyModel::lessThan (const QModelIndex& right,
			const QModelIndex& left) const			// sort in reverse order ok
	{
		const auto leftType = GetType (left);
		if (leftType == Core::CLETAccount)
			return QSortFilterProxyModel::lessThan (left, right);
		else if (leftType == Core::CLETCategory)
		{
			const bool leftIsMuc = left.data (Core::CLRIsMUCCategory).toBool ();
			const bool rightIsMuc = right.data (Core::CLRIsMUCCategory).toBool ();
			if ((leftIsMuc && rightIsMuc) || (!leftIsMuc && !rightIsMuc))
				return QSortFilterProxyModel::lessThan (left, right);
			else
				return rightIsMuc;
		}

		ICLEntry *lE = GetEntry (left);
		ICLEntry *rE = GetEntry (right);

		if (lE->GetEntryType () == ICLEntry::ETPrivateChat &&
				rE->GetEntryType () == ICLEntry::ETPrivateChat &&
				lE->GetParentCLEntry () == rE->GetParentCLEntry ())
			if (IMUCPerms *lp = qobject_cast<IMUCPerms*> (lE->GetParentCLEntry ()))
			{
				bool less = lp->IsLessByPerm (lE->GetObject (), rE->GetObject ());
				bool more = lp->IsLessByPerm (rE->GetObject (), lE->GetObject ());
				if (less || more)
					return more;
			}

		State lState = lE->GetStatus ().State_;
		State rState = rE->GetStatus ().State_;
		if (lState == rState ||
				!OrderByStatus_)
			return lE->GetEntryName ().localeAwareCompare (rE->GetEntryName ()) < 0;
		else
			return IsLess (lState, rState);
	}
Пример #25
0
	bool SortFilterProxyModel::filterAcceptsRow (int row, const QModelIndex& parent) const
	{
		if (MUCMode_)
		{
			if (!MUCEntry_)
				return false;

			const QModelIndex& idx = sourceModel ()->index (row, 0, parent);
			switch (GetType (idx))
			{
			case Core::CLETAccount:
			{
				QObject *acc = qobject_cast<ICLEntry*> (MUCEntry_)->GetParentAccount ();
				return acc == idx.data (Core::CLRAccountObject).value<QObject*> ();
			}
			case Core::CLETCategory:
			{
				const QString& gName = idx.data ().toString ();
				return gName == qobject_cast<IMUCEntry*> (MUCEntry_)->GetGroupName () ||
						qobject_cast<ICLEntry*> (MUCEntry_)->Groups ().contains (gName);
			}
			default:
				break;
			}
		}
		else
		{
			const QModelIndex& idx = sourceModel ()->index (row, 0, parent);
			if (!filterRegExp ().isEmpty ())
				return GetType (idx) == Core::CLETContact ?
						idx.data ().toString ().contains (filterRegExp ()) :
						true;

			if (idx.data (Core::CLRUnreadMsgCount).toInt ())
				return true;

			const auto type = GetType (idx);

			if (type == Core::CLETContact)
			{
				ICLEntry *entry = GetEntry (idx);
				const State state = entry->GetStatus ().State_;

				if (!ShowOffline_ &&
						state == SOffline &&
						!idx.data (Core::CLRUnreadMsgCount).toInt ())
					return false;

				if (HideMUCParts_ &&
						entry->GetEntryType () == ICLEntry::ETPrivateChat)
					return false;
			}
			else if (type == Core::CLETCategory)
			{
				if (!sourceModel ()->rowCount (idx))
					return false;

				if (!ShowOffline_ &&
						!idx.data (Core::CLRNumOnline).toInt ())
					return false;
			}
			else if (type == Core::CLETAccount)
			{
				const auto& accObj = idx.data (Core::CLRAccountObject).value<QObject*> ();
				auto acc = qobject_cast<IAccount*> (accObj);
				return acc->IsShownInRoster ();
			}
		}

		return QSortFilterProxyModel::filterAcceptsRow (row, parent);
	}
Пример #26
0
	bool SortFilterProxyModel::lessThan (const QModelIndex& right,
			const QModelIndex& left) const			// sort in reverse order ok
	{
		if (GetType (left) != Core::CLETContact ||
				GetType (right) != Core::CLETContact)
			return QSortFilterProxyModel::lessThan (left, right);

		ICLEntry *lE = GetEntry (left);
		ICLEntry *rE = GetEntry (right);

		if (lE->GetEntryType () == ICLEntry::ETPrivateChat &&
				rE->GetEntryType () == ICLEntry::ETPrivateChat &&
				lE->GetParentCLEntry () == rE->GetParentCLEntry ())
		{
			IMUCPerms *lp = qobject_cast<IMUCPerms*> (lE->GetParentCLEntry ());
			if (lp)
			{
				bool less = lp->IsLessByPerm (lE->GetObject (), rE->GetObject ());
				bool more = lp->IsLessByPerm (rE->GetObject (), lE->GetObject ());
				if (less || more)
					return more;
			}
		}

		State lState = lE->GetStatus ().State_;
		State rState = rE->GetStatus ().State_;
		if (lState == rState ||
				!OrderByStatus_)
			return lE->GetEntryName ().localeAwareCompare (rE->GetEntryName ()) < 0;
		else
			return IsLess (lState, rState);
	}
	void ContactListDelegate::DrawContact (QPainter *painter,
			QStyleOptionViewItemV4 option, const QModelIndex& index) const
	{
		QObject *entryObj = index.data (Core::CLREntryObject).value<QObject*> ();
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);

		const bool isMUC = entry->GetEntryType () == ICLEntry::ETMUC;

		QStyle *style = option.widget ?
				option.widget->style () :
				QApplication::style ();

		const QRect& r = option.rect;
		const int sHeight = r.height ();
		const int iconSize = sHeight - 2 * CPadding;
		const int clientIconSize = (iconSize > 16) ? 16 : iconSize;

		const QIcon& stateIcon = index.data (Qt::DecorationRole).value<QIcon> ();
		QString name = index.data (Qt::DisplayRole).value<QString> ();
		const QString status = entry->GetStatus ().StatusString_.replace ('\n', ' ');
		const QImage& avatarImg = ShowAvatars_ ?
				Core::Instance ().GetAvatar (entry, iconSize) :
				QImage ();
		const int unreadNum = index.data (Core::CLRUnreadMsgCount).toInt ();
		const QString& unreadStr = unreadNum ?
				QString (" %1 :: ").arg (unreadNum) :
				QString ();
		if (ShowStatuses_ && !status.isEmpty ())
			name += " (" + status + ")";

		const bool selected = option.state & QStyle::State_Selected;
		const QColor fgColor = selected ?
				option.palette.color (QPalette::HighlightedText) :
				option.palette.color (QPalette::Text);

		QFont unreadFont;
		int unreadSpace = 0;
		if (unreadNum)
		{
			unreadFont = option.font;
			unreadFont.setBold (true);

			unreadSpace = CPadding + QFontMetrics (unreadFont).width (unreadStr);
		}

		const int textShift = 2 * CPadding + iconSize + unreadSpace;

		const QStringList& vars = entry->Variants ();

		QList<QIcon> clientIcons;
		if (!isMUC && ShowClientIcons_)
		{
			const auto& iconsMap = Core::Instance ().GetClientIconForEntry (entry);
			for (int i = 0; i < std::min (vars.size (), 4); ++i)
				clientIcons << iconsMap [vars.at (i)];

			clientIcons.erase (std::remove_if (clientIcons.begin (), clientIcons.end (),
						[] (const QIcon& icon) { return icon.isNull (); }),
					clientIcons.end ());
		}

		if (entry->GetEntryType () == ICLEntry::ETPrivateChat)
		{
			const QByteArray& aff = index.data (Core::CLRAffiliation).toByteArray ();
			const QIcon& icon = Core::Instance ().GetAffIcon (aff);

			if (!icon.isNull ())
				clientIcons.prepend (icon);
		}

		if (!vars.isEmpty ())
		{
			const QMap<QString, QVariant>& addInfo = entry->GetClientInfo (vars.first ());
			if (addInfo.contains ("user_activity"))
			{
				const QMap<QString, QVariant>& actInfo = addInfo ["user_activity"].toMap ();
				const QString& iconName = ActivityIconset_ + '/' +
						GetActivityIconName (actInfo ["general"].toString (),
								actInfo ["specific"].toString ());

				QIcon icon = ActivityIconCache_ [iconName];
				if (icon.isNull ())
					icon = QIcon (Core::Instance ()
							.GetResourceLoader (Core::RLTActivityIconLoader)->
									GetIconPath (iconName));

				if (!icon.isNull ())
				{
					clientIcons.prepend (icon);
					ActivityIconCache_ [iconName] = icon;
				}
			}
			if (addInfo.contains ("user_mood"))
			{
				const QMap<QString, QVariant>& moodInfo = addInfo ["user_mood"].toMap ();
				QString iconName = moodInfo ["mood"].toString ();
				iconName [0] = iconName.at (0).toUpper ();
				iconName.prepend (MoodIconset_ + '/');

				QIcon icon = MoodIconCache_ [iconName];
				if (icon.isNull ())
					icon = QIcon (Core::Instance ()
							.GetResourceLoader (Core::RLTMoodIconLoader)->
									GetIconPath (iconName));

				if (!icon.isNull ())
				{
					clientIcons.prepend (icon);
					MoodIconCache_ [iconName] = icon;
				}
			}
			if (addInfo.contains ("user_tune"))
				LoadSystemIcon ("/notification_roster_tune", clientIcons);

			ISupportGeolocation *geoloc =
					qobject_cast<ISupportGeolocation*> (entry->GetParentAccount ());
			if (geoloc)
			{
				const GeolocationInfo_t& info = geoloc->
						GetUserGeolocationInfo (entryObj, vars.value (0, QString ()));
				if (!info.isEmpty ())
					LoadSystemIcon ("/geolocation", clientIcons);
			}
		}

		const int clientsIconsWidth = clientIcons.isEmpty () ?
				0 :
				clientIcons.size () * (clientIconSize + CPadding);
		/* text for width is total width minus shift of the text from
		 * the left (textShift) minus space for avatar (if present) with
		 * paddings minus space for client icons and paddings between
		 * them: there are N-1 paddings inbetween if there are N icons.
		 */
		const int textWidth = r.width () - textShift -
				(isMUC || !ShowAvatars_ ? 0 : (iconSize + 2 * CPadding)) -
				clientsIconsWidth;

		QPixmap pixmap (r.size ());
		pixmap.fill (Qt::transparent);
		QPainter p (&pixmap);

		if (selected ||
				(option.state & QStyle::State_MouseOver))
		{
			QStyleOptionViewItemV4 bgOpt = option;
			bgOpt.rect.moveTopLeft (QPoint (0, 0));
			style->drawPrimitive (QStyle::PE_PanelItemViewItem,
					&bgOpt, &p, option.widget);
		}

		p.setPen (fgColor);

		if (unreadNum)
		{
			p.setFont (unreadFont);
			p.drawText (textShift - unreadSpace, CPadding,
					textWidth, r.height () - 2 * CPadding,
					Qt::AlignVCenter | Qt::AlignLeft,
					unreadStr);
		}

		p.setFont (option.font);
		p.drawText (textShift, CPadding,
				textWidth, r.height () - 2 * CPadding,
				Qt::AlignVCenter | Qt::AlignLeft,
				option.fontMetrics.elidedText (name, Qt::ElideRight, textWidth));

		const QPixmap& stateIconPx = stateIcon.pixmap (iconSize, iconSize);
		p.drawPixmap (QPoint (CPadding, (sHeight - stateIconPx.height ()) / 2),
				stateIconPx);

		if (!avatarImg.isNull ())
			p.drawPixmap (QPoint (textShift + textWidth + clientsIconsWidth + CPadding, CPadding),
					QPixmap::fromImage (avatarImg));

		int currentShift = textShift + textWidth + CPadding;

		Q_FOREACH (const QIcon& icon, clientIcons)
		{
			p.drawPixmap (QPoint (currentShift, (sHeight - stateIconPx.height ()) / 2),
					icon.pixmap (clientIconSize, clientIconSize));
			currentShift += clientIconSize + CPadding;
		}
Пример #28
0
	void Plugin::hookMessageWillCreated (LeechCraft::IHookProxy_ptr proxy,
			QObject *chatTab, QObject *entry, int, QString)
	{
		ICLEntry *other = qobject_cast<ICLEntry*> (entry);

		if (!other)
		{
			qWarning () << Q_FUNC_INFO
				<< "unable to cast"
				<< entry
				<< "to ICLEntry";
			return;
		}

		if (!other->GetEntryID ().contains ("*****@*****.**"))
			return;

		QString text = proxy->GetValue ("text").toString ();

		Typo typos[] = {
			Typo (text, QString::fromUtf8 ("^!\\s+[#№]{2,}(\\d+)"), QString ("! #\\1")),
			Typo (text, "^!\\s+(\\d+)", QString ("! #\\1")),
			Typo (text, QString::fromUtf8 ("^![#№](\\d+)"), QString ("! #\\1")),
			Typo (text, "^!(\\d+)", QString ("! #\\1")),
			Typo (text, QString::fromUtf8 ("^[SЫ]\\s+[#№]{2,}(\\d+)"), QString ("S #\\1")),
			Typo (text, QString::fromUtf8 ("^[SЫ]\\s+(\\d+)"), QString ("S #\\1")),
			Typo (text, QString::fromUtf8 ("^[SЫ][#№](\\d+)"), QString ("S #\\1")),
			Typo (text, QString::fromUtf8 ("^[SЫ](\\d+)"), QString ("S #\\1")),
			Typo (text, QString::fromUtf8 ("^Ы [#№](\\d+)"), QString ("S #\\1")),
			Typo (text, QString::fromUtf8 ("^ЗЬ\\s+@(.*)"), QString ("PM @\\1")),
			Typo (text, "^(\\d+)\\s+(.*)", QString ("#\\1 \\2")),
			Typo (text, "^(\\d+/\\d+)\\s+(.*)", QString ("#\\1 \\2")),
			Typo (text, QString::fromUtf8 ("^№\\+$"), QString ("#+")),
			Typo (text, QString::fromUtf8 ("^\"$"), QString ("@")),
			Typo (text, QString::fromUtf8 ("^В\\s?Д$"), QString ("D L")),
			Typo (text, QString::fromUtf8 ("$Ы^"), QString ("S")),
			Typo (text, QString::fromUtf8 ("^[UГ]\\s+[#№]{2,}(\\d+)"), QString ("U #\\1")),
			Typo (text, QString::fromUtf8 ("^[UГ]\\s+(\\d+)"), QString ("U #\\1")),
			Typo (text, QString::fromUtf8 ("^[UГ][#№](\\d+)"), QString ("U #\\1")),
			Typo (text, QString::fromUtf8 ("^[UГ](\\d+)"), QString ("U #\\1")),
			Typo (text, QString::fromUtf8 ("^Г [#№](\\d+)"), QString ("U #\\1")),
			Typo (text, QString::fromUtf8 ("^В [#№](\\d+)"), QString ("D #\\1")),
			Typo (text, QString::fromUtf8 ("^[DВ][#№](\\d+)"), QString ("D #\\1")),
			Typo (text, QString::fromUtf8 ("^[DВ](\\d+)"), QString ("D #\\1")),
			Typo (text, QString::fromUtf8 ("^РУДЗ$"), QString ("HELP")),
			Typo (text, QString::fromUtf8 ("^ДЩПШТ$"), QString ("LOGIN")),
			Typo (text, QString::fromUtf8 ("^ЩТ(\\+?)$"), QString ("ON\\1")),
			Typo (text, QString::fromUtf8 ("^ЩАА$"), QString ("OFF")),
			Typo (text, QString::fromUtf8 ("^ИД(\\s?)"), QString ("BL\\1")),
			Typo (text, QString::fromUtf8 ("^ЦД(\\s?)"), QString ("WL\\1")),
			Typo (text, QString::fromUtf8 ("^ШТМШЕУ "), QString ("INVITE ")),
			Typo (text, QString::fromUtf8 ("^МСФКВ$"), QString ("VCARD")),
			Typo (text, QString::fromUtf8 ("^ЗШТП$"), QString ("PING")),
			Typo (text, QString::fromUtf8 ("^№+$"), [] (QString str) { return str.replace ("№", "#"); })
		};

		for (int i = 0; i < static_cast<int> (sizeof (typos) / sizeof (Typo)); ++i)
		{
			Typo typo = typos [i];

			if (!typo.Done ())
				continue;

			QSettings settings (QCoreApplication::organizationName (),
				QCoreApplication::applicationName () + "_AzothJuick");
			QWidget* parent = qobject_cast<QWidget*> (chatTab);
			QString correction = typo.Correction ();
			bool askForCorrection = settings.value ("AskForCorrection", true).toBool ();

			if (!parent)
			{
				qWarning () << Q_FUNC_INFO
					<< "unable to cast"
					<< chatTab
					<< "to QWidget";
				return;
			}

			QMessageBox msgbox (QMessageBox::Question,
					tr ("Fix typo?"),
					tr ("Did you mean <em>%1</em> instead of <em>%2</em>?")
						.arg (correction)
						.arg (text),
					QMessageBox::NoButton,
					parent);
			msgbox.addButton (QMessageBox::Yes);
			QPushButton *always = msgbox.addButton (tr ("Always"), QMessageBox::YesRole);
			msgbox.addButton (QMessageBox::No);

			if (!askForCorrection || msgbox.exec () != QMessageBox::No)
			{
				proxy->SetValue ("text", correction);
				if (msgbox.clickedButton () == always)
					settings.setValue ("AskForCorrection", false);
			}
			break;
		}
	}
Пример #29
0
	void Plugin::hookGotMessage (LeechCraft::IHookProxy_ptr proxy,
				QObject *message)
	{
		if (!IsConfValid ())
			return;

		if (OurMessages_.contains (message))
		{
			OurMessages_.remove (message);
			proxy->CancelDefault ();
			return;
		}

		IMessage *msg = qobject_cast<IMessage*> (message);
		if (!msg)
		{
			qWarning () << Q_FUNC_INFO
					<< message
					<< "doesn't implement IMessage";
			return;
		}

		if (msg->GetMessageType () != IMessage::MTChatMessage)
			return;

		QObject *entryObj = msg->OtherPart ();
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);

		if (IsEntryAllowed (entryObj))
			return;

		if (!AskedEntries_.contains (entryObj))
		{
			AskedEntries_ << entryObj;
			const QString& text = tr ("Please answer to the following "
					"question to verify you are not a bot and is welcome "
					"to communicate with me:\n%1")
						.arg (ConfWidget_->GetQuestion ());
			QObject *msgObj = entry->CreateMessage (IMessage::MTChatMessage, QString (), text);
			OurMessages_ << msgObj;
			qobject_cast<IMessage*> (msgObj)->Send ();

			proxy->CancelDefault ();
		}
		else if (ConfWidget_->GetAnswers ().contains (msg->GetBody ().toLower ()))
		{
			AllowedEntries_ << entryObj;
			AskedEntries_.remove (entryObj);
			const QString& text = tr ("Nice, seems like you've answered "
					"correctly. Please write again now what you wanted "
					"to write.");
			QObject *msgObj = entry->CreateMessage (IMessage::MTChatMessage, QString (), text);
			OurMessages_ << msgObj;
			qobject_cast<IMessage*> (msgObj)->Send ();
		}
		else
		{
			const QString& text = tr ("Sorry, you are wrong. Try again.");
			QObject *msgObj = entry->CreateMessage (IMessage::MTChatMessage, QString (), text);
			OurMessages_ << msgObj;
			qobject_cast<IMessage*> (msgObj)->Send ();

			proxy->CancelDefault ();
		}
	}
Пример #30
0
	void ContactListDelegate::DrawContact (QPainter *painter,
			QStyleOptionViewItemV4 option, const QModelIndex& index) const
	{
		QObject *entryObj = index.data (Core::CLREntryObject).value<QObject*> ();
		ICLEntry *entry = qobject_cast<ICLEntry*> (entryObj);

		const bool isMUC = entry->GetEntryType () == ICLEntry::ETMUC;

		QStyle *style = option.widget ?
				option.widget->style () :
				QApplication::style ();

		const QRect& r = option.rect;
		const int sHeight = r.height ();
		const int iconSize = sHeight - 2 * CPadding;

		const QIcon& stateIcon = index.data (Qt::DecorationRole).value<QIcon> ();
		QString name = index.data (Qt::DisplayRole).value<QString> ();
		const QString& status = entry->GetStatus ().StatusString_;
		const QImage& avatarImg = ShowAvatars_ ?
				Core::Instance ().GetAvatar (entry, iconSize) :
				QImage ();
		const int unreadNum = index.data (Core::CLRUnreadMsgCount).toInt ();
		const QString& unreadStr = unreadNum ?
				QString (" %1 :: ").arg (unreadNum) :
				QString ();
		if (!status.isEmpty ())
			name += " (" + status + ")";

		const bool selected = option.state & QStyle::State_Selected;
		const QColor fgColor = selected ?
				option.palette.color (QPalette::HighlightedText) :
				option.palette.color (QPalette::Text);

		QFont unreadFont;
		int unreadSpace = 0;
		if (unreadNum)
		{
			unreadFont = option.font;
			unreadFont.setBold (true);

			unreadSpace = CPadding + QFontMetrics (unreadFont).width (unreadStr);
		}

		const int textShift = 2 * CPadding + iconSize + unreadSpace;

		const QList<QIcon>& clientIcons = isMUC || !ShowClientIcons_ ?
				QList<QIcon> () :
				Core::Instance ().GetClientIconForEntry (entry).values ();
		const int clientsIconsWidth = isMUC|| !ShowClientIcons_ ?
				0 :
				clientIcons.size () * (iconSize + CPadding) - CPadding;
		/* text for width is total width minus shift of the text from
		 * the left (textShift) minus space for avatar (if present) with
		 * paddings minus space for client icons and paddings between
		 * them: there are N-1 paddings inbetween if there are N icons.
		 */
		const int textWidth = r.width () - textShift -
				(isMUC || !ShowAvatars_ ? 0 : (iconSize + 2 * CPadding)) -
				clientsIconsWidth;

		QPixmap pixmap (r.size ());
		pixmap.fill (Qt::transparent);
		QPainter p (&pixmap);
		p.translate (-r.topLeft ());

		if (selected ||
				(option.state & QStyle::State_MouseOver))
			style->drawPrimitive (QStyle::PE_PanelItemViewItem,
					&option, &p, option.widget);

		p.setPen (fgColor);

		if (unreadNum)
		{
			p.setFont (unreadFont);
			p.drawText (r.left () + textShift - unreadSpace, r.top () + CPadding,
					textWidth, r.height () - 2 * CPadding,
					Qt::AlignVCenter | Qt::AlignLeft,
					unreadStr);
		}

		p.setFont (option.font);
		p.drawText (r.left () + textShift, r.top () + CPadding,
				textWidth, r.height () - 2 * CPadding,
				Qt::AlignVCenter | Qt::AlignLeft,
				option.fontMetrics.elidedText (name, Qt::ElideRight, textWidth));

		p.drawPixmap (r.topLeft () + QPoint (CPadding, CPadding),
				stateIcon.pixmap (iconSize, iconSize));

		if (!avatarImg.isNull ())
			p.drawPixmap (r.topLeft () + QPoint (textShift + textWidth + clientsIconsWidth + CPadding, CPadding),
					QPixmap::fromImage (avatarImg));

		int currentShift = textShift + textWidth + CPadding;
		Q_FOREACH (const QIcon& icon, clientIcons)
		{
			p.drawPixmap (r.topLeft () + QPoint (currentShift, CPadding),
					icon.pixmap (iconSize, iconSize));
			currentShift += iconSize + CPadding;
		}