lib Library API Documentation

koFilterManager.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00003                  2000, 2001 Werner Trobin <trobin@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 
00022 #include <koFilterManager.h>
00023 #include <koFilterManager_p.h>
00024 
00025 #include <qfile.h>
00026 #include <qlabel.h>
00027 #include <qlayout.h>
00028 #include <qptrlist.h>
00029 #include <qapplication.h>
00030 
00031 #include <klocale.h>
00032 #include <kmessagebox.h>
00033 #include <koDocument.h>
00034 #include <klibloader.h>
00035 #include <klistbox.h>
00036 #include <kmimetype.h>
00037 #include <kdebug.h>
00038 
00039 #include <queue>
00040 
00041 #include <unistd.h>
00042 
00043 
00044 KoFilterChooser::KoFilterChooser (QWidget *parent, const QStringList &mimeTypes, const QString &nativeFormat)
00045     : KDialogBase (parent, "kofilterchooser", true, i18n ("Choose Filter"),
00046                    KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true),
00047     m_mimeTypes (mimeTypes)
00048 {
00049     setInitialSize (QSize (300, 350));
00050 
00051     QWidget *page = new QWidget (this);
00052     setMainWidget (page);
00053 
00054     // looks too squashed together without * 2
00055     QVBoxLayout *layout = new QVBoxLayout (page, marginHint (), spacingHint () * 2);
00056 
00057     QLabel *filterLabel = new QLabel (i18n ("Select a filter:"), page, "filterlabel");
00058     layout->addWidget (filterLabel);
00059 
00060     m_filterList = new KListBox (page, "filterlist");
00061     layout->addWidget (m_filterList);
00062 
00063     Q_ASSERT (!m_mimeTypes.isEmpty ());
00064     for (QStringList::ConstIterator it = m_mimeTypes.begin ();
00065             it != m_mimeTypes.end ();
00066             it++)
00067     {
00068         KMimeType::Ptr mime = KMimeType::mimeType (*it);
00069         m_filterList->insertItem (mime->comment ());
00070     }
00071 
00072     if (nativeFormat == "application/x-kword")
00073     {
00074         const int index = m_mimeTypes.findIndex ("text/plain");
00075         if (index > -1)
00076             m_filterList->setCurrentItem (index);
00077     }
00078 
00079     if (m_filterList->currentItem () == -1)
00080         m_filterList->setCurrentItem (0);
00081 
00082     m_filterList->centerCurrentItem ();
00083     m_filterList->setFocus ();
00084 
00085     connect (m_filterList, SIGNAL (selected (int)), this, SLOT (slotOk ()));
00086 }
00087 
00088 KoFilterChooser::~KoFilterChooser ()
00089 {
00090 }
00091 
00092 QString KoFilterChooser::filterSelected ()
00093 {
00094     const int item = m_filterList->currentItem ();
00095 
00096     if (item > -1)
00097         return m_mimeTypes [item];
00098     else
00099         return QString::null;
00100 }
00101 
00102 
00103 // static cache for filter availability
00104 QMap<QString, bool> KoFilterManager::m_filterAvailable;
00105 
00106 const int KoFilterManager::s_area = 30500;
00107 
00108 
00109 KoFilterManager::KoFilterManager( KoDocument* document ) :
00110     m_document( document ), m_parentChain( 0 ), m_graph( "" ), d( 0 )
00111 {
00112     if ( document )
00113         QObject::connect( this, SIGNAL( sigProgress( int ) ),
00114                           document, SIGNAL( sigProgress( int ) ) );
00115 }
00116 
00117 
00118 KoFilterManager::KoFilterManager( const QString& url, const QCString& mimetypeHint,
00119                                   KoFilterChain* const parentChain ) :
00120     m_document( 0 ), m_parentChain( parentChain ), m_importUrl( url ), m_importUrlMimetypeHint( mimetypeHint ),
00121     m_graph( "" ), d( 0 )
00122 {
00123 }
00124 
00125 KoFilterManager::~KoFilterManager()
00126 {
00127 }
00128 
00129 QString KoFilterManager::import( const QString& url, KoFilter::ConversionStatus& status )
00130 {
00131     // Find the mime type for the file to be imported.
00132     KURL u;
00133     u.setPath( url );
00134     KMimeType::Ptr t = KMimeType::findByURL( u, 0, true );
00135     if ( t->name() == KMimeType::defaultMimeType() ) {
00136         kdError(s_area) << "No mimetype found for " << url << endl;
00137         status = KoFilter::BadMimeType;
00138         return QString::null;
00139     }
00140 
00141     m_graph.setSourceMimeType( t->name().latin1() );  // .latin1() is okay here (Werner)
00142     if ( !m_graph.isValid() ) {
00143         bool userCancelled = false;
00144 
00145         kdWarning(s_area) << "Can't open " << t->name () << ", trying filter chooser" << endl;
00146         if ( m_document )
00147         {
00148         if ( !m_document->isAutoErrorHandlingEnabled() )
00149         {
00150         status = KoFilter::BadConversionGraph;
00151         return QString::null;
00152         }
00153             QCString nativeFormat = m_document->nativeFormatMimeType ();
00154 
00155             QApplication::setOverrideCursor( arrowCursor );
00156             KoFilterChooser chooser(0,
00157                                     KoFilterManager::mimeFilter (nativeFormat, KoFilterManager::Import),
00158                                     nativeFormat);
00159             if (chooser.exec ())
00160             {
00161                 QCString f = chooser.filterSelected ().latin1();
00162 
00163                 if (f == nativeFormat)
00164                 {
00165                     status = KoFilter::OK;
00166                     QApplication::restoreOverrideCursor();
00167                     return url;
00168                 }
00169 
00170                 m_graph.setSourceMimeType (f);
00171             }
00172             else
00173                 userCancelled = true;
00174             QApplication::restoreOverrideCursor();
00175         }
00176 
00177         if (!m_graph.isValid())
00178         {
00179             kdError(s_area) << "Couldn't create a valid graph for this source mimetype: "
00180                             << t->name() << endl;
00181             importErrorHelper( t->name(), userCancelled );
00182             status = KoFilter::BadConversionGraph;
00183             return QString::null;
00184         }
00185     }
00186 
00187     KoFilterChain::Ptr chain( 0 );
00188     // Are we owned by a KoDocument?
00189     if ( m_document ) {
00190         QCString mimeType( m_document->nativeFormatMimeType() );
00191         chain = m_graph.chain( this, mimeType );
00192     }
00193     else {
00194         kdError(s_area) << "You aren't supposed to use import() from a filter!" << endl;
00195         status = KoFilter::UsageError;
00196         return QString::null;
00197     }
00198 
00199     if ( !chain ) {
00200         kdError(s_area) << "Couldn't create a valid filter chain!" << endl;
00201         importErrorHelper( t->name() );
00202         status = KoFilter::BadConversionGraph;
00203         return QString::null;
00204     }
00205 
00206     // Okay, let's invoke the filters one after the other
00207     m_direction = Import; // vital information!
00208     m_importUrl = url;  // We want to load that file
00209     m_exportUrl = QString::null;  // This is null for sure, as embedded stuff isn't
00210                                   // allowed to use that method
00211     status = chain->invokeChain();
00212 
00213     m_importUrl = QString::null;  // Reset the import URL
00214 
00215     if ( status == KoFilter::OK )
00216         return chain->chainOutput();
00217     return QString::null;
00218 }
00219 
00220 KoFilter::ConversionStatus KoFilterManager::exp0rt( const QString& url, QCString& mimeType )
00221 {
00222     bool userCancelled = false;
00223 
00224     // The import url should already be set correctly (null if we have a KoDocument
00225     // file manager and to the correct URL if we have an embedded manager)
00226     m_direction = Export; // vital information!
00227     m_exportUrl = url;
00228 
00229     if ( m_document )
00230         m_graph.setSourceMimeType( m_document->nativeFormatMimeType() );
00231     else if ( !m_importUrlMimetypeHint.isEmpty() ) {
00232         kdDebug(s_area) << "Using the mimetype hint: '" << m_importUrlMimetypeHint << "'" << endl;
00233         m_graph.setSourceMimeType( m_importUrlMimetypeHint );
00234     }
00235     else {
00236         KURL u;
00237         u.setPath( m_importUrl );
00238         KMimeType::Ptr t = KMimeType::findByURL( u, 0, true );
00239         if ( t->name() == KMimeType::defaultMimeType() ) {
00240             kdError(s_area) << "No mimetype found for " << m_importUrl << endl;
00241             return KoFilter::BadMimeType;
00242         }
00243         m_graph.setSourceMimeType( t->name().latin1() );
00244 
00245         if ( !m_graph.isValid() ) {
00246             kdWarning(s_area) << "Can't open " << t->name () << ", trying filter chooser" << endl;
00247 
00248             QApplication::setOverrideCursor( arrowCursor );
00249             KoFilterChooser chooser(0, KoFilterManager::mimeFilter ());
00250             if (chooser.exec ())
00251                 m_graph.setSourceMimeType (chooser.filterSelected ().latin1 ());
00252             else
00253                 userCancelled = true;
00254 
00255             QApplication::restoreOverrideCursor();
00256         }
00257     }
00258 
00259     if (!m_graph.isValid ())
00260     {
00261         kdError(s_area) << "Couldn't create a valid graph for this source mimetype." << endl;
00262         if (!userCancelled) KMessageBox::error( 0L, i18n("Could not export file."), i18n("Missing Export Filter") );
00263         return KoFilter::BadConversionGraph;
00264     }
00265 
00266     KoFilterChain::Ptr chain = m_graph.chain( this, mimeType );
00267 
00268     if ( !chain ) {
00269         kdError(s_area) << "Couldn't create a valid filter chain!" << endl;
00270         KMessageBox::error( 0L, i18n("Could not export file."), i18n("Missing Export Filter") );
00271         return KoFilter::BadConversionGraph;
00272     }
00273 
00274     return chain->invokeChain();
00275 }
00276 
00277 namespace  // in order not to mess with the global namespace ;)
00278 {
00279     // This class is needed only for the static mimeFilter method
00280     class Vertex
00281     {
00282     public:
00283         Vertex( const QCString& mimeType ) : m_color( White ), m_mimeType( mimeType ) {}
00284 
00285         enum Color { White, Gray, Black };
00286         Color color() const { return m_color; }
00287         void setColor( Color color ) { m_color = color; }
00288 
00289         QCString mimeType() const { return m_mimeType; }
00290 
00291         void addEdge( Vertex* vertex ) { if ( vertex ) m_edges.append( vertex ); }
00292         QPtrList<Vertex> edges() const { return m_edges; }
00293 
00294     private:
00295         Color m_color;
00296         QCString m_mimeType;
00297         QPtrList<Vertex> m_edges;
00298     };
00299 
00300     // Some helper methods for the static stuff
00301     // This method builds up the graph in the passed ascii dict
00302     void buildGraph( QAsciiDict<Vertex>& vertices, KoFilterManager::Direction direction )
00303     {
00304         vertices.setAutoDelete( true );
00305 
00306         // partly copied from build graph, but I don't see any other
00307         // way without crude hacks, as we have to obey the direction here
00308         QValueList<KoDocumentEntry> parts( KoDocumentEntry::query(false, QString::null) );
00309         QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
00310         QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
00311 
00312         while ( partIt != partEnd ) {
00313             QCString key( ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString().latin1() );
00314             if ( !key.isEmpty() )
00315                 vertices.insert( key, new Vertex( key ) );
00316             ++partIt;
00317         }
00318 
00319         QValueList<KoFilterEntry::Ptr> filters = KoFilterEntry::query(); // no constraint here - we want *all* :)
00320         QValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin();
00321         QValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end();
00322 
00323         for ( ; it != end; ++it ) {
00324             // First add the "starting points" to the dict
00325             QStringList::ConstIterator importIt = ( *it )->import.begin();
00326             QStringList::ConstIterator importEnd = ( *it )->import.end();
00327             for ( ; importIt != importEnd; ++importIt ) {
00328                 QCString key = ( *importIt ).latin1();  // latin1 is okay here (werner)
00329                 // already there?
00330                 if ( !vertices[ key ] )
00331                     vertices.insert( key, new Vertex( key ) );
00332             }
00333 
00334             // Are we allowed to use this filter at all?
00335             if ( KoFilterManager::filterAvailable( *it ) ) {
00336                 QStringList::ConstIterator exportIt = ( *it )->export_.begin();
00337                 QStringList::ConstIterator exportEnd = ( *it )->export_.end();
00338 
00339                 for ( ; exportIt != exportEnd; ++exportIt ) {
00340                     // First make sure the export vertex is in place
00341                     QCString key = ( *exportIt ).latin1();  // latin1 is okay here
00342                     Vertex* exp = vertices[ key ];
00343                     if ( !exp ) {
00344                         exp = new Vertex( key );
00345                         vertices.insert( key, exp );
00346                     }
00347                     // Then create the appropriate edges depending on the
00348                     // direction (import/export)
00349                     // This is the chunk of code which actually differs from the
00350                     // graph stuff (apart from the different vertex class)
00351                     importIt = ( *it )->import.begin();
00352                     if ( direction == KoFilterManager::Import ) {
00353                         for ( ; importIt != importEnd; ++importIt )
00354                             exp->addEdge( vertices[ ( *importIt ).latin1() ] );
00355                     } else {
00356                         for ( ; importIt != importEnd; ++importIt )
00357                             vertices[ ( *importIt ).latin1() ]->addEdge( exp );
00358                     }
00359                 }
00360             }
00361             else
00362                 kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " doesn't apply." << endl;
00363         }
00364     }
00365 
00366     // This method runs a BFS on the graph to determine the connected
00367     // nodes. Make sure that the graph is "cleared" (the colors of the
00368     // nodes are all white)
00369     QStringList connected( const QAsciiDict<Vertex>& vertices, const QCString& mimetype )
00370     {
00371         if ( mimetype.isEmpty() )
00372             return QStringList();
00373         Vertex *v = vertices[ mimetype ];
00374         if ( !v )
00375             return QStringList();
00376 
00377         v->setColor( Vertex::Gray );
00378         std::queue<Vertex*> queue;
00379         queue.push( v );
00380         QStringList connected;
00381 
00382         while ( !queue.empty() ) {
00383             v = queue.front();
00384             queue.pop();
00385             QPtrList<Vertex> edges = v->edges();
00386             QPtrListIterator<Vertex> it( edges );
00387             for ( ; it.current(); ++it ) {
00388                 if ( it.current()->color() == Vertex::White ) {
00389                     it.current()->setColor( Vertex::Gray );
00390                     queue.push( it.current() );
00391                 }
00392             }
00393             v->setColor( Vertex::Black );
00394             connected.append( v->mimeType() );
00395         }
00396         return connected;
00397     }
00398 }
00399 
00400 // The static method to figure out to which parts of the
00401 // graph this mimetype has a connection to.
00402 QStringList KoFilterManager::mimeFilter( const QCString& mimetype, Direction direction )
00403 {
00404     QAsciiDict<Vertex> vertices;
00405     buildGraph( vertices, direction );
00406     return connected( vertices, mimetype );
00407 }
00408 
00409 QStringList KoFilterManager::mimeFilter()
00410 {
00411     QAsciiDict<Vertex> vertices;
00412     buildGraph( vertices, KoFilterManager::Import );
00413 
00414     QValueList<KoDocumentEntry> parts( KoDocumentEntry::query(false, QString::null) );
00415     QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
00416     QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
00417 
00418     if ( partIt == partEnd )
00419         return QStringList();
00420 
00421     // To find *all* reachable mimetypes, we have to resort to
00422     // a small hat trick, in order to avoid multiple searches:
00423     // We introduce a fake vertex, which is connected to every
00424     // single KOffice mimetype. Due to that one BFS is enough :)
00425     // Now we just need an... ehrm.. unique name for our fake mimetype
00426     Vertex *v = new Vertex( "supercalifragilistic/x-pialadocious" );
00427     vertices.insert( "supercalifragilistic/x-pialadocious", v );
00428     while ( partIt != partEnd ) {
00429         QCString key( ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString().latin1() );
00430         if ( !key.isEmpty() )
00431             v->addEdge( vertices[ key ] );
00432         ++partIt;
00433     }
00434     QStringList result = connected( vertices, "supercalifragilistic/x-pialadocious" );
00435 
00436     // Finally we have to get rid of our fake mimetype again
00437     result.remove( "supercalifragilistic/x-pialadocious" );
00438     return result;
00439 }
00440 
00441 // Here we check whether the filter is available. This stuff is quite slow,
00442 // but I don't see any other convenient (for the user) way out :}
00443 bool KoFilterManager::filterAvailable( KoFilterEntry::Ptr entry )
00444 {
00445     if ( !entry )
00446         return false;
00447     if ( entry->available != "check" )
00448         return true;
00449 
00450     //kdDebug( 30500 ) << "Checking whether " << entry->service()->name() << " applies." << endl;
00451     // generate some "unique" key
00452     QString key( entry->service()->name() );
00453     key += " - ";
00454     key += entry->service()->library();
00455 
00456     if ( !m_filterAvailable.contains( key ) ) {
00457         //kdDebug( 30500 ) << "Not cached, checking..." << endl;
00458 
00459         KLibrary* library = KLibLoader::self()->library( QFile::encodeName( entry->service()->library() ) );
00460         if ( !library ) {
00461             kdWarning( 30500 ) << "Huh?? Couldn't load the lib: "
00462                                << KLibLoader::self()->lastErrorMessage() << endl;
00463             m_filterAvailable[ key ] = false;
00464             return false;
00465         }
00466 
00467         // This code is "borrowed" from klibloader ;)
00468         QCString symname;
00469         symname.sprintf("check_%s", library->name().latin1() );
00470         void* sym = library->symbol( symname );
00471         if ( !sym )
00472         {
00473             kdWarning( 30500 ) << "The library " << library->name()
00474                                << " does not offer a check_" << library->name()
00475                                << " function." << endl;
00476             m_filterAvailable[ key ] = false;
00477         }
00478         else {
00479             typedef int (*t_func)();
00480             t_func check = (t_func)sym;
00481             m_filterAvailable[ key ] = check() == 1;
00482         }
00483     }
00484     return m_filterAvailable[ key ];
00485 }
00486 
00487 void KoFilterManager::importErrorHelper( const QString& mimeType, const bool suppressDialog )
00488 {
00489     QString tmp = i18n("Could not import file of type\n%1").arg( mimeType );
00490     // ###### FIXME: use KLibLoader::lastErrorMessage() here
00491     if (!suppressDialog) KMessageBox::error( 0L, tmp, i18n("Missing Import Filter") );
00492 }
00493 
00494 #include <koFilterManager.moc>
00495 #include <koFilterManager_p.moc>
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 13:19:23 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2003