• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

ksvgiconengine.cpp
00001 /*
00002     Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
00003     This file is part of the KDE project
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., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <tqdom.h>
00022 #include <tqfile.h>
00023 #include <tqcolor.h>
00024 #include <tqimage.h>
00025 #include <tqwmatrix.h>
00026 #include <tqregexp.h>
00027 
00028 #include <kmdcodec.h>
00029 
00030 #include <zlib.h>
00031 
00032 #include "ksvgiconpainter.h"
00033 #include "ksvgiconengine.h"
00034 
00035 class KSVGIconEngineHelper
00036 {
00037 public:
00038     KSVGIconEngineHelper(KSVGIconEngine *engine)
00039     {
00040         m_engine = engine;
00041     }
00042 
00043     ~KSVGIconEngineHelper()
00044     {
00045     }
00046 
00047     double toPixel(const TQString &s, bool hmode)
00048     {
00049         return m_engine->painter()->toPixel(s, hmode);
00050     }
00051 
00052     ArtGradientStop *parseGradientStops(TQDomElement element, int &offsets)
00053     {
00054         if (!element.hasChildNodes())
00055             return 0;
00056 
00057         TQValueList<ArtGradientStop> stopList;
00058 
00059         float oldOffset = -1, newOffset = -1;
00060         for(TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
00061         {
00062             TQDomElement element = node.toElement();
00063 
00064             oldOffset = newOffset;
00065             TQString temp = element.attribute("offset");
00066 
00067             if(temp.contains("%"))
00068             {
00069                 temp = temp.left(temp.length() - 1);
00070                 newOffset = temp.toFloat() / 100.0;
00071             }
00072             else
00073                 newOffset = temp.toFloat();
00074 
00075             // Spec  skip double offset specifications
00076             if(oldOffset == newOffset)
00077                 continue;
00078 
00079             offsets++;
00080             stopList.append(ArtGradientStop());
00081 
00082             ArtGradientStop &stop = stopList.last();
00083 
00084             stop.offset = newOffset;
00085 
00086             TQString parseOpacity;
00087             TQString parseColor;
00088 
00089             if(element.hasAttribute("stop-opacity"))
00090                 parseOpacity = element.attribute("stop-opacity");
00091 
00092             if(element.hasAttribute("stop-color"))
00093                 parseColor = element.attribute("stop-color");
00094 
00095             if(parseOpacity.isEmpty() || parseColor.isEmpty())
00096             {
00097                 TQString style = element.attribute("style");
00098 
00099                 TQStringList substyles = TQStringList::split(';', style);
00100                 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
00101                 {
00102                     TQStringList substyle = TQStringList::split(':', (*it));
00103                     TQString command = substyle[0];
00104                     TQString params = substyle[1];
00105                     command = command.stripWhiteSpace();
00106                     params = params.stripWhiteSpace();
00107 
00108                     if(command == "stop-color")
00109                     {
00110                         parseColor = params;
00111 
00112                         if(!parseOpacity.isEmpty())
00113                             break;
00114                     }
00115                     else if(command == "stop-opacity")
00116                     {
00117                         parseOpacity = params;
00118 
00119                         if(!parseColor.isEmpty())
00120                             break;
00121                     }
00122                 }
00123             }
00124 
00125             // Parse color using KSVGIconPainter (which uses Qt)
00126             // Supports all svg-needed color formats
00127             TQColor qStopColor = m_engine->painter()->parseColor(parseColor);
00128 
00129             // Convert in a libart suitable form
00130             TQ_UINT32 stopColor = m_engine->painter()->toArtColor(qStopColor);
00131 
00132             int opacity = m_engine->painter()->parseOpacity(parseOpacity);
00133 
00134             TQ_UINT32 rgba = (stopColor << 8) | opacity;
00135             TQ_UINT32 r, g, b, a;
00136 
00137             // Convert from separated to premultiplied alpha
00138             a = rgba & 0xff;
00139             r = (rgba >> 24) * a + 0x80;
00140             r = (r + (r >> 8)) >> 8;
00141             g = ((rgba >> 16) & 0xff) * a + 0x80;
00142             g = (g + (g >> 8)) >> 8;
00143             b = ((rgba >> 8) & 0xff) * a + 0x80;
00144             b = (b + (b >> 8)) >> 8;
00145 
00146             stop.color[0] = ART_PIX_MAX_FROM_8(r);
00147             stop.color[1] = ART_PIX_MAX_FROM_8(g);
00148             stop.color[2] = ART_PIX_MAX_FROM_8(b);
00149             stop.color[3] = ART_PIX_MAX_FROM_8(a);
00150         }
00151 
00152         if (stopList.isEmpty())
00153             return 0;
00154 
00155         ArtGradientStop *stops = new ArtGradientStop[stopList.count()];
00156 
00157         TQValueList<ArtGradientStop>::iterator it = stopList.begin();
00158         TQValueList<ArtGradientStop>::iterator end = stopList.end();
00159 
00160         for (int i = 0; it != end; ++i, ++it)
00161             stops[i] = *it;
00162 
00163         return stops;
00164     }
00165 
00166     TQPointArray parsePoints(TQString points)
00167     {
00168         if(points.isEmpty())
00169             return TQPointArray();
00170 
00171         points = points.simplifyWhiteSpace();
00172 
00173         if(points.contains(",,") || points.contains(", ,"))
00174             return TQPointArray();
00175 
00176         points.replace(',', ' ');
00177         points.replace('\r', TQString());
00178         points.replace('\n', TQString());
00179 
00180         points = points.simplifyWhiteSpace();
00181 
00182         TQStringList pointList = TQStringList::split(' ', points);
00183 
00184         TQPointArray array(pointList.count() / 2);
00185         int i = 0;
00186 
00187         for(TQStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
00188         {
00189             float x = (*(it++)).toFloat();
00190             float y = (*(it)).toFloat();
00191 
00192             array.setPoint(i, static_cast<int>(x), static_cast<int>(y));
00193             i++;
00194         }
00195 
00196         return array;
00197     }
00198 
00199     void parseTransform(const TQString &transform)
00200     {
00201         // Combine new and old matrix
00202         TQWMatrix matrix = m_engine->painter()->parseTransform(transform);
00203 
00204         TQWMatrix *current = m_engine->painter()->worldMatrix();
00205 #ifdef USE_QT4
00206 printf("[FIXME] *current = matrix * *current locks up under Qt4; bypassing for now\n");
00207 #else // USE_QT4
00208         *current = matrix * *current;
00209 #endif // USE_QT4
00210     }
00211 
00212     void parseCommonAttributes(TQDomNode &node)
00213     {
00214         // Set important default attributes
00215         m_engine->painter()->setFillColor("black");
00216         m_engine->painter()->setStrokeColor("none");
00217         m_engine->painter()->setStrokeDashArray("");
00218         m_engine->painter()->setStrokeWidth(1);
00219         m_engine->painter()->setJoinStyle("");
00220         m_engine->painter()->setCapStyle("");
00221     //  m_engine->painter()->setFillOpacity(255, true);
00222     //  m_engine->painter()->setStrokeOpacity(255, true);
00223 
00224         // Collect parent node's attributes
00225         TQPtrList<TQDomNamedNodeMap> applyList;
00226         applyList.setAutoDelete(true);
00227 
00228         TQDomNode shape = node.parentNode();
00229         for(; !shape.isNull() ; shape = shape.parentNode())
00230             applyList.prepend(new TQDomNamedNodeMap(shape.attributes()));
00231 
00232         // Apply parent attributes
00233         for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
00234         {
00235             TQDomNamedNodeMap attr = *map;
00236 
00237             for(unsigned int i = 0; i < attr.count(); i++)
00238             {
00239                 TQString name, value;
00240 
00241                 name = attr.item(i).nodeName().lower();
00242                 value = attr.item(i).nodeValue();
00243 
00244                 if(name == "transform")
00245                     parseTransform(value);
00246                 else if(name == "style")
00247                     parseStyle(value);
00248                 else
00249                     parsePA(name, value);
00250             }
00251         }
00252 
00253         // Apply local attributes
00254         TQDomNamedNodeMap attr = node.attributes();
00255 
00256         for(unsigned int i = 0; i < attr.count(); i++)
00257         {
00258             TQDomNode current = attr.item(i);
00259 
00260             if(current.nodeName().lower() == "transform")
00261                 parseTransform(current.nodeValue());
00262             else if(current.nodeName().lower() == "style")
00263                 parseStyle(current.nodeValue());
00264             else
00265                 parsePA(current.nodeName().lower(), current.nodeValue());
00266         }
00267     }
00268 
00269     bool handleTags(TQDomElement element, bool paint)
00270     {
00271         if(element.attribute("display") == "none")
00272             return false;
00273         if(element.tagName() == "linearGradient")
00274         {
00275             ArtGradientLinear *gradient = new ArtGradientLinear();
00276 
00277             int offsets = -1;
00278             gradient->stops = parseGradientStops(element, offsets);
00279             gradient->n_stops = offsets + 1;
00280 
00281             TQString spread = element.attribute("spreadMethod");
00282             if(spread == "repeat")
00283                 gradient->spread = ART_GRADIENT_REPEAT;
00284             else if(spread == "reflect")
00285                 gradient->spread = ART_GRADIENT_REFLECT;
00286             else
00287                 gradient->spread = ART_GRADIENT_PAD;
00288 
00289             m_engine->painter()->addLinearGradient(element.attribute("id"), gradient);
00290             m_engine->painter()->addLinearGradientElement(gradient, element);
00291             return true;
00292         }
00293         else if(element.tagName() == "radialGradient")
00294         {
00295             ArtGradientRadial *gradient = new ArtGradientRadial();
00296 
00297             int offsets = -1;
00298             gradient->stops = parseGradientStops(element, offsets);
00299             gradient->n_stops = offsets + 1;
00300 
00301             m_engine->painter()->addRadialGradient(element.attribute("id"), gradient);
00302             m_engine->painter()->addRadialGradientElement(gradient, element);
00303             return true;
00304         }
00305 
00306         if(!paint)
00307             return true;
00308 
00309         // TODO: Default attribute values
00310         if(element.tagName() == "rect")
00311         {
00312             double x = toPixel(element.attribute("x"), true);
00313             double y = toPixel(element.attribute("y"), false);
00314             double w = toPixel(element.attribute("width"), true);
00315             double h = toPixel(element.attribute("height"), false);
00316 
00317             double rx = 0.0;
00318             double ry = 0.0;
00319 
00320             if(element.hasAttribute("rx"))
00321                 rx = toPixel(element.attribute("rx"), true);
00322 
00323             if(element.hasAttribute("ry"))
00324                 ry = toPixel(element.attribute("ry"), false);
00325 
00326             m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
00327         }
00328         else if(element.tagName() == "switch")
00329         {
00330             TQDomNode iterate = element.firstChild();
00331 
00332             while(!iterate.isNull())
00333             {
00334                 // Reset matrix
00335                 m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
00336 
00337                 // Parse common attributes, style / transform
00338                 parseCommonAttributes(iterate);
00339 
00340                 if(handleTags(iterate.toElement(), true))
00341                     return true;
00342                 iterate = iterate.nextSibling();
00343             }
00344             return true;
00345         }
00346         else if(element.tagName() == "g" || element.tagName() == "defs")
00347         {
00348             TQDomNode iterate = element.firstChild();
00349 
00350             while(!iterate.isNull())
00351             {
00352                 // Reset matrix
00353                 m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
00354 
00355                 // Parse common attributes, style / transform
00356                 parseCommonAttributes(iterate);
00357 
00358                 handleTags(iterate.toElement(), (element.tagName() == "defs") ? false : true);
00359                 iterate = iterate.nextSibling();
00360             }
00361             return true;
00362         }
00363         else if(element.tagName() == "line")
00364         {
00365             double x1 = toPixel(element.attribute("x1"), true);
00366             double y1 = toPixel(element.attribute("y1"), false);
00367             double x2 = toPixel(element.attribute("x2"), true);
00368             double y2 = toPixel(element.attribute("y2"), false);
00369 
00370             m_engine->painter()->drawLine(x1, y1, x2, y2);
00371             return true;
00372         }
00373         else if(element.tagName() == "circle")
00374         {
00375             double cx = toPixel(element.attribute("cx"), true);
00376             double cy = toPixel(element.attribute("cy"), false);
00377 
00378             double r = toPixel(element.attribute("r"), true); // TODO: horiz correct?
00379 
00380             m_engine->painter()->drawEllipse(cx, cy, r, r);
00381             return true;
00382         }
00383         else if(element.tagName() == "ellipse")
00384         {
00385             double cx = toPixel(element.attribute("cx"), true);
00386             double cy = toPixel(element.attribute("cy"), false);
00387 
00388             double rx = toPixel(element.attribute("rx"), true);
00389             double ry = toPixel(element.attribute("ry"), false);
00390 
00391             m_engine->painter()->drawEllipse(cx, cy, rx, ry);
00392             return true;
00393         }
00394         else if(element.tagName() == "polyline")
00395         {
00396             TQPointArray polyline = parsePoints(element.attribute("points"));
00397             m_engine->painter()->drawPolyline(polyline);
00398             return true;
00399         }
00400         else if(element.tagName() == "polygon")
00401         {
00402             TQPointArray polygon = parsePoints(element.attribute("points"));
00403             m_engine->painter()->drawPolygon(polygon);
00404             return true;
00405         }
00406         else if(element.tagName() == "path")
00407         {
00408             bool filled = true;
00409 
00410             if(element.hasAttribute("fill") && element.attribute("fill").contains("none"))
00411                 filled = false;
00412 
00413             if(element.attribute("style").contains("fill") && element.attribute("style").stripWhiteSpace().contains("fill:none"))
00414                 filled = false;
00415 
00416             m_engine->painter()->drawPath(element.attribute("d"), filled);
00417             return true;
00418         }
00419         else if(element.tagName() == "image")
00420         {
00421             double x = toPixel(element.attribute("x"), true);
00422             double y = toPixel(element.attribute("y"), false);
00423             double w = toPixel(element.attribute("width"), true);
00424             double h = toPixel(element.attribute("height"), false);
00425 
00426             TQString href = element.attribute("xlink:href");
00427 
00428             TQImage image;
00429             if(href.startsWith("data:"))
00430             {
00431                 // Get input
00432                 TQCString input = TQString(href.remove(TQRegExp("^data:image/.*;base64,"))).utf8();
00433 
00434                 // Decode into 'output'
00435                 TQByteArray output;
00436                 KCodecs::base64Decode(input, output);
00437 
00438                 // Display
00439                 image.loadFromData(output);
00440             }
00441             else
00442                 image.load(href);
00443 
00444             if (!image.isNull())
00445             {
00446                 // Scale, if needed
00447                 if(image.width() != (int) w || image.height() != (int) h)
00448                     image = image.smoothScale((int) w, (int) h, TQ_ScaleFree);
00449 
00450                 m_engine->painter()->drawImage(x, y, image);
00451             }
00452 
00453             return true;
00454         }
00455         return false;
00456     }
00457 
00458     void parseStyle(const TQString &style)
00459     {
00460         TQStringList substyles = TQStringList::split(';', style);
00461         for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
00462         {
00463             TQStringList substyle = TQStringList::split(':', (*it));
00464             TQString command = substyle[0];
00465             TQString params = substyle[1];
00466             command = command.stripWhiteSpace();
00467             params = params.stripWhiteSpace();
00468 
00469             parsePA(command, params);
00470         }
00471     }
00472 
00473     void parsePA(const TQString &command, const TQString &value)
00474     {
00475         if(command == "stroke-width") // TODO: horiz:false correct?
00476             m_engine->painter()->setStrokeWidth(toPixel(value, false));
00477         else if(command == "stroke-miterlimit")
00478             m_engine->painter()->setStrokeMiterLimit(value);
00479         else if(command == "stroke-linecap")
00480             m_engine->painter()->setCapStyle(value);
00481         else if(command == "stroke-linejoin")
00482             m_engine->painter()->setJoinStyle(value);
00483         else if(command == "stroke-dashoffset")
00484             m_engine->painter()->setStrokeDashOffset(value);
00485         else if(command == "stroke-dasharray" && value != "none")
00486             m_engine->painter()->setStrokeDashArray(value);
00487         else if(command == "stroke")
00488             m_engine->painter()->setStrokeColor(value);
00489         else if(command == "fill")
00490             m_engine->painter()->setFillColor(value);
00491         else if(command == "fill-rule")
00492             m_engine->painter()->setFillRule(value);
00493         else if(command == "fill-opacity" || command == "stroke-opacity" || command == "opacity")
00494         {
00495             if(command == "fill-opacity")
00496                 m_engine->painter()->setFillOpacity(value);
00497             else if(command == "stroke-value")
00498                 m_engine->painter()->setStrokeOpacity(value);
00499             else
00500             {
00501                 m_engine->painter()->setOpacity(value);
00502                 m_engine->painter()->setFillOpacity(value);
00503                 m_engine->painter()->setStrokeOpacity(value);
00504             }
00505         }
00506     }
00507 
00508 private:
00509     friend class KSVGIconEngine;
00510 
00511     KSVGIconEngine *m_engine;
00512     TQWMatrix m_initialMatrix;
00513 };
00514 
00515 struct KSVGIconEngine::Private
00516 {
00517     KSVGIconPainter *painter;
00518     KSVGIconEngineHelper *helper;
00519 
00520     double width;
00521     double height;
00522 };
00523 
00524 KSVGIconEngine::KSVGIconEngine() : d(new Private())
00525 {
00526     d->painter = 0;
00527     d->helper = new KSVGIconEngineHelper(this);
00528 
00529     d->width = 0.0;
00530     d->height = 0.0;
00531 }
00532 
00533 KSVGIconEngine::~KSVGIconEngine()
00534 {
00535     if(d->painter)
00536         delete d->painter;
00537 
00538     delete d->helper;
00539 
00540     delete d;
00541 }
00542 
00543 bool KSVGIconEngine::load(int width, int height, const TQString &path)
00544 {
00545     if(path.isNull()) return false;
00546 
00547     TQDomDocument svgDocument("svg");
00548     TQFile file(path);
00549 
00550     if(path.right(3).upper() == "SVG")
00551     {
00552         // Open SVG Icon
00553         if(!file.open(IO_ReadOnly))
00554             return false;
00555 
00556         svgDocument.setContent(&file);
00557     }
00558     else // SVGZ
00559     {
00560         gzFile svgz = gzopen(path.latin1(), "ro");
00561         if(!svgz)
00562             return false;
00563 
00564         TQString data;
00565         bool done = false;
00566 
00567         TQCString buffer(1024);
00568         int length = 0;
00569 
00570         while(!done)
00571         {
00572             int ret = gzread(svgz, buffer.data() + length, 1024);
00573             if(ret == 0)
00574                 done = true;
00575             else if(ret == -1)
00576                 return false;
00577             else {
00578                 buffer.resize(buffer.size()+1024);
00579                 length += ret;
00580             }
00581         }
00582 
00583         gzclose(svgz);
00584 
00585         svgDocument.setContent(buffer);
00586     }
00587 
00588     if(svgDocument.isNull())
00589         return false;
00590 
00591     // Check for root element
00592     TQDomNode rootNode = svgDocument.namedItem("svg");
00593     if(rootNode.isNull() || !rootNode.isElement())
00594         return false;
00595 
00596     // Detect width and height
00597     TQDomElement rootElement = rootNode.toElement();
00598 
00599     // Create icon painter
00600     d->painter = new KSVGIconPainter(width, height);
00601 
00602     d->width = width; // this sets default for no width -> 100% case
00603     if(rootElement.hasAttribute("width"))
00604         d->width = d->helper->toPixel(rootElement.attribute("width"), true);
00605 
00606     d->height = height; // this sets default for no height -> 100% case
00607     if(rootElement.hasAttribute("height"))
00608         d->height = d->helper->toPixel(rootElement.attribute("height"), false);
00609 
00610     // Create icon painter
00611     d->painter->setDrawWidth(static_cast<int>(d->width));
00612     d->painter->setDrawHeight(static_cast<int>(d->height));
00613 
00614     // Set viewport clipping rect
00615     d->painter->setClippingRect(0, 0, width, height);
00616 
00617     // Apply viewbox
00618     if(rootElement.hasAttribute("viewBox"))
00619     {
00620         TQStringList points = TQStringList::split(' ', rootElement.attribute("viewBox").simplifyWhiteSpace());
00621 
00622         float w = points[2].toFloat();
00623         float h = points[3].toFloat();
00624 
00625         double vratiow = width / w;
00626         double vratioh = height / h;
00627 
00628         d->width = w;
00629         d->height = h;
00630 
00631         d->painter->worldMatrix()->scale(vratiow, vratioh);
00632     }
00633     else
00634     {
00635         // Fit into 'width' and 'height'
00636         // FIXME: Use an aspect ratio
00637         double ratiow = width / d->width;
00638         double ratioh = height / d->height;
00639 
00640         d->painter->worldMatrix()->scale(ratiow, ratioh);
00641     }
00642 
00643     TQWMatrix initialMatrix = *d->painter->worldMatrix();
00644     d->helper->m_initialMatrix = initialMatrix;
00645 
00646     // Apply transform
00647     if(rootElement.hasAttribute("transform"))
00648         d->helper->parseTransform(rootElement.attribute("transform"));
00649 
00650     // Go through all elements
00651     TQDomNode svgNode = rootElement.firstChild();
00652     while(!svgNode.isNull())
00653     {
00654         TQDomElement svgChild = svgNode.toElement();
00655         if(!svgChild.isNull())
00656         {
00657             d->helper->parseCommonAttributes(svgNode);
00658             d->helper->handleTags(svgChild, true);
00659         }
00660 
00661         svgNode = svgNode.nextSibling();
00662 
00663         // Reset matrix
00664         d->painter->setWorldMatrix(new TQWMatrix(initialMatrix));
00665     }
00666 
00667     d->painter->finish();
00668 
00669     return true;
00670 }
00671 
00672 KSVGIconPainter *KSVGIconEngine::painter()
00673 {
00674     return d->painter;
00675 }
00676 
00677 TQImage *KSVGIconEngine::image()
00678 {
00679     return d->painter->image();
00680 }
00681 
00682 double KSVGIconEngine::width()
00683 {
00684     return d->width;
00685 }
00686 
00687 double KSVGIconEngine::height()
00688 {
00689     return d->height;
00690 }
00691 
00692 // vim:ts=4:noet

tdecore

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

tdecore

Skip menu "tdecore"
  • 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 tdecore by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.