/*************************************************************************** winkswidget.cpp - description ------------------- begin : Sat Mar 8 2009 copyright : (C) 2009 by Antonio Nastasi email : sifcenter (at) gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "winkswidget.h" #include "../contact/msnobject.h" #include "../utils/kmessconfig.h" #include "../currentaccount.h" #include "../kmessdebug.h" #include <QDir> #include <QDomDocument> #include <QLabel> #include <QListWidgetItem> #include <QTextDocument> #include <KIcon> #include <KLocalizedString> #include <KProcess> #include <KStandardDirs> WinksWidget::WinksWidget( QWidget *parent ) : QWidget(parent) , noWinksWarning_(0) { // Create and set the properties for list widget list_ = new QListWidget( this ); list_->setViewMode( QListView::IconMode ); list_->setResizeMode( QListView::Adjust ); list_->setIconSize( QSize( 32,32 ) ); list_->setMovement( QListView::Static ); list_->setSelectionMode( QAbstractItemView::SingleSelection ); list_->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); list_->setFrameShape(QFrame::NoFrame ); list_->setStyleSheet( "QListWidget { background-color: transparent; }" ); // Create an initial layout for the widget layout_ = new QBoxLayout( QBoxLayout::LeftToRight, this ); layout_->addWidget( list_ ); winksDir_ = KMessConfig::instance()->getAccountsDirectory() + "/winks/"; setMinimumSize( minimumSizeHint() ); } WinksWidget::~WinksWidget() { clearList(); delete noWinksWarning_; } void WinksWidget::clearList() { if( list_->count() > 0 ) { while( list_->count() > 0 ) { delete list_->takeItem( 0 ); } } } // Found better method to find out which winks is availables // TODO may be we can create this cache one time and put one button to winks page to // refresh this cache void WinksWidget::refresh() { clearList(); const QDir directory( winksDir_, "*.cab" ); QDir winkDirectory ( winksDir_, "*.png" ); QFileInfo fileInfo; KIcon icon; QString iconPath; const QStringList& cabFiles( directory.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ) ); if( cabFiles.isEmpty() ) { if( noWinksWarning_ == 0 ) { // Create and set the properties for the warning message noWinksWarning_ = new QLabel( this ); noWinksWarning_->setScaledContents( true ); noWinksWarning_->setWordWrap( true ); noWinksWarning_->setText( i18nc( "Informative label on the chat's winks bar", "<html><p><b>You do not have any winks yet.</b></p>" "<p>To add new ones, have your contacts send you some!</p></html>" ) ); // Enable the link click action connect( noWinksWarning_, SIGNAL( linkActivated(const QString&) ), this, SLOT ( slotNoEmoticonsLinkClicked() ) ); layout_->addWidget( noWinksWarning_ ); } // Show the label and hide the list noWinksWarning_->show(); list_->hide(); #ifdef KMESSDEBUG_WINKSWIDGET kDebug() << "There are no winks"; #endif return; } // If the label was created, hide it! if( noWinksWarning_ != 0 ) { list_->show(); noWinksWarning_->hide(); } foreach( const QString& file, cabFiles ) { // We need for file information to grep only it names ( without extension ) fileInfo.setFile( winksDir_ + file ); if( QFile::exists( winksDir_ + file + ".stamp" ) ) { winkDirectory.setPath( winksDir_ + fileInfo.baseName() ); // If the wink directory doens't exist we can't find the image of wink // so icon is set to one default // TODO we should try to extract the cab file to one directory so we be able to find the image if( ! winkDirectory.exists() ) { iconPath = "view-media-artist"; } else { // Search for wink image const QStringList& images( winkDirectory.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ) ); if( images.isEmpty() ) { iconPath = "view-media-artist"; } else { iconPath = winkDirectory.path() + "/" + images.at(0); } } QString winkFriendly; QFile handle( winksDir_ + file + ".name" ); if( handle.open( QIODevice::ReadOnly ) ) { winkFriendly = handle.readAll(); handle.close(); } // Add the item QListWidgetItem *item = new QListWidgetItem( KIcon( iconPath ), "", list_ ); item->setData( Qt::UserRole, file ); item->setToolTip( winkFriendly ); } } } //TODO clean this method WinksWidget::CABEXTRACTOR WinksWidget::getHtmlFromWink( const QString &filename, QString &html ) { const QFileInfo fileInfo( filename ); const QString& winksDir( KMessConfig::instance()->getAccountsDirectory() + "/winks/" + fileInfo.baseName() ); QFile metaFile( winksDir + + "/content.xml" ); // See if the file was extracted already if( ! metaFile.exists() ) { // Check if cabextract is installed if( KStandardDirs::findExe( "cabextract" ).isEmpty() ) { return NOTINSTALLED; } // Run cabextract to get the contents of the file // -q: quiet, only print errors, -d: output dir, -l: force lowercase // (avoiding incompatibilities because win32 is case-insensitive) KProcess cabextract; cabextract.closeReadChannel( QProcess::StandardOutput ); cabextract.setOutputChannelMode( KProcess::SeparateChannels ); cabextract.setReadChannel( QProcess::StandardError ); cabextract << "cabextract" << "-q" << "-d" << winksDir << filename; int exitCode = cabextract.execute( 5000 ); // TODO: capture `cabextract` error messages, display nicely. if( exitCode != 0 ) { const QByteArray& errorOutput( cabextract.readAll() ); kWarning().nospace() << "cabextract failed with the following output: " << errorOutput.trimmed(); return FAILED; } if( ! metaFile.exists() ) { return UNKNOW; } } // Open the file if( ! metaFile.open( QIODevice::ReadOnly ) ) { kWarning() << "file could not be opened: " << metaFile.fileName() << "!"; return UNKNOW; } // Parse the XML QString xmlErrorMessage; QDomDocument xml; if( ! xml.setContent( &metaFile, true, &xmlErrorMessage ) ) { kWarning() << "parsing of wink XML failed: " << xmlErrorMessage << "!"; return UNKNOW; } // Get the package nodes const QDomElement& packageNode( xml.namedItem("package").toElement() ); if( packageNode.isNull() ) { kWarning() << "XML element /package not found!"; return UNKNOW; } if( packageNode.attribute( "type" ) != "wink" ) { kWarning() << "XML element /package/@type is not 'wink'!"; return UNKNOW; } if( packageNode.attribute( "contenttype" ) != "D" ) { kWarning() << "XML element /package/@contenttype is not 'D'!"; } // Get the child nodes const QDomNodeList& packageItems( packageNode.childNodes() ); QString thumbnail; QString animation; QString animationType; for(int i = 0; i < packageItems.count(); i++) { const QDomElement& child( packageItems.item(i).toElement() ); if( child.tagName() == "item" ) { const QString& type( child.attribute( "type" ) ); if( type == "animation" ) { animation = child.attribute( "file" ); animationType = child.attribute( "mimetype" ); } else if( type == "thumbnail-show" || type == "thumbnail" ) { thumbnail = child.attribute( "file" ); // Type not stored. There are winks with mimetype="image/png" while they're sending a JPG. } else { kWarning() << "unknown wink element type: '" << type << "'!"; } } } if( thumbnail.isEmpty() || animation.isEmpty() || animationType.isEmpty() ) { kWarning() << "XML elements are missing, could not play wink!"; return UNKNOW; } // Escape data for inclusion in HTML. animation = Qt::escape(animation).replace("\"", """); // quotes are not escaped by QStyleSheet::escape(). animationType = animationType.remove( QRegExp("[^a-zA-Z0-9\\-/]") ); const QString& filePath( winksDir + "/" + animation ); // Height of 150 is a lot, but most winks are designed to be full-screen, so they can hardly be displayed smaller. html = "<div class='winkContainer'>\n" " <embed type=\"application/x-shockwave-flash\" src=\"" + filePath + "\" " "quality=\"autohigh\" wmode=\"transparent\" loop=\"false\" menu=\"false\" " "width=\"90%\" height=\"150\" />\n" "</div>\n"; return SUCCESS; } const MsnObject WinksWidget::getMsnObjectWinkSelected() { // Check if there is item selected QListWidgetItem *item = list_->currentItem(); if( item == 0 ) { return MsnObject(); } // Grep the fileName of wink that the user selected and the handle to put in msn object const QString& fileName( item->data( Qt::UserRole).toString() ); const QString& handle( CurrentAccount::instance()->getHandle() ); // First, check if the file exists QFile file( winksDir_ + fileName ); if( ! file.open( QIODevice::ReadOnly ) ) { return MsnObject(); } // Create the fileInfo object to grep only name of file without extension const QFileInfo fileInfo( file ); // Second, read the content of wink ( it will be used for compute sha1c in msnobject ) const QByteArray& data( file.readAll() ); file.close(); // Try to read the certificate for the wink, without it the WLM doesn't show the wink received //TODO we could send it to no WLM client if the file( certificate ) isn't present.... // I don't know which clients check for it file.setFileName( winksDir_ + fileName + ".stamp" ); if( ! file.open( QIODevice::ReadOnly ) ) { return MsnObject(); } // Third, read the certificate const QByteArray& stamp( file.readAll() ); file.close(); if( stamp.isEmpty() ) { return MsnObject(); } // Then find out if the wink has a name QString winkFriendly; file.setFileName( winksDir_ + fileName + ".name" ); if( file.open( QIODevice::ReadOnly ) ) { winkFriendly = file.readAll(); file.close(); } // Finally, return the msn object return MsnObject( handle, fileInfo.baseName(), winkFriendly, MsnObject::WINK, data, stamp ); } #include "winkswidget.moc"