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