source: psi/trunk/libpsi/psiwidgets/psitextview.cpp@ 5

Last change on this file since 5 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 10.3 KB
Line 
1/*
2 * psitextview.cpp - Icon-aware QTextView subclass widget
3 * Copyright (C) 2003 Michail Pishchagin
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include "psitextview.h"
22
23#include <qpopupmenu.h>
24#include <qapplication.h>
25#include <qclipboard.h>
26#include <qtooltip.h>
27
28#include <qstylesheet.h>
29#include <private/qrichtext_p.h>
30
31#ifndef WIDGET_PLUGIN
32#include "iconset.h"
33#else
34class Icon;
35class Iconset;
36#endif
37
38//----------------------------------------------------------------------------
39// URLObject - helper class for handling links
40//----------------------------------------------------------------------------
41
42class URLObject : public QObject
43{
44 Q_OBJECT
45
46public:
47 URLObject(QObject *parent = 0)
48 : QObject(qApp) { Q_UNUSED(parent); }
49
50 QPopupMenu *createPopupMenu(const QString &lnk);
51
52private:
53 QString link;
54
55 QString copyString(QString from)
56 {
57 QString l = from;
58
59 int colon = l.find(':');
60 if ( colon == -1 )
61 colon = 0;
62 QString service = l.left( colon );
63
64 if ( service == "mailto" || service == "jabber" || service == "jid" || service == "xmpp" ) {
65 if ( colon > -1 )
66 l = l.mid( colon + 1 );
67
68 while ( l[0] == '/' )
69 l = l.mid( 1 );
70 }
71
72 return l;
73 }
74
75signals:
76 void openURL(QString);
77
78public slots:
79 void popupAction(QString lnk) {
80 emit openURL(lnk);
81 }
82
83 void popupAction() {
84 popupAction(link);
85 }
86
87 void popupCopy(QString lnk) {
88 QApplication::clipboard()->setText( copyString(lnk), QClipboard::Clipboard );
89 if(QApplication::clipboard()->supportsSelection())
90 QApplication::clipboard()->setText( copyString(lnk), QClipboard::Selection );
91 }
92
93 void popupCopy() {
94 popupCopy(link);
95 }
96};
97
98static URLObject *urlObject = 0;
99
100QPopupMenu *URLObject::createPopupMenu(const QString &lnk)
101{
102 link = lnk;
103 if ( link.isEmpty() )
104 return 0;
105
106 int colon = link.find(':');
107 if ( colon == -1 )
108 colon = 0;
109 QString service = link.left( colon );
110
111 QString action = "ERROR";
112 QString iconName;
113
114 if ( service == "mailto" ) {
115 action = URLLabel::tr("Open mail composer");
116 iconName = "psi/email";
117 }
118 else if ( service == "jabber" || service == "jid" || service == "xmpp" ) {
119 // TODO: need more actions to jabber item. Ex: "add to roster", "send message"
120 action = URLLabel::tr("Add to Roster");
121 iconName = "psi/add";
122 }
123 else { //if ( service == "http" || service == "https" || service.isEmpty() ) {
124 action = URLLabel::tr("Open web browser");
125 iconName = "psi/www";
126 }
127
128#ifndef WIDGET_PLUGIN
129 const Icon *icon = 0;
130 if ( !iconName.isEmpty() )
131 icon = IconsetFactory::iconPtr( iconName );
132#endif
133
134 QPopupMenu *m = new QPopupMenu;
135
136#ifndef WIDGET_PLUGIN
137 if ( icon )
138 m->insertItem(/*QIconSet(*/ *icon /*)*/, action, this, SLOT(popupAction()));
139 else
140#endif
141 m->insertItem(action, this, SLOT(popupAction()));
142 m->insertItem(URLLabel::tr("Copy location"), this, SLOT(popupCopy()));
143
144 return m;
145}
146
147//----------------------------------------------------------------------------
148// TextIcon - helper class for PsiTextView
149//----------------------------------------------------------------------------
150
151class TextIcon : public QObject, public QTextCustomItem
152{
153 Q_OBJECT
154
155public:
156 TextIcon(QTextDocument *p, const QMap<QString, QString> &attr, const QString &context, QMimeSourceFactory &factory);
157 ~TextIcon();
158
159 void adjustToPainter(QPainter *);
160 void draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool selected);
161 QString richText() const;
162
163 Placement placement() const { return place; }
164 int minimumWidth() const { return width; }
165
166private slots:
167 void iconUpdated(const QPixmap &);
168
169private:
170 Placement place;
171 int tmpwidth, tmpheight;
172 QMap<QString, QString> attributes;
173 QString imgId;
174 Icon *icon;
175};
176
177TextIcon::TextIcon(QTextDocument *p, const QMap<QString, QString> &attr, const QString &context, QMimeSourceFactory &factory)
178: QObject(0, 0), QTextCustomItem (p)
179{
180 Q_UNUSED(context);
181 Q_UNUSED(factory);
182
183 width = height = 0;
184
185 icon = 0;
186 QString iconName = attr["name"];
187
188 if ( iconName.isEmpty() )
189 iconName = attr["src"];
190
191 if ( iconName.isEmpty() )
192 iconName = attr["source"];
193
194#ifndef WIDGET_PLUGIN
195 if ( !iconName.isEmpty() ) {
196 icon = (Icon *)IconsetFactory::iconPtr( iconName );
197 if ( icon )
198 icon = new Icon(*icon);
199
200 if ( icon ) {
201 icon->activated();
202 connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
203
204 width = icon->pixmap().width();
205 height = icon->pixmap().height();
206 }
207 }
208#endif
209
210 if ( !icon && (width*height)==0 )
211 width = height = 50;
212
213 place = PlaceInline;
214 if ( attr["align"] == "left" )
215 place = PlaceLeft;
216 else if ( attr["align"] == "right" )
217 place = PlaceRight;
218
219 tmpwidth = width;
220 tmpheight = height;
221
222 attributes = attr;
223}
224
225TextIcon::~TextIcon()
226{
227#ifndef WIDGET_PLUGIN
228 if ( icon ) {
229 icon->stop();
230 delete icon;
231 }
232#endif
233}
234
235void TextIcon::adjustToPainter(QPainter *)
236{
237 // FIXME: This class can be incorrectly printed. Check this sometime later.
238}
239
240void TextIcon::draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool /*selected*/)
241{
242 if ( placement() != PlaceInline ) {
243 x = xpos;
244 y = ypos;
245 }
246
247 if ( !icon ) {
248 p->fillRect( x, y, width, height, cg.dark() );
249 return;
250 }
251
252 if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) )
253 return;
254
255#ifndef WIDGET_PLUGIN
256 if ( placement() == PlaceInline )
257 p->drawPixmap( x , y, icon->pixmap() );
258 else
259 p->drawPixmap( cx , cy, icon->pixmap(), cx - x, cy - y, cw, ch );
260#endif
261}
262
263void TextIcon::iconUpdated(const QPixmap &)
264{
265 // TODO: any ideas what it should do? ;-)
266}
267
268QString TextIcon::richText() const
269{
270 QString s;
271 s += "<icon ";
272 QMap<QString, QString>::ConstIterator it = attributes.begin();
273 for ( ; it != attributes.end(); ++it ) {
274 s += it.key() + "=";
275 if ( (*it).find( ' ' ) != -1 )
276 s += "\"" + *it + "\"" + " ";
277 else
278 s += *it + " ";
279 }
280 s += ">";
281 return s;
282}
283
284//----------------------------------------------------------------------------
285// PsiStyleSheet - helper class for PsiTextView
286//----------------------------------------------------------------------------
287
288class PsiStyleSheet : public QStyleSheet
289{
290 Q_OBJECT
291private:
292 PsiStyleSheet(QObject *parent = 0, const char *name = 0)
293 : QStyleSheet(parent, name)
294 {
295 new QStyleSheetItem(this, QString::fromLatin1("icon"));
296 }
297
298public:
299 QTextCustomItem *tag(const QString &name, const QMap<QString, QString> &attr,
300 const QString &context,
301 const QMimeSourceFactory &factory,
302 bool emptyTag, QTextDocument *doc ) const
303 {
304 const QStyleSheetItem *style = item( name );
305 if ( style && style->name() == "icon" )
306 return new TextIcon( doc, attr, context, (QMimeSourceFactory&)factory );
307 return QStyleSheet::tag(name, attr, context, factory, emptyTag, doc);
308 }
309
310 static PsiStyleSheet *self();
311
312private:
313 static PsiStyleSheet *psiStyleSheet;
314};
315
316PsiStyleSheet *PsiStyleSheet::psiStyleSheet = 0;
317
318PsiStyleSheet *PsiStyleSheet::self()
319{
320 if ( !psiStyleSheet ) {
321 psiStyleSheet = new PsiStyleSheet();
322 }
323
324 return psiStyleSheet;
325}
326
327//----------------------------------------------------------------------------
328// PsiTextView
329//----------------------------------------------------------------------------
330
331class PsiTextView::Private : public QObject
332{
333 Q_OBJECT
334
335public:
336 Private(QObject *parent)
337 : QObject(parent, "PsiTextView::Private") { }
338};
339
340PsiTextView::PsiTextView(QWidget *parent, const char *name)
341: QTextEdit(parent, name)
342{
343 d = new Private(this);
344
345 setReadOnly(true);
346 setTextFormat(RichText);
347
348 setStyleSheet( styleSheet() );
349}
350
351QStyleSheet *PsiTextView::styleSheet()
352{
353 return PsiStyleSheet::self();
354}
355
356QPopupMenu *PsiTextView::createPopupMenu(const QPoint &pos)
357{
358 QString link = anchorAt(pos);
359 if ( link.isEmpty() )
360 return QTextEdit::createPopupMenu(pos);
361
362 if ( !urlObject )
363 urlObject = new URLObject();
364 return urlObject->createPopupMenu( link );
365}
366
367void PsiTextView::emitHighlighted(const QString &)
368{
369}
370
371void PsiTextView::emitLinkClicked(const QString &s)
372{
373 if ( !urlObject )
374 urlObject = new URLObject();
375 urlObject->popupAction( s );
376}
377
378//----------------------------------------------------------------------------
379// URLLabel
380//----------------------------------------------------------------------------
381
382class URLLabel::Private
383{
384public:
385 QString url;
386 QString title;
387};
388
389URLLabel::URLLabel(QWidget *parent, const char *name)
390: QLabel(parent, name)
391{
392 d = new Private;
393 setCursor( pointingHandCursor );
394}
395
396URLLabel::~URLLabel()
397{
398 delete d;
399}
400
401const QString &URLLabel::url() const
402{
403 return d->url;
404}
405
406void URLLabel::setUrl(const QString &url)
407{
408 d->url = url;
409 updateText();
410}
411
412const QString &URLLabel::title() const
413{
414 return d->title;
415}
416
417void URLLabel::setTitle(const QString &t)
418{
419 d->title = t;
420 updateText();
421}
422
423void URLLabel::updateText()
424{
425 setText( QString("<a href=\"%1\">%2</a>").arg(d->url).arg(d->title) );
426
427 if ( d->url != d->title )
428 QToolTip::add(this, d->url);
429 else
430 QToolTip::remove(this);
431}
432
433void URLLabel::mouseReleaseEvent (QMouseEvent *e)
434{
435 QLabel::mouseReleaseEvent (e);
436
437 switch ( e->button() ) {
438 case LeftButton:
439 if ( !urlObject )
440 urlObject = new URLObject();
441 urlObject->popupAction( url() );
442 break;
443
444 case MidButton:
445 break;
446
447 case RightButton:
448 {
449 if ( !urlObject )
450 urlObject = new URLObject();
451 QPopupMenu *m = urlObject->createPopupMenu( d->url );
452
453 if ( m ) {
454 m->exec( e->globalPos() );
455 delete m;
456 }
457 break;
458 }
459
460 default:
461 ; // to supress warnings
462 }
463}
464
465void URLLabel::enterEvent (QEvent *e)
466{
467 QLabel::enterEvent (e);
468}
469
470void URLLabel::leaveEvent (QEvent *e)
471{
472 QLabel::leaveEvent (e);
473}
474
475void URLLabel::connectOpenURL(QObject *receiver, const char *slot)
476{
477 if ( !urlObject )
478 urlObject = new URLObject();
479
480 connect(urlObject, SIGNAL(openURL(QString)), receiver, slot);
481}
482
483#include "psitextview.moc"
Note: See TracBrowser for help on using the repository browser.