00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kmultipart.h"
00021
00022 #include <qvbox.h>
00023 #include <kinstance.h>
00024 #include <kmimetype.h>
00025 #include <klocale.h>
00026 #include <kio/job.h>
00027 #include <qfile.h>
00028 #include <ktempfile.h>
00029 #include <kmessagebox.h>
00030 #include <kparts/componentfactory.h>
00031 #include <kparts/genericfactory.h>
00032 #include <khtml_part.h>
00033 #include <unistd.h>
00034 #include <kxmlguifactory.h>
00035 #include <qtimer.h>
00036
00037 typedef KParts::GenericFactory<KMultiPart> KMultiPartFactory;
00038 K_EXPORT_COMPONENT_FACTORY( libkmultipart , KMultiPartFactory )
00039
00040
00041
00042 class KLineParser
00043 {
00044 public:
00045 KLineParser() {
00046 m_lineComplete = false;
00047 }
00048 void addChar( char c, bool storeNewline ) {
00049 if ( !storeNewline && c == '\r' )
00050 return;
00051 Q_ASSERT( !m_lineComplete );
00052 if ( storeNewline || c != '\n' ) {
00053 int sz = m_currentLine.size();
00054 m_currentLine.resize( sz+1, QGArray::SpeedOptim );
00055 m_currentLine[sz] = c;
00056 }
00057 if ( c == '\n' )
00058 m_lineComplete = true;
00059 }
00060 bool isLineComplete() const {
00061 return m_lineComplete;
00062 }
00063 QByteArray currentLine() const {
00064 return m_currentLine;
00065 }
00066 void clearLine() {
00067 Q_ASSERT( m_lineComplete );
00068 reset();
00069 }
00070 void reset() {
00071 m_currentLine.resize( 0, QGArray::SpeedOptim );
00072 m_lineComplete = false;
00073 }
00074 private:
00075 QByteArray m_currentLine;
00076 bool m_lineComplete;
00077 };
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096 KMultiPart::KMultiPart( QWidget *parentWidget, const char *widgetName,
00097 QObject *parent, const char *name, const QStringList& )
00098 : KParts::ReadOnlyPart( parent, name )
00099 {
00100 m_filter = 0L;
00101
00102 setInstance( KMultiPartFactory::instance() );
00103
00104 QVBox *box = new QVBox( parentWidget, widgetName );
00105 setWidget( box );
00106
00107 m_extension = new KParts::BrowserExtension( this );
00108
00109
00110
00111 m_part = 0L;
00112 m_isHTMLPart = false;
00113 m_job = 0L;
00114 m_lineParser = new KLineParser;
00115 m_tempFile = 0L;
00116
00117 m_timer = new QTimer( this );
00118 connect( m_timer, SIGNAL( timeout() ), this, SLOT( slotProgressInfo() ) );
00119 }
00120
00121 KMultiPart::~KMultiPart()
00122 {
00123
00124
00125
00126
00127
00128
00129
00130 if ( m_part )
00131 delete static_cast<KParts::ReadOnlyPart *>( m_part );
00132 delete m_job;
00133 delete m_lineParser;
00134 if ( m_tempFile ) {
00135 m_tempFile->setAutoDelete( true );
00136 delete m_tempFile;
00137 }
00138 delete m_filter;
00139 m_filter = 0L;
00140 }
00141
00142
00143 void KMultiPart::startHeader()
00144 {
00145 m_bParsingHeader = true;
00146 m_bGotAnyHeader = false;
00147 m_gzip = false;
00148
00149 delete m_filter;
00150 m_filter = 0L;
00151 }
00152
00153
00154 bool KMultiPart::openURL( const KURL &url )
00155 {
00156 m_url = url;
00157 m_lineParser->reset();
00158 startHeader();
00159
00160 KParts::URLArgs args = m_extension->urlArgs();
00161
00162
00163
00164
00165
00166 m_job = KIO::get( url, args.reload, false );
00167
00168 emit started( 0 );
00169
00170 connect( m_job, SIGNAL( result( KIO::Job * ) ),
00171 this, SLOT( slotJobFinished( KIO::Job * ) ) );
00172 connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00173 this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
00174
00175 m_numberOfFrames = 0;
00176 m_numberOfFramesSkipped = 0;
00177 m_totalNumberOfFrames = 0;
00178 m_qtime.start();
00179 m_timer->start( 1000 );
00180
00181 return true;
00182 }
00183
00184
00185
00186
00187 void KMultiPart::slotData( KIO::Job *job, const QByteArray &data )
00188 {
00189 if (m_boundary.isNull())
00190 {
00191 QString tmp = job->queryMetaData("media-boundary");
00192 kdDebug() << "Got Boundary from kio-http '" << tmp << "'" << endl;
00193 if ( !tmp.isEmpty() ) {
00194 if (tmp.startsWith("--"))
00195 m_boundary = tmp.latin1();
00196 else
00197 m_boundary = QCString("--")+tmp.latin1();
00198 m_boundaryLength = m_boundary.length();
00199 }
00200 }
00201
00202 for ( uint i = 0; i < data.size() ; ++i )
00203 {
00204
00205 m_lineParser->addChar( data[i], !m_bParsingHeader );
00206 if ( m_lineParser->isLineComplete() )
00207 {
00208 QByteArray lineData = m_lineParser->currentLine();
00209 #ifdef DEBUG_PARSING
00210 kdDebug() << "lineData.size()=" << lineData.size() << endl;
00211 #endif
00212 QCString line( lineData.data(), lineData.size()+1 );
00213
00214
00215 int sz = line.size();
00216 if ( sz > 0 )
00217 line[sz-1] = '\0';
00218 #ifdef DEBUG_PARSING
00219 kdDebug() << "[" << m_bParsingHeader << "] line='" << line << "'" << endl;
00220 #endif
00221 if ( m_bParsingHeader )
00222 {
00223 if ( !line.isEmpty() )
00224 m_bGotAnyHeader = true;
00225 if ( m_boundary.isNull() )
00226 {
00227 if ( !line.isEmpty() ) {
00228 #ifdef DEBUG_PARSING
00229 kdDebug() << "Boundary is " << line << endl;
00230 #endif
00231 m_boundary = line;
00232 m_boundaryLength = m_boundary.length();
00233 }
00234 }
00235 else if ( !qstrnicmp( line.data(), "Content-Encoding:", 17 ) )
00236 {
00237 QString encoding = QString::fromLatin1(line.data()+17).stripWhiteSpace().lower();
00238 if (encoding == "gzip" || encoding == "x-gzip") {
00239 m_gzip = true;
00240 } else {
00241 kdDebug() << "FIXME: unhandled encoding type in KMultiPart: " << encoding << endl;
00242 }
00243 }
00244
00245 else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) )
00246 {
00247 Q_ASSERT( m_nextMimeType.isNull() );
00248 m_nextMimeType = QString::fromLatin1( line.data() + 14 ).stripWhiteSpace();
00249 int semicolon = m_nextMimeType.find( ';' );
00250 if ( semicolon != -1 )
00251 m_nextMimeType = m_nextMimeType.left( semicolon );
00252 kdDebug() << "m_nextMimeType=" << m_nextMimeType << endl;
00253 }
00254
00255 else if ( line.isEmpty() && m_bGotAnyHeader )
00256 {
00257 m_bParsingHeader = false;
00258 #ifdef DEBUG_PARSING
00259 kdDebug() << "end of headers" << endl;
00260 #endif
00261 startOfData();
00262 }
00263
00264 else if ( line == m_boundary )
00265 ;
00266 else if ( !line.isEmpty() )
00267 kdDebug() << "Ignoring header " << line << endl;
00268 } else {
00269 if ( !qstrncmp( line, m_boundary, m_boundaryLength ) )
00270 {
00271 #ifdef DEBUG_PARSING
00272 kdDebug() << "boundary found!" << endl;
00273 kdDebug() << "after it is " << line.data() + m_boundaryLength << endl;
00274 #endif
00275
00276 if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) )
00277 {
00278 #ifdef DEBUG_PARSING
00279 kdDebug() << "Completed!" << endl;
00280 #endif
00281 endOfData();
00282 emit completed();
00283 } else
00284 {
00285 char nextChar = *(line.data() + m_boundaryLength);
00286 #ifdef DEBUG_PARSING
00287 kdDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'" << endl;
00288 #endif
00289 if ( nextChar == '\n' || nextChar == '\r' ) {
00290 endOfData();
00291 startHeader();
00292 }
00293 else {
00294
00295 sendData( lineData );
00296 }
00297 }
00298 } else {
00299
00300 sendData( lineData );
00301 }
00302 }
00303 m_lineParser->clearLine();
00304 }
00305 }
00306 }
00307
00308 void KMultiPart::setPart( const QString& mimeType )
00309 {
00310 KXMLGUIFactory *guiFactory = factory();
00311 if ( guiFactory )
00312 guiFactory->removeClient( this );
00313 kdDebug() << "KMultiPart::setPart " << mimeType << endl;
00314 delete m_part;
00315
00316 m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>
00317 ( m_mimeType, QString::null, widget(), 0L, this, 0L );
00318 if ( !m_part ) {
00319
00320 KMessageBox::error( widget(), i18n("No handler found for %1!").arg(m_mimeType) );
00321 return;
00322 }
00323
00324 insertChildClient( m_part );
00325 m_part->widget()->show();
00326
00327 connect( m_part, SIGNAL( completed() ),
00328 this, SLOT( slotPartCompleted() ) );
00329
00330 m_isHTMLPart = ( mimeType == "text/html" );
00331 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
00332
00333 if ( childExtension )
00334 {
00335
00336
00337
00338
00339 connect( childExtension, SIGNAL( openURLNotify() ),
00340 m_extension, SIGNAL( openURLNotify() ) );
00341
00342 connect( childExtension, SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
00343 m_extension, SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ) );
00344
00345 connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ),
00346 m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ) );
00347 connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs &, const KParts::WindowArgs &, KParts::ReadOnlyPart *& ) ),
00348 m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & , const KParts::WindowArgs &, KParts::ReadOnlyPart *&) ) );
00349
00350
00351 connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ),
00352 m_extension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ) );
00353 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ),
00354 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ) );
00355 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ),
00356 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags ) ) );
00357 connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ),
00358 m_extension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ) );
00359 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ),
00360 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ) );
00361 connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ),
00362 m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const KParts::URLArgs &, KParts::BrowserExtension::PopupFlags, mode_t ) ) );
00363
00364
00365 if ( m_isHTMLPart )
00366 connect( childExtension, SIGNAL( infoMessage( const QString & ) ),
00367 m_extension, SIGNAL( infoMessage( const QString & ) ) );
00368
00369
00370 childExtension->setBrowserInterface( m_extension->browserInterface() );
00371
00372 connect( childExtension, SIGNAL( enableAction( const char *, bool ) ),
00373 m_extension, SIGNAL( enableAction( const char *, bool ) ) );
00374 connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ),
00375 m_extension, SIGNAL( setLocationBarURL( const QString& ) ) );
00376 connect( childExtension, SIGNAL( setIconURL( const KURL& ) ),
00377 m_extension, SIGNAL( setIconURL( const KURL& ) ) );
00378 connect( childExtension, SIGNAL( loadingProgress( int ) ),
00379 m_extension, SIGNAL( loadingProgress( int ) ) );
00380 if ( m_isHTMLPart )
00381 connect( childExtension, SIGNAL( speedProgress( int ) ),
00382 m_extension, SIGNAL( speedProgress( int ) ) );
00383 connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ),
00384 m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) );
00385 connect( childExtension, SIGNAL( selectionInfo( const QString& ) ),
00386 m_extension, SIGNAL( selectionInfo( const QString& ) ) );
00387 connect( childExtension, SIGNAL( selectionInfo( const KURL::List& ) ),
00388 m_extension, SIGNAL( selectionInfo( const KURL::List& ) ) );
00389 connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem* ) ),
00390 m_extension, SIGNAL( mouseOverInfo( const KFileItem* ) ) );
00391 connect( childExtension, SIGNAL( moveTopLevelWidget( int, int ) ),
00392 m_extension, SIGNAL( moveTopLevelWidget( int, int ) ) );
00393 connect( childExtension, SIGNAL( resizeTopLevelWidget( int, int ) ),
00394 m_extension, SIGNAL( resizeTopLevelWidget( int, int ) ) );
00395 }
00396
00397 m_partIsLoading = false;
00398
00399
00400
00401 loadPlugins( this, m_part, m_part->instance() );
00402
00403 if ( guiFactory )
00404 guiFactory->addClient( this );
00405 }
00406
00407 void KMultiPart::startOfData()
00408 {
00409 kdDebug() << "KMultiPart::startOfData" << endl;
00410 Q_ASSERT( !m_nextMimeType.isNull() );
00411 if( m_nextMimeType.isNull() )
00412 return;
00413
00414 if ( m_gzip )
00415 {
00416 m_filter = new HTTPFilterGZip;
00417 connect( m_filter, SIGNAL( output( const QByteArray& ) ), this, SLOT( reallySendData( const QByteArray& ) ) );
00418 }
00419
00420 if ( m_mimeType != m_nextMimeType )
00421 {
00422
00423 m_mimeType = m_nextMimeType;
00424 setPart( m_mimeType );
00425 }
00426 Q_ASSERT( m_part );
00427
00428 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
00429 if ( childExtension )
00430 childExtension->setURLArgs( m_extension->urlArgs() );
00431
00432 m_nextMimeType = QString::null;
00433 if ( m_tempFile ) {
00434 m_tempFile->setAutoDelete( true );
00435 delete m_tempFile;
00436 m_tempFile = 0;
00437 }
00438 if ( m_isHTMLPart )
00439 {
00440 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00441 htmlPart->begin( url() );
00442 }
00443 else
00444 {
00445
00446 m_tempFile = new KTempFile;
00447 }
00448 }
00449
00450 void KMultiPart::sendData( const QByteArray& line )
00451 {
00452 if ( m_filter )
00453 {
00454 m_filter->slotInput( line );
00455 }
00456 else
00457 {
00458 reallySendData( line );
00459 }
00460 }
00461
00462 void KMultiPart::reallySendData( const QByteArray& line )
00463 {
00464 if ( m_isHTMLPart )
00465 {
00466 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00467 htmlPart->write( line.data(), line.size() );
00468 }
00469 else if ( m_tempFile )
00470 {
00471 m_tempFile->file()->writeBlock( line.data(), line.size() );
00472 }
00473 }
00474
00475 void KMultiPart::endOfData()
00476 {
00477 Q_ASSERT( m_part );
00478 if ( m_isHTMLPart )
00479 {
00480 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00481 htmlPart->end();
00482 } else if ( m_tempFile )
00483 {
00484 m_tempFile->close();
00485 if ( m_partIsLoading )
00486 {
00487
00488
00489 kdDebug() << "KMultiPart::endOfData part isn't ready, skipping frame" << endl;
00490 ++m_numberOfFramesSkipped;
00491 m_tempFile->setAutoDelete( true );
00492 }
00493 else
00494 {
00495 kdDebug() << "KMultiPart::endOfData opening " << m_tempFile->name() << endl;
00496 KURL url;
00497 url.setPath( m_tempFile->name() );
00498 m_partIsLoading = true;
00499 (void) m_part->openURL( url );
00500 }
00501 delete m_tempFile;
00502 m_tempFile = 0L;
00503 }
00504 }
00505
00506 void KMultiPart::slotPartCompleted()
00507 {
00508 if ( !m_isHTMLPart )
00509 {
00510 Q_ASSERT( m_part );
00511
00512 Q_ASSERT( m_part->url().isLocalFile() );
00513 kdDebug() << "slotPartCompleted deleting " << m_part->url().path() << endl;
00514 (void) unlink( QFile::encodeName( m_part->url().path() ) );
00515 m_partIsLoading = false;
00516 ++m_numberOfFrames;
00517
00518 }
00519 }
00520
00521 bool KMultiPart::closeURL()
00522 {
00523 m_timer->stop();
00524 if ( m_part )
00525 return m_part->closeURL();
00526 return true;
00527 }
00528
00529 void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * )
00530 {
00531
00532
00533
00534 }
00535
00536 void KMultiPart::slotJobFinished( KIO::Job *job )
00537 {
00538 if ( job->error() )
00539 {
00540
00541 job->showErrorDialog();
00542 emit canceled( job->errorString() );
00543 }
00544 else
00545 {
00546
00547
00548
00549
00550
00551
00552 emit completed();
00553
00554
00555 }
00556 m_job = 0L;
00557 }
00558
00559 void KMultiPart::slotProgressInfo()
00560 {
00561 int time = m_qtime.elapsed();
00562 if ( !time ) return;
00563 if ( m_totalNumberOfFrames == m_numberOfFrames + m_numberOfFramesSkipped )
00564 return;
00565
00566 QString str( "%1 frames per second, %2 frames skipped per second" );
00567 str = str.arg( 1000.0 * (double)m_numberOfFrames / (double)time );
00568 str = str.arg( 1000.0 * (double)m_numberOfFramesSkipped / (double)time );
00569 m_totalNumberOfFrames = m_numberOfFrames + m_numberOfFramesSkipped;
00570
00571 emit m_extension->infoMessage( str );
00572 }
00573
00574 KAboutData* KMultiPart::createAboutData()
00575 {
00576 KAboutData* aboutData = new KAboutData( "kmultipart", I18N_NOOP("KMultiPart"),
00577 "0.1",
00578 I18N_NOOP( "Embeddable component for multipart/mixed" ),
00579 KAboutData::License_GPL,
00580 "(c) 2001, David Faure <david@mandrakesoft.com>");
00581 return aboutData;
00582 }
00583
00584 #if 0
00585 KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name )
00586 : KParts::BrowserExtension( parent, name )
00587 {
00588 m_imgPart = parent;
00589 }
00590
00591 int KMultiPartBrowserExtension::xOffset()
00592 {
00593 return m_imgPart->doc()->view()->contentsX();
00594 }
00595
00596 int KMultiPartBrowserExtension::yOffset()
00597 {
00598 return m_imgPart->doc()->view()->contentsY();
00599 }
00600
00601 void KMultiPartBrowserExtension::print()
00602 {
00603 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print();
00604 }
00605
00606 void KMultiPartBrowserExtension::reparseConfiguration()
00607 {
00608 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration();
00609 m_imgPart->doc()->setAutoloadImages( true );
00610 }
00611 #endif
00612
00613 #include "kmultipart.moc"