2 This file is part of libkdepim.
4 Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
24 #include <qpopupmenu.h>
27 #include <kabc/address.h>
28 #include <kabc/addressee.h>
29 #include <kabc/phonenumber.h>
30 #include <kactionclasses.h>
31 #include <kapplication.h>
34 #include <kglobalsettings.h>
35 #include <kiconloader.h>
39 #include <kmessagebox.h>
41 #include <kstringhandler.h>
42 #include <ktempfile.h>
46 #include "addresseeview.h"
50 AddresseeView::AddresseeView( QWidget
*parent
, const char *name
,
52 : KTextBrowser( parent
, name
), mDefaultConfig( false ), mImageJob( 0 ),
53 mLinkMask( AddressLinks
| EmailLinks
| PhoneLinks
| URLLinks
| IMLinks
)
55 setWrapPolicy( QTextEdit::AtWordBoundary
);
56 setLinkUnderline( false );
57 setVScrollBarMode( QScrollView::AlwaysOff
);
58 setHScrollBarMode( QScrollView::AlwaysOff
);
60 QStyleSheet
*sheet
= styleSheet();
61 QStyleSheetItem
*link
= sheet
->item( "a" );
62 link
->setColor( KGlobalSettings::linkColor() );
64 connect( this, SIGNAL( mailClick( const QString
&, const QString
& ) ),
65 this, SLOT( slotMailClicked( const QString
&, const QString
& ) ) );
66 connect( this, SIGNAL( urlClick( const QString
& ) ),
67 this, SLOT( slotUrlClicked( const QString
& ) ) );
68 connect( this, SIGNAL( highlighted( const QString
& ) ),
69 this, SLOT( slotHighlighted( const QString
& ) ) );
71 setNotifyClick( true );
73 mActionShowBirthday
= new KToggleAction( i18n( "Show Birthday" ) );
74 mActionShowBirthday
->setCheckedState(i18n("Hide Birthday"));
75 mActionShowAddresses
= new KToggleAction( i18n( "Show Postal Addresses" ) );
76 mActionShowAddresses
->setCheckedState(i18n("Hide Postal Addresses"));
77 mActionShowEmails
= new KToggleAction( i18n( "Show Email Addresses" ) );
78 mActionShowEmails
->setCheckedState(i18n("Hide Email Addresses"));
79 mActionShowPhones
= new KToggleAction( i18n( "Show Telephone Numbers" ) );
80 mActionShowPhones
->setCheckedState(i18n("Hide Telephone Numbers"));
81 mActionShowURLs
= new KToggleAction( i18n( "Show Web Pages (URLs)" ) );
82 mActionShowURLs
->setCheckedState(i18n("Hide Web Pages (URLs)"));
84 connect( mActionShowBirthday
, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) );
85 connect( mActionShowAddresses
, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) );
86 connect( mActionShowEmails
, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) );
87 connect( mActionShowPhones
, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) );
88 connect( mActionShowURLs
, SIGNAL( toggled( bool ) ), SLOT( configChanged() ) );
91 mConfig
= new KConfig( "kaddressbookrc" );
92 mDefaultConfig
= true;
98 // set up IMProxy to display contacts' IM presence and make connections to keep the display live
99 mKIMProxy
= ::KIMProxy::instance( kapp
->dcopClient() );
100 connect( mKIMProxy
, SIGNAL( sigContactPresenceChanged( const QString
& ) ), this, SLOT( slotPresenceChanged( const QString
& ) ) );
101 connect( mKIMProxy
, SIGNAL( sigPresenceInfoExpired() ), this, SLOT( slotPresenceInfoExpired() ) );
104 AddresseeView::~AddresseeView()
106 if ( mDefaultConfig
)
110 delete mActionShowBirthday
;
111 delete mActionShowAddresses
;
112 delete mActionShowEmails
;
113 delete mActionShowPhones
;
114 delete mActionShowURLs
;
119 void AddresseeView::setAddressee( const KABC::Addressee
& addr
)
128 mImageData
.truncate( 0 );
133 void AddresseeView::enableLinks( int linkMask
)
135 mLinkMask
= linkMask
;
138 QString
AddresseeView::vCardAsHTML( const KABC::Addressee
& addr
, ::KIMProxy
*proxy
, int linkMask
,
139 bool internalLoading
,
140 bool showBirthday
, bool showAddresses
,
141 bool showEmails
, bool showPhones
, bool showURLs
,
142 bool showIMAddresses
)
144 QString image
= QString( "contact_%1_image" ).arg( addr
.uid() );
146 // Style strings from Gentix; this is just an initial version.
148 // These will be substituted into various HTML strings with .arg().
149 // Search for @STYLE@ to find where. Note how we use %1 as a
150 // placeholder where we fill in something else (in this case,
151 // the global background color).
153 QString backgroundColor
= KGlobalSettings::alternateBackgroundColor().name();
154 QString cellStyle
= QString::fromLatin1(
156 "padding-right: 2px; "
157 "border-right: #000 dashed 1px; "
158 "background: %1;\"").arg(backgroundColor
);
159 QString backgroundColor2
= KGlobalSettings::baseColor().name();
160 QString cellStyle2
= QString::fromLatin1(
162 "padding-left: 2px; "
163 "background: %1;\"").arg(backgroundColor2
);
164 QString tableStyle
= QString::fromLatin1(
166 "border: solid 1px; "
169 // We'll be building a table to display the vCard in.
170 // Each row of the table will be built using this string for its HTML.
172 QString rowFmtStr
= QString::fromLatin1(
174 "<td align=\"right\" valign=\"top\" width=\"30%\" "); // Tag unclosed
175 rowFmtStr
.append( cellStyle
);
176 rowFmtStr
.append( QString::fromLatin1(
180 "<td align=\"left\" valign=\"top\" width=\"70%\" ") ); // Tag unclosed
181 rowFmtStr
.append( cellStyle2
);
182 rowFmtStr
.append( QString::fromLatin1(
189 // Build the table's rows here
193 if ( !internalLoading
) {
194 KABC::Picture pic
= addr
.photo();
195 if ( pic
.isIntern() && !pic
.data().isNull() ) {
196 image
= pixmapAsDataUrl( pic
.data() );
197 } else if ( !pic
.url().isEmpty() ) {
198 image
= (pic
.url().startsWith( "http://" ) || pic
.url().startsWith( "https://" ) ? pic
.url() : "http://" + pic
.url());
200 image
= "file:" + KGlobal::iconLoader()->iconPath( "personal", KIcon::Desktop
);
204 if ( showBirthday
) {
205 QDate date
= addr
.birthday().date();
207 if ( date
.isValid() )
208 dynamicPart
+= rowFmtStr
209 .arg( KABC::Addressee::birthdayLabel() )
210 .arg( KGlobal::locale()->formatDate( date
, true ) );
214 KABC::PhoneNumber::List phones
= addr
.phoneNumbers();
215 KABC::PhoneNumber::List::ConstIterator phoneIt
;
216 for ( phoneIt
= phones
.begin(); phoneIt
!= phones
.end(); ++phoneIt
) {
217 QString number
= (*phoneIt
).number();
220 if ( (*phoneIt
).type() & KABC::PhoneNumber::Fax
)
221 url
= QString::fromLatin1( "fax:" ) + number
;
223 url
= QString::fromLatin1( "phone:" ) + number
;
225 if ( linkMask
& PhoneLinks
) {
226 dynamicPart
+= rowFmtStr
227 .arg( KABC::PhoneNumber::typeLabel( (*phoneIt
).type() ).replace( " ", " " ) )
228 .arg( QString::fromLatin1( "<a href=\"%1\">%2</a>" ).arg(url
).arg(number
) );
230 dynamicPart
+= rowFmtStr
231 .arg( KABC::PhoneNumber::typeLabel( (*phoneIt
).type() ).replace( " ", " " ) )
238 QStringList emails
= addr
.emails();
239 QStringList::ConstIterator emailIt
;
240 QString type
= i18n( "Email" );
241 for ( emailIt
= emails
.begin(); emailIt
!= emails
.end(); ++emailIt
) {
242 QString fullEmail
= addr
.fullEmail( *emailIt
);
243 QUrl::encode( fullEmail
);
245 if ( linkMask
& EmailLinks
) {
246 dynamicPart
+= rowFmtStr
.arg( type
)
247 .arg( QString::fromLatin1( "<a href=\"mailto:%1\">%2</a>" )
248 .arg( fullEmail
, *emailIt
) );
250 dynamicPart
+= rowFmtStr
.arg( type
).arg( *emailIt
);
253 type
= i18n( "Other" );
258 if ( !addr
.url().url().isEmpty() ) {
259 if ( linkMask
& URLLinks
) {
260 QString url
= (addr
.url().url().startsWith( "http://" ) || addr
.url().url().startsWith( "https://" ) ? addr
.url().url() :
261 "http://" + addr
.url().url());
262 dynamicPart
+= rowFmtStr
263 .arg( i18n( "Homepage" ) )
264 .arg( KStringHandler::tagURLs( url
) );
266 dynamicPart
+= rowFmtStr
267 .arg( i18n( "Homepage" ) )
268 .arg( addr
.url().url() );
273 if ( showAddresses
) {
274 KABC::Address::List addresses
= addr
.addresses();
275 KABC::Address::List::ConstIterator addrIt
;
276 for ( addrIt
= addresses
.begin(); addrIt
!= addresses
.end(); ++addrIt
) {
277 if ( (*addrIt
).label().isEmpty() ) {
278 QString formattedAddress
;
280 #if KDE_IS_VERSION(3,1,90)
281 formattedAddress
= (*addrIt
).formattedAddress().stripWhiteSpace();
283 if ( !(*addrIt
).street().isEmpty() )
284 formattedAddress
+= (*addrIt
).street() + "\n";
286 if ( !(*addrIt
).postOfficeBox().isEmpty() )
287 formattedAddress
+= (*addrIt
).postOfficeBox() + "\n";
289 formattedAddress
+= (*addrIt
).locality() + QString::fromLatin1(" ") + (*addrIt
).region();
291 if ( !(*addrIt
).postalCode().isEmpty() )
292 formattedAddress
+= QString::fromLatin1(", ") + (*addrIt
).postalCode();
294 formattedAddress
+= "\n";
296 if ( !(*addrIt
).country().isEmpty() )
297 formattedAddress
+= (*addrIt
).country() + "\n";
299 formattedAddress
+= (*addrIt
).extended();
302 formattedAddress
= formattedAddress
.replace( '\n', "<br>" );
304 QString link
= "<a href=\"addr:" + (*addrIt
).id() + "\">" +
305 formattedAddress
+ "</a>";
307 if ( linkMask
& AddressLinks
) {
308 dynamicPart
+= rowFmtStr
309 .arg( KABC::Address::typeLabel( (*addrIt
).type() ) )
312 dynamicPart
+= rowFmtStr
313 .arg( KABC::Address::typeLabel( (*addrIt
).type() ) )
314 .arg( formattedAddress
);
317 QString link
= "<a href=\"addr:" + (*addrIt
).id() + "\">" +
318 (*addrIt
).label().replace( '\n', "<br>" ) + "</a>";
320 if ( linkMask
& AddressLinks
) {
321 dynamicPart
+= rowFmtStr
322 .arg( KABC::Address::typeLabel( (*addrIt
).type() ) )
325 dynamicPart
+= rowFmtStr
326 .arg( KABC::Address::typeLabel( (*addrIt
).type() ) )
327 .arg( (*addrIt
).label().replace( '\n', "<br>" ) );
334 if ( !addr
.note().isEmpty() ) {
335 // @STYLE@ - substitute the cell style in first, and append
336 // the data afterwards (keeps us safe from possible % signs
338 notes
= rowFmtStr
.arg( i18n( "Notes" ) ).arg( addr
.note().replace( '\n', "<br>" ) ) ;
341 QString
name( addr
.realName() );
342 QString
role( addr
.role() );
343 QString
organization( addr
.organization() );
345 if ( proxy
&& showIMAddresses
)
347 if ( proxy
->isPresent( addr
.uid() ) )
349 // set image source to either a QMimeSourceFactory key or a data:/ URL
351 if ( internalLoading
)
353 imgSrc
= QString::fromLatin1( "im_status_%1_image").arg( addr
.uid() );
354 QMimeSourceFactory::defaultFactory()->setPixmap( imgSrc
, proxy
->presenceIcon( addr
.uid() ) );
357 imgSrc
= pixmapAsDataUrl( proxy
->presenceIcon( addr
.uid() ) );
359 // make the status a link, if required
361 if ( linkMask
& IMLinks
)
362 imStatus
= QString::fromLatin1( "<a href=\"im:\"><img src=\"%1\"> (%2)</a>" );
364 imStatus
= QString::fromLatin1( "<img src=\"%1\"> (%2)" );
366 // append our status to the rest of the dynamic part of the addressee
367 dynamicPart
+= rowFmtStr
368 .arg( i18n( "Presence" ) )
371 .arg( proxy
->presenceString( addr
.uid() ) )
376 // @STYLE@ - construct the string by parts, substituting in
377 // the styles first. There are lots of appends, but we need to
378 // do it this way to avoid cases where the substituted string
379 // contains %1 and the like.
381 QString strAddr
= QString::fromLatin1(
382 "<div align=\"center\">"
383 "<table cellpadding=\"1\" cellspacing=\"0\" %1>"
384 "<tr>").arg(tableStyle
);
386 strAddr
.append( QString::fromLatin1(
387 "<td align=\"right\" valign=\"top\" width=\"30%\" rowspan=\"3\" %2>")
389 strAddr
.append( QString::fromLatin1(
390 "<img src=\"%1\" width=\"50\" vspace=\"1\">" // image
393 strAddr
.append( QString::fromLatin1(
394 "<td align=\"left\" width=\"70%\" %2>")
395 .arg( cellStyle2
) );
396 strAddr
.append( QString::fromLatin1(
397 "<font size=\"+2\"><b>%2</b></font></td>" // name
400 strAddr
.append( QString::fromLatin1(
402 "<td align=\"left\" width=\"70%\" %2>")
403 .arg( cellStyle2
) );
404 strAddr
.append( QString::fromLatin1(
408 strAddr
.append( QString::fromLatin1(
410 "<td align=\"left\" width=\"70%\" %2>")
411 .arg( cellStyle2
) );
412 strAddr
.append( QString::fromLatin1(
413 "%4</td>" // organization
415 .arg( organization
) );
416 strAddr
.append( QString::fromLatin1(
419 strAddr
.append( QString::fromLatin1(
420 " </td><td %2> </td></tr>")
421 .arg( cellStyle2
) );
422 strAddr
.append( dynamicPart
);
423 strAddr
.append( notes
);
424 strAddr
.append( QString::fromLatin1("</table></div>\n") );
429 QString
AddresseeView::pixmapAsDataUrl( const QPixmap
& pixmap
)
432 QBuffer
buffer( ba
);
433 buffer
.open( IO_WriteOnly
);
434 pixmap
.save( &buffer
, "PNG" );
435 QString
encoded( "data:image/png;base64," );
436 encoded
.append( KCodecs::base64Encode( ba
) );
440 void AddresseeView::updateView()
443 setText( QString::null
);
445 if ( mAddressee
.isEmpty() )
452 mImageData
.truncate( 0 );
455 QString strAddr
= vCardAsHTML( mAddressee
, mKIMProxy
, mLinkMask
, true,
456 mActionShowBirthday
->isChecked(),
457 mActionShowAddresses
->isChecked(),
458 mActionShowEmails
->isChecked(),
459 mActionShowPhones
->isChecked(),
460 mActionShowURLs
->isChecked() );
462 strAddr
= QString::fromLatin1(
464 "<body text=\"%1\" bgcolor=\"%2\">" // text and background color
468 .arg( KGlobalSettings::textColor().name() )
469 .arg( KGlobalSettings::baseColor().name() )
472 QString imageURL
= QString( "contact_%1_image" ).arg( mAddressee
.uid() );
474 KABC::Picture picture
= mAddressee
.photo();
475 if ( picture
.isIntern() && !picture
.data().isNull() )
476 QMimeSourceFactory::defaultFactory()->setImage( imageURL
, picture
.data() );
478 if ( !picture
.url().isEmpty() ) {
479 if ( mImageData
.count() > 0 )
480 QMimeSourceFactory::defaultFactory()->setImage( imageURL
, mImageData
);
482 mImageJob
= KIO::get( KURL( picture
.url() ), false, false );
483 connect( mImageJob
, SIGNAL( data( KIO::Job
*, const QByteArray
& ) ),
484 this, SLOT( data( KIO::Job
*, const QByteArray
& ) ) );
485 connect( mImageJob
, SIGNAL( result( KIO::Job
* ) ),
486 this, SLOT( result( KIO::Job
* ) ) );
489 QMimeSourceFactory::defaultFactory()->setPixmap( imageURL
,
490 KGlobal::iconLoader()->loadIcon( "personal", KIcon::Desktop
, 128 ) );
494 // at last display it...
498 KABC::Addressee
AddresseeView::addressee() const
503 void AddresseeView::urlClicked( const QString
&url
)
505 kapp
->invokeBrowser( url
);
508 void AddresseeView::emailClicked( const QString
&email
)
510 kapp
->invokeMailer( email
, QString::null
);
513 void AddresseeView::phoneNumberClicked( const QString
&number
)
515 KConfig
config( "kaddressbookrc" );
516 config
.setGroup( "General" );
517 QString commandLine
= config
.readEntry( "PhoneHookApplication" );
519 if ( commandLine
.isEmpty() ) {
520 KMessageBox::sorry( this, i18n( "There is no application set which could be executed. Please go to the settings dialog and configure one." ) );
524 commandLine
.replace( "%N", number
);
525 KRun::runCommand( commandLine
);
528 void AddresseeView::faxNumberClicked( const QString
&number
)
530 KConfig
config( "kaddressbookrc" );
531 config
.setGroup( "General" );
532 QString commandLine
= config
.readEntry( "FaxHookApplication", "kdeprintfax --phone %N" );
534 if ( commandLine
.isEmpty() ) {
535 KMessageBox::sorry( this, i18n( "There is no application set which could be executed. Please go to the settings dialog and configure one." ) );
539 commandLine
.replace( "%N", number
);
540 KRun::runCommand( commandLine
);
543 void AddresseeView::imAddressClicked()
545 mKIMProxy
->chatWithContact( mAddressee
.uid() );
548 QPopupMenu
*AddresseeView::createPopupMenu( const QPoint
& )
550 QPopupMenu
*menu
= new QPopupMenu( this );
551 mActionShowBirthday
->plug( menu
);
552 mActionShowAddresses
->plug( menu
);
553 mActionShowEmails
->plug( menu
);
554 mActionShowPhones
->plug( menu
);
555 mActionShowURLs
->plug( menu
);
560 void AddresseeView::slotMailClicked( const QString
&, const QString
&email
)
562 emailClicked( email
);
565 void AddresseeView::slotUrlClicked( const QString
&url
)
567 if ( url
.startsWith( "phone:" ) )
568 phoneNumberClicked( strippedNumber( url
.mid( 8 ) ) );
569 else if ( url
.startsWith( "fax:" ) )
570 faxNumberClicked( strippedNumber( url
.mid( 6 ) ) );
571 else if ( url
.startsWith( "addr:" ) )
572 emit
addressClicked( url
.mid( 7 ) );
573 else if ( url
.startsWith( "im:" ) )
579 void AddresseeView::slotHighlighted( const QString
&link
)
581 if ( link
.startsWith( "mailto:" ) ) {
582 QString email
= link
.mid( 7 );
584 emit
emailHighlighted( email
);
585 emit
highlightedMessage( i18n( "Send mail to '%1'" ).arg( email
) );
586 } else if ( link
.startsWith( "phone:" ) ) {
587 QString number
= link
.mid( 8 );
589 emit
phoneNumberHighlighted( strippedNumber( number
) );
590 emit
highlightedMessage( i18n( "Call number %1" ).arg( number
) );
591 } else if ( link
.startsWith( "fax:" ) ) {
592 QString number
= link
.mid( 6 );
594 emit
faxNumberHighlighted( strippedNumber( number
) );
595 emit
highlightedMessage( i18n( "Send fax to %1" ).arg( number
) );
596 } else if ( link
.startsWith( "addr:" ) ) {
597 emit
highlightedMessage( i18n( "Show address on map" ) );
598 } else if ( link
.startsWith( "http:" ) || link
.startsWith( "https:" ) ) {
599 emit
urlHighlighted( link
);
600 emit
highlightedMessage( i18n( "Open URL %1" ).arg( link
) );
601 } else if ( link
.startsWith( "im:" ) ) {
602 emit
highlightedMessage( i18n( "Chat with %1" ).arg( mAddressee
.realName() ) );
604 emit
highlightedMessage( "" );
607 void AddresseeView::slotPresenceChanged( const QString
&uid
)
609 kdDebug() << k_funcinfo
<< " uid is: " << uid
<< " mAddressee is: " << mAddressee
.uid() << endl
;
610 if ( uid
== mAddressee
.uid() )
615 void AddresseeView::slotPresenceInfoExpired()
620 void AddresseeView::configChanged()
626 void AddresseeView::data( KIO::Job
*, const QByteArray
&d
)
628 unsigned int oldSize
= mImageData
.size();
629 mImageData
.resize( oldSize
+ d
.size() );
630 memcpy( mImageData
.data() + oldSize
, d
.data(), d
.size() );
633 void AddresseeView::result( KIO::Job
*job
)
638 mImageData
.truncate( 0 );
643 void AddresseeView::load()
645 mConfig
->setGroup( "AddresseeViewSettings" );
646 mActionShowBirthday
->setChecked( mConfig
->readBoolEntry( "ShowBirthday", false ) );
647 mActionShowAddresses
->setChecked( mConfig
->readBoolEntry( "ShowAddresses", true ) );
648 mActionShowEmails
->setChecked( mConfig
->readBoolEntry( "ShowEmails", true ) );
649 mActionShowPhones
->setChecked( mConfig
->readBoolEntry( "ShowPhones", true ) );
650 mActionShowURLs
->setChecked( mConfig
->readBoolEntry( "ShowURLs", true ) );
653 void AddresseeView::save()
655 mConfig
->setGroup( "AddresseeViewSettings" );
656 mConfig
->writeEntry( "ShowBirthday", mActionShowBirthday
->isChecked() );
657 mConfig
->writeEntry( "ShowAddresses", mActionShowAddresses
->isChecked() );
658 mConfig
->writeEntry( "ShowEmails", mActionShowEmails
->isChecked() );
659 mConfig
->writeEntry( "ShowPhones", mActionShowPhones
->isChecked() );
660 mConfig
->writeEntry( "ShowURLs", mActionShowURLs
->isChecked() );
664 QString
AddresseeView::strippedNumber( const QString
&number
)
668 for ( uint i
= 0; i
< number
.length(); ++i
) {
669 QChar c
= number
[ i
];
670 if ( c
.isDigit() || c
== '*' || c
== '#' || c
== '+' && i
== 0 )
677 #include "addresseeview.moc"