30 #include <QtCore/QByteArray>
31 #include <QtCore/QLatin1String>
32 #include <QtCore/QList>
33 #include <QtCore/QPointer>
34 #include <QtCore/QRect>
35 #include <QtCore/QRegExp>
36 #include <QtCore/QTextCodec>
37 #include <QtCore/QTextStream>
38 #include <QtCore/QTimer>
39 #include <QtCore/QBuffer>
40 #include <QtGui/QClipboard>
41 #include <QtGui/QCursor>
42 #include <QtGui/QPixmap>
43 #include <QtGui/QTextDocument>
44 #include <QtGui/QScrollBar>
46 #include <QApplication>
47 #include <QFileDialog>
49 #include <Phonon/MediaObject>
50 #include <Phonon/Path>
51 #include <Phonon/AudioOutput>
52 #include <Phonon/Global>
55 #include <dom/dom_doc.h>
56 #include <dom/dom_text.h>
57 #include <dom/dom_element.h>
58 #include <dom/html_base.h>
59 #include <dom/html_document.h>
60 #include <dom/html_inline.h>
61 #include <dom/html_form.h>
62 #include <dom/dom2_events.h>
66 #include <kactioncollection.h>
68 #include <kdeversion.h>
69 #include <kfiledialog.h>
70 #include <khtmlview.h>
72 #include <kmessagebox.h>
75 #include <kstringhandler.h>
76 #include <ktemporaryfile.h>
77 #include <kio/copyjob.h>
78 #include <kstandardaction.h>
79 #include <kiconloader.h>
84 #include "kopetecontact.h"
85 #include "kopetecontactlist.h"
87 #include "kopetechatsession.h"
88 #include "kopetemetacontact.h"
89 #include "kopetepluginmanager.h"
90 #include "kopeteprotocol.h"
91 #include "kopeteaccount.h"
92 #include "kopeteglobal.h"
93 #include "kopeteemoticons.h"
94 #include "kopeteview.h"
95 #include "kopetepicture.h"
96 #include "kopeteappearancesettings.h"
97 #include "kopetebehaviorsettings.h"
99 #include "kopetetransfermanager.h"
108 class ChatMessagePart::Private
112 : scrollPressed(false), scrollToEndDelayed(false),
manager(0),
113 copyAction(0), saveAction(0), printAction(0),
114 closeAction(0),copyURLAction(0), currentChatStyle(0),
115 latestDirection(Kopete::Message::Inbound), latestType(Kopete::Message::TypeNormal),
116 latestTime(0), htmlEventListener(0)
129 bool scrollToEndDelayed;
132 DOM::HTMLElement activeElement;
142 Kopete::Message::MessageDirection latestDirection;
143 Kopete::Message::MessageType latestType;
216 if (d->currentChatStyle)
217 connect( d->currentChatStyle, SIGNAL(destroyed(
QObject*)),
this, SLOT(clearStyle()) );
219 connect(
this, SIGNAL(completed()),
this, SLOT(slotRenderingFinished()) );
222 setJScriptEnabled(
false ) ;
223 setJavaEnabled(
false );
224 setPluginsEnabled(
false );
225 setMetaRefreshEnabled(
false );
226 setOnlyLocalReferences(
true );
235 view()->setAcceptDrops(
false);
237 connect( Kopete::AppearanceSettings::self(), SIGNAL(messageOverridesChanged()),
238 this, SLOT(slotAppearanceChanged()) );
239 connect( Kopete::AppearanceSettings::self(), SIGNAL(appearanceChanged()),
240 this, SLOT(slotRefreshView()) );
242 this, SLOT(slotRefreshView()) );
249 connect( d->manager, SIGNAL(displayNameChanged()),
this, SLOT(slotUpdateHeaderDisplayName()) );
250 connect( d->manager, SIGNAL(photoChanged()),
this, SLOT(slotUpdateHeaderPhoto()) );
255 connect ( browserExtension(), SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
256 this, SLOT(slotOpenURLRequest(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)) );
262 view()->verticalScrollBar()->setTracking(
true);
263 connect( view()->verticalScrollBar(), SIGNAL(valueChanged(
int)),
264 this, SLOT(slotScrollingTo(
int)) );
266 connect( Kopete::TransferManager::transferManager(), SIGNAL(askIncomingDone(uint)),
267 this, SLOT(slotFileTransferIncomingDone(uint)) );
269 connect( KGlobalSettings::self(), SIGNAL(kdisplayFontChanged()),
270 this, SLOT(slotRefreshView()) );
273 d->copyAction = KStandardAction::copy(
this, SLOT(
copy()), actionCollection() );
274 d->saveAction = KStandardAction::saveAs(
this, SLOT(
save()), actionCollection() );
275 d->printAction = KStandardAction::print(
this, SLOT(
print()),actionCollection() );
276 d->closeAction = KStandardAction::close(
this, SLOT(slotCloseView()),actionCollection() );
277 d->copyURLAction =
new KAction( KIcon(
"edit-copy"), i18n(
"Copy Link Address" ), actionCollection() );
278 actionCollection()->addAction(
"editcopy", d->copyURLAction );
279 connect( d->copyURLAction, SIGNAL(triggered(
bool)),
this, SLOT(slotCopyURL()) );
291 for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
293 if ( (*it).type() == Kopete::Message::TypeFileTransferRequest && !(*it).fileTransferDisabled() )
295 Kopete::TransferManager::transferManager()->cancelIncomingTransfer( (*it).id() );
303 void ChatMessagePart::slotScrollingTo(
int y )
305 int scrolledTo = y + view()->visibleHeight();
306 d->scrollPressed = scrolledTo < ( view()->contentsHeight() - 10 );
313 dlg->setCaption( i18n(
"Save Conversation" ) );
314 dlg->setOperationMode( KFileDialog::Saving );
316 if ( dlg->exec() != QDialog::Accepted )
325 KUrl saveURL = dlg->selectedUrl();
326 KTemporaryFile *tempFile =
new KTemporaryFile();
327 tempFile->setAutoRemove(
false);
336 for(it = d->allMessages.constBegin(); it != itEnd; ++it)
338 Kopete::Message tempMessage = *it;
339 stream <<
"[" << KGlobal::locale()->formatDateTime(tempMessage.timestamp()) <<
"] ";
340 if( tempMessage.from() && tempMessage.from()->metaContact() )
342 stream <<
formatName(tempMessage.from()->metaContact()->displayName(), Qt::RichText);
344 stream <<
": " << tempMessage.plainBody() <<
"\n";
349 stream << htmlDocument().toString().
string() <<
'\n';
355 QString fileName = tempFile->fileName();
358 KIO::CopyJob *moveJob = KIO::move( KUrl( fileName ), saveURL, KIO::HideProgressInfo );
362 KMessageBox::queuedMessageBox( view(), KMessageBox::Error,
363 i18n(
"<qt>Could not open <b>%1</b> for writing.</qt>", saveURL.prettyUrl() ),
364 i18n(
"Error While Saving") );
370 view()->scrollBy( 0, -view()->visibleHeight() );
375 view()->scrollBy( 0, view()->visibleHeight() );
378 void ChatMessagePart::slotOpenURLRequest(
const KUrl &url,
const KParts::OpenUrlArguments &,
const KParts::BrowserArguments &)
380 kDebug(14000) <<
"url=" << url.url();
383 Kopete::Contact *contact = d->manager->account()->contacts().value( url.host() );
389 KRun *runner =
new KRun( url, 0, 0,
false );
390 runner->setRunExecutables(
false );
395 void ChatMessagePart::slotFileTransferIncomingDone(
unsigned int id )
398 while ( it != d->allMessages.begin() )
401 if ( (*it).id() == id )
403 (*it).setFileTransferDisabled(
true );
404 disableFileTransferButtons(
id );
410 void ChatMessagePart::readOverrides()
412 d->fmtOverride = Kopete::AppearanceSettings::self()->chatFmtOverride();
428 if (d->currentChatStyle)
429 disconnect( d->currentChatStyle, SIGNAL(destroyed(
QObject*)),
this, SLOT(clearStyle()) );
431 d->currentChatStyle = style;
432 if (d->currentChatStyle)
433 connect( d->currentChatStyle, SIGNAL(destroyed(
QObject*)),
this, SLOT(clearStyle()) );
440 void ChatMessagePart::clearStyle()
447 DOM::HTMLElement variantNode = document().getElementById(
QString(
"mainStyle") );
448 if( !variantNode.isNull() )
449 variantNode.setInnerText(
QString(
"@import url(\"%1\");").arg( adjustStyleVariantForChatSession( variantPath) ) );
455 while ( it != d->allMessages.begin() )
458 if ( (*it).id() == messageId )
460 (*it).setState( state );
461 changeMessageStateElement( messageId, state );
467 void ChatMessagePart::slotAppearanceChanged()
476 if ( !d->currentChatStyle )
479 if ( !message.classes().contains(
"history") )
480 message.setFormattingOverride( d->fmtOverride );
482 #ifdef STYLE_TIMETEST
487 bool isConsecutiveMessage =
false;
488 int bufferLen = Kopete::BehaviorSettings::self()->chatWindowBufferViewSize();
492 DOM::HTMLElement chatNode = htmlDocument().getElementById(
"Chat" );
494 if( chatNode.isNull() )
496 kDebug(14000) <<
"WARNING: Chat Node was null !";
506 isConsecutiveMessage = (message.direction() == d->latestDirection && !d->latestContact.isNull()
507 && d->latestContact == message.from() && message.type() == d->latestType
508 && message.type() != Kopete::Message::TypeFileTransferRequest );
510 if(message.timestamp().isValid()){
511 uint next = message.timestamp().toTime_t();
513 isConsecutiveMessage =
false;
515 d->latestTime = next;
520 if(message.type() == Kopete::Message::TypeAction)
523 if( d->currentChatStyle->hasActionTemplate() )
525 switch(message.direction())
527 case Kopete::Message::Inbound:
528 formattedMessageHtml = d->currentChatStyle->getActionIncomingHtml();
530 case Kopete::Message::Outbound:
531 formattedMessageHtml = d->currentChatStyle->getActionOutgoingHtml();
540 formattedMessageHtml = d->currentChatStyle->getStatusHtml();
543 else if(message.type() == Kopete::Message::TypeFileTransferRequest)
545 formattedMessageHtml = d->currentChatStyle->getFileTransferIncomingHtml();
547 else if(message.type() == Kopete::Message::TypeVoiceClipRequest)
549 formattedMessageHtml = d->currentChatStyle->getVoiceClipIncomingHtml();
553 switch(message.direction())
555 case Kopete::Message::Inbound:
557 if(isConsecutiveMessage)
559 formattedMessageHtml = d->currentChatStyle->getNextIncomingHtml();
563 formattedMessageHtml = d->currentChatStyle->getIncomingHtml();
567 case Kopete::Message::Outbound:
569 if(isConsecutiveMessage)
571 formattedMessageHtml = d->currentChatStyle->getNextOutgoingHtml();
575 formattedMessageHtml = d->currentChatStyle->getOutgoingHtml();
579 case Kopete::Message::Internal:
581 formattedMessageHtml = d->currentChatStyle->getStatusHtml();
587 formattedMessageHtml = formatStyleKeywords( formattedMessageHtml, message );
591 DOM::HTMLElement newMessageNode = document().createElement(
QString(
"span") );
592 newMessageNode.setInnerHTML( formattedMessageHtml );
595 DOM::HTMLElement insertNode = document().getElementById(
QString(
"insert") );
597 if( isConsecutiveMessage && !insertNode.isNull() )
600 insertNode.parentNode().replaceChild(newMessageNode, insertNode);
605 if( !insertNode.isNull() )
606 insertNode.parentNode().removeChild(insertNode);
608 chatNode.appendChild(newMessageNode);
611 if ( message.type() == Kopete::Message::TypeNormal )
613 if ( message.direction() == Kopete::Message::Outbound )
614 changeMessageStateElement( message.id(), message.state() );
616 else if ( message.type() == Kopete::Message::TypeFileTransferRequest )
618 if ( message.fileTransferDisabled() )
619 disableFileTransferButtons( message.id() );
621 addFileTransferButtonsEventListener( message.id() );
623 else if ( message.type() == Kopete::Message::TypeVoiceClipRequest )
625 addVoiceClipsButtonsEventListener( message.id() );
631 d->latestDirection = message.direction();
632 d->latestType = message.type();
633 d->latestContact =
const_cast<Kopete::Contact*
>(message.from());
637 d->allMessages.append(message);
639 while ( bufferLen>0 && d->allMessages.count() >= bufferLen )
641 d->allMessages.pop_front();
647 chatNode.removeChild( chatNode.firstChild() );
651 if ( !d->scrollPressed )
654 #ifdef STYLE_TIMETEST
659 void ChatMessagePart::slotRefreshView()
664 DOM::HTMLElement kopeteNode = document().getElementById(
QString(
"KopeteStyle") );
665 if( !kopeteNode.isNull() )
666 kopeteNode.setInnerText( styleHTML() );
668 DOM::HTMLBodyElement bodyElement = htmlDocument().body();
669 bodyElement.setBgColor( Kopete::AppearanceSettings::self()->chatBackgroundColor().name() );
674 if ( !d->scrollPressed )
678 const QString ChatMessagePart::styleHTML()
const
680 Kopete::AppearanceSettings *settings = Kopete::AppearanceSettings::self();
683 "body{background-color:%1;font-family:%2;font-size:%3pt;color:%4}"
684 "td{font-family:%5;font-size:%6pt;color:%7}"
685 "input{font-family:%8;font-size:%9pt;color:%10}"
686 "a{color:%11}a.visited{color:%12}"
687 "a.KopeteDisplayName{text-decoration:none;color:inherit;}"
688 "a.KopeteDisplayName:hover{text-decoration:underline;color:inherit}"
689 ".KopeteLink{cursor:pointer;}.KopeteLink:hover{text-decoration:underline}"
690 ".KopeteMessageBody > p:first-child{margin:0;padding:0;display:inline;}" )
691 .
arg( settings->chatBackgroundColor().name() )
692 .arg( d->chatFont.family() )
693 .arg( d->chatFont.pointSize() )
694 .arg( settings->chatTextColor().name() )
695 .arg( d->chatFont.family() )
696 .arg( d->chatFont.pointSize() )
697 .arg( settings->chatTextColor().name() )
698 .arg( d->chatFont.family() )
699 .arg( d->chatFont.pointSize() )
700 .arg( settings->chatTextColor().name() )
701 .arg( settings->chatLinkColor().name() )
702 .arg( settings->chatLinkColor().name() );
713 d->latestContact = 0;
717 for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
719 if ( (*it).type() == Kopete::Message::TypeFileTransferRequest && !(*it).fileTransferDisabled() )
721 Kopete::TransferManager::transferManager()->cancelIncomingTransfer( (*it).id() );
726 d->allMessages.clear();
729 Kopete::Contact *ChatMessagePart::contactFromNode(
const DOM::Node &n )
const
737 while ( !node.isNull() && ( node.nodeType() == DOM::Node::TEXT_NODE || ((DOM::HTMLElement)node).className() !=
"KopeteDisplayName" ) )
738 node = node.parentNode();
740 DOM::HTMLElement element = node;
741 if ( element.className() !=
"KopeteDisplayName" )
744 m = d->manager->members();
745 if ( element.hasAttribute(
"contactid" ) )
747 QString contactId = element.getAttribute(
"contactid" ).string();
748 for (
int i =0; i != m.
size(); ++i )
749 if ( m.
at(i)->contactId() == contactId )
755 foreach ( Kopete::Contact *contact, m )
758 if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
759 contactNick = contact->metaContact()->displayName();
761 contactNick = contact->displayName();
763 if ( contactNick == nick )
771 void ChatMessagePart::slotRightClick(
const QString &,
const QPoint &point )
774 DOM::Node activeNode = nodeUnderMouse();
775 while ( !activeNode.isNull() && activeNode.nodeType() != DOM::Node::ELEMENT_NODE )
776 activeNode = activeNode.parentNode();
779 d->activeElement = activeNode;
780 if ( d->activeElement.isNull() )
783 KMenu *chatWindowPopup = 0L;
785 if ( Kopete::Contact *contact = contactFromNode( d->activeElement ) )
787 chatWindowPopup = contact->popupMenu();
788 connect( chatWindowPopup, SIGNAL(aboutToHide()), chatWindowPopup , SLOT(deleteLater()) );
792 chatWindowPopup =
new KMenu();
795 if ( d->activeElement.className() ==
QLatin1String(
"KopeteDisplayName") )
797 action = chatWindowPopup->addAction( i18n(
"User Has Left" ) );
799 chatWindowPopup->addSeparator();
801 else if ( d->activeElement.tagName().lower() ==
QLatin1String(
"a" ) )
803 chatWindowPopup->addAction( d->copyURLAction );
804 chatWindowPopup->addSeparator();
807 d->copyAction->setEnabled( hasSelection() );
808 chatWindowPopup->addAction( d->copyAction );
809 chatWindowPopup->addAction( d->saveAction );
810 chatWindowPopup->addAction( d->printAction );
811 chatWindowPopup->addSeparator();
812 chatWindowPopup->addAction( d->closeAction );
814 connect( chatWindowPopup, SIGNAL(aboutToHide()), chatWindowPopup, SLOT(deleteLater()) );
815 chatWindowPopup->popup( point );
821 chatWindowPopup->popup( point );
824 QString ChatMessagePart::textUnderMouse()
826 DOM::Node activeNode = nodeUnderMouse();
827 if( activeNode.nodeType() != DOM::Node::TEXT_NODE )
830 DOM::Text textNode = activeNode;
834 int mouseLeft = view()->mapFromGlobal(
QCursor::pos() ).x(),
835 nodeLeft = activeNode.getRect().x(),
841 while( cPos < dataLen && nodeLeft < mouseLeft )
843 QChar c = data[cPos++];
849 nodeLeft += metrics.width(c);
854 QChar c = data[cPos++];
855 while( cPos < dataLen && !c.
isSpace() )
865 void ChatMessagePart::slotCopyURL()
867 DOM::HTMLAnchorElement a = d->activeElement;
875 void ChatMessagePart::slotScrollView()
878 d->scrollToEndDelayed =
true;
880 view()->scrollBy( 0, view()->contentsHeight() );
883 void ChatMessagePart::slotRenderingFinished()
885 if ( d->scrollToEndDelayed )
887 d->scrollToEndDelayed =
false;
888 if ( !d->scrollPressed )
889 view()->scrollBy( 0, view()->contentsHeight() );
901 QString htmltext = selectedTextAsHTML();
913 #ifndef QT_NO_MIMECLIPBOARD
943 KHTMLPart::khtmlDrawContentsEvent(event);
947 void ChatMessagePart::slotCloseView(
bool force )
949 if (d->manager && d->manager->view())
950 d->manager->view()->closeView( force );
953 void ChatMessagePart::emitTooltipEvent(
const QString &textUnderMouse,
QString &toolTip )
959 QString ChatMessagePart::formatStyleKeywords(
const QString &sourceHTML,
const Kopete::Message &_message )
961 if ( !d->currentChatStyle )
964 Kopete::Message message=_message;
965 QString resultHTML = sourceHTML;
966 QString nick, contactId, service, protocolIcon, nickLink;
970 nick =
formatName(message.from(), Qt::RichText);
971 contactId = message.from()->contactId();
980 if(message.from()->protocol() && !message.from()->protocol()->displayName().isNull())
982 service = message.from()->protocol()->displayName();
983 iconName = message.from()->protocol()->pluginIcon();
986 protocolIcon = KIconLoader::global()->iconPath( iconName, KIconLoader::Small );
988 nickLink=
QString(
"<a href=\"kopetemessage://%1/?protocolId=%2&accountId=%3\" class=\"KopeteDisplayName\">")
989 .
arg(
Qt::escape(message.from()->contactId()).replace(
'"',
"""),
990 Qt::escape(message.from()->protocol()->pluginId()).replace(
'"',
"""),
991 Qt::escape(message.from()->account()->accountId() ).replace(
'"',
"""));
1002 if ( Kopete::BehaviorSettings::showDates() && message.timestamp().date() !=
QDate::currentDate() )
1003 resultHTML.
replace(
QLatin1String(
"%time%"), KGlobal::locale()->formatDateTime(message.timestamp(), KLocale::ShortDate,
true) );
1005 resultHTML.
replace(
QLatin1String(
"%time%"), KGlobal::locale()->formatTime(message.timestamp().time(),
true) );
1014 QRegExp timeRegExp(
"%time\\{([^}]*)\\}%");
1016 while( (pos=timeRegExp.indexIn(resultHTML , pos) ) != -1 )
1018 QString timeKeyword = formatTime( timeRegExp.cap(1), message.timestamp() );
1019 resultHTML.
replace( pos , timeRegExp.cap(0).length() , timeKeyword );
1027 if( message.importance() == Kopete::Message::Highlight && Kopete::BehaviorSettings::self()->highlightEnabled() )
1029 bgColor = Kopete::AppearanceSettings::self()->highlightBackgroundColor().name();
1032 QRegExp textBackgroundRegExp(
"%textbackgroundcolor\\{([^}]*)\\}%");
1034 while( (textPos=textBackgroundRegExp.indexIn(resultHTML, textPos) ) != -1 )
1036 resultHTML.
replace( textPos , textBackgroundRegExp.cap(0).length() , bgColor );
1040 if( message.from() )
1042 QString photoPath = photoForContact( message.from() );
1045 if(message.direction() == Kopete::Message::Inbound)
1046 photoPath = d->currentChatStyle->getStyleBaseHref() +
QLatin1String(
"Incoming/buddy_icon.png");
1047 else if(message.direction() == Kopete::Message::Outbound)
1048 photoPath = d->currentChatStyle->getStyleBaseHref() +
QLatin1String(
"Outgoing/buddy_icon.png");
1055 if( message.type() == Kopete::Message::TypeAction && !d->currentChatStyle->hasActionTemplate() )
1057 kDebug(14000) <<
"Map Action message to Status template. ";
1060 QString newBody = boldNick + message.parsedBody();
1061 message.setHtmlBody(newBody );
1065 resultHTML.
replace(
QLatin1String(
"%messageDirection%"), message.isRightToLeft() ?
"rtl" :
"ltr" );
1069 static const char*
const nameColors[] =
1071 "red",
"blue" ,
"gray",
"magenta",
"violet",
"#808000",
"yellowgreen",
1072 "darkred",
"darkgreen",
"darksalmon",
"darkcyan",
"#B07D2B",
1073 "mediumpurple",
"peru",
"olivedrab",
"#B01712",
"darkorange",
"slateblue",
1074 "slategray",
"goldenrod",
"orangered",
"tomato",
"#1E90FF",
"steelblue",
1075 "deeppink",
"saddlebrown",
"coral",
"royalblue"
1078 static const int nameColorsLen =
sizeof(nameColors) /
sizeof(nameColors[0]) - 1;
1081 for(
int f = 0; f < contactId.length(); ++f )
1082 hash += contactId[f].unicode() * f;
1083 const QString colorName = nameColors[ hash % nameColorsLen ];
1086 QRegExp senderColorRegExp(
"%senderColor(?:\\{([^}]*)\\})?%");
1088 while( (textPos=senderColorRegExp.indexIn(resultHTML, textPos) ) != -1 )
1092 if(senderColorRegExp.numCaptures()>=1)
1094 light=senderColorRegExp.cap(1).toUInt(&doLight);
1098 if ( doLight && lightColorName.
isNull() )
1101 resultHTML.
replace( textPos , senderColorRegExp.cap(0).length(),
1102 doLight ? lightColorName : colorName );
1105 if ( message.type() == Kopete::Message::TypeFileTransferRequest )
1108 if ( !message.filePreview().isNull() )
1111 QBuffer tempBuffer( &tempArray );
1112 tempBuffer.open( QIODevice::WriteOnly );
1113 if( message.filePreview().save( &tempBuffer,
"PNG" ) )
1114 fileIcon =
QString(
"data:image/png;base64," ) + tempArray.
toBase64();
1119 QString iconName = KMimeType::iconNameForUrl( message.fileName() );
1120 fileIcon = KIconLoader::global()->iconPath( iconName, -KIconLoader::SizeMedium );
1124 resultHTML.
replace(
QLatin1String(
"%fileSize%"), KGlobal::locale()->formatByteSize( message.fileSize() ).replace(
'"',
""") );
1132 if ( message.type() == Kopete::Message::TypeVoiceClipRequest )
1136 QString iconName = KMimeType::iconNameForUrl( message.fileName() );
1137 fileIcon = KIconLoader::global()->iconPath( iconName, -KIconLoader::SizeMedium );
1145 if ( message.type() == Kopete::Message::TypeNormal && message.direction() == Kopete::Message::Outbound )
1157 QString ChatMessagePart::formatStyleKeywords(
const QString &sourceHTML )
1159 QString resultHTML = sourceHTML;
1162 if( !d->manager->members().isEmpty() && d->manager->myself() )
1164 QString sourceName, destinationName;
1166 Kopete::Contact *remoteContact = d->manager->members().first();
1169 sourceName = d->manager->myself()->displayName();
1170 if( remoteContact->metaContact() )
1171 destinationName = remoteContact->metaContact()->displayName();
1173 destinationName = remoteContact->displayName();
1185 QRegExp timeRegExp(
"%timeOpened\\{([^}]*)\\}%");
1187 while( (pos=timeRegExp.indexIn(resultHTML, pos) ) != -1 )
1190 resultHTML.
replace( pos , timeRegExp.cap(0).length() , timeKeyword );
1193 QString photoIncoming = photoForContact( remoteContact );
1194 QString photoOutgoing = photoForContact( d->manager->myself() );
1197 photoIncoming = d->currentChatStyle->getStyleBaseHref() +
QLatin1String(
"Incoming/buddy_icon.png");
1202 photoOutgoing = d->currentChatStyle->getStyleBaseHref() +
QLatin1String(
"Outgoing/buddy_icon.png");
1216 QString timeFormat = _timeFormat;
1221 const QString timeFormat = _timeFormat;
1224 time_t timeT = dateTime.
toTime_t();
1226 struct tm* loctime = localtime (&timeT);
1227 strftime (buffer, 256, timeFormat.
toAscii(), loctime);
1234 QString formattedName = sourceName;
1237 if( Kopete::BehaviorSettings::self()->truncateContactName() )
1239 formattedName = KStringHandler::csqueeze( sourceName, Kopete::BehaviorSettings::self()->truncateContactNameLength() );
1242 if ( format == Qt::RichText )
1244 formattedName = Kopete::Message::escape(formattedName);
1247 return formattedName;
1259 if ( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
1261 return formatName( contact->metaContact()->displayName(), format );
1266 return formatName( contact->displayName(), format );
1270 QString ChatMessagePart::formatMessageBody(
const Kopete::Message &message)
1272 QString formattedBody(
"<span ");
1274 formattedBody += message.getHtmlStyleAttribute();
1277 classes+=message.classes();
1280 formattedBody +=
QString(
"class=\"%1\">%2</span>")
1281 .
arg(classes.join(
" "), message.parsedBody());
1283 return formattedBody;
1286 void ChatMessagePart::slotUpdateHeaderDisplayName()
1289 DOM::HTMLElement kopeteChatNameNode = document().getElementById(
QString(
"KopeteHeaderChatNameInternal") );
1290 if( !kopeteChatNameNode.isNull() )
1291 kopeteChatNameNode.setInnerText(
formatName(d->manager->displayName(), Qt::RichText) );
1294 void ChatMessagePart::slotUpdateHeaderPhoto()
1301 void ChatMessagePart::changeStyle()
1303 #ifdef STYLE_TIMETEST
1307 d->latestContact = 0;
1314 for(it = d->allMessages.constBegin(); it != itEnd; ++it)
1316 Kopete::Message tempMessage = *it;
1319 kDebug(14000) <<
"Finish changing style.";
1320 #ifdef STYLE_TIMETEST
1325 void ChatMessagePart::writeTemplate()
1329 #ifdef STYLE_TIMETEST
1345 if ( d->currentChatStyle )
1348 xhtmlBase +=
QString(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1349 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1350 "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1351 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1353 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\n\" />\n"
1354 "<base href=\"%1\">\n"
1355 "<style id=\"KopeteStyle\" type=\"text/css\" media=\"screen,print\">\n"
1358 "<style id=\"baseStyle\" type=\"text/css\" media=\"screen,print\">\n"
1359 " @import url(\"main.css\");\n"
1360 " *{ word-wrap:break-word; }\n"
1362 "<style id=\"mainStyle\" type=\"text/css\" media=\"screen,print\">\n"
1363 " @import url(\"%4\");\n"
1368 "<div id=\"Chat\">\n</div>\n"
1372 ).
arg( d->currentChatStyle->getStyleBaseHref() )
1373 .arg( formatStyleKeywords(d->currentChatStyle->getHeaderHtml()) )
1374 .
arg( formatStyleKeywords(d->currentChatStyle->getFooterHtml()) )
1376 .
arg( styleHTML() );
1380 xhtmlBase = i18n(
"Chat style could not be found, or is invalid." );
1384 #ifdef STYLE_TIMETEST
1389 void ChatMessagePart::resendMessage( uint messageId )
1392 for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
1394 if ( (*it).id() == messageId )
1396 if ( !( d->manager->protocol()->capabilities() & Kopete::Protocol::CanSendOffline ) )
1398 bool reachableContactFound =
false;
1399 foreach ( Kopete::Contact* c, (*it).to() )
1401 if ( c->isReachable() )
1403 reachableContactFound =
true;
1409 if ( !reachableContactFound )
1413 Kopete::Message msg( (*it).from(), (*it).to() );
1414 msg.setDirection( Kopete::Message::Outbound );
1415 msg.setBody( (*it).body() );
1418 msg.setForegroundColor( (*it).foregroundColor() );
1419 msg.setFont( (*it).font() );
1420 d->manager->sendMessage( msg );
1426 void ChatMessagePart::saveVoiceClip( uint messageId )
1429 for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
1431 if ( (*it).id() == messageId )
1433 if(!(*it).fileName().isEmpty())
1435 QString temporaryExtension =
"*.wav";
1437 i18n(
"Save File as"),
QString(), i18n(
"Wav file (*.wav)"), &temporaryExtension);
1446 void ChatMessagePart::playVoiceClip( uint messageId )
1449 for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
1451 if ( (*it).id() == messageId )
1453 if(!(*it).fileName().isEmpty())
1457 connect(media, SIGNAL(finished()), media, SLOT(deleteLater()));
1458 connect(media, SIGNAL(finished()), audioOutput, SLOT(deleteLater()));
1468 QString ChatMessagePart::adjustStyleVariantForChatSession(
const QString & styleVariant )
const
1470 if ( d->currentChatStyle && d->manager->form() == Kopete::ChatSession::Chatroom
1472 return d->currentChatStyle->compact( styleVariant );
1474 return styleVariant;
1477 QString ChatMessagePart::photoForContact(
const Kopete::Contact *contact )
const
1483 if( contact->metaContact() == Kopete::ContactList::self()->myself() )
1485 photo = contact->property(Kopete::Global::Properties::self()->photo().key()).value().toString();
1487 else if( !contact->metaContact()->picture().isNull() )
1489 photo =
QString(
"data:image/png;base64," ) + contact->metaContact()->picture().base64();
1495 void ChatMessagePart::addFileTransferButtonsEventListener(
unsigned int id )
1498 registerClickEventListener( document().getElementById( elementId ) );
1501 registerClickEventListener( document().getElementById( elementId ) );
1504 registerClickEventListener( document().getElementById( elementId ) );
1507 void ChatMessagePart::addVoiceClipsButtonsEventListener(
unsigned int id )
1510 registerClickEventListener( document().getElementById( elementId ) );
1513 registerClickEventListener( document().getElementById( elementId ) );
1517 void ChatMessagePart::disableFileTransferButtons(
unsigned int id )
1520 DOM::HTMLInputElement element = document().getElementById( elementId );
1521 if ( !element.isNull() )
1522 element.setDisabled(
true );
1525 element = document().getElementById( elementId );
1526 if ( !element.isNull() )
1527 element.setDisabled(
true );
1530 element = document().getElementById( elementId );
1531 if ( !element.isNull() )
1532 element.setDisabled(
true );
1535 void ChatMessagePart::changeMessageStateElement( uint
id, Kopete::Message::MessageState state )
1537 if ( !d->currentChatStyle )
1541 DOM::HTMLElement element = document().getElementById( elementId );
1542 if ( element.isNull() )
1548 case Kopete::Message::StateUnknown:
1549 statusHTML = d->currentChatStyle->getOutgoingStateUnknownHtml();
1551 case Kopete::Message::StateSending:
1552 statusHTML = d->currentChatStyle->getOutgoingStateSendingHtml();
1554 case Kopete::Message::StateSent:
1555 statusHTML = d->currentChatStyle->getOutgoingStateSentHtml();
1557 case Kopete::Message::StateError:
1558 statusHTML = d->currentChatStyle->getOutgoingStateErrorHtml();
1564 element.setInnerHTML( statusHTML );
1566 registerClickEventListener( document().getElementById( resendId ) );
1569 void ChatMessagePart::registerClickEventListener( DOM::HTMLElement element )
1571 if ( element.isNull() )
1574 if ( !d->htmlEventListener )
1577 connect( d->htmlEventListener, SIGNAL(resendMessage(uint)),
this, SLOT(resendMessage(uint)) );
1578 connect( d->htmlEventListener, SIGNAL(playVoiceClip(uint)),
this, SLOT(playVoiceClip(uint)) );
1579 connect( d->htmlEventListener, SIGNAL(saveVoiceClip(uint)),
this, SLOT(saveVoiceClip(uint)) );
1581 element.addEventListener(
"click", d->htmlEventListener,
false );
1584 void ChatMessagePart::readChatFont()
1586 Kopete::AppearanceSettings *settings = Kopete::AppearanceSettings::self();
1588 d->chatFont = KGlobalSettings::generalFont();
1589 if ( settings->chatFontSelection() == 1 )
1590 d->chatFont = settings->chatFont();
1595 DOM::HTMLInputElement element =
event.currentTarget();
1596 if ( !element.isNull() )
1599 unsigned int messageId = element.id().string().
mid(4).
toUInt();
1602 Kopete::TransferManager::transferManager()->saveIncomingTransfer( messageId );
1604 Kopete::TransferManager::transferManager()->saveIncomingTransfer( messageId );
1606 Kopete::TransferManager::transferManager()->cancelIncomingTransfer( messageId );
1616 #include "chatmessagepart.moc"
void setCodec(QTextCodec *codec)
void setStyleVariant(const QString &variantPath)
Change the current variant for the current style.
void clear()
Clear the message window.
This class represent a single chat window style.
void truncate(int position)
void appendMessage(Kopete::Message &message, bool restoring=false)
Appends a message to the messave view.
static ChatWindowStyleManager * self()
Singleton access to this class.
QColor light(int factor) const
static bool useCompact()
Get Use a compact variant of the chat style for chatrooms.
void keepScrolledDown()
Immediately scroll the chat to the bottom, as long as it has not been intentionally scrolled away fro...
ChatMessagePart(Kopete::ChatSession *manager, QWidget *parent)
Create a new ChatMessage Part.
const T & at(int i) const
virtual Kopete::ChatSession * manager(Kopete::Contact::CanCreateFlags)
static KopeteChatWindowSettings * self()
QString formatName(const Kopete::Contact *contact, Qt::TextFormat format) const
Format contact's nickname/displayname according to preferences.
int msecsTo(const QTime &t) const
void setStyle(const QString &styleName)
Change the current style.
void pageDown()
Scroll the view down a page.
virtual void khtmlDrawContentsEvent(khtml::DrawContentsEvent *)
void playVoiceClip(uint messageId)
void save()
Save the contents of the chat to a file.
bool copy(const QString &newName)
void resendMessage(uint messageId)
void pageUp()
Scroll the view up a page.
void setText(const QString &text)
void setMimeData(QMimeData *src, Mode mode)
Path createPath(MediaNode *source, MediaNode *sink)
void saveVoiceClip(uint messageId)
void tooltipEvent(const QString &textUnderMouse, QString &toolTip)
Emits before the tooltip is about to show.
void contextMenuEvent(const QString &textUnderMouse, KMenu *popupMenu)
Emits before the context menu is about to show.
QString & replace(int position, int n, QChar after)
QDateTime currentDateTime()
QString mid(int position, int n) const
void print()
Print out the contents of the chatwindow.
virtual void handleEvent(DOM::Event &event)
QString escape(const QString &plain)
QTextCodec * codecForName(const QByteArray &name)
void messageStateChanged(uint messageId, Kopete::Message::MessageState state)
ChatWindowStyle * getValidStyleFromPool(const QString &styleName)
Get a instance of a ChatWindowStyle from the pool.
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
static const uint ConsecutiveMessageTimeout
QString left(int n) const
KDE_DEPRECATED void slotToggleGraphicOverride(bool enable)
void setText(const QString &text, Mode mode)
QByteArray toBase64() const
void setHtml(const QString &html)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
void copy(bool justselection=false)
Initiates a copy action If there is text selected in the HTML view, that text is copied Otherwise if ...
QByteArray toAscii() const
uint toUInt(bool *ok, int base) const