• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

previewjob.cpp

00001 // -*- c++ -*-
00002 // vim: ts=4 sw=4 et
00003 /*  This file is part of the KDE libraries
00004     Copyright (C) 2000 David Faure <faure@kde.org>
00005                   2000 Carsten Pfeiffer <pfeiffer@kde.org>
00006                   2001 Malte Starostik <malte.starostik@t-online.de>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "previewjob.h"
00025 
00026 #include <sys/stat.h>
00027 #ifdef __FreeBSD__
00028     #include <machine/param.h>
00029 #endif
00030 #include <sys/types.h>
00031 
00032 #ifdef Q_OS_UNIX
00033 #include <sys/ipc.h>
00034 #include <sys/shm.h>
00035 #endif
00036 
00037 #include <tqdir.h>
00038 #include <tqfile.h>
00039 #include <tqimage.h>
00040 #include <tqtimer.h>
00041 #include <tqregexp.h>
00042 
00043 #include <kdatastream.h> // Do not remove, needed for correct bool serialization
00044 #include <tdefileitem.h>
00045 #include <tdeapplication.h>
00046 #include <tdetempfile.h>
00047 #include <ktrader.h>
00048 #include <kmdcodec.h>
00049 #include <tdeglobal.h>
00050 #include <kstandarddirs.h>
00051 
00052 #include <tdeio/kservice.h>
00053 
00054 #include "previewjob.moc"
00055 
00056 namespace TDEIO { struct PreviewItem; }
00057 using namespace TDEIO;
00058 
00059 struct TDEIO::PreviewItem
00060 {
00061     KFileItem *item;
00062     KService::Ptr plugin;
00063 };
00064 
00065 struct TDEIO::PreviewJobPrivate
00066 {
00067     enum { STATE_STATORIG, // if the thumbnail exists
00068            STATE_GETORIG, // if we create it
00069            STATE_CREATETHUMB // thumbnail:/ slave
00070     } state;
00071     KFileItemList initialItems;
00072     const TQStringList *enabledPlugins;
00073     // Our todo list :)
00074     TQValueList<PreviewItem> items;
00075     // The current item
00076     PreviewItem currentItem;
00077     // The modification time of that URL
00078     time_t tOrig;
00079     // Path to thumbnail cache for the current size
00080     TQString thumbPath;
00081     // Original URL of current item in TMS format
00082     // (file:///path/to/file instead of file:/path/to/file)
00083     TQString origName;
00084     // Thumbnail file name for current item
00085     TQString thumbName;
00086     // Size of thumbnail
00087     int width;
00088     int height;
00089     // Unscaled size of thumbnail (128 or 256 if cache is enabled)
00090     int cacheWidth;
00091     int cacheHeight;
00092     // Whether the thumbnail should be scaled
00093     bool bScale;
00094     // Whether we should save the thumbnail
00095     bool bSave;
00096     // If the file to create a thumb for was a temp file, this is its name
00097     TQString tempName;
00098     // Over that, it's too much
00099     unsigned long maximumSize;
00100     // the size for the icon overlay
00101     int iconSize;
00102     // the transparency of the blended mimetype icon
00103     int iconAlpha;
00104     // Shared memory segment Id. The segment is allocated to a size
00105     // of extent x extent x 4 (32 bit image) on first need.
00106     int shmid;
00107     // And the data area
00108     uchar *shmaddr;
00109     // Delete the KFileItems when done?
00110     bool deleteItems;
00111     bool succeeded;
00112     // Root of thumbnail cache
00113     TQString thumbRoot;
00114     bool ignoreMaximumSize;
00115     TQTimer startPreviewTimer;
00116 };
00117 
00118 PreviewJob::PreviewJob( const KFileItemList &items, int width, int height,
00119     int iconSize, int iconAlpha, bool scale, bool save,
00120     const TQStringList *enabledPlugins, bool deleteItems )
00121     : TDEIO::Job( false /* no GUI */ )
00122 {
00123     d = new PreviewJobPrivate;
00124     d->tOrig = 0;
00125     d->shmid = -1;
00126     d->shmaddr = 0;
00127     d->initialItems = items;
00128     d->enabledPlugins = enabledPlugins;
00129     d->width = width;
00130     d->height = height ? height : width;
00131     d->cacheWidth = d->width;
00132     d->cacheHeight = d->height;
00133     d->iconSize = iconSize;
00134     d->iconAlpha = iconAlpha;
00135     d->deleteItems = deleteItems;
00136     d->bScale = scale;
00137     d->bSave = save && scale;
00138     d->succeeded = false;
00139     d->currentItem.item = 0;
00140     d->thumbRoot = TQDir::homeDirPath() + "/.thumbnails/";
00141     d->ignoreMaximumSize = false;
00142 
00143     // Return to event loop first, determineNextFile() might delete this;
00144     connect(&d->startPreviewTimer, TQT_SIGNAL(timeout()), TQT_SLOT(startPreview()) );
00145     d->startPreviewTimer.start(0, true);
00146 }
00147 
00148 PreviewJob::~PreviewJob()
00149 {
00150 #ifdef Q_OS_UNIX
00151     if (d->shmaddr) {
00152         shmdt((char*)d->shmaddr);
00153         shmctl(d->shmid, IPC_RMID, 0);
00154     }
00155 #endif
00156     delete d;
00157 }
00158 
00159 void PreviewJob::startPreview()
00160 {
00161     // Load the list of plugins to determine which mimetypes are supported
00162     TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00163     TQMap<TQString, KService::Ptr> mimeMap;
00164 
00165     for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00166         if (!d->enabledPlugins || d->enabledPlugins->contains((*it)->desktopEntryName()))
00167     {
00168         TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
00169         for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
00170             mimeMap.insert(*mt, *it);
00171     }
00172 
00173     // Look for images and store the items in our todo list :)
00174     bool bNeedCache = false;
00175     for (KFileItemListIterator it(d->initialItems); it.current(); ++it )
00176     {
00177         PreviewItem item;
00178         item.item = it.current();
00179         TQMap<TQString, KService::Ptr>::ConstIterator plugin = mimeMap.find(it.current()->mimetype());
00180         if (plugin == mimeMap.end()
00181             && (it.current()->mimetype() != "application/x-desktop")
00182             && (it.current()->mimetype() != "media/builtin-mydocuments")
00183             && (it.current()->mimetype() != "media/builtin-mycomputer")
00184             && (it.current()->mimetype() != "media/builtin-mynetworkplaces")
00185             && (it.current()->mimetype() != "media/builtin-printers")
00186             && (it.current()->mimetype() != "media/builtin-trash")
00187             && (it.current()->mimetype() != "media/builtin-webbrowser"))
00188         {
00189             TQString mimeType = it.current()->mimetype();
00190             plugin = mimeMap.find(mimeType.replace(TQRegExp("/.*"), "/*"));
00191 
00192             if (plugin == mimeMap.end())
00193             {
00194                 // check mime type inheritance
00195                 KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
00196                 TQString parentMimeType = mimeInfo->parentMimeType();
00197                 while (!parentMimeType.isEmpty())
00198                 {
00199                     plugin = mimeMap.find(parentMimeType);
00200                     if (plugin != mimeMap.end()) break;
00201 
00202                     KMimeType::Ptr parentMimeInfo = KMimeType::mimeType(parentMimeType);
00203                     if (!parentMimeInfo) break;
00204 
00205                     parentMimeType = parentMimeInfo->parentMimeType();
00206                 }
00207             }
00208 
00209             if (plugin == mimeMap.end())
00210             {
00211                 // check X-TDE-Text property
00212                 KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
00213                 TQVariant textProperty = mimeInfo->property("X-TDE-text");
00214                 if (textProperty.isValid() && textProperty.type() == TQVariant::Bool)
00215                 {
00216                     if (textProperty.toBool())
00217                     {
00218                         plugin = mimeMap.find("text/plain");
00219                         if (plugin == mimeMap.end())
00220                         {
00221                             plugin = mimeMap.find( "text/*" );
00222                         }
00223                     }
00224                 }
00225             }
00226         }
00227 
00228         if (plugin != mimeMap.end())
00229         {
00230             item.plugin = *plugin;
00231             d->items.append(item);
00232             if (!bNeedCache && d->bSave &&
00233                 (it.current()->url().protocol() != "file" ||
00234                  !it.current()->url().directory( false ).startsWith(d->thumbRoot)) &&
00235                 (*plugin)->property("CacheThumbnail").toBool())
00236                 bNeedCache = true;
00237         }
00238         else
00239         {
00240             emitFailed(it.current());
00241             if (d->deleteItems)
00242                 delete it.current();
00243         }
00244     }
00245 
00246   // Read configuration value for the maximum allowed size
00247     TDEConfig * config = TDEGlobal::config();
00248     TDEConfigGroupSaver cgs( config, "PreviewSettings" );
00249     d->maximumSize = config->readNumEntry( "MaximumSize", 1024*1024 /* 1MB */ );
00250 
00251     if (bNeedCache)
00252     {
00253         if (d->width <= 128 && d->height <= 128) d->cacheWidth = d->cacheHeight = 128;
00254         else d->cacheWidth = d->cacheHeight = 256;
00255         d->thumbPath = d->thumbRoot + (d->cacheWidth == 128 ? "normal/" : "large/");
00256         TDEStandardDirs::makeDir(d->thumbPath, 0700);
00257     }
00258     else
00259         d->bSave = false;
00260     determineNextFile();
00261 }
00262 
00263 void PreviewJob::removeItem( const KFileItem *item )
00264 {
00265     for (TQValueList<PreviewItem>::Iterator it = d->items.begin(); it != d->items.end(); ++it)
00266         if ((*it).item == item)
00267         {
00268             d->items.remove(it);
00269             break;
00270         }
00271 
00272     if (d->currentItem.item == item)
00273     {
00274         subjobs.first()->kill();
00275         subjobs.removeFirst();
00276         determineNextFile();
00277     }
00278 }
00279 
00280 void PreviewJob::setIgnoreMaximumSize(bool ignoreSize)
00281 {
00282     d->ignoreMaximumSize = ignoreSize;
00283 }
00284 
00285 void PreviewJob::determineNextFile()
00286 {
00287     if (d->currentItem.item)
00288     {
00289         if (!d->succeeded)
00290             emitFailed();
00291         if (d->deleteItems) {
00292             delete d->currentItem.item;
00293             d->currentItem.item = 0L;
00294         }
00295     }
00296     // No more items ?
00297     if ( d->items.isEmpty() )
00298     {
00299         emitResult();
00300         return;
00301     }
00302     else
00303     {
00304         // First, stat the orig file
00305         d->state = PreviewJobPrivate::STATE_STATORIG;
00306         d->currentItem = d->items.first();
00307         d->succeeded = false;
00308         d->items.remove(d->items.begin());
00309         TDEIO::Job *job = TDEIO::stat( d->currentItem.item->url(), false );
00310         job->addMetaData( "no-auth-prompt", "true" );
00311         addSubjob(job);
00312     }
00313 }
00314 
00315 void PreviewJob::slotResult( TDEIO::Job *job )
00316 {
00317     subjobs.remove( job );
00318     Q_ASSERT ( subjobs.isEmpty() ); // We should have only one job at a time ...
00319     switch ( d->state )
00320     {
00321         case PreviewJobPrivate::STATE_STATORIG:
00322         {
00323             if (job->error()) // that's no good news...
00324             {
00325                 // Drop this one and move on to the next one
00326                 determineNextFile();
00327                 return;
00328             }
00329             TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
00330             TDEIO::UDSEntry::ConstIterator it = entry.begin();
00331             d->tOrig = 0;
00332             int found = 0;
00333             for( ; it != entry.end() && found < 2; it++ )
00334             {
00335                 if ( (*it).m_uds == TDEIO::UDS_MODIFICATION_TIME )
00336                 {
00337                     d->tOrig = (time_t)((*it).m_long);
00338                     found++;
00339                 }
00340                 else if ( (*it).m_uds == TDEIO::UDS_SIZE )
00341                     {
00342                     if ( filesize_t((*it).m_long) > d->maximumSize &&
00343                          !d->ignoreMaximumSize &&
00344                          !d->currentItem.plugin->property("IgnoreMaximumSize").toBool() )
00345                     {
00346                         determineNextFile();
00347                         return;
00348                     }
00349                     found++;
00350                 }
00351             }
00352 
00353             if ( !d->currentItem.plugin->property( "CacheThumbnail" ).toBool() )
00354             {
00355                 // This preview will not be cached, no need to look for a saved thumbnail
00356                 // Just create it, and be done
00357                 getOrCreateThumbnail();
00358                 return;
00359             }
00360 
00361             if ( statResultThumbnail() )
00362                 return;
00363 
00364             getOrCreateThumbnail();
00365             return;
00366         }
00367         case PreviewJobPrivate::STATE_GETORIG:
00368         {
00369             if (job->error())
00370             {
00371                 determineNextFile();
00372                 return;
00373             }
00374 
00375             createThumbnail( static_cast<TDEIO::FileCopyJob*>(job)->destURL().path() );
00376             return;
00377         }
00378         case PreviewJobPrivate::STATE_CREATETHUMB:
00379         {
00380             if (!d->tempName.isEmpty())
00381             {
00382                 TQFile::remove(d->tempName);
00383                 d->tempName = TQString::null;
00384             }
00385             determineNextFile();
00386             return;
00387         }
00388     }
00389 }
00390 
00391 bool PreviewJob::statResultThumbnail()
00392 {
00393     if ( d->thumbPath.isEmpty() )
00394         return false;
00395 
00396     KURL url = d->currentItem.item->url();
00397     // Don't include the password if any
00398     url.setPass(TQString::null);
00399     // The TMS defines local files as file:///path/to/file instead of KDE's
00400     // way (file:/path/to/file)
00401 #ifdef KURL_TRIPLE_SLASH_FILE_PROT
00402     d->origName = url.url();
00403 #else
00404     if (url.protocol() == "file") d->origName = "file://" + url.path();
00405     else d->origName = url.url();
00406 #endif
00407 
00408     KMD5 md5( TQFile::encodeName( d->origName ).data() );
00409     d->thumbName = TQFile::encodeName( md5.hexDigest() ) + ".png";
00410 
00411     TQImage thumb;
00412     if ( !thumb.load( d->thumbPath + d->thumbName ) ) return false;
00413 
00414     if ( thumb.text( "Thumb::URI", 0 ) != d->origName ||
00415          thumb.text( "Thumb::MTime", 0 ).toInt() != d->tOrig ) return false;
00416 
00417     // Found it, use it
00418     emitPreview( thumb );
00419     d->succeeded = true;
00420     determineNextFile();
00421     return true;
00422 }
00423 
00424 
00425 void PreviewJob::getOrCreateThumbnail()
00426 {
00427     // We still need to load the orig file ! (This is getting tedious) :)
00428     const KFileItem* item = d->currentItem.item;
00429     const TQString localPath = item->localPath();
00430     if ( !localPath.isEmpty() )
00431         createThumbnail( localPath );
00432     else
00433     {
00434         d->state = PreviewJobPrivate::STATE_GETORIG;
00435         KTempFile localFile;
00436         KURL localURL;
00437         localURL.setPath( d->tempName = localFile.name() );
00438         const KURL currentURL = item->url();
00439         TDEIO::Job * job = TDEIO::file_copy( currentURL, localURL, -1, true,
00440                                          false, false /* No GUI */ );
00441         job->addMetaData("thumbnail","1");
00442         addSubjob(job);
00443     }
00444 }
00445 
00446 // KDE 4: Make it const TQString &
00447 void PreviewJob::createThumbnail( TQString pixPath )
00448 {
00449     d->state = PreviewJobPrivate::STATE_CREATETHUMB;
00450     KURL thumbURL;
00451     thumbURL.setProtocol("thumbnail");
00452     thumbURL.setPath(pixPath);
00453     TDEIO::TransferJob *job = TDEIO::get(thumbURL, false, false);
00454     addSubjob(job);
00455     connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), TQT_SLOT(slotThumbData(TDEIO::Job *, const TQByteArray &)));
00456     bool save = d->bSave && d->currentItem.plugin->property("CacheThumbnail").toBool();
00457     job->addMetaData("mimeType", d->currentItem.item->mimetype());
00458     job->addMetaData("width", TQString().setNum(save ? d->cacheWidth : d->width));
00459     job->addMetaData("height", TQString().setNum(save ? d->cacheHeight : d->height));
00460     job->addMetaData("iconSize", TQString().setNum(save ? 64 : d->iconSize));
00461     job->addMetaData("iconAlpha", TQString().setNum(d->iconAlpha));
00462     job->addMetaData("plugin", d->currentItem.plugin->library());
00463 #ifdef Q_OS_UNIX
00464     if (d->shmid == -1)
00465     {
00466         if (d->shmaddr) {
00467             shmdt((char*)d->shmaddr);
00468             shmctl(d->shmid, IPC_RMID, 0);
00469         }
00470         d->shmid = shmget(IPC_PRIVATE, d->cacheWidth * d->cacheHeight * 4, IPC_CREAT|0600);
00471         if (d->shmid != -1)
00472         {
00473             d->shmaddr = (uchar *)(shmat(d->shmid, 0, SHM_RDONLY));
00474             if (d->shmaddr == (uchar *)-1)
00475             {
00476                 shmctl(d->shmid, IPC_RMID, 0);
00477                 d->shmaddr = 0;
00478                 d->shmid = -1;
00479             }
00480         }
00481         else
00482             d->shmaddr = 0;
00483     }
00484     if (d->shmid != -1)
00485         job->addMetaData("shmid", TQString().setNum(d->shmid));
00486 #endif
00487 }
00488 
00489 void PreviewJob::slotThumbData(TDEIO::Job *, const TQByteArray &data)
00490 {
00491     bool save = d->bSave &&
00492                 d->currentItem.plugin->property("CacheThumbnail").toBool() &&
00493                 (d->currentItem.item->url().protocol() != "file" ||
00494                  !d->currentItem.item->url().directory( false ).startsWith(d->thumbRoot));
00495     TQImage thumb;
00496 #ifdef Q_OS_UNIX
00497     if (d->shmaddr)
00498     {
00499         TQDataStream str(data, IO_ReadOnly);
00500         int width, height, depth;
00501         bool alpha;
00502         str >> width >> height >> depth >> alpha;
00503         thumb = TQImage(d->shmaddr, width, height, depth, 0, 0, TQImage::IgnoreEndian);
00504         thumb.setAlphaBuffer(alpha);
00505     }
00506     else
00507 #endif
00508         thumb.loadFromData(data);
00509 
00510     if (save)
00511     {
00512         thumb.setText("Thumb::URI", 0, d->origName);
00513         thumb.setText("Thumb::MTime", 0, TQString::number(d->tOrig));
00514         thumb.setText("Thumb::Size", 0, number(d->currentItem.item->size()));
00515         thumb.setText("Thumb::Mimetype", 0, d->currentItem.item->mimetype());
00516         thumb.setText("Software", 0, "KDE Thumbnail Generator");
00517         KTempFile temp(d->thumbPath + "kde-tmp-", ".png");
00518         if (temp.status() == 0) //Only try to write out the thumbnail if we
00519         {                       //actually created the temp file.
00520             thumb.save(temp.name(), "PNG");
00521             rename(TQFile::encodeName(temp.name()), TQFile::encodeName(d->thumbPath + d->thumbName));
00522         }
00523     }
00524     emitPreview( thumb );
00525     d->succeeded = true;
00526 }
00527 
00528 void PreviewJob::emitPreview(const TQImage &thumb)
00529 {
00530     TQPixmap pix;
00531     if (thumb.width() > d->width || thumb.height() > d->height)
00532     {
00533         double imgRatio = (double)thumb.height() / (double)thumb.width();
00534         if (imgRatio > (double)d->height / (double)d->width)
00535             pix.convertFromImage(
00536                 thumb.smoothScale((int)TQMAX((double)d->height / imgRatio, 1), d->height));
00537         else pix.convertFromImage(
00538             thumb.smoothScale(d->width, (int)TQMAX((double)d->width * imgRatio, 1)));
00539     }
00540     else pix.convertFromImage(thumb);
00541     emit gotPreview(d->currentItem.item, pix);
00542 }
00543 
00544 void PreviewJob::emitFailed(const KFileItem *item)
00545 {
00546     if (!item)
00547         item = d->currentItem.item;
00548     emit failed(item);
00549 }
00550 
00551 TQStringList PreviewJob::availablePlugins()
00552 {
00553     TQStringList result;
00554     TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00555     for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00556         if (!result.contains((*it)->desktopEntryName()))
00557             result.append((*it)->desktopEntryName());
00558     return result;
00559 }
00560 
00561 TQStringList PreviewJob::supportedMimeTypes()
00562 {
00563     TQStringList result;
00564     TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
00565     for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
00566         result += (*it)->property("MimeTypes").toStringList();
00567     return result;
00568 }
00569 
00570 void PreviewJob::kill( bool quietly )
00571 {
00572     d->startPreviewTimer.stop();
00573     Job::kill( quietly );
00574 }
00575 
00576 PreviewJob *TDEIO::filePreview( const KFileItemList &items, int width, int height,
00577     int iconSize, int iconAlpha, bool scale, bool save,
00578     const TQStringList *enabledPlugins )
00579 {
00580     return new PreviewJob(items, width, height, iconSize, iconAlpha,
00581                           scale, save, enabledPlugins);
00582 }
00583 
00584 PreviewJob *TDEIO::filePreview( const KURL::List &items, int width, int height,
00585     int iconSize, int iconAlpha, bool scale, bool save,
00586     const TQStringList *enabledPlugins )
00587 {
00588     KFileItemList fileItems;
00589     for (KURL::List::ConstIterator it = items.begin(); it != items.end(); ++it)
00590         fileItems.append(new KFileItem(KFileItem::Unknown, KFileItem::Unknown, *it, true));
00591     return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
00592                           scale, save, enabledPlugins, true);
00593 }
00594 
00595 void PreviewJob::virtual_hook( int id, void* data )
00596 { TDEIO::Job::virtual_hook( id, data ); }
00597 

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.7.1
This website is maintained by Timothy Pearson.