/* * psipopup.cpp - the Psi passive popup class * Copyright (C) 2003 Michail Pishchagin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "psipopup.h" #include "common.h" #include "fancypopup.h" #include "fancylabel.h" #include "userlist.h" #include "alerticon.h" #include "psievent.h" #include "im.h" #include "psicon.h" #include "psiaccount.h" #include #include //---------------------------------------------------------------------------- // PsiPopupList //---------------------------------------------------------------------------- class PsiPopupList : public QObject, public QPtrList { Q_OBJECT private: int numPopups; public: PsiPopupList() : QObject(qApp) { setAutoDelete(true); } PsiPopup *last() { if ( !count() ) return 0; return QPtrList::first(); } void prepend(const PsiPopup *d) { if ( isEmpty() ) emit started(); connect(d, SIGNAL(destroyed(QObject *)), SLOT(popupDestroyed(QObject *))); QPtrList::prepend(d); } signals: void started(); void finished(); private slots: void popupDestroyed(QObject *p) { setAutoDelete(false); remove((PsiPopup *)p); setAutoDelete(true); if ( isEmpty() ) emit finished(); } }; static PsiPopupList *psiPopupList = 0; //---------------------------------------------------------------------------- // PsiPopup::Private //---------------------------------------------------------------------------- class PsiPopup::Private : public QObject { Q_OBJECT public: Private(PsiPopup *p); ~Private(); void init(const Icon *titleIcon, QString titleText, PsiAccount *_acc, PopupType type); QString clipText(QString); QBoxLayout *createContactInfo(const Icon *icon, QString text); private slots: void popupDestroyed(); void popupClicked(int); void eventDestroyed(); public: PsiCon *psi; PsiAccount *account; FancyPopup *popup; PsiPopup *psiPopup; QString id; PopupType popupType; Jid jid; Status status; PsiEvent *event; Icon *titleIcon; bool display; }; PsiPopup::Private::Private(PsiPopup *p) { psiPopup = p; popup = 0; popupType = AlertNone; event = 0; titleIcon = 0; } PsiPopup::Private::~Private() { if ( popup ) delete popup; if ( titleIcon ) delete titleIcon; popup = 0; } void PsiPopup::Private::init(const Icon *_titleIcon, QString titleText, PsiAccount *acc, PopupType type) { psi = acc->psi(); account = acc; display = true; if ( !psiPopupList ) psiPopupList = new PsiPopupList(); FancyPopup *last = 0; if ( psiPopupList->last() ) last = psiPopupList->last()->popup(); if ( type != AlertNone ) titleIcon = new AlertIcon(_titleIcon); else titleIcon = new Icon(*_titleIcon); FancyPopup::setHideTimeout( option.ppHideTime ); FancyPopup::setBorderColor( option.ppBorderColor ); popup = new FancyPopup(titleText, titleIcon, last, false); connect(popup, SIGNAL(clicked(int)), SLOT(popupClicked(int))); connect(popup, SIGNAL(destroyed()), SLOT(popupDestroyed())); // create id if ( _titleIcon ) id += _titleIcon->name(); id += titleText; } void PsiPopup::Private::popupDestroyed() { popup = 0; psiPopup->deleteLater(); } void PsiPopup::Private::popupClicked(int button) { if ( button == (int)LeftButton ) { if ( event ) psi->processEvent( event ); else if ( account ) { // FIXME: it should work in most cases, but // maybe it's better to fix UserList::find()? Jid j( jid.userHost() ); account->actionDefault( j ); } } if ( popup ) popup->hide(); } void PsiPopup::Private::eventDestroyed() { if ( (popupType == AlertMessage || popupType == AlertHeadline) && !option.popupMsgs ) delete popup; if ( popupType == AlertChat && !option.popupChats /*&& option.alertOpenChats*/ ) delete popup; if ( popupType == AlertHeadline && !option.popupHeadlines) delete popup; if ( popupType == AlertFile && !option.popupFiles) delete popup; event = 0; } QString PsiPopup::Private::clipText(QString text) { if ( option.ppTextClip > 0 ) { // richtext will give us trouble here if ( ((int)text.length()) > option.ppTextClip ) { text = text.left( option.ppTextClip ); // delete last unclosed tag /*if ( text.find(" text.find(">") ) { text = text.left( text.find("setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); iconLabel->setIcon(icon); dataBox->addWidget(iconLabel); dataBox->addSpacing(5); } IconLabel *textLabel = new IconLabel(popup); QFont font; font.fromString( option.font[fPopup] ); textLabel->setFont(font); textLabel->setText(QString("%1").arg(clipText(text))); textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); dataBox->addWidget(textLabel); return dataBox; } //---------------------------------------------------------------------------- // PsiPopup //---------------------------------------------------------------------------- PsiPopup::PsiPopup(const Icon *titleIcon, QString titleText, PsiAccount *acc) { d = new Private(this); d->init(titleIcon, titleText, acc, AlertNone); } PsiPopup::~PsiPopup() { delete d; } PsiPopup::PsiPopup(PopupType type, PsiAccount *acc) { d = new Private(this); d->popupType = type; Icon *icon = 0; QString text = "Psi: "; bool doAlertIcon = false; switch(type) { case AlertOnline: text += PsiPopup::tr("Contact online"); icon = (Icon *)IconsetFactory::iconPtr("status/online"); break; case AlertOffline: text += PsiPopup::tr("Contact offline"); icon = (Icon *)IconsetFactory::iconPtr("status/offline"); break; case AlertStatusChange: text += PsiPopup::tr("Status change"); icon = (Icon *)IconsetFactory::iconPtr("status/online"); break; case AlertMessage: text += PsiPopup::tr("Incoming message"); icon = (Icon *)IconsetFactory::iconPtr("psi/message"); doAlertIcon = true; break; case AlertChat: text += PsiPopup::tr("Incoming chat message"); icon= (Icon *)IconsetFactory::iconPtr("psi/chat"); doAlertIcon = true; break; case AlertHeadline: text += PsiPopup::tr("Headline"); icon= (Icon *)IconsetFactory::iconPtr("psi/headline"); doAlertIcon = true; break; case AlertFile: text += PsiPopup::tr("Incoming file"); icon= (Icon *)IconsetFactory::iconPtr("psi/file"); doAlertIcon = true; break; default: break; } d->init(icon, text, acc, doAlertIcon ? type : AlertNone); } void PsiPopup::setData(const Icon *icon, QString text) { if ( !d->popup ) { deleteLater(); return; } d->popup->addLayout( d->createContactInfo(icon, text) ); // update id if ( icon ) d->id += icon->name(); d->id += text; show(); } void PsiPopup::setData(const Jid &j, const Resource &r, const UserListItem *u, const PsiEvent *event) { if ( !d->popup ) { deleteLater(); return; } d->jid = j; d->status = r.status(); d->event = (PsiEvent *)event; if ( event ) connect(event, SIGNAL(destroyed()), d, SLOT(eventDestroyed())); Icon *icon = is->statusPtr(j, r.status()); QString text; QString jid = j.full(); if ( option.ppJidClip > 0 && ((int)jid.length()) > option.ppJidClip ) jid = jid.left( option.ppJidClip ) + "..."; QString status; if ( option.ppStatusClip != 0 ) status = r.status().status(); if ( option.ppStatusClip > 0 ) if ( ((int)status.length()) > option.ppStatusClip ) status = status.left ( option.ppStatusClip ) + "..."; QString name; if ( u && !u->name().isEmpty() ) { if ( !option.ppJidClip ) name = "" + u->name() + ""; else name = "" + u->name() + " <" + jid + ">" + ""; } else name = "<" + jid + ">"; QString statusString = plain2rich(status); if ( option.useEmoticons ) statusString = emoticonify(statusString); if ( !statusString.isEmpty() ) statusString = "
" + statusString; QString contactText = "" + name + "" + statusString; // hack for duplicate "Contact Online"/"Status Change" popups PsiPopup *pp = psiPopupList->first(); while ( pp ) { if ( d->jid.full() == pp->d->jid.full() && d->status.show() == pp->d->status.show() && d->status.status() == d->status.status() ) { if ( d->popupType == AlertStatusChange && pp->d->popupType == AlertOnline ) { d->display = false; deleteLater(); break; } } pp = psiPopupList->next(); } // show popup if ( d->popupType != AlertHeadline && (d->popupType != AlertFile || !option.popupFiles) ) setData(icon, contactText); else if ( d->popupType == AlertHeadline ) { QVBoxLayout *vbox = new QVBoxLayout(0); vbox->addLayout( d->createContactInfo(icon, contactText) ); vbox->addSpacing(5); const Message *jmessage = &((MessageEvent *)event)->message(); QString message; if ( !jmessage->subject().isEmpty() ) message += "" + tr("Subject:") + " " + jmessage->subject() + "
"; message += plain2rich( jmessage->body() ); QLabel *messageLabel = new QLabel(d->popup); QFont font = messageLabel->font(); font.setPointSize(option.smallFontSize); messageLabel->setFont(font); messageLabel->setTextFormat(RichText); messageLabel->setText( d->clipText(linkify( message )) ); messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); vbox->addWidget(messageLabel); // update id if ( icon ) d->id += icon->name(); d->id += contactText; d->id += message; d->popup->addLayout( vbox ); show(); } } void PsiPopup::show() { if ( !d->popup || !option.ppIsOn ) { deleteLater(); return; } if ( !d->id.isEmpty() /*&& option.ppNoDupes*/ ) { PsiPopup *pp = psiPopupList->first(); while ( pp ) { if ( d->id == pp->id() && pp->popup() ) { pp->popup()->restartHideTimer(); d->display = false; break; } pp = psiPopupList->next(); } } if ( d->display ) { psiPopupList->prepend( this ); d->popup->show(); } else { deleteLater(); } } QString PsiPopup::id() const { return d->id; } FancyPopup *PsiPopup::popup() { return d->popup; } void PsiPopup::deleteAll() { if ( !psiPopupList ) return; psiPopupList->clear(); delete psiPopupList; psiPopupList = 0; } #include "psipopup.moc"