/* * psitextview.cpp - Icon-aware QTextView subclass widget * Copyright (C) 2003 Michail Pishchagin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "psitextview.h" #include #include #include #include #include #include #ifndef WIDGET_PLUGIN #include "iconset.h" #else class Icon; class Iconset; #endif //---------------------------------------------------------------------------- // URLObject - helper class for handling links //---------------------------------------------------------------------------- class URLObject : public QObject { Q_OBJECT public: URLObject(QObject *parent = 0) : QObject(qApp) { Q_UNUSED(parent); } QPopupMenu *createPopupMenu(const QString &lnk); private: QString link; QString copyString(QString from) { QString l = from; int colon = l.find(':'); if ( colon == -1 ) colon = 0; QString service = l.left( colon ); if ( service == "mailto" || service == "jabber" || service == "jid" || service == "xmpp" ) { if ( colon > -1 ) l = l.mid( colon + 1 ); while ( l[0] == '/' ) l = l.mid( 1 ); } return l; } signals: void openURL(QString); public slots: void popupAction(QString lnk) { emit openURL(lnk); } void popupAction() { popupAction(link); } void popupCopy(QString lnk) { QApplication::clipboard()->setText( copyString(lnk), QClipboard::Clipboard ); if(QApplication::clipboard()->supportsSelection()) QApplication::clipboard()->setText( copyString(lnk), QClipboard::Selection ); } void popupCopy() { popupCopy(link); } }; static URLObject *urlObject = 0; QPopupMenu *URLObject::createPopupMenu(const QString &lnk) { link = lnk; if ( link.isEmpty() ) return 0; int colon = link.find(':'); if ( colon == -1 ) colon = 0; QString service = link.left( colon ); QString action = "ERROR"; QString iconName; if ( service == "mailto" ) { action = URLLabel::tr("Open mail composer"); iconName = "psi/email"; } else if ( service == "jabber" || service == "jid" || service == "xmpp" ) { // TODO: need more actions to jabber item. Ex: "add to roster", "send message" action = URLLabel::tr("Add to Roster"); iconName = "psi/add"; } else { //if ( service == "http" || service == "https" || service.isEmpty() ) { action = URLLabel::tr("Open web browser"); iconName = "psi/www"; } #ifndef WIDGET_PLUGIN const Icon *icon = 0; if ( !iconName.isEmpty() ) icon = IconsetFactory::iconPtr( iconName ); #endif QPopupMenu *m = new QPopupMenu; #ifndef WIDGET_PLUGIN if ( icon ) m->insertItem(/*QIconSet(*/ *icon /*)*/, action, this, SLOT(popupAction())); else #endif m->insertItem(action, this, SLOT(popupAction())); m->insertItem(URLLabel::tr("Copy location"), this, SLOT(popupCopy())); return m; } //---------------------------------------------------------------------------- // TextIcon - helper class for PsiTextView //---------------------------------------------------------------------------- class TextIcon : public QObject, public QTextCustomItem { Q_OBJECT public: TextIcon(QTextDocument *p, const QMap &attr, const QString &context, QMimeSourceFactory &factory); ~TextIcon(); void adjustToPainter(QPainter *); void draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool selected); QString richText() const; Placement placement() const { return place; } int minimumWidth() const { return width; } private slots: void iconUpdated(const QPixmap &); private: Placement place; int tmpwidth, tmpheight; QMap attributes; QString imgId; Icon *icon; }; TextIcon::TextIcon(QTextDocument *p, const QMap &attr, const QString &context, QMimeSourceFactory &factory) : QObject(0, 0), QTextCustomItem (p) { Q_UNUSED(context); Q_UNUSED(factory); width = height = 0; icon = 0; QString iconName = attr["name"]; if ( iconName.isEmpty() ) iconName = attr["src"]; if ( iconName.isEmpty() ) iconName = attr["source"]; #ifndef WIDGET_PLUGIN if ( !iconName.isEmpty() ) { icon = (Icon *)IconsetFactory::iconPtr( iconName ); if ( icon ) icon = new Icon(*icon); if ( icon ) { icon->activated(); connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &))); width = icon->pixmap().width(); height = icon->pixmap().height(); } } #endif if ( !icon && (width*height)==0 ) width = height = 50; place = PlaceInline; if ( attr["align"] == "left" ) place = PlaceLeft; else if ( attr["align"] == "right" ) place = PlaceRight; tmpwidth = width; tmpheight = height; attributes = attr; } TextIcon::~TextIcon() { #ifndef WIDGET_PLUGIN if ( icon ) { icon->stop(); delete icon; } #endif } void TextIcon::adjustToPainter(QPainter *) { // FIXME: This class can be incorrectly printed. Check this sometime later. } void TextIcon::draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool /*selected*/) { if ( placement() != PlaceInline ) { x = xpos; y = ypos; } if ( !icon ) { p->fillRect( x, y, width, height, cg.dark() ); return; } if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) ) return; #ifndef WIDGET_PLUGIN if ( placement() == PlaceInline ) p->drawPixmap( x , y, icon->pixmap() ); else p->drawPixmap( cx , cy, icon->pixmap(), cx - x, cy - y, cw, ch ); #endif } void TextIcon::iconUpdated(const QPixmap &) { // TODO: any ideas what it should do? ;-) } QString TextIcon::richText() const { QString s; s += "::ConstIterator it = attributes.begin(); for ( ; it != attributes.end(); ++it ) { s += it.key() + "="; if ( (*it).find( ' ' ) != -1 ) s += "\"" + *it + "\"" + " "; else s += *it + " "; } s += ">"; return s; } //---------------------------------------------------------------------------- // PsiStyleSheet - helper class for PsiTextView //---------------------------------------------------------------------------- class PsiStyleSheet : public QStyleSheet { Q_OBJECT private: PsiStyleSheet(QObject *parent = 0, const char *name = 0) : QStyleSheet(parent, name) { new QStyleSheetItem(this, QString::fromLatin1("icon")); } public: QTextCustomItem *tag(const QString &name, const QMap &attr, const QString &context, const QMimeSourceFactory &factory, bool emptyTag, QTextDocument *doc ) const { const QStyleSheetItem *style = item( name ); if ( style && style->name() == "icon" ) return new TextIcon( doc, attr, context, (QMimeSourceFactory&)factory ); return QStyleSheet::tag(name, attr, context, factory, emptyTag, doc); } static PsiStyleSheet *self(); private: static PsiStyleSheet *psiStyleSheet; }; PsiStyleSheet *PsiStyleSheet::psiStyleSheet = 0; PsiStyleSheet *PsiStyleSheet::self() { if ( !psiStyleSheet ) { psiStyleSheet = new PsiStyleSheet(); } return psiStyleSheet; } //---------------------------------------------------------------------------- // PsiTextView //---------------------------------------------------------------------------- class PsiTextView::Private : public QObject { Q_OBJECT public: Private(QObject *parent) : QObject(parent, "PsiTextView::Private") { } }; PsiTextView::PsiTextView(QWidget *parent, const char *name) : QTextEdit(parent, name) { d = new Private(this); setReadOnly(true); setTextFormat(RichText); setStyleSheet( styleSheet() ); } QStyleSheet *PsiTextView::styleSheet() { return PsiStyleSheet::self(); } QPopupMenu *PsiTextView::createPopupMenu(const QPoint &pos) { QString link = anchorAt(pos); if ( link.isEmpty() ) return QTextEdit::createPopupMenu(pos); if ( !urlObject ) urlObject = new URLObject(); return urlObject->createPopupMenu( link ); } void PsiTextView::emitHighlighted(const QString &) { } void PsiTextView::emitLinkClicked(const QString &s) { if ( !urlObject ) urlObject = new URLObject(); urlObject->popupAction( s ); } //---------------------------------------------------------------------------- // URLLabel //---------------------------------------------------------------------------- class URLLabel::Private { public: QString url; QString title; }; URLLabel::URLLabel(QWidget *parent, const char *name) : QLabel(parent, name) { d = new Private; setCursor( pointingHandCursor ); } URLLabel::~URLLabel() { delete d; } const QString &URLLabel::url() const { return d->url; } void URLLabel::setUrl(const QString &url) { d->url = url; updateText(); } const QString &URLLabel::title() const { return d->title; } void URLLabel::setTitle(const QString &t) { d->title = t; updateText(); } void URLLabel::updateText() { setText( QString("%2").arg(d->url).arg(d->title) ); if ( d->url != d->title ) QToolTip::add(this, d->url); else QToolTip::remove(this); } void URLLabel::mouseReleaseEvent (QMouseEvent *e) { QLabel::mouseReleaseEvent (e); switch ( e->button() ) { case LeftButton: if ( !urlObject ) urlObject = new URLObject(); urlObject->popupAction( url() ); break; case MidButton: break; case RightButton: { if ( !urlObject ) urlObject = new URLObject(); QPopupMenu *m = urlObject->createPopupMenu( d->url ); if ( m ) { m->exec( e->globalPos() ); delete m; } break; } default: ; // to supress warnings } } void URLLabel::enterEvent (QEvent *e) { QLabel::enterEvent (e); } void URLLabel::leaveEvent (QEvent *e) { QLabel::leaveEvent (e); } void URLLabel::connectOpenURL(QObject *receiver, const char *slot) { if ( !urlObject ) urlObject = new URLObject(); connect(urlObject, SIGNAL(openURL(QString)), receiver, slot); } #include "psitextview.moc"