KHTML
kmultipart.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2002 David Faure <david@mandrakesoft.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kmultipart.h" 00021 00022 00023 #include <kcomponentdata.h> 00024 #include <kmimetype.h> 00025 #include <klocale.h> 00026 #include <kjobuidelegate.h> 00027 #include <kio/job.h> 00028 #include <QtCore/QFile> 00029 #include <ktemporaryfile.h> 00030 #include <kmessagebox.h> 00031 #include <kmimetypetrader.h> 00032 #include <kpluginfactory.h> 00033 #include <khtml_part.h> 00034 #include <unistd.h> 00035 #include <kxmlguifactory.h> 00036 #include <QtCore/QTimer> 00037 #include <kvbox.h> 00038 00039 static KAboutData kmultipartAboutData() 00040 { 00041 KAboutData aboutData( "kmultipart", 0, ki18n("KMultiPart"), 00042 "0.1", 00043 ki18n( "Embeddable component for multipart/mixed" ), 00044 KAboutData::License_GPL, 00045 ki18n("Copyright 2001-2011, David Faure <email>faure@kde.org</email>")); 00046 return aboutData; 00047 } 00048 00049 K_PLUGIN_FACTORY(KMultiPartFactory, registerPlugin<KMultiPart>();) 00050 K_EXPORT_PLUGIN(KMultiPartFactory(kmultipartAboutData())) 00051 00052 //#define DEBUG_PARSING 00053 00054 class KLineParser 00055 { 00056 public: 00057 KLineParser() { 00058 m_lineComplete = false; 00059 } 00060 void addChar( char c, bool storeNewline ) { 00061 if ( !storeNewline && c == '\r' ) 00062 return; 00063 Q_ASSERT( !m_lineComplete ); 00064 if ( storeNewline || c != '\n' ) { 00065 int sz = m_currentLine.size(); 00066 m_currentLine.resize( sz+1 ); 00067 m_currentLine[sz] = c; 00068 } 00069 if ( c == '\n' ) 00070 m_lineComplete = true; 00071 } 00072 bool isLineComplete() const { 00073 return m_lineComplete; 00074 } 00075 QByteArray currentLine() const { 00076 return m_currentLine; 00077 } 00078 void clearLine() { 00079 Q_ASSERT( m_lineComplete ); 00080 reset(); 00081 } 00082 void reset() { 00083 m_currentLine.resize( 0 ); 00084 m_lineComplete = false; 00085 } 00086 private: 00087 QByteArray m_currentLine; 00088 bool m_lineComplete; // true when ending with '\n' 00089 }; 00090 00091 /* testcase: 00092 Content-type: multipart/mixed;boundary=ThisRandomString 00093 00094 --ThisRandomString 00095 Content-type: text/plain 00096 00097 Data for the first object. 00098 00099 --ThisRandomString 00100 Content-type: text/plain 00101 00102 Data for the second and last object. 00103 00104 --ThisRandomString-- 00105 */ 00106 00107 00108 KMultiPart::KMultiPart( QWidget *parentWidget, 00109 QObject *parent, const QVariantList& ) 00110 : KParts::ReadOnlyPart( parent ) 00111 { 00112 m_filter = 0L; 00113 00114 setComponentData( KMultiPartFactory::componentData() ); 00115 00116 KVBox *box = new KVBox( parentWidget ); 00117 setWidget( box ); 00118 00119 m_extension = new KParts::BrowserExtension( this ); 00120 00121 m_part = 0L; 00122 m_isHTMLPart = false; 00123 m_job = 0L; 00124 m_lineParser = new KLineParser; 00125 m_tempFile = 0; 00126 00127 m_timer = new QTimer( this ); 00128 connect( m_timer, SIGNAL( timeout() ), this, SLOT( slotProgressInfo() ) ); 00129 } 00130 00131 KMultiPart::~KMultiPart() 00132 { 00133 // important: delete the nested part before the part or qobject destructor runs. 00134 // we now delete the nested part which deletes the part's widget which makes 00135 // _OUR_ m_widget 0 which in turn avoids our part destructor to delete the 00136 // widget ;-) 00137 // ### additional note: it _can_ be that the part has been deleted before: 00138 // when we're in a html frameset and the view dies first, then it will also 00139 // kill the htmlpart 00140 if ( m_part ) 00141 delete static_cast<KParts::ReadOnlyPart *>( m_part ); 00142 delete m_job; 00143 delete m_lineParser; 00144 if ( m_tempFile ) { 00145 m_tempFile->setAutoRemove( true ); 00146 delete m_tempFile; 00147 } 00148 delete m_filter; 00149 m_filter = 0L; 00150 } 00151 00152 00153 void KMultiPart::startHeader() 00154 { 00155 m_bParsingHeader = true; // we expect a header to come first 00156 m_bGotAnyHeader = false; 00157 m_gzip = false; 00158 // just to be sure for now 00159 delete m_filter; 00160 m_filter = 0L; 00161 } 00162 00163 00164 bool KMultiPart::openUrl( const KUrl &url ) 00165 { 00166 setUrl(url); 00167 m_lineParser->reset(); 00168 startHeader(); 00169 00170 //m_mimeType = arguments().mimeType(); 00171 00172 // Hmm, args.reload is set to true when reloading, but this doesn't seem to be enough... 00173 // I get "HOLD: Reusing held slave for <url>", and the old data 00174 00175 m_job = KIO::get( url, 00176 arguments().reload() ? KIO::Reload : KIO::NoReload, 00177 KIO::HideProgressInfo ); 00178 00179 emit started( 0 /*m_job*/ ); // don't pass the job, it would interfere with our own infoMessage 00180 00181 connect( m_job, SIGNAL( result( KJob * ) ), 00182 this, SLOT( slotJobFinished( KJob * ) ) ); 00183 connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), 00184 this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) ); 00185 00186 m_numberOfFrames = 0; 00187 m_numberOfFramesSkipped = 0; 00188 m_totalNumberOfFrames = 0; 00189 m_qtime.start(); 00190 m_timer->start( 1000 ); //1s 00191 00192 return true; 00193 } 00194 00195 // Yes, libkdenetwork's has such a parser already (MultiPart), 00196 // but it works on the complete string, expecting the whole data to be available.... 00197 // The version here is asynchronous. 00198 void KMultiPart::slotData( KIO::Job *job, const QByteArray &data ) 00199 { 00200 if (m_boundary.isNull()) 00201 { 00202 QString tmp = job->queryMetaData("media-boundary"); 00203 kDebug() << "Got Boundary from kio-http '" << tmp << "'"; 00204 if ( !tmp.isEmpty() ) { 00205 // as per r437578, sometimes we se something like this: 00206 // Content-Type: multipart/x-mixed-replace; boundary=--myboundary 00207 // .. 00208 // --myboundary 00209 // e.g. the hashes specified in the header are extra. However, 00210 // we also see the following on the w3c bugzilla: 00211 // boundary="------- =_aaaaaaaaaa0" 00212 // .. 00213 //--------- =_aaaaaaaaaa0 00214 // e.g. the hashes are accurate. For now, we consider the quoted 00215 // case to be quirk-free, and only apply the -- stripping quirk 00216 // when we're unquoted. 00217 if (tmp.startsWith(QLatin1String("--")) && 00218 job->queryMetaData("media-boundary-kio-quoted") != "true") 00219 m_boundary = tmp.toLatin1(); 00220 else 00221 m_boundary = QByteArray("--")+tmp.toLatin1(); 00222 m_boundaryLength = m_boundary.length(); 00223 } 00224 } 00225 // Append to m_currentLine until eol 00226 for ( int i = 0; i < data.size() ; ++i ) 00227 { 00228 // Store char. Skip if '\n' and currently parsing a header. 00229 m_lineParser->addChar( data[i], !m_bParsingHeader ); 00230 if ( m_lineParser->isLineComplete() ) 00231 { 00232 QByteArray line = m_lineParser->currentLine(); 00233 #ifdef DEBUG_PARSING 00234 kDebug() << "line.size()=" << line.size(); 00235 #endif 00236 #ifdef DEBUG_PARSING 00237 kDebug() << "[" << m_bParsingHeader << "] line='" << line << "'"; 00238 #endif 00239 if ( m_bParsingHeader ) 00240 { 00241 if ( !line.isEmpty() ) 00242 m_bGotAnyHeader = true; 00243 if ( m_boundary.isNull() ) 00244 { 00245 if ( !line.isEmpty() ) { 00246 #ifdef DEBUG_PARSING 00247 kDebug() << "Boundary is " << line; 00248 #endif 00249 m_boundary = line; 00250 m_boundaryLength = m_boundary.length(); 00251 } 00252 } 00253 else if ( !qstrnicmp( line.data(), "Content-Encoding:", 17 ) ) 00254 { 00255 QString encoding = QString::fromLatin1(line.data()+17).trimmed().toLower(); 00256 if (encoding == "gzip" || encoding == "x-gzip") { 00257 m_gzip = true; 00258 } else { 00259 kDebug() << "FIXME: unhandled encoding type in KMultiPart: " << encoding; 00260 } 00261 } 00262 // parse Content-Type 00263 else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) ) 00264 { 00265 Q_ASSERT( m_nextMimeType.isNull() ); 00266 m_nextMimeType = QString::fromLatin1( line.data() + 14 ).trimmed(); 00267 int semicolon = m_nextMimeType.indexOf( ';' ); 00268 if ( semicolon != -1 ) 00269 m_nextMimeType = m_nextMimeType.left( semicolon ); 00270 kDebug() << "m_nextMimeType=" << m_nextMimeType; 00271 } 00272 // Empty line, end of headers (if we had any header line before) 00273 else if ( line.isEmpty() && m_bGotAnyHeader ) 00274 { 00275 m_bParsingHeader = false; 00276 #ifdef DEBUG_PARSING 00277 kDebug() << "end of headers"; 00278 #endif 00279 startOfData(); 00280 } 00281 // First header (when we know it from kio_http) 00282 else if ( line == m_boundary ) 00283 ; // nothing to do 00284 else if ( !line.isEmpty() ) // this happens with e.g. Set-Cookie: 00285 kDebug() << "Ignoring header " << line; 00286 } else { 00287 if ( !qstrncmp( line, m_boundary, m_boundaryLength ) ) 00288 { 00289 #ifdef DEBUG_PARSING 00290 kDebug() << "boundary found!"; 00291 kDebug() << "after it is " << line.data() + m_boundaryLength; 00292 #endif 00293 // Was it the very last boundary ? 00294 if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) ) 00295 { 00296 #ifdef DEBUG_PARSING 00297 kDebug() << "Completed!"; 00298 #endif 00299 endOfData(); 00300 emit completed(); 00301 } else 00302 { 00303 char nextChar = *(line.data() + m_boundaryLength); 00304 #ifdef DEBUG_PARSING 00305 kDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'"; 00306 #endif 00307 if ( nextChar == '\n' || nextChar == '\r' ) { 00308 endOfData(); 00309 startHeader(); 00310 } 00311 else { 00312 // otherwise, false hit, it has trailing stuff 00313 sendData(line); 00314 } 00315 } 00316 } else { 00317 // send to part 00318 sendData(line); 00319 } 00320 } 00321 m_lineParser->clearLine(); 00322 } 00323 } 00324 } 00325 00326 void KMultiPart::setPart( const QString& mimeType ) 00327 { 00328 KXMLGUIFactory *guiFactory = factory(); 00329 if ( guiFactory ) // seems to be 0 when restoring from SM 00330 guiFactory->removeClient( this ); 00331 kDebug() << "KMultiPart::setPart " << mimeType; 00332 delete m_part; 00333 // Try to find an appropriate viewer component 00334 m_part = KMimeTypeTrader::createPartInstanceFromQuery<KParts::ReadOnlyPart> 00335 ( m_mimeType, widget(), this ); 00336 if ( !m_part ) { 00337 // TODO launch external app 00338 KMessageBox::error( widget(), i18n("No handler found for %1.", m_mimeType) ); 00339 return; 00340 } 00341 // By making the part a child XMLGUIClient of ours, we get its GUI merged in. 00342 insertChildClient( m_part ); 00343 m_part->widget()->show(); 00344 00345 connect( m_part, SIGNAL( completed() ), 00346 this, SLOT( slotPartCompleted() ) ); 00347 connect( m_part, SIGNAL(completed(bool)), 00348 this, SLOT(slotPartCompleted()) ); 00349 00350 m_isHTMLPart = ( mimeType == "text/html" ); 00351 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part ); 00352 00353 if ( childExtension ) 00354 { 00355 00356 // Forward signals from the part's browser extension 00357 // this is very related (but not exactly like) KHTMLPart::processObjectRequest 00358 00359 connect( childExtension, SIGNAL( openURLNotify() ), 00360 m_extension, SIGNAL( openURLNotify() ) ); 00361 00362 connect( childExtension, SIGNAL( openUrlRequestDelayed( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments& ) ), 00363 m_extension, SIGNAL( openUrlRequest( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments & ) ) ); 00364 00365 connect( childExtension, SIGNAL( createNewWindow( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments&, const KParts::WindowArgs &, KParts::ReadOnlyPart** ) ), 00366 m_extension, SIGNAL( createNewWindow( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments& , const KParts::WindowArgs &, KParts::ReadOnlyPart**) ) ); 00367 00368 // Keep in sync with khtml_part.cpp 00369 connect( childExtension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)), 00370 m_extension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) ); 00371 connect( childExtension, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)), 00372 m_extension, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) ); 00373 00374 if ( m_isHTMLPart ) 00375 connect( childExtension, SIGNAL( infoMessage( const QString & ) ), 00376 m_extension, SIGNAL( infoMessage( const QString & ) ) ); 00377 // For non-HTML we prefer to show our infoMessage ourselves. 00378 00379 childExtension->setBrowserInterface( m_extension->browserInterface() ); 00380 00381 connect( childExtension, SIGNAL( enableAction( const char *, bool ) ), 00382 m_extension, SIGNAL( enableAction( const char *, bool ) ) ); 00383 connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ), 00384 m_extension, SIGNAL( setLocationBarURL( const QString& ) ) ); 00385 connect( childExtension, SIGNAL( setIconUrl( const KUrl& ) ), 00386 m_extension, SIGNAL( setIconUrl( const KUrl& ) ) ); 00387 connect( childExtension, SIGNAL( loadingProgress( int ) ), 00388 m_extension, SIGNAL( loadingProgress( int ) ) ); 00389 if ( m_isHTMLPart ) // for non-HTML we have our own 00390 connect( childExtension, SIGNAL( speedProgress( int ) ), 00391 m_extension, SIGNAL( speedProgress( int ) ) ); 00392 connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ), 00393 m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) ); 00394 connect( childExtension, SIGNAL( selectionInfo( const QString& ) ), 00395 m_extension, SIGNAL( selectionInfo( const QString& ) ) ); 00396 connect( childExtension, SIGNAL( selectionInfo( const KUrl::List& ) ), 00397 m_extension, SIGNAL( selectionInfo( const KUrl::List& ) ) ); 00398 connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem& ) ), 00399 m_extension, SIGNAL( mouseOverInfo( const KFileItem& ) ) ); 00400 connect( childExtension, SIGNAL( moveTopLevelWidget( int, int ) ), 00401 m_extension, SIGNAL( moveTopLevelWidget( int, int ) ) ); 00402 connect( childExtension, SIGNAL( resizeTopLevelWidget( int, int ) ), 00403 m_extension, SIGNAL( resizeTopLevelWidget( int, int ) ) ); 00404 } 00405 00406 m_partIsLoading = false; 00407 // Load the part's plugins too. 00408 // ###### This is a hack. The bug is that KHTMLPart doesn't load its plugins 00409 // if className != "Browser/View". 00410 loadPlugins( this, m_part, m_part->componentData() ); 00411 // Get the part's GUI to appear 00412 if ( guiFactory ) 00413 guiFactory->addClient( this ); 00414 } 00415 00416 void KMultiPart::startOfData() 00417 { 00418 kDebug() << "KMultiPart::startOfData"; 00419 Q_ASSERT( !m_nextMimeType.isNull() ); 00420 if( m_nextMimeType.isNull() ) 00421 return; 00422 00423 if ( m_gzip ) 00424 { 00425 // We can't use KFilterDev because it assumes it can read as much data as necessary 00426 // from the underlying device. It's a pull strategy, while KMultiPart has to do 00427 // a push strategy. 00428 m_filter = new HTTPFilterGZip; 00429 connect(m_filter, SIGNAL(output(QByteArray)), this, SLOT(reallySendData(QByteArray))); 00430 } 00431 00432 if ( m_mimeType != m_nextMimeType ) 00433 { 00434 // Need to switch parts (or create the initial one) 00435 m_mimeType = m_nextMimeType; 00436 setPart( m_mimeType ); 00437 } 00438 Q_ASSERT( m_part ); 00439 // Pass args (e.g. reload) 00440 m_part->setArguments( arguments() ); 00441 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part ); 00442 if ( childExtension ) 00443 childExtension->setBrowserArguments( m_extension->browserArguments() ); 00444 00445 m_nextMimeType.clear(); 00446 if ( m_tempFile ) { 00447 m_tempFile->setAutoRemove( true ); 00448 delete m_tempFile; 00449 m_tempFile = 0; 00450 } 00451 if ( m_isHTMLPart ) 00452 { 00453 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00454 htmlPart->begin( url() ); 00455 } 00456 else 00457 { 00458 // ###### TODO use a QByteArray and a data: URL instead 00459 m_tempFile = new KTemporaryFile; 00460 m_tempFile->open(); 00461 } 00462 } 00463 00464 void KMultiPart::sendData( const QByteArray& line ) 00465 { 00466 if ( m_filter ) 00467 { 00468 m_filter->slotInput( line ); 00469 } 00470 else 00471 { 00472 reallySendData( line ); 00473 } 00474 } 00475 00476 void KMultiPart::reallySendData( const QByteArray& line ) 00477 { 00478 if ( m_isHTMLPart ) 00479 { 00480 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00481 htmlPart->write( line.data(), line.size() ); 00482 } 00483 else if ( m_tempFile ) 00484 { 00485 m_tempFile->write( line.data(), line.size() ); 00486 } 00487 } 00488 00489 void KMultiPart::endOfData() 00490 { 00491 Q_ASSERT( m_part ); 00492 if ( m_isHTMLPart ) 00493 { 00494 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) ); 00495 htmlPart->end(); 00496 } else if ( m_tempFile ) 00497 { 00498 const QString tempFileName = m_tempFile->fileName(); 00499 m_tempFile->close(); 00500 if ( m_partIsLoading ) 00501 { 00502 // The part is still loading the last data! Let it proceed then 00503 // Otherwise we'd keep canceling it, and nothing would ever show up... 00504 kDebug() << "KMultiPart::endOfData part isn't ready, skipping frame"; 00505 ++m_numberOfFramesSkipped; 00506 m_tempFile->setAutoRemove( true ); 00507 } 00508 else 00509 { 00510 kDebug() << "KMultiPart::endOfData opening " << tempFileName; 00511 KUrl url(tempFileName); 00512 m_partIsLoading = true; 00513 (void) m_part->openUrl( url ); 00514 } 00515 delete m_tempFile; 00516 m_tempFile = 0L; 00517 } 00518 } 00519 00520 void KMultiPart::slotPartCompleted() 00521 { 00522 if ( !m_isHTMLPart ) 00523 { 00524 Q_ASSERT( m_part ); 00525 // Delete temp file used by the part 00526 Q_ASSERT( m_part->url().isLocalFile() ); 00527 kDebug() << "slotPartCompleted deleting " << m_part->url().toLocalFile(); 00528 (void) unlink( QFile::encodeName( m_part->url().toLocalFile() ) ); 00529 m_partIsLoading = false; 00530 ++m_numberOfFrames; 00531 // Do not emit completed from here. 00532 } 00533 } 00534 00535 bool KMultiPart::closeUrl() 00536 { 00537 m_timer->stop(); 00538 if ( m_part ) 00539 return m_part->closeUrl(); 00540 return true; 00541 } 00542 00543 void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * ) 00544 { 00545 // Not public! 00546 //if ( m_part ) 00547 // m_part->guiActivateEvent( e ); 00548 } 00549 00550 void KMultiPart::slotJobFinished( KJob *job ) 00551 { 00552 if ( job->error() ) 00553 { 00554 // TODO use khtml's error:// scheme 00555 job->uiDelegate()->showErrorMessage(); 00556 emit canceled( job->errorString() ); 00557 } 00558 else 00559 { 00560 /*if ( m_khtml->view()->contentsY() == 0 ) 00561 { 00562 const KParts::OpenUrlArguments args = arguments(); 00563 m_khtml->view()->setContentsPos( args.xOffset(), args.yOffset() ); 00564 }*/ 00565 00566 emit completed(); 00567 00568 //QTimer::singleShot( 0, this, SLOT( updateWindowCaption() ) ); 00569 } 00570 m_job = 0L; 00571 } 00572 00573 void KMultiPart::slotProgressInfo() 00574 { 00575 int time = m_qtime.elapsed(); 00576 if ( !time ) return; 00577 if ( m_totalNumberOfFrames == m_numberOfFrames + m_numberOfFramesSkipped ) 00578 return; // No change, don't overwrite statusbar messages if any 00579 //kDebug() << m_numberOfFrames << " in " << time << " milliseconds"; 00580 QString str( "%1 frames per second, %2 frames skipped per second" ); 00581 str = str.arg( 1000.0 * (double)m_numberOfFrames / (double)time ); 00582 str = str.arg( 1000.0 * (double)m_numberOfFramesSkipped / (double)time ); 00583 m_totalNumberOfFrames = m_numberOfFrames + m_numberOfFramesSkipped; 00584 //kDebug() << str; 00585 emit m_extension->infoMessage( str ); 00586 } 00587 00588 00589 #if 0 00590 KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name ) 00591 : KParts::BrowserExtension( parent, name ) 00592 { 00593 m_imgPart = parent; 00594 } 00595 00596 int KMultiPartBrowserExtension::xOffset() 00597 { 00598 return m_imgPart->doc()->view()->contentsX(); 00599 } 00600 00601 int KMultiPartBrowserExtension::yOffset() 00602 { 00603 return m_imgPart->doc()->view()->contentsY(); 00604 } 00605 00606 void KMultiPartBrowserExtension::print() 00607 { 00608 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print(); 00609 } 00610 00611 void KMultiPartBrowserExtension::reparseConfiguration() 00612 { 00613 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration(); 00614 m_imgPart->doc()->setAutoloadImages( true ); 00615 } 00616 #endif 00617 00618 #include "kmultipart.moc"
KDE 4.7 API Reference