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 06:54:15 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003