• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Kate

katetextbuffer.cpp

Go to the documentation of this file.
00001 /*  This file is part of the Kate project.
00002  *
00003  *  Copyright (C) 2010 Christoph Cullmann <cullmann@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., 51 Franklin Street, Fifth Floor,
00018  *  Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "config.h"
00022 
00023 #include "katetextbuffer.h"
00024 #include "katetextloader.h"
00025 
00026 // this is unfortunate, but needed for performance
00027 #include "katedocument.h"
00028 #include "kateview.h"
00029 
00030 #include <kde_file.h>
00031 
00032 namespace Kate {
00033 
00034 TextBuffer::TextBuffer (KTextEditor::Document *parent, int blockSize)
00035   : QObject (parent)
00036   , m_document (parent)
00037   , m_history (*this)
00038   , m_blockSize (blockSize)
00039   , m_lines (0)
00040   , m_lastUsedBlock (0)
00041   , m_revision (0)
00042   , m_editingTransactions (0)
00043   , m_editingLastRevision (0)
00044   , m_editingLastLines (0)
00045   , m_editingMinimalLineChanged (-1)
00046   , m_editingMaximalLineChanged (-1)
00047   , m_encodingProberType (KEncodingProber::Universal)
00048   , m_fallbackTextCodec (0)
00049   , m_textCodec (0)
00050   , m_generateByteOrderMark (false)
00051   , m_endOfLineMode (eolUnix)
00052   , m_removeTrailingSpaces (false)
00053 {
00054   // minimal block size must be > 0
00055   Q_ASSERT (m_blockSize > 0);
00056 
00057   // create initial state
00058   clear ();
00059 }
00060 
00061 TextBuffer::~TextBuffer ()
00062 {
00063   // remove document pointer, this will avoid any notifyAboutRangeChange to have a effect
00064   m_document = 0;
00065 
00066   // not allowed during editing
00067   Q_ASSERT (m_editingTransactions == 0);
00068 
00069   // kill all ranges, work on copy, they will remove themself from the hash
00070   QSet<TextRange *> copyRanges = m_ranges;
00071   qDeleteAll (copyRanges);
00072   Q_ASSERT (m_ranges.empty());
00073 
00074   // clean out all cursors and lines, only cursors belonging to range will survive
00075   foreach(TextBlock* block, m_blocks)
00076     block->deleteBlockContent ();
00077 
00078   // delete all blocks, now that all cursors are really deleted
00079   // else asserts in destructor of blocks will fail!
00080   qDeleteAll (m_blocks);
00081   m_blocks.clear ();
00082 
00083   // kill all invalid cursors, do this after block deletion, to uncover if they might be still linked in blocks
00084   QSet<TextCursor *> copyCursors = m_invalidCursors;
00085   qDeleteAll (copyCursors);
00086   Q_ASSERT (m_invalidCursors.empty());
00087 }
00088 
00089 void TextBuffer::invalidateRanges()
00090 {
00091   // invalidate all ranges, work on copy, they might delete themself...
00092   QSet<TextRange *> copyRanges = m_ranges;
00093   foreach (TextRange *range, copyRanges)
00094     range->setRange (KTextEditor::Cursor::invalid(), KTextEditor::Cursor::invalid());
00095 }
00096 
00097 void TextBuffer::clear ()
00098 {
00099   // not allowed during editing
00100   Q_ASSERT (m_editingTransactions == 0);
00101 
00102   invalidateRanges();
00103 
00104   // new block for empty buffer
00105   TextBlock *newBlock = new TextBlock (this, 0);
00106   newBlock->appendLine (TextLine (new TextLineData()));
00107 
00108   // clean out all cursors and lines, either move them to newBlock or invalidate them, if belonging to a range
00109   foreach(TextBlock* block, m_blocks)
00110     block->clearBlockContent (newBlock);
00111 
00112   // kill all buffer blocks
00113   qDeleteAll (m_blocks);
00114   m_blocks.clear ();
00115 
00116   // insert one block with one empty line
00117   m_blocks.append (newBlock);
00118 
00119   // reset lines and last used block
00120   m_lines = 1;
00121   m_lastUsedBlock = 0;
00122 
00123   // reset revision
00124   m_revision = 0;
00125 
00126   // reset bom detection
00127   m_generateByteOrderMark = false;
00128 
00129   // reset the filter device
00130   m_mimeTypeForFilterDev = "text/plain";
00131 
00132   // clear edit history
00133   m_history.clear ();
00134 
00135   // we got cleared
00136   emit cleared ();
00137 }
00138 
00139 TextLine TextBuffer::line (int line) const
00140 {
00141   // get block, this will assert on invalid line
00142   int blockIndex = blockForLine (line);
00143 
00144   // get line
00145   return m_blocks.at(blockIndex)->line (line);
00146 }
00147 
00148 QString TextBuffer::text () const
00149 {
00150   QString text;
00151 
00152   // combine all blocks
00153   foreach(TextBlock* block, m_blocks)
00154     block->text (text);
00155 
00156   // return generated string
00157   return text;
00158 }
00159 
00160 bool TextBuffer::startEditing ()
00161 {
00162   // increment transaction counter
00163   ++m_editingTransactions;
00164 
00165   // if not first running transaction, do nothing
00166   if (m_editingTransactions > 1)
00167     return false;
00168 
00169   // reset informations about edit...
00170   m_editingLastRevision = m_revision;
00171   m_editingLastLines = m_lines;
00172   m_editingMinimalLineChanged = -1;
00173   m_editingMaximalLineChanged = -1;
00174 
00175   // transaction has started
00176   emit editingStarted ();
00177 
00178   // first transaction started
00179   return true;
00180 }
00181 
00182 bool TextBuffer::finishEditing ()
00183 {
00184   // only allowed if still transactions running
00185   Q_ASSERT (m_editingTransactions > 0);
00186 
00187   // decrement counter
00188   --m_editingTransactions;
00189 
00190   // if not last running transaction, do nothing
00191   if (m_editingTransactions > 0)
00192     return false;
00193 
00194   // assert that if buffer changed, the line ranges are set and valid!
00195   Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged != -1 && m_editingMaximalLineChanged != -1));
00196   Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged <= m_editingMaximalLineChanged));
00197   Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged >= 0 && m_editingMinimalLineChanged < m_lines));
00198   Q_ASSERT (!editingChangedBuffer() || (m_editingMaximalLineChanged >= 0 && m_editingMaximalLineChanged < m_lines));
00199 
00200   // transaction has finished
00201   emit editingFinished ();
00202 
00203   // last transaction finished
00204   return true;
00205 }
00206 
00207 void TextBuffer::wrapLine (const KTextEditor::Cursor &position)
00208 {
00209   // only allowed if editing transaction running
00210   Q_ASSERT (m_editingTransactions > 0);
00211 
00212   // get block, this will assert on invalid line
00213   int blockIndex = blockForLine (position.line());
00214 
00215   // let the block handle the wrapLine
00216   // this can only lead to one more line in this block
00217   // no other blocks will change
00218   ++m_lines; // first alter the line counter, as functions called will need the valid one
00219   m_blocks.at(blockIndex)->wrapLine (position);
00220 
00221   // remember changes
00222   ++m_revision;
00223 
00224   // update changed line interval
00225   if (position.line() < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1)
00226     m_editingMinimalLineChanged = position.line();
00227 
00228   if (position.line() <= m_editingMaximalLineChanged)
00229     ++m_editingMaximalLineChanged;
00230   else
00231     m_editingMaximalLineChanged = position.line() + 1;
00232 
00233   // fixup all following blocks
00234   fixStartLines (blockIndex);
00235 
00236   // balance the changed block if needed
00237   balanceBlock (blockIndex);
00238 
00239   // emit signal about done change
00240   emit lineWrapped (position);
00241 }
00242 
00243 void TextBuffer::unwrapLine (int line)
00244 {
00245   // only allowed if editing transaction running
00246   Q_ASSERT (m_editingTransactions > 0);
00247 
00248   // line 0 can't be unwrapped
00249   Q_ASSERT (line > 0);
00250 
00251   // get block, this will assert on invalid line
00252   int blockIndex = blockForLine (line);
00253 
00254   // is this the first line in the block?
00255   bool firstLineInBlock = (line == m_blocks.at(blockIndex)->startLine());
00256 
00257   // let the block handle the unwrapLine
00258   // this can either lead to one line less in this block or the previous one
00259   // the previous one could even end up with zero lines
00260   m_blocks.at(blockIndex)->unwrapLine (line, (blockIndex > 0) ? m_blocks.at(blockIndex-1) : 0);
00261   --m_lines;
00262 
00263   // decrement index for later fixup, if we modified the block in front of the found one
00264   if (firstLineInBlock)
00265     --blockIndex;
00266 
00267   // remember changes
00268   ++m_revision;
00269 
00270   // update changed line interval
00271    if ((line - 1) < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1)
00272     m_editingMinimalLineChanged = line - 1;
00273 
00274   if (line <= m_editingMaximalLineChanged)
00275     --m_editingMaximalLineChanged;
00276   else
00277     m_editingMaximalLineChanged = line -1;
00278 
00279   // fixup all following blocks
00280   fixStartLines (blockIndex);
00281 
00282   // balance the changed block if needed
00283   balanceBlock (blockIndex);
00284 
00285   // emit signal about done change
00286   emit lineUnwrapped (line);
00287 }
00288 
00289 void TextBuffer::insertText (const KTextEditor::Cursor &position, const QString &text)
00290 {
00291   // only allowed if editing transaction running
00292   Q_ASSERT (m_editingTransactions > 0);
00293 
00294   // skip work, if no text to insert
00295   if (text.isEmpty())
00296     return;
00297 
00298   // get block, this will assert on invalid line
00299   int blockIndex = blockForLine (position.line());
00300 
00301   // let the block handle the insertText
00302   m_blocks.at(blockIndex)->insertText (position, text);
00303 
00304   // remember changes
00305   ++m_revision;
00306 
00307   // update changed line interval
00308   if (position.line () < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1)
00309     m_editingMinimalLineChanged = position.line ();
00310 
00311   if (position.line () > m_editingMaximalLineChanged)
00312     m_editingMaximalLineChanged = position.line ();
00313 
00314   // emit signal about done change
00315   emit textInserted (position, text);
00316 }
00317 
00318 void TextBuffer::removeText (const KTextEditor::Range &range)
00319 {
00320   // only allowed if editing transaction running
00321   Q_ASSERT (m_editingTransactions > 0);
00322 
00323   // only ranges on one line are supported
00324   Q_ASSERT (range.start().line() == range.end().line());
00325 
00326   // start colum <= end column and >= 0
00327   Q_ASSERT (range.start().column() <= range.end().column());
00328   Q_ASSERT (range.start().column() >= 0);
00329 
00330   // skip work, if no text to remove
00331   if (range.isEmpty())
00332     return;
00333 
00334   // get block, this will assert on invalid line
00335   int blockIndex = blockForLine (range.start().line());
00336 
00337   // let the block handle the removeText, retrieve removed text
00338   QString text;
00339   m_blocks.at(blockIndex)->removeText (range, text);
00340 
00341   // remember changes
00342   ++m_revision;
00343 
00344   // update changed line interval
00345   if (range.start().line() < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1)
00346     m_editingMinimalLineChanged = range.start().line();
00347 
00348   if (range.start().line() > m_editingMaximalLineChanged)
00349     m_editingMaximalLineChanged = range.start().line();
00350 
00351   // emit signal about done change
00352   emit textRemoved (range, text);
00353 }
00354 
00355 int TextBuffer::blockForLine (int line) const
00356 {
00357   // only allow valid lines
00358   Q_ASSERT (line >= 0);
00359   Q_ASSERT (line < lines());
00360 
00361   // block to start search with
00362   int index = m_lastUsedBlock;
00363   int blockStart = 0;
00364   int blockEnd = m_blocks.size();
00365 
00366   // check if start is ok
00367   if (index < 0 || index >= m_blocks.size())
00368     index = 0;
00369 
00370   // search for right block
00371   forever {
00372     // facts bout this block
00373     TextBlock* block = m_blocks.at(index);
00374     const int start = block->startLine();
00375     const int lines = block->lines ();
00376 
00377     // right block found, remember it and return it
00378     if (start <= line && line < (start + lines)) {
00379       m_lastUsedBlock = index;
00380       return index;
00381     }
00382 
00383     if (line < start)
00384     {
00385       // Search left of index
00386       blockEnd = index;
00387     }else{
00388       // Search right of index
00389       blockStart = index+1;
00390     }
00391     index = (blockStart + (blockEnd-1)) / 2;
00392   }
00393 
00394   // we should always find a block
00395   Q_ASSERT (false);
00396   return -1;
00397 }
00398 
00399 void TextBuffer::fixStartLines (int startBlock)
00400 {
00401   // only allow valid start block
00402   Q_ASSERT (startBlock >= 0);
00403   Q_ASSERT (startBlock < m_blocks.size());
00404 
00405   // new start line for next block
00406   TextBlock* block = m_blocks.at(startBlock);
00407   int newStartLine = block->startLine () + block->lines ();
00408 
00409   // fixup block
00410   for (int index = startBlock + 1; index < m_blocks.size(); ++index) {
00411     // set new start line
00412     block = m_blocks.at(index);
00413     block->setStartLine (newStartLine);
00414 
00415     // calculate next start line
00416     newStartLine += block->lines ();
00417   }
00418 }
00419 
00420 void TextBuffer::balanceBlock (int index)
00421 {
00425   TextBlock *blockToBalance = m_blocks.at(index);
00426 
00427   // first case, too big one, split it
00428   if (blockToBalance->lines () >= 2 * m_blockSize) {
00429     // half the block
00430     int halfSize = blockToBalance->lines () / 2;
00431 
00432     // create and insert new block behind current one, already set right start line
00433     TextBlock *newBlock = blockToBalance->splitBlock (halfSize);
00434     Q_ASSERT (newBlock);
00435     m_blocks.insert (m_blocks.begin() + index + 1, newBlock);
00436 
00437     // split is done
00438     return;
00439   }
00440 
00441   // second case: possibly too small block
00442 
00443   // if only one block, no chance to unite
00444   // same if this is first block, we always append to previous one
00445   if (index == 0)
00446     return;
00447 
00448   // block still large enough, do nothing
00449   if (2 * blockToBalance->lines () > m_blockSize)
00450     return;
00451 
00452   // unite small block with predecessor
00453   TextBlock *targetBlock = m_blocks.at(index-1);
00454 
00455   // merge block
00456   blockToBalance->mergeBlock (targetBlock);
00457 
00458   // delete old block
00459   delete blockToBalance;
00460   m_blocks.erase (m_blocks.begin() + index);
00461 }
00462 
00463 void TextBuffer::debugPrint (const QString &title) const
00464 {
00465   // print header with title
00466   printf ("%s (lines: %d bs: %d)\n", qPrintable (title), m_lines, m_blockSize);
00467 
00468   // print all blocks
00469   for (int i = 0; i < m_blocks.size(); ++i)
00470     m_blocks.at(i)->debugPrint (i);
00471 }
00472 
00473 bool TextBuffer::load (const QString &filename, bool &encodingErrors)
00474 {
00475   // fallback codec must exist
00476   Q_ASSERT (m_fallbackTextCodec);
00477 
00478   // codec must be set!
00479   Q_ASSERT (m_textCodec);
00480 
00484   clear ();
00485 
00489   KDE_struct_stat sbuf;
00490   if (KDE::stat(filename, &sbuf) != 0 || !S_ISREG(sbuf.st_mode))
00491     return false;
00492 
00496   Kate::TextLoader file (filename, m_encodingProberType);
00497 
00505   for (int i = 0; i < 4;  ++i) {
00509     for (int b = 1; b < m_blocks.size(); ++b) {
00510       TextBlock* block = m_blocks.at(b);
00511       block->m_lines.clear ();
00512       delete block;
00513     }
00514     m_blocks.resize (1);
00515 
00519     m_blocks.last()->m_lines.clear ();
00520     m_lines = 0;
00521 
00528     QTextCodec *codec = m_textCodec;
00529     if (i == 1)
00530       codec = 0;
00531     else if (i == 2)
00532       codec = m_fallbackTextCodec;
00533 
00534     if (!file.open (codec)) {
00535       // create one dummy textline, in any case
00536       m_blocks.last()->appendLine (TextLine (new TextLineData()));
00537       m_lines++;
00538       return false;
00539     }
00540 
00541     // read in all lines...
00542     encodingErrors = false;
00543     while ( !file.eof() )
00544     {
00545       // read line
00546       int offset = 0, length = 0;
00547       bool currentError = !file.readLine (offset, length);
00548       encodingErrors = encodingErrors || currentError;
00549 
00550       // bail out on encoding error, if not last round!
00551       if (encodingErrors && i < 3) {
00552         kDebug (13020) << "Failed try to load file" << filename << "with codec" <<
00553                           (file.textCodec() ? file.textCodec()->name() : "(null)");
00554         break;
00555       }
00556 
00557       // get unicode data for this line
00558       const QChar *unicodeData = file.unicode () + offset;
00559 
00560       // construct new text line with content from file
00561       TextLine textLine = TextLine (new TextLineData(QString (unicodeData, length)));
00562 
00563       // ensure blocks aren't too large
00564       if (m_blocks.last()->lines() >= m_blockSize)
00565         m_blocks.append (new TextBlock (this, m_blocks.last()->startLine() + m_blocks.last()->lines()));
00566 
00567       m_blocks.last()->appendLine (textLine);
00568       m_lines++;
00569     }
00570 
00571     // if no encoding error, break out of reading loop
00572     if (!encodingErrors) {
00573       // remember used codec
00574       m_textCodec = file.textCodec ();
00575       break;
00576     }
00577   }
00578 
00579   // remember if BOM was found
00580   if (file.byteOrderMarkFound ())
00581     setGenerateByteOrderMark (true);
00582 
00583   // remember eol mode, if any found in file
00584   if (file.eol() != eolUnknown)
00585     setEndOfLineMode (file.eol());
00586 
00587   // remember mime type for filter device
00588   m_mimeTypeForFilterDev = file.mimeTypeForFilterDev ();
00589 
00590   // assert that one line is there!
00591   Q_ASSERT (m_lines > 0);
00592 
00593   // report CODEC + ERRORS
00594   kDebug (13020) << "Loaded file " << filename << "with codec" << m_textCodec->name()
00595     << (encodingErrors ? "with" : "without") << "encoding errors";
00596 
00597   // report BOM
00598   kDebug (13020) << (file.byteOrderMarkFound () ? "Found" : "Didn't find") << "byte order mark";
00599 
00600   // report filter device mime-type
00601   kDebug (13020) << "used filter device for mime-type" << m_mimeTypeForFilterDev;
00602 
00603   // emit success
00604   emit loaded (filename, encodingErrors);
00605 
00606   // file loading worked, modulo encoding problems
00607   return true;
00608 }
00609 
00610 bool TextBuffer::save (const QString &filename)
00611 {
00612   // codec must be set!
00613   Q_ASSERT (m_textCodec);
00614 
00618   QIODevice *file = KFilterDev::deviceForFile (filename, m_mimeTypeForFilterDev, false);
00619   if (!file->open (QIODevice::WriteOnly)) {
00620     delete file;
00621     return false;
00622   }
00623 
00627   QTextStream stream (file);
00628   stream.setCodec (QTextCodec::codecForName("UTF-16"));
00629 
00630   // set the correct codec
00631   stream.setCodec (m_textCodec);
00632 
00633   // generate byte order mark?
00634   stream.setGenerateByteOrderMark (generateByteOrderMark());
00635 
00636   // our loved eol string ;)
00637   QString eol = "\n"; //m_doc->config()->eolString ();
00638   if (endOfLineMode() == eolDos)
00639     eol = QString ("\r\n");
00640   else if (endOfLineMode() == eolMac)
00641     eol = QString ("\r");
00642 
00643   // just dump the lines out ;)
00644   for (int i = 0; i < m_lines; ++i)
00645   {
00646     // get line to save
00647     Kate::TextLine textline = line (i);
00648 
00649     // strip trailing spaces
00650     if (m_removeTrailingSpaces)
00651     {
00652       int lastChar = textline->lastChar();
00653       if (lastChar > -1)
00654       {
00655         stream << textline->text().left (lastChar+1);
00656       }
00657     }
00658     else // simple, dump the line
00659       stream << textline->text();
00660 
00661     // append correct end of line string
00662     if ((i+1) < m_lines)
00663       stream << eol;
00664   }
00665 
00666   // flush stream
00667   stream.flush ();
00668 
00669   // close and delete file
00670   file->close ();
00671   delete file;
00672 
00673 #ifndef Q_OS_WIN
00674   // ensure that the file is written to disk
00675   // we crete new qfile, as the above might be wrapper around compression
00676   QFile syncFile (filename);
00677   syncFile.open (QIODevice::ReadOnly);
00678 
00679 #ifdef HAVE_FDATASYNC
00680   fdatasync (syncFile.handle());
00681 #else
00682   fsync (syncFile.handle());
00683 #endif
00684 #endif
00685 
00686   // did save work?
00687   bool ok = stream.status() == QTextStream::Ok;
00688 
00689   // remember this revision as last saved if we had success!
00690   if (ok)
00691     m_history.setLastSavedRevision ();
00692 
00693   // report CODEC + ERRORS
00694   kDebug (13020) << "Saved file " << filename << "with codec" << m_textCodec->name()
00695     << (ok ? "without" : "with") << "errors";
00696 
00697   // emit signal on success
00698   if (ok)
00699     emit saved (filename);
00700 
00701   // return success or not
00702   return ok;
00703 }
00704 
00705 void TextBuffer::notifyAboutRangeChange (KTextEditor::View *view, int startLine, int endLine, bool rangeWithAttribute)
00706 {
00710   if (!m_document)
00711     return;
00712 
00717   const QList<KTextEditor::View *> &views = m_document->views ();
00718   foreach(KTextEditor::View* curView, views) {
00719     // filter wrong views
00720     if (view && view != curView)
00721      continue;
00722 
00723     // notify view, it is really a kate view
00724     static_cast<KateView *> (curView)->notifyAboutRangeChange (startLine, endLine, rangeWithAttribute);
00725   }
00726 }
00727 
00728 QList<TextRange *> TextBuffer::rangesForLine (int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const
00729 {
00730   // get block, this will assert on invalid line
00731   const int blockIndex = blockForLine (line);
00732 
00733   // get the ranges of the right block
00734   QList<TextRange *> rightRanges;
00735   foreach (const QSet<TextRange *> &ranges, m_blocks.at(blockIndex)->rangesForLine (line)) {
00736     foreach (TextRange * const range, ranges) {
00740         if (rangesWithAttributeOnly && !range->hasAttribute())
00741             continue;
00742 
00746         if (!view && range->attributeOnlyForViews())
00747             continue;
00748 
00752         if (range->view() && range->view() != view)
00753             continue;
00754 
00758         if (range->startInternal().lineInternal() <= line && line <= range->endInternal().lineInternal())
00759           rightRanges.append (range);
00760     }
00761   }
00762 
00763   // return right ranges
00764   return rightRanges;
00765 }
00766 
00767 }

Kate

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal