| # khtml Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kmultipart.cpp

    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., 59 Temple Place - Suite 330,
    00017    Boston, MA 02111-1307, USA.
    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
    00036 typedef KParts::GenericFactory<KMultiPart> KMultiPartFactory; // factory for the part
    00037 K_EXPORT_COMPONENT_FACTORY( libkmultipart /*library name*/, KMultiPartFactory )
    00038
    00039 //#define DEBUG_PARSING
    00040
    00041 class KLineParser
    00042 {
    00043 public:
    00044     KLineParser() {
    00045         m_lineComplete = false;
    00046     }
    00047     void addChar( char c, bool storeNewline ) {
    00048         if ( !storeNewline && c == '\r' )
    00049             return;
    00050         Q_ASSERT( !m_lineComplete );
    00051         if ( storeNewline || c != '\n' ) {
    00052             int sz = m_currentLine.size();
    00053             m_currentLine.resize( sz+1, QGArray::SpeedOptim );
    00054             m_currentLine[sz] = c;
    00055         }
    00056         if ( c == '\n' )
    00057             m_lineComplete = true;
    00058     }
    00059     bool isLineComplete() const {
    00060         return m_lineComplete;
    00061     }
    00062     QByteArray currentLine() const {
    00063         return m_currentLine;
    00064     }
    00065     void clearLine() {
    00066         Q_ASSERT( m_lineComplete );
    00067         reset();
    00068     }
    00069     void reset() {
    00070         m_currentLine.resize( 0, QGArray::SpeedOptim );
    00071         m_lineComplete = false;
    00072     }
    00073 private:
    00074     QByteArray m_currentLine;
    00075     bool m_lineComplete; // true when ending with '\n'
    00076 };
    00077
    00078 /* testcase:
    00079    Content-type: multipart/mixed;boundary=ThisRandomString
    00080
    00081 --ThisRandomString
    00082 Content-type: text/plain
    00083
    00084 Data for the first object.
    00085
    00086 --ThisRandomString
    00087 Content-type: text/plain
    00088
    00089 Data for the second and last object.
    00090
    00091 --ThisRandomString--
    00092 */
    00093
    00094
    00095 KMultiPart::KMultiPart( QWidget *parentWidget, const char *widgetName,
    00096                         QObject *parent, const char *name, const QStringList& )
    00097     : KParts::ReadOnlyPart( parent, name )
    00098 {
    00099     setInstance( KMultiPartFactory::instance() );
    00100
    00101     QVBox *box = new QVBox( parentWidget, widgetName );
    00102     setWidget( box );
    00103
    00104     m_extension = new KParts::BrowserExtension( this );
    00105
    00106     // We probably need to use m_extension to get the urlArgs in openURL...
    00107
    00108     m_part = 0L;
    00109     m_job = 0L;
    00110     m_lineParser = new KLineParser;
    00111     m_tempFile = 0L;
    00112 }
    00113
    00114 KMultiPart::~KMultiPart()
    00115 {
    00116     // important: delete the nested part before the part or qobject destructor runs.
    00117     // we now delete the nested part which deletes the part's widget which makes
    00118     // _OUR_ m_widget 0 which in turn avoids our part destructor to delete the
    00119     // widget ;-)
    00120     // ### additional note: it _can_ be that the part has been deleted before:
    00121     // when we're in a html frameset and the view dies first, then it will also
    00122     // kill the htmlpart
    00123     if ( m_part )
    00124         delete static_cast<KParts::ReadOnlyPart *>( m_part );
    00125     delete m_job;
    00126     delete m_lineParser;
    00127     delete m_tempFile;
    00128 }
    00129
    00130 bool KMultiPart::openURL( const KURL &url )
    00131 {
    00132     m_url = url;
    00133     m_lineParser->reset();
    00134     m_bParsingHeader = true; // we expect a header to come first
    00135     m_bGotAnyHeader = false;
    00136
    00137     KParts::URLArgs args = m_extension->urlArgs();
    00138     //m_mimeType = args.serviceType;
    00139
    00140     // Hmm, args.reload is set to true when reloading, but this doesn't seem to be enough...
    00141     // I get "HOLD: Reusing held slave for <url>", and the old data
    00142
    00143     m_job = KIO::get( url, args.reload, false );
    00144
    00145     emit started( m_job );
    00146
    00147     connect( m_job, SIGNAL( result( KIO::Job * ) ),
    00148              this, SLOT( slotJobFinished( KIO::Job * ) ) );
    00149     connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
    00150              this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
    00151
    00152     return true;
    00153 }
    00154
    00155 // Yes, libkdenetwork's has such a parser already (MultiPart),
    00156 // but it works on the complete string, expecting the whole data to be available....
    00157 // The version here is asynchronous.
    00158 void KMultiPart::slotData( KIO::Job *job, const QByteArray &data )
    00159 {
    00160     if (m_boundary.isNull())
    00161     {
    00162        QString tmp = job->queryMetaData("media-boundary");
    00163        kdDebug() << "Got Boundary from kio-http '" << tmp << "'" << endl;
    00164        if ( !tmp.isEmpty() ) {
    00165            m_boundary = QCString("--")+tmp.latin1();
    00166            m_boundaryLength = m_boundary.length();
    00167        }
    00168     }
    00169     // Append to m_currentLine until eol
    00170     for ( uint i = 0; i < data.size() ; ++i )
    00171     {
    00172         // Store char. Skip if '\n' and currently parsing a header.
    00173         m_lineParser->addChar( data[i], !m_bParsingHeader );
    00174         if ( m_lineParser->isLineComplete() )
    00175         {
    00176             QByteArray lineData = m_lineParser->currentLine();
    00177 #ifdef DEBUG_PARSING
    00178             kdDebug() << "lineData.size()=" << lineData.size() << endl;
    00179 #endif
    00180             QCString line( lineData.data(), lineData.size()+1 ); // deep copy
    00181             // 0-terminate the data, but only for the line-based tests below
    00182             // We want to keep the raw data in case it ends up in sendData()
    00183             int sz = line.size();
    00184             if ( sz > 0 )
    00185                 line[sz-1] = '\0';
    00186 #ifdef DEBUG_PARSING
    00187             kdDebug() << "[" << m_bParsingHeader << "] line='" << line << "'" << endl;
    00188 #endif
    00189             if ( m_bParsingHeader )
    00190             {
    00191                 if ( !line.isEmpty() )
    00192                     m_bGotAnyHeader = true;
    00193                 if ( m_boundary.isNull() )
    00194                 {
    00195                     if ( !line.isEmpty() ) {
    00196 #ifdef DEBUG_PARSING
    00197                         kdDebug() << "Boundary is " << line << endl;
    00198 #endif
    00199                         m_boundary = line;
    00200                         m_boundaryLength = m_boundary.length();
    00201                     }
    00202                 }
    00203                 // parse Content-Type
    00204                 else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) )
    00205                 {
    00206                     Q_ASSERT( m_nextMimeType.isNull() );
    00207                     m_nextMimeType = QString::fromLatin1( line.data() + 14 ).stripWhiteSpace();
    00208                     kdDebug() << "m_nextMimeType=" << m_nextMimeType << endl;
    00209                 }
    00210                 // Empty line, end of headers (if we had any header line before)
    00211                 else if ( line.isEmpty() && m_bGotAnyHeader )
    00212                 {
    00213                     m_bParsingHeader = false;
    00214 #ifdef DEBUG_PARSING
    00215                     kdDebug() << "end of headers" << endl;
    00216 #endif
    00217                     startOfData();
    00218                 }
    00219                 // First header (when we know it from kio_http)
    00220                 else if ( line == m_boundary )
    00221                     ; // nothing to do
    00222                 else if ( !line.isEmpty() ) // this happens with e.g. Set-Cookie:
    00223                     kdDebug() << "Ignoring header " << line << endl;
    00224             } else {
    00225                 if ( !qstrncmp( line, m_boundary, m_boundaryLength ) )
    00226                 {
    00227 #ifdef DEBUG_PARSING
    00228                     kdDebug() << "boundary found!" << endl;
    00229                     kdDebug() << "after it is " << line.data() + m_boundaryLength << endl;
    00230 #endif
    00231                     // Was it the very last boundary ?
    00232                     if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) )
    00233                     {
    00234 #ifdef DEBUG_PARSING
    00235                         kdDebug() << "Completed!" << endl;
    00236 #endif
    00237                         endOfData();
    00238                         emit completed();
    00239                     } else
    00240                     {
    00241                         char nextChar = *(line.data() + m_boundaryLength);
    00242 #ifdef DEBUG_PARSING
    00243                         kdDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'" << endl;
    00244 #endif
    00245                         if ( nextChar == '\n' || nextChar == '\r' ) {
    00246                             endOfData();
    00247                             m_bParsingHeader = true;
    00248                             m_bGotAnyHeader = false;
    00249                         }
    00250                         else {
    00251                             // otherwise, false hit, it has trailing stuff
    00252                             sendData( lineData );
    00253                         }
    00254                     }
    00255                 } else {
    00256                     // send to part
    00257                     sendData( lineData );
    00258                 }
    00259             }
    00260             m_lineParser->clearLine();
    00261         }
    00262     }
    00263 }
    00264
    00265 void KMultiPart::setPart( const QString& mimeType )
    00266 {
    00267     KXMLGUIFactory *guiFactory = factory();
    00268     if ( guiFactory ) // seems to be 0 when restoring from SM
    00269         guiFactory->removeClient( this );
    00270     kdDebug() << "KMultiPart::setPart " << mimeType << endl;
    00271     delete m_part;
    00272     // Try to find an appropriate viewer component
    00273     m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>
    00274              ( m_mimeType, QString::null, widget(), 0L, this, 0L );
    00275     if ( !m_part ) {
    00276         // TODO launch external app
    00277         KMessageBox::error( widget(), i18n("No handler found for %1!").arg(m_mimeType) );
    00278         return;
    00279     }
    00280     // By making the part a child XMLGUIClient of ours, we get its GUI merged in.
    00281     insertChildClient( m_part );
    00282     m_part->widget()->show();
    00283
    00284     connect( m_part, SIGNAL( completed() ),
    00285              this, SLOT( slotPartCompleted() ) );
    00286
    00287     KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
    00288
    00289     if ( childExtension )
    00290     {
    00291
    00292         // Forward signals from the part's browser extension
    00293         // this is very related (but not exactly like) KHTMLPart::processObjectRequest
    00294
    00295         connect( childExtension, SIGNAL( openURLNotify() ),
    00296                  m_extension, SIGNAL( openURLNotify() ) );
    00297
    00298         connect( childExtension, SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
    00299                  m_extension, SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ) );
    00300
    00301         connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ),
    00302                  m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & ) ) );
    00303         connect( childExtension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs &, const KParts::WindowArgs &, KParts::ReadOnlyPart *& ) ),
    00304                  m_extension, SIGNAL( createNewWindow( const KURL &, const KParts::URLArgs & , const KParts::WindowArgs &, KParts::ReadOnlyPart *&) ) );
    00305
    00306         connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ),
    00307                  m_extension, SIGNAL( popupMenu( const QPoint &, const KFileItemList & ) ) );
    00308         connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ),
    00309                  m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KFileItemList & ) ) );
    00310         connect( childExtension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ),
    00311                  m_extension, SIGNAL( popupMenu( const QPoint &, const KURL &, const QString &, mode_t ) ) );
    00312         connect( childExtension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ),
    00313                  m_extension, SIGNAL( popupMenu( KXMLGUIClient *, const QPoint &, const KURL &, const QString &, mode_t ) ) );
    00314
    00315         connect( childExtension, SIGNAL( infoMessage( const QString & ) ),
    00316                  m_extension, SIGNAL( infoMessage( const QString & ) ) );
    00317
    00318         childExtension->setBrowserInterface( m_extension->browserInterface() );
    00319
    00320         connect( childExtension, SIGNAL( enableAction( const char *, bool ) ),
    00321                  m_extension, SIGNAL( enableAction( const char *, bool ) ) );
    00322         connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ),
    00323                  m_extension, SIGNAL( setLocationBarURL( const QString& ) ) );
    00324         connect( childExtension, SIGNAL( setIconURL( const KURL& ) ),
    00325                  m_extension, SIGNAL( setIconURL( const KURL& ) ) );
    00326         connect( childExtension, SIGNAL( loadingProgress( int ) ),
    00327                  m_extension, SIGNAL( loadingProgress( int ) ) );
    00328         connect( childExtension, SIGNAL( speedProgress( int ) ),
    00329                  m_extension, SIGNAL( speedProgress( int ) ) );
    00330         connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ),
    00331                  m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) );
    00332         connect( childExtension, SIGNAL( selectionInfo( const QString& ) ),
    00333                  m_extension, SIGNAL( selectionInfo( const QString& ) ) );
    00334         connect( childExtension, SIGNAL( selectionInfo( const KURL::List& ) ),
    00335                  m_extension, SIGNAL( selectionInfo( const KURL::List& ) ) );
    00336         connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem* ) ),
    00337                  m_extension, SIGNAL( mouseOverInfo( const KFileItem* ) ) );
    00338     }
    00339
    00340     m_isHTMLPart = ( mimeType == "text/html" );
    00341     // Load the part's plugins too.
    00342     // ###### This is a hack. The bug is that KHTMLPart doesn't load its plugins
    00343     // if className != "Browser/View".
    00344     loadPlugins( this, m_part, m_part->instance() );
    00345     // Get the part's GUI to appear
    00346     if ( guiFactory )
    00347         guiFactory->addClient( this );
    00348 }
    00349
    00350 void KMultiPart::startOfData()
    00351 {
    00352     kdDebug() << "KMultiPart::startOfData" << endl;
    00353     Q_ASSERT( !m_nextMimeType.isNull() );
    00354     if( m_nextMimeType.isNull() )
    00355         return;
    00356     if ( m_mimeType != m_nextMimeType )
    00357     {
    00358         // Need to switch parts (or create the initial one)
    00359         m_mimeType = m_nextMimeType;
    00360         setPart( m_mimeType );
    00361     }
    00362     Q_ASSERT( m_part );
    00363     // Pass URLArgs (e.g. reload)
    00364     KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
    00365     if ( childExtension )
    00366         childExtension->setURLArgs( m_extension->urlArgs() );
    00367
    00368     m_nextMimeType = QString::null;
    00369     delete m_tempFile;
    00370     m_tempFile = 0L;
    00371     if ( m_isHTMLPart )
    00372     {
    00373         KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
    00374         htmlPart->begin( url() );
    00375     }
    00376     else
    00377         m_tempFile = new KTempFile;
    00378 }
    00379
    00380 void KMultiPart::sendData( const QByteArray& line )
    00381 {
    00382     if ( m_isHTMLPart )
    00383     {
    00384         KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
    00385         htmlPart->write( line.data(), line.size() );
    00386     }
    00387     else if ( m_tempFile )
    00388     {
    00389         m_tempFile->file()->writeBlock( line.data(), line.size() );
    00390     }
    00391 }
    00392
    00393 void KMultiPart::endOfData()
    00394 {
    00395     Q_ASSERT( m_part );
    00396     if ( m_isHTMLPart )
    00397     {
    00398         KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
    00399         htmlPart->end();
    00400     } else if ( m_tempFile )
    00401     {
    00402         m_tempFile->close();
    00403         kdDebug() << "KMultiPart::endOfData opening " << m_tempFile->name() << endl;
    00404         KURL url;
    00405         url.setPath( m_tempFile->name() );
    00406         (void) m_part->openURL( url );
    00407         delete m_tempFile;
    00408         m_tempFile = 0L;
    00409     }
    00410 }
    00411
    00412 void KMultiPart::slotPartCompleted()
    00413 {
    00414     if ( !m_isHTMLPart )
    00415     {
    00416         Q_ASSERT( m_part );
    00417         // Delete temp file used by the part
    00418         Q_ASSERT( m_part->url().isLocalFile() );
    00419         kdDebug() << "slotPartCompleted deleting " << m_part->url().path() << endl;
    00420         (void) unlink( QFile::encodeName( m_part->url().path() ) );
    00421         // Do not emit completed from here.
    00422     }
    00423 }
    00424
    00425 bool KMultiPart::closeURL()
    00426 {
    00427     if ( m_part )
    00428         return m_part->closeURL();
    00429     return true;
    00430 }
    00431
    00432 void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * )
    00433 {
    00434     // Not public!
    00435     //if ( m_part )
    00436     //    m_part->guiActivateEvent( e );
    00437 }
    00438
    00439 void KMultiPart::slotJobFinished( KIO::Job *job )
    00440 {
    00441     if ( job->error() )
    00442     {
    00443         // TODO use khtml's error:// scheme
    00444         job->showErrorDialog();
    00445         emit canceled( job->errorString() );
    00446     }
    00447     else
    00448     {
    00449         /*if ( m_khtml->view()->contentsY() == 0 )
    00450         {
    00451             KParts::URLArgs args = m_ext->urlArgs();
    00452             m_khtml->view()->setContentsPos( args.xOffset, args.yOffset );
    00453         }*/
    00454
    00455         emit completed();
    00456
    00457         //QTimer::singleShot( 0, this, SLOT( updateWindowCaption() ) );
    00458     }
    00459     m_job = 0L;
    00460 }
    00461
    00462 KAboutData* KMultiPart::createAboutData()
    00463 {
    00464     KAboutData* aboutData = new KAboutData( "kmultipart", I18N_NOOP("KMultiPart"),
    00465                                              "0.1",
    00466                                             I18N_NOOP( "Embeddable component for multipart/mixed" ),
    00467                                              KAboutData::License_GPL,
    00468                                              "(c) 2001, David Faure <david@mandrakesoft.com>");
    00469     return aboutData;
    00470 }
    00471
    00472 #if 0
    00473 KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name )
    00474     : KParts::BrowserExtension( parent, name )
    00475 {
    00476     m_imgPart = parent;
    00477 }
    00478
    00479 int KMultiPartBrowserExtension::xOffset()
    00480 {
    00481     return m_imgPart->doc()->view()->contentsX();
    00482 }
    00483
    00484 int KMultiPartBrowserExtension::yOffset()
    00485 {
    00486     return m_imgPart->doc()->view()->contentsY();
    00487 }
    00488
    00489 void KMultiPartBrowserExtension::print()
    00490 {
    00491     static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print();
    00492 }
    00493
    00494 void KMultiPartBrowserExtension::reparseConfiguration()
    00495 {
    00496     static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration();
    00497     m_imgPart->doc()->setAutoloadImages( true );
    00498 }
    00499 #endif
    00500
    00501 #include "kmultipart.moc"

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:28 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001