[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kopete-devel
Subject:    [kopete-devel] [PATCH] Incoming file transfer in chat window v2
From:       "Roman Jarosz" <roman.jarosz () gmail ! com>
Date:       2008-08-24 19:34:47
Message-ID: op.ugeon9zcrj95b0 () localhost
[Download RAW message or body]

Hi,

here's next version of my patch.

Changes since last version
* disabled javascript as many people don't like it to be enabled.
* added unique id into Message class which is also used for file transfer id.
* file transfer information in Messages are stored in separate objects
  so we don't waste memory
* if protocol doesn't specify icon we show icon for current file extension.
* use Kopete default font for buttons
* some cleanups and fixes

http://kedge.wz.cz/kopete/kopeteft4.png

If we want to support Adium file transfer chat styles we have to use javascript
so i'm starting a pool :)
1. Should we add option into Kopete config dialog (as Michal suggested) to
   allow users to turn on javascript and use Adium ft styles
2. Do we want to use only non javascript ft styles which means
   <form action="..."><input type="submit"></form> or <a href="..."></a>.

Anyway is it ok to commit?

Regards,
Roman
["ftchatrequest3.diff" (ftchatrequest3.diff)]

Index: kopete/chatwindow/kopetechatwindowstyle.cpp
===================================================================
--- kopete/chatwindow/kopetechatwindowstyle.cpp	(revision 851800)
+++ kopete/chatwindow/kopetechatwindowstyle.cpp	(working copy)
@@ -27,6 +27,7 @@
 
 // KDE includes
 #include <kdebug.h>
+#include <klocale.h>
 #include <kstandarddirs.h>
 
 class ChatWindowStyle::Private
@@ -46,6 +47,7 @@
 	QString statusHtml;
 	QString actionIncomingHtml;
 	QString actionOutgoingHtml;
+	QString fileTransferIncomingHtml;
 	QHash<QString, bool> compactVariants;
 };
 
@@ -153,6 +155,11 @@
 	return d->actionOutgoingHtml;
 }
 
+QString ChatWindowStyle::getFileTransferIncomingHtml() const
+{
+	return d->fileTransferIncomingHtml;
+}
+
 bool ChatWindowStyle::hasActionTemplate() const
 {
 	return ( !d->actionIncomingHtml.isEmpty() && !d->actionOutgoingHtml.isEmpty() );
@@ -199,6 +206,7 @@
 	QString statusFile = d->baseHref + QString("Status.html");
 	QString actionIncomingFile = d->baseHref + QString("Incoming/Action.html");
 	QString actionOutgoingFile = d->baseHref + QString("Outgoing/Action.html");
+	QString fileTransferIncomingFile = d->baseHref + \
QString("Incoming/FileTransferRequest.html");  
 	QFile fileAccess;
 	// First load header file.
@@ -301,6 +309,41 @@
 		kDebug(14000) << "ActionOutgoing HTML: " << d->actionOutgoingHtml;
 		fileAccess.close();
 	}
+	// Load FileTransfer Incoming file
+	if( QFile::exists(fileTransferIncomingFile) )
+	{
+		fileAccess.setFileName(fileTransferIncomingFile);
+		fileAccess.open(QIODevice::ReadOnly);
+		QTextStream headerStream(&fileAccess);
+		headerStream.setCodec(QTextCodec::codecForName("UTF-8"));
+		d->fileTransferIncomingHtml = headerStream.readAll();
+		kDebug(14000) << "fileTransferIncoming HTML: " << d->fileTransferIncomingHtml;
+		fileAccess.close();
+	}
+	
+	if ( d->fileTransferIncomingHtml.isEmpty() )
+	{	// Create default html
+		d->fileTransferIncomingHtml = d->incomingHtml;
+		QString message = QString( "%message%\n"
+		                           "<div>\n"
+		                           " <div style=\"width:37px; float:left;\">\n"
+		                           "  <img src=\"%fileIconPath%\" style=\"width:32px; \
height:32px; vertical-align:middle;\" />\n" +		                           " </div>\n"
+		                           " <div>\n"
+		                           "  <span><b>%fileName%</b> (%fileSize%)</span><br>\n"
+		                           "  <span>\n"
+		                           "  <form action=\"%saveFileAsHandler%\" \
style=\"float:left\" >\n" +		                           "   <input \
id=\"%saveFileAsHandlerId%\" type=\"submit\" value=\"%1\">\n" +		                     \
"  </form>\n" +		                           "  <form \
action=\"%cancelRequestHandler%\" style=\"float:left\" >\n" +		                       \
"   <input id=\"%cancelRequestHandlerId%\" type=\"submit\" value=\"%2\">\n" +		       \
"  </form>\n" +		                           "  </span>\n"
+		                           " </div>\n"
+		                           "</div>" )
+		                           .arg( i18n( "Download" ), i18n( "Cancel" ) );
+		d->fileTransferIncomingHtml.replace( QLatin1String("%message%"), message );
+	}
 }
 
 void ChatWindowStyle::reload()
Index: kopete/chatwindow/chatmessagepart.cpp
===================================================================
--- kopete/chatwindow/chatmessagepart.cpp	(revision 851800)
+++ kopete/chatwindow/chatmessagepart.cpp	(working copy)
@@ -35,6 +35,7 @@
 #include <QtCore/QTextCodec>
 #include <QtCore/QTextStream>
 #include <QtCore/QTimer>
+#include <QtCore/QBuffer>
 #include <QtGui/QClipboard>
 #include <QtGui/QCursor>
 #include <QtGui/QPixmap>
@@ -50,6 +51,7 @@
 #include <dom/html_base.h>
 #include <dom/html_document.h>
 #include <dom/html_inline.h>
+#include <dom/html_form.h>
 
 
 // KDE includes
@@ -86,6 +88,7 @@
 #include "kopeteappearancesettings.h"
 #include "kopetebehaviorsettings.h"
 #include "kopetechatwindowsettings.h"
+#include "kopetetransfermanager.h"
 
 #include "kopetechatwindowstyle.h"
 #include "kopetechatwindowstylemanager.h"
@@ -205,7 +208,6 @@
 	setJavaEnabled( false );
 	setPluginsEnabled( false );
 	setMetaRefreshEnabled( false );
-	setOnlyLocalReferences( true );
 
 	// Write the template to KHTMLPart
 	writeTemplate();
@@ -234,6 +236,9 @@
 	connect( view()->verticalScrollBar(), SIGNAL(sliderMoved(int)),
 	         this, SLOT(slotScrollingTo(int)) );
 
+	connect( Kopete::TransferManager::transferManager(), \
SIGNAL(askIncomingDone(unsigned int)), +	         this, \
SLOT(slotFileTransferIncomingDone(unsigned int)) ); +	
 	//initActions
 	d->copyAction = KStandardAction::copy( this, SLOT(copy()), actionCollection() );
 	d->saveAction = KStandardAction::saveAs( this, SLOT(save()), actionCollection() );
@@ -250,6 +255,17 @@
 ChatMessagePart::~ChatMessagePart()
 {
 	kDebug(14000) ;
+
+	// Cancel all pending file transfer requests
+	QList<Kopete::Message>::ConstIterator it, itEnd = d->allMessages.constEnd();
+	for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
+	{
+		if ( (*it).type() == Kopete::Message::TypeFileTransferRequest && \
!(*it).fileTransferDisabled() ) +		{
+			Kopete::TransferManager::transferManager()->cancelIncomingTransfer( (*it).id() );
+		}
+	}
+
 	//delete d->tt;
 	delete d;
 }
@@ -330,14 +346,41 @@
 		if ( contact )
 			contact->execute();
 	}
-	else
+	else if ( url.protocol() == QLatin1String("kopeteftsave") )
 	{
-		KRun *runner = new KRun( url, 0, false ); // false = non-local files
+		Kopete::TransferManager::transferManager()->saveIncomingTransfer( \
url.host().toInt() ); +	}
+	else if ( url.protocol() == QLatin1String("kopeteftsaveas") )
+	{
+		Kopete::TransferManager::transferManager()->saveIncomingTransfer( \
url.host().toInt() ); +	}
+	else if ( url.protocol() == QLatin1String("kopeteftcancel") )
+	{
+		Kopete::TransferManager::transferManager()->cancelIncomingTransfer( \
url.host().toInt() ); +	}
+	else if ( url.protocol() != QLatin1String("file") )
+	{
+		KRun *runner = new KRun( url, 0, 0, false ); // false = non-local files
 		runner->setRunExecutables( false ); //security
 		//KRun autodeletes itself by default when finished.
 	}
 }
 
+void ChatMessagePart::slotFileTransferIncomingDone( unsigned int id )
+{
+	QList<Kopete::Message>::Iterator it = d->allMessages.end();
+	while ( it != d->allMessages.begin() )
+	{
+		--it;
+		if ( (*it).id() == id )
+		{
+			(*it).setFileTransferDisabled( true );
+			disableFileTransferButtons( id );
+			break;
+		}
+	}
+}
+
 void ChatMessagePart::readOverrides()
 {
 	d->bgOverride = Kopete::AppearanceSettings::self()->chatBgOverride();
@@ -414,7 +457,9 @@
 	// Group only if the user want it.
 	if( KopeteChatWindowSettings::self()->groupConsecutiveMessages() )
 	{
-		isConsecutiveMessage = (message.direction() == d->latestDirection && \
!d->latestContact.isNull() && d->latestContact == message.from() && message.type() == \
d->latestType); +		isConsecutiveMessage = (message.direction() == d->latestDirection \
&& !d->latestContact.isNull() +		                        && d->latestContact == \
message.from() && message.type() == d->latestType +		                        && \
message.type() != Kopete::Message::TypeFileTransferRequest );  }
 
 	// Don't test it in the switch to don't break consecutive messages.
@@ -441,6 +486,10 @@
 			formattedMessageHtml = d->currentChatStyle->getStatusHtml();
 		}
 	}
+	else if(message.type() == Kopete::Message::TypeFileTransferRequest)
+	{
+		formattedMessageHtml = d->currentChatStyle->getFileTransferIncomingHtml();
+	}
 	else
 	{
 		switch(message.direction())
@@ -501,6 +550,9 @@
 		chatNode.appendChild(newMessageNode);
 	}
 
+	if ( message.type() == Kopete::Message::TypeFileTransferRequest && \
message.fileTransferDisabled() ) +		disableFileTransferButtons( message.id() );
+
 	// Keep the direction to see on next message
 	// if it's a consecutive message
 	// Keep also the from() contact.
@@ -555,7 +607,8 @@
 	QString style = QString(
 		"body{background-color:%1;font-family:%2;font-size:%3pt;color:%4}"
 		"td{font-family:%5;font-size:%6pt;color:%7}"
-		"a{color:%8}a.visited{color:%9}"
+		"input{font-family:%8;font-size:%9pt;color:%10}"
+		"a{color:%11}a.visited{color:%12}"
 		"a.KopeteDisplayName{text-decoration:none;color:inherit;}"
 		"a.KopeteDisplayName:hover{text-decoration:underline;color:inherit}"
 		".KopeteLink{cursor:pointer;}.KopeteLink:hover{text-decoration:underline}"
@@ -567,6 +620,9 @@
 		.arg( settings->chatFont().family() )
 		.arg( settings->chatFont().pointSize() )
 		.arg( settings->chatTextColor().name() )
+		.arg( settings->chatFont().family() )
+		.arg( settings->chatFont().pointSize() )
+		.arg( settings->chatTextColor().name() )
 		.arg( settings->chatLinkColor().name() )
 		.arg( settings->chatLinkColor().name() );
 
@@ -580,6 +636,17 @@
 
 	// Reset consecutive messages
 	d->latestContact = 0;
+
+	// Cancel all pending file transfer requests
+	QList<Kopete::Message>::ConstIterator it, itEnd = d->allMessages.constEnd();
+	for ( it = d->allMessages.constBegin(); it != itEnd; ++it )
+	{
+		if ( (*it).type() == Kopete::Message::TypeFileTransferRequest && \
!(*it).fileTransferDisabled() ) +		{
+			Kopete::TransferManager::transferManager()->cancelIncomingTransfer( (*it).id() );
+		}
+	}
+
 	// Remove all stored messages.
 	d->allMessages.clear();
 }
@@ -841,18 +908,18 @@
 
 
 	// Replace sender (contact nick)
-	resultHTML = resultHTML.replace( QLatin1String("%sender%"), nickLink+nick+"</a>" );
+	resultHTML.replace( QLatin1String("%sender%"), nickLink+nick+"</a>" );
 	// Replace time, by default display only time and display seconds(that was true \
means).  if ( Kopete::BehaviorSettings::showDates() )
-		resultHTML = resultHTML.replace( QLatin1String("%time%"), \
KGlobal::locale()->formatDateTime(message.timestamp(), KLocale::ShortDate, true) ); \
+		resultHTML.replace( QLatin1String("%time%"), \
KGlobal::locale()->formatDateTime(message.timestamp(), KLocale::ShortDate, true) );  \
                else
-		resultHTML = resultHTML.replace( QLatin1String("%time%"), \
KGlobal::locale()->formatTime(message.timestamp().time(), true) ); \
+		resultHTML.replace( QLatin1String("%time%"), \
KGlobal::locale()->formatTime(message.timestamp().time(), true) );  // Replace \
                %screenName% (contact ID)
-	resultHTML = resultHTML.replace( QLatin1String("%senderScreenName%"), \
nickLink+Qt::escape(contactId)+"</a>" ); +	resultHTML.replace( \
QLatin1String("%senderScreenName%"), nickLink+Qt::escape(contactId)+"</a>" );  // \
                Replace service name (protocol name)
-	resultHTML = resultHTML.replace( QLatin1String("%service%"), Qt::escape(service) );
+	resultHTML.replace( QLatin1String("%service%"), Qt::escape(service) );
 	// Replace protocolIcon (sender statusIcon)
-	resultHTML = resultHTML.replace( QLatin1String("%senderStatusIcon%"), \
Qt::escape(protocolIcon).replace('"',"&quot;") ); +	resultHTML.replace( \
QLatin1String("%senderStatusIcon%"), Qt::escape(protocolIcon).replace('"',"&quot;") \
);  
 	// Look for %time{X}%
 	QRegExp timeRegExp("%time\\{([^}]*)\\}%");
@@ -860,7 +927,7 @@
 	while( (pos=timeRegExp.indexIn(resultHTML , pos) ) != -1 )
 	{
 		QString timeKeyword = formatTime( timeRegExp.cap(1), message.timestamp() );
-		resultHTML = resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword );
+		resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword );
 	}
 
 	// Look for %textbackgroundcolor{X}%
@@ -877,7 +944,7 @@
 	int textPos=0;
 	while( (textPos=textBackgroundRegExp.indexIn(resultHTML, textPos) ) != -1 )
 	{
-		resultHTML = resultHTML.replace( textPos , textBackgroundRegExp.cap(0).length() , \
bgColor ); +		resultHTML.replace( textPos , textBackgroundRegExp.cap(0).length() , \
bgColor );  }
 
 	// Replace userIconPath
@@ -891,7 +958,7 @@
 			else if(message.direction() == Kopete::Message::Outbound)
 				photoPath = d->currentChatStyle->getStyleBaseHref() + \
QLatin1String("Outgoing/buddy_icon.png");  }
-		resultHTML = resultHTML.replace(QLatin1String("%userIconPath%"), photoPath);
+		resultHTML.replace(QLatin1String("%userIconPath%"), photoPath);
 	}
 
 	// Replace messages.
@@ -906,7 +973,7 @@
 	}
 
 	// Set message direction("rtl"(Right-To-Left) or "ltr"(Left-to-right))
-	resultHTML = resultHTML.replace( QLatin1String("%messageDirection%"), \
message.isRightToLeft() ? "rtl" : "ltr" ); +	resultHTML.replace( \
QLatin1String("%messageDirection%"), message.isRightToLeft() ? "rtl" : "ltr" );  
 	// These colors are used for coloring nicknames. I tried to use
 	// colors both visible on light and dark background.
@@ -942,12 +1009,42 @@
 		if ( doLight && lightColorName.isNull() )
 			lightColorName = QColor( colorName ).light( light ).name();
 
-		resultHTML = resultHTML.replace( textPos , senderColorRegExp.cap(0).length(),
+		resultHTML.replace( textPos , senderColorRegExp.cap(0).length(),
 			doLight ? lightColorName : colorName );
 	}
 
+	if ( message.type() == Kopete::Message::TypeFileTransferRequest )
+	{
+		QString fileIcon;
+		if ( !message.filePreview().isNull() )
+		{
+			QByteArray tempArray;
+			QBuffer tempBuffer( &tempArray );
+			tempBuffer.open( QIODevice::WriteOnly );
+			if( message.filePreview().save( &tempBuffer, "PNG" ) )
+				fileIcon = QString( "data:image/png;base64," ) + tempArray.toBase64();
+		}
+
+		if ( fileIcon.isEmpty() )
+		{
+			QString iconName = KMimeType::iconNameForUrl( message.fileName() );
+			fileIcon = KIconLoader::global()->iconPath( iconName, -KIconLoader::SizeMedium );
+		}
+
+		resultHTML.replace( QLatin1String("%fileName%"), Qt::escape( message.fileName() \
).replace('"',"&quot;") ); +		resultHTML.replace( QLatin1String("%fileSize%"), \
KGlobal::locale()->formatByteSize( message.fileSize() / 8 ).replace('"',"&quot;") ); \
+		resultHTML.replace( QLatin1String("%fileIconPath%"), fileIcon ); \
+		resultHTML.replace( QLatin1String("%saveFileHandler%"), \
QString("kopeteftsave://%1/").arg( message.id() ) ); +		resultHTML.replace( \
QLatin1String("%saveFileAsHandler%"), QString("kopeteftsaveas://%1/").arg( \
message.id() ) ); +		resultHTML.replace( QLatin1String("%cancelRequestHandler%"), \
QString("kopeteftcancel://%1/").arg( message.id() ) ); +		
+		resultHTML.replace( QLatin1String("%saveFileHandlerId%"), QString( "ftS%1" ).arg( \
message.id() ) ); +		resultHTML.replace( QLatin1String("%saveFileAsHandlerId%"), \
QString( "ftSA%1" ).arg( message.id() ) ); +		resultHTML.replace( \
QLatin1String("%cancelRequestHandlerId%"), QString( "ftC%1" ).arg( message.id() ) ); \
+	} +
 	// Replace message at the end, maybe someone could put a Adium keyword in his \
                message :P
-	resultHTML = resultHTML.replace( QLatin1String("%message%"), \
formatMessageBody(message) ); +	resultHTML.replace( QLatin1String("%message%"), \
formatMessageBody(message) );  
 	// TODO: %status
 //	resultHTML = addNickLinks( resultHTML );
@@ -974,13 +1071,13 @@
 			destinationName = remoteContact->nickName();
 
 		// Replace %chatName%, create a internal span to update it by DOM when asked.
-		resultHTML = resultHTML.replace( QLatin1String("%chatName%"), QString("<span \
id=\"KopeteHeaderChatNameInternal\">%1</span>").arg( \
formatName(d->manager->displayName(), Qt::RichText) ) ); +		resultHTML.replace( \
QLatin1String("%chatName%"), QString("<span \
id=\"KopeteHeaderChatNameInternal\">%1</span>").arg( \
formatName(d->manager->displayName(), Qt::RichText) ) );  // Replace %sourceName%
-		resultHTML = resultHTML.replace( QLatin1String("%sourceName%"), \
formatName(sourceName, Qt::RichText) ); +		resultHTML.replace( \
QLatin1String("%sourceName%"), formatName(sourceName, Qt::RichText) );  // Replace \
                %destinationName%
-		resultHTML = resultHTML.replace( QLatin1String("%destinationName%"), \
formatName(destinationName, Qt::RichText) ); +		resultHTML.replace( \
QLatin1String("%destinationName%"), formatName(destinationName, Qt::RichText) );  // \
                For %timeOpened%, display the date and time (also the seconds).
-		resultHTML = resultHTML.replace( QLatin1String("%timeOpened%"), \
KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), KLocale::ShortDate, \
true ) ); +		resultHTML.replace( QLatin1String("%timeOpened%"), \
KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), KLocale::ShortDate, \
true ) );  
 		// Look for %timeOpened{X}%
 		QRegExp timeRegExp("%timeOpened\\{([^}]*)\\}%");
@@ -988,7 +1085,7 @@
 		while( (pos=timeRegExp.indexIn(resultHTML, pos) ) != -1 )
 		{
 			QString timeKeyword = formatTime( timeRegExp.cap(1), QDateTime::currentDateTime() \
                );
-			resultHTML = resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword \
); +			resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword );
 		}
 		// Get contact image paths
 		QString photoIncoming = photoForContact( remoteContact );
@@ -1003,8 +1100,8 @@
 			photoOutgoing = d->currentChatStyle->getStyleBaseHref() + \
QLatin1String("Outgoing/buddy_icon.png");  }
 
-		resultHTML = resultHTML.replace( QLatin1String("%incomingIconPath%"), \
                photoIncoming );
-		resultHTML = resultHTML.replace( QLatin1String("%outgoingIconPath%"), \
photoOutgoing ); +		resultHTML.replace( QLatin1String("%incomingIconPath%"), \
photoIncoming ); +		resultHTML.replace( QLatin1String("%outgoingIconPath%"), \
photoOutgoing );  }
 
 	return resultHTML;
@@ -1201,6 +1298,24 @@
 	return photo;
 }
 
+void ChatMessagePart::disableFileTransferButtons( unsigned int id )
+{
+	QString elementId = QString( "ftS%1" ).arg( id );
+	DOM::HTMLInputElement element = document().getElementById( elementId );
+	if ( !element.isNull() )
+		element.setDisabled( true );
+	
+	elementId = QString( "ftSA%1" ).arg( id );
+	element = document().getElementById( elementId );
+	if ( !element.isNull() )
+		element.setDisabled( true );
+	
+	elementId = QString( "ftC%1" ).arg( id );
+	element = document().getElementById( elementId );
+	if ( !element.isNull() )
+		element.setDisabled( true );
+}
+
 #include "chatmessagepart.moc"
 
 // vim: set noet ts=4 sts=4 sw=4:
Index: kopete/chatwindow/kopetechatwindowstyle.h
===================================================================
--- kopete/chatwindow/kopetechatwindowstyle.h	(revision 851800)
+++ kopete/chatwindow/kopetechatwindowstyle.h	(working copy)
@@ -96,6 +96,8 @@
 	QString getActionIncomingHtml() const;
 	QString getActionOutgoingHtml() const;
 
+	QString getFileTransferIncomingHtml() const;
+
 	/**
 	 * Check if the style has the support for Kopete Action template (Kopete extension)
 	 * @return true if the style has Action template.
Index: kopete/chatwindow/chatmessagepart.h
===================================================================
--- kopete/chatwindow/chatmessagepart.h	(revision 851800)
+++ kopete/chatwindow/chatmessagepart.h	(working copy)
@@ -156,6 +156,7 @@
 
 private slots:
 	void slotOpenURLRequest( const KUrl &url, const KParts::OpenUrlArguments &, const \
KParts::BrowserArguments & ); +	void slotFileTransferIncomingDone( unsigned int id );
 	void slotScrollView();
 	void slotAppearanceChanged();
 
@@ -264,6 +265,8 @@
 	 */
 	QString photoForContact( const Kopete::Contact *contact ) const;
 
+	void disableFileTransferButtons( unsigned int id );
+
 	class Private;
 	Private *d;
 };
Index: libkopete/kopetemessage.cpp
===================================================================
--- libkopete/kopetemessage.cpp	(revision 851800)
+++ libkopete/kopetemessage.cpp	(working copy)
@@ -47,13 +47,14 @@
 {
 public:
 	Private()
-		: direction(Internal), format(Qt::PlainText), type(TypeNormal), \
importance(Normal), backgroundOverride(false), +		: id(nextId++), \
direction(Internal), format(Qt::PlainText), type(TypeNormal), importance(Normal), \
                backgroundOverride(false),
 		  foregroundOverride(false), richTextOverride(false), isRightToLeft(false), \
                timeStamp( QDateTime::currentDateTime() ),
-		  body(new QTextDocument), escapedBodyDirty(true)
+		  body(new QTextDocument), escapedBodyDirty(true), fileTransfer(0)
 	{}
 	Private (const Private &other);
 	~Private();
 
+	const uint id;
 	QPointer<Contact> from;
 	ContactPtrList to;
 	QPointer<ChatSession> manager;
@@ -78,10 +79,27 @@
 	QTextDocument* body;
 	mutable QString escapedBody;
 	mutable bool escapedBodyDirty;
+
+	class FileTransferInfo
+	{
+	public:
+		FileTransferInfo() : disabled(false), fileSize(0)
+		{}
+
+		bool disabled;
+		QString fileName;
+		unsigned long fileSize;
+		QPixmap filePreview;
+	};
+	FileTransferInfo* fileTransfer;
+
+	static uint nextId;
 };
 
+uint Message::Private::nextId = 0;
+
 Message::Private::Private (const Message::Private &other)
-	: QSharedData (other)
+	: QSharedData (other), id(other.id)
 {
 	from = other.from;
 	to = other.to;
@@ -107,10 +125,18 @@
 	body = other.body->clone();
 	escapedBody = other.escapedBody;
 	escapedBodyDirty = other.escapedBodyDirty;
+
+	if ( other.fileTransfer )
+		fileTransfer = new FileTransferInfo( *other.fileTransfer );
+	else
+		fileTransfer = 0;
 }
 
 Message::Private::~Private ()
 {
+	if ( fileTransfer )
+		delete fileTransfer;
+
 	delete body;
 }
 
@@ -151,6 +177,16 @@
 {
 }
 
+uint Message::id() const
+{
+	return d->id;
+}
+
+uint Message::nextId()
+{
+	return Message::Private::nextId++;
+}
+
 void Message::setBackgroundOverride( bool enabled )
 {
 	d->backgroundOverride = enabled;
@@ -558,6 +594,58 @@
 	return styleAttribute;
 }
 
+void Message::setFileTransferDisabled( bool disabled )
+{
+	if ( !d->fileTransfer )
+		d->fileTransfer = new Message::Private::FileTransferInfo();
+
+	d->fileTransfer->disabled = disabled;
+}
+
+bool Message::fileTransferDisabled() const
+{
+	return ( d->fileTransfer ) ? d->fileTransfer->disabled : false;
+}
+
+void Message::setFileName( const QString &fileName )
+{
+	if ( !d->fileTransfer )
+		d->fileTransfer = new Message::Private::FileTransferInfo();
+	
+	d->fileTransfer->fileName = fileName;
+}
+
+QString Message::fileName() const
+{
+	return ( d->fileTransfer ) ? d->fileTransfer->fileName : QString();
+}
+
+void Message::setFileSize( unsigned long size )
+{
+	if ( !d->fileTransfer )
+		d->fileTransfer = new Message::Private::FileTransferInfo();
+	
+	d->fileTransfer->fileSize = size;
+}
+
+unsigned long Message::fileSize() const
+{
+	return ( d->fileTransfer ) ? d->fileTransfer->fileSize : 0;
+}
+
+void Message::setFilePreview( const QPixmap &preview )
+{
+	if ( !d->fileTransfer )
+		d->fileTransfer = new Message::Private::FileTransferInfo();
+	
+	d->fileTransfer->filePreview = preview;
+}
+
+QPixmap Message::filePreview() const
+{
+	return ( d->fileTransfer ) ? d->fileTransfer->filePreview : QPixmap();
+}
+
 // prime candidate for removal
 #if 0 
 QString Message::decodeString( const QByteArray &message, const QTextCodec \
                *providedCodec, bool *success )
Index: libkopete/kopetemessage.h
===================================================================
--- libkopete/kopetemessage.h	(revision 851800)
+++ libkopete/kopetemessage.h	(working copy)
@@ -34,6 +34,7 @@
 class QTextCodec;
 class QTextDocument;
 class QStringList;
+class QPixmap;
 
 namespace Kopete {
 
@@ -96,7 +97,8 @@
 	enum MessageType
 	{
 		TypeNormal, ///< A typical message
-		TypeAction ///< An IRC-style action.
+		TypeAction, ///< An IRC-style action.
+		TypeFileTransferRequest ///< A incoming file transfer request message
 	};
 
 	/**
@@ -148,6 +150,18 @@
 	Message & operator=( const Message &other );
 
 	/**
+	 * @brief Get unique message id.
+	 * @return message id
+	 */
+	uint id() const;
+
+	/**
+	 * @brief Get next unique message id.
+	 * @return next id
+	 */
+	static uint nextId();
+
+	/**
 	 * @brief Accessor method for the timestamp of the message
 	 * @return The message's timestamp
 	 */
@@ -382,8 +396,56 @@
 	 * @return A string formatted like this: "style=attr"
 	 */
 	QString getHtmlStyleAttribute() const;
-	
+
 	/**
+	 * @brief Set the state of incoming file transfer
+	 * @param disabled flag to indicate if the file transfer request should be enabled \
or disabled. +	 */
+	void setFileTransferDisabled( bool disabled );
+
+	/**
+	 * @brief Accessor method for the file transfer state
+	 * @return if file transfer request should be enable or disable
+	 */
+	bool fileTransferDisabled() const;
+
+	/**
+	 * @brief Set file name of incoming file transfer
+	 * @param fileName file name
+	 */
+	void setFileName( const QString &fileName );
+
+	/**
+	 * @brief Accessor method for the file name of incoming file transfer
+	 * @return file name of incoming file transfer
+	 */
+	QString fileName() const;
+
+	/**
+	 * @brief Set file transfer size
+	 * @param size file transfer size
+	 */
+	void setFileSize( unsigned long size );
+
+	/**
+	 * @brief Accessor method for the file transfer size
+	 * @return file transfer size
+	 */
+	unsigned long fileSize() const;
+
+	/**
+	 * @brief Set file preview icon for file transfer
+	 * @param preview file preview icon
+	 */
+	void setFilePreview( const QPixmap &preview );
+
+	/**
+	 * @brief Accessor method for the file preview icon
+	 * @return file preview icon
+	 */
+	QPixmap filePreview() const;
+
+	/**
 	 * @return The list of classes
 	 * Class are used to give different notification on a message. They are also used \
                in the chatwindow as an HTML class 
 	 */
Index: libkopete/kopetetransfermanager.cpp
===================================================================
--- libkopete/kopetetransfermanager.cpp	(revision 851800)
+++ libkopete/kopetetransfermanager.cpp	(working copy)
@@ -30,12 +30,18 @@
 #include "kopeteuiglobal.h"
 
 #include "kopetetransfermanager.h"
-#include "kopetefileconfirmdialog.h"
 
 /***************************
  *  Kopete::FileTransferInfo *
  ***************************/
 
+Kopete::FileTransferInfo::FileTransferInfo()
+{
+	mId = 0;
+	mContact = 0;
+	mSize = 0;
+}
+
 Kopete::FileTransferInfo::FileTransferInfo(  Kopete::Contact *contact, const \
QString& file, const unsigned long size, const QString &recipient, \
KopeteTransferDirection di, const unsigned int id, QString internalId, const QPixmap \
&preview)  {
 	mContact = contact;
@@ -198,50 +204,101 @@
 
 Kopete::TransferManager::TransferManager( QObject *parent ) : QObject( parent )
 {
-	nextID = 0;
 }
 
 Kopete::Transfer* Kopete::TransferManager::addTransfer(  Kopete::Contact *contact, \
const QString& file, const unsigned long size, const QString &recipient , \
Kopete::FileTransferInfo::KopeteTransferDirection di)  {
-//	if (nextID != 0)
-		nextID++;
-	Kopete::FileTransferInfo info(contact, file, size, recipient,di,  nextID);
+	// Use message id to make file transfer id unique because we already use it for \
incoming file transfer. +	uint id = Kopete::Message::nextId();
+	Kopete::FileTransferInfo info(contact, file, size, recipient, di, id);
 	Kopete::Transfer *trans = new Kopete::Transfer(info, contact);
 	connect(trans, SIGNAL(result(KJob *)), this, SLOT(slotComplete(KJob *)));
-	mTransfersMap.insert(nextID, trans);
+	mTransfersMap.insert(id, trans);
 	return trans;
 }
 
-void Kopete::TransferManager::slotAccepted(const Kopete::FileTransferInfo& info, \
const QString& filename) +int Kopete::TransferManager::askIncomingTransfer( \
Kopete::Contact *contact, const QString& file, const unsigned long size, const \
QString& description, QString internalId, const QPixmap &preview )  {
-	Kopete::Transfer *trans = new Kopete::Transfer(info, filename);
-	connect(trans, SIGNAL(result(KJob *)), this, SLOT(slotComplete(KJob *)));
-	mTransfersMap.insert(info.transferId(), trans);
-	emit accepted(trans,filename);
+	Kopete::ChatSession *cs = contact->manager( Kopete::Contact::CanCreate );
+	if ( !cs )
+		return 0;
+	
+	QString dn = contact ? (contact->metaContact() ? \
contact->metaContact()->displayName() : contact->contactId()) : i18n("<unknown>"); +	
+	Kopete::Message msg( contact, cs->myself() );
+	msg.setType( Kopete::Message::TypeFileTransferRequest );
+	msg.setDirection( Kopete::Message::Inbound );
+	msg.setPlainBody( description );
+	msg.setFileName( file );
+	msg.setFileSize( size );
+	msg.setFilePreview( preview );
+	
+	Kopete::FileTransferInfo info( contact, file, size, dn, \
Kopete::FileTransferInfo::Incoming, msg.id(), internalId, preview ); \
+	mTransferRequestInfoMap.insert( msg.id(), info ); +	
+	cs->appendMessage( msg );
+	
+	return msg.id();
 }
 
-int Kopete::TransferManager::askIncomingTransfer(  Kopete::Contact *contact, const \
QString& file, const unsigned long size, const QString& description, QString \
internalId, const QPixmap &preview) +void \
Kopete::TransferManager::saveIncomingTransfer( unsigned int id )  {
-//	if (nextID != 0)
-		nextID++;
+	Kopete::FileTransferInfo info = mTransferRequestInfoMap.value( id );
+	if ( !info.isValid() )
+		return;
 
-	QString dn= contact ? (contact->metaContact() ? \
contact->metaContact()->displayName() : contact->contactId()) : i18n("<unknown>"); \
+	KConfigGroup cg( KGlobal::config(), "File Transfer" ); +	const QString defaultPath \
= cg.readEntry( "defaultPath", QDir::homePath() ); +	KUrl url = defaultPath + \
QLatin1String( "/" ) + info.file();  
-	Kopete::FileTransferInfo info(contact, file, size, dn, \
Kopete::FileTransferInfo::Incoming , nextID , internalId, preview); +	for ( ;; )
+	{
+		url = KFileDialog::getSaveUrl( url, QLatin1String( "*" ), 0, i18n( "File Transfer" \
) ); +		if ( !url.isValid() )
+		{
+			emit askIncomingDone( id );
+			emit refused( info );
+			mTransferRequestInfoMap.remove( id );
+			return;
+		}
+		
+		if ( !url.isLocalFile() )
+		{
+			KMessageBox::queuedMessageBox( 0, KMessageBox::Sorry, i18n( "You must provide a \
valid local filename" ) ); +			continue;
+		}
 
-	//FIXME!!! this will not be deleted if it's still open when kopete exits
-	KopeteFileConfirmDialog *diag= new KopeteFileConfirmDialog(info, description , 0 )  \
; +		if ( QFile::exists( url.path() ) )
+		{
+			int ret = KMessageBox::warningContinueCancel( 0, i18n( "The file '%1' already \
exists.\nDo you want to overwrite it ?", url.path() ), +			                           \
i18n( "Overwrite File" ), KStandardGuiItem::save() ); +			if ( ret == \
KMessageBox::Cancel ) +				continue;
+		}
+		break;
+	}
 
-	connect( diag, SIGNAL( accepted(const Kopete::FileTransferInfo&, const QString&)) , \
                this, SLOT( slotAccepted(const Kopete::FileTransferInfo&, const \
                QString&) ) );
-	connect( diag, SIGNAL( refused(const Kopete::FileTransferInfo&)) , this, SIGNAL( \
                refused(const Kopete::FileTransferInfo&) ) );
-	diag->show();
-	return nextID;
+	const QString directory = url.directory();
+	if( !directory.isEmpty() )
+		cg.writeEntry( "defaultPath", directory );
+
+	Kopete::Transfer *trans = new Kopete::Transfer( info, url.path() );
+	connect( trans, SIGNAL(result(KJob *)), this, SLOT(slotComplete(KJob *)) );
+	mTransfersMap.insert( info.transferId(), trans );
+	emit askIncomingDone( id );
+	emit accepted( trans, url.path() );
+	mTransferRequestInfoMap.remove( id );
 }
 
-void Kopete::TransferManager::removeTransfer( unsigned int id )
+void Kopete::TransferManager::cancelIncomingTransfer( unsigned int id )
 {
-	mTransfersMap.remove(id);
-	//we don't need to delete the job, the job get deleted itself
+	Kopete::FileTransferInfo info = mTransferRequestInfoMap.value( id );
+	if ( !info.isValid() )
+		return;
+
+	emit askIncomingDone( id );
+	emit refused( info );
+	mTransferRequestInfoMap.remove( id );
 }
 
 void Kopete::TransferManager::slotComplete(KJob *job)
@@ -305,5 +362,11 @@
 	}
 }
 
+void Kopete::TransferManager::removeTransfer( unsigned int id )
+{
+	mTransfersMap.remove(id);
+	//we don't need to delete the job, the job get deleted itself
+}
+
 #include "kopetetransfermanager.moc"
 
Index: libkopete/kopetetransfermanager.h
===================================================================
--- libkopete/kopetetransfermanager.h	(revision 851800)
+++ libkopete/kopetetransfermanager.h	(working copy)
@@ -42,8 +42,11 @@
 public:
 	enum KopeteTransferDirection { Incoming, Outgoing };
 
+	FileTransferInfo();
 	FileTransferInfo( Contact *, const QString&, const unsigned long size, const \
QString &, KopeteTransferDirection di, const unsigned int id, QString \
internalId=QString(), const QPixmap &preview=QPixmap() );  ~FileTransferInfo() {}
+
+	bool isValid() const { return (mContact && mId > 0); }
 	unsigned int transferId() const { return mId; }
 	Contact* contact() const { return mContact; }
 	QString file() const { return mFile; }
@@ -82,10 +85,23 @@
 	 * @brief Adds a file transfer to the Kopete::TransferManager
 	 */
 	Transfer *addTransfer( Contact *contact, const QString& file, const unsigned long \
size, const QString &recipient , FileTransferInfo::KopeteTransferDirection di); +
+	/**
+	 * @brief Adds incoming file transfer request to the Kopete::TransferManager.
+	 **/
 	int askIncomingTransfer( Contact *contact, const QString& file, const unsigned long \
size, const QString& description=QString(), QString internalId=QString(), const \
                QPixmap &preview=QPixmap());
-	void removeTransfer( unsigned int id );
 
 	/**
+	 * @brief Shows save file dialog and accepts/rejects incoming file transfer \
request. +	 **/
+	void saveIncomingTransfer( unsigned int id );
+
+	/**
+	 * @brief Cancels incoming file transfer request.
+	 **/
+	void cancelIncomingTransfer( unsigned int id );
+
+	/**
 	 * @brief Ask the user which file to send when they click Send File.
 	 *
 	 * Possibly ask the user which file to send when they click Send File. Sends a \
signal indicating KUrl to @@ -116,18 +132,22 @@
 	/** @brief Signals the transfer has been rejected */
 	void refused(const Kopete::FileTransferInfo& );
 
+	/** @brief Signals the incoming transfer has been rejected or accepted */
+	void askIncomingDone( unsigned int id );
+
 	/** @brief Send a file */
 	void sendFile(const KUrl &file, const QString &localFile, unsigned int fileSize);
 
 private slots:
-	void slotAccepted(const Kopete::FileTransferInfo&, const QString&);
 	void slotComplete(KJob*);
 
 private:
 	TransferManager( QObject *parent );
 
-	int nextID;
+	void removeTransfer( unsigned int id );
+
 	QMap<unsigned int, Transfer *> mTransfersMap;
+	QMap<unsigned int, FileTransferInfo> mTransferRequestInfoMap;
 };
 
 /**
Index: libkopete/CMakeLists.txt
===================================================================
--- libkopete/CMakeLists.txt	(revision 851800)
+++ libkopete/CMakeLists.txt	(working copy)
@@ -43,7 +43,6 @@
   ui/collapsiblewidget.cpp
   ui/editaccountwidget.cpp
   ui/kopetecontactaction.cpp
-  ui/kopetefileconfirmdialog.cpp
   ui/kopeteinfodialog.cpp
   ui/kopetelistview.cpp
   ui/kopetelistviewitem.cpp
@@ -131,7 +130,6 @@
  ui/addressbookselectorwidget_base.ui
  ui/avatarselectorwidget.ui
  ui/contactaddednotifywidget.ui
- ui/fileconfirmbase.ui
  ui/kopeteawaydialogbase.ui
  ui/kopetepasswordwidgetbase.ui
  ui/metacontactselectorwidget_base.ui
@@ -167,7 +165,6 @@
 ########### install files ###############
 
 install(FILES
- ${CMAKE_CURRENT_BINARY_DIR}/ui_fileconfirmbase.h
  ${CMAKE_CURRENT_BINARY_DIR}/ui_kopeteawaydialogbase.h
  ${CMAKE_CURRENT_BINARY_DIR}/ui_kopetepasswordwidgetbase.h
  ui/accountselector.h
@@ -178,7 +175,6 @@
  ui/avatarselectorwidget.h
  ui/editaccountwidget.h
  ui/kopetecontactaction.h
- ui/kopetefileconfirmdialog.h
  ui/kopeteinfodialog.h
  ui/kopetelistview.h
  ui/kopetelistviewitem.h



_______________________________________________
kopete-devel mailing list
kopete-devel@kde.org
https://mail.kde.org/mailman/listinfo/kopete-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic