• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kio

kfiledetailview.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 /* This file is part of the KDE libraries
00003    Copyright (C) 1997 Stephan Kulow <coolo@kde.org>
00004                  2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.   If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include <qevent.h>
00023 #include <qkeycode.h>
00024 #include <qheader.h>
00025 #include <qpainter.h>
00026 #include <qpixmap.h>
00027 
00028 #include <kapplication.h>
00029 #include <kfileitem.h>
00030 #include <kglobal.h>
00031 #include <kglobalsettings.h>
00032 #include <kiconloader.h>
00033 #include <kicontheme.h>
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036 #include <kurldrag.h>
00037 
00038 #include "kfiledetailview.h"
00039 #include "config-kfile.h"
00040 
00041 #define COL_NAME 0
00042 #define COL_SIZE 1
00043 #define COL_DATE 2
00044 #define COL_PERM 3
00045 #define COL_OWNER 4
00046 #define COL_GROUP 5
00047 
00048 class KFileDetailView::KFileDetailViewPrivate
00049 {
00050 public:
00051    KFileDetailViewPrivate() : dropItem(0)
00052    { }
00053 
00054    KFileListViewItem *dropItem;
00055    QTimer autoOpenTimer;
00056 };
00057 
00058 KFileDetailView::KFileDetailView(QWidget *parent, const char *name)
00059     : KListView(parent, name), KFileView(), d(new KFileDetailViewPrivate())
00060 {
00061     // this is always the static section, not the index depending on column order
00062     m_sortingCol = COL_NAME; 
00063     m_blockSortingSignal = false;
00064     setViewName( i18n("Detailed View") );
00065 
00066     addColumn( i18n( "Name" ) );
00067     addColumn( i18n( "Size" ) );
00068     addColumn( i18n( "Date" ) );
00069     addColumn( i18n( "Permissions" ) );
00070     addColumn( i18n( "Owner" ) );
00071     addColumn( i18n( "Group" ) );
00072     setShowSortIndicator( true );
00073     setAllColumnsShowFocus( true );
00074     setDragEnabled(true);
00075 
00076     connect( header(), SIGNAL( clicked(int)),
00077              SLOT(slotSortingChanged(int) ));
00078 
00079 
00080     connect( this, SIGNAL( returnPressed(QListViewItem *) ),
00081          SLOT( slotActivate( QListViewItem *) ) );
00082 
00083     connect( this, SIGNAL( clicked(QListViewItem *, const QPoint&, int)),
00084          SLOT( selected( QListViewItem *) ) );
00085     connect( this, SIGNAL( doubleClicked(QListViewItem *, const QPoint&, int)),
00086          SLOT( slotActivate( QListViewItem *) ) );
00087 
00088     connect( this, SIGNAL(contextMenuRequested( QListViewItem *,
00089                                                 const QPoint &, int )),
00090          this, SLOT( slotActivateMenu( QListViewItem *, const QPoint& )));
00091 
00092     KFile::SelectionMode sm = KFileView::selectionMode();
00093     switch ( sm ) {
00094     case KFile::Multi:
00095     QListView::setSelectionMode( QListView::Multi );
00096     break;
00097     case KFile::Extended:
00098     QListView::setSelectionMode( QListView::Extended );
00099     break;
00100     case KFile::NoSelection:
00101     QListView::setSelectionMode( QListView::NoSelection );
00102     break;
00103     default: // fall through
00104     case KFile::Single:
00105     QListView::setSelectionMode( QListView::Single );
00106     break;
00107     }
00108 
00109     // for highlighting
00110     if ( sm == KFile::Multi || sm == KFile::Extended )
00111     connect( this, SIGNAL( selectionChanged() ),
00112          SLOT( slotSelectionChanged() ));
00113     else
00114     connect( this, SIGNAL( selectionChanged( QListViewItem * ) ),
00115          SLOT( highlighted( QListViewItem * ) ));
00116         
00117     // DND
00118     connect( &(d->autoOpenTimer), SIGNAL( timeout() ),
00119              this, SLOT( slotAutoOpen() ));
00120 
00121     setSorting( sorting() );
00122 
00123     m_resolver =
00124         new KMimeTypeResolver<KFileListViewItem,KFileDetailView>( this );
00125 }
00126 
00127 KFileDetailView::~KFileDetailView()
00128 {
00129     delete m_resolver;
00130     delete d;
00131 }
00132 
00133 void KFileDetailView::readConfig( KConfig *config, const QString& group )
00134 {
00135     restoreLayout( config, group );
00136 }
00137 
00138 void KFileDetailView::writeConfig( KConfig *config, const QString& group )
00139 {
00140     saveLayout( config, group );
00141 }
00142 
00143 void KFileDetailView::setSelected( const KFileItem *info, bool enable )
00144 {
00145     if ( !info )
00146     return;
00147 
00148     // we can only hope that this casts works
00149     KFileListViewItem *item = (KFileListViewItem*)info->extraData( this );
00150 
00151     if ( item )
00152     KListView::setSelected( item, enable );
00153 }
00154 
00155 void KFileDetailView::setCurrentItem( const KFileItem *item )
00156 {
00157     if ( !item )
00158         return;
00159     KFileListViewItem *it = (KFileListViewItem*) item->extraData( this );
00160     if ( it )
00161         KListView::setCurrentItem( it );
00162 }
00163 
00164 KFileItem * KFileDetailView::currentFileItem() const
00165 {
00166     KFileListViewItem *current = static_cast<KFileListViewItem*>( currentItem() );
00167     if ( current )
00168         return current->fileInfo();
00169 
00170     return 0L;
00171 }
00172 
00173 void KFileDetailView::clearSelection()
00174 {
00175     KListView::clearSelection();
00176 }
00177 
00178 void KFileDetailView::selectAll()
00179 {
00180     if (KFileView::selectionMode() == KFile::NoSelection ||
00181         KFileView::selectionMode() == KFile::Single)
00182     return;
00183 
00184     KListView::selectAll( true );
00185 }
00186 
00187 void KFileDetailView::invertSelection()
00188 {
00189     KListView::invertSelection();
00190 }
00191 
00192 void KFileDetailView::slotActivateMenu (QListViewItem *item,const QPoint& pos )
00193 {
00194     if ( !item ) {
00195         sig->activateMenu( 0, pos );
00196         return;
00197     }
00198     KFileListViewItem *i = (KFileListViewItem*) item;
00199     sig->activateMenu( i->fileInfo(), pos );
00200 }
00201 
00202 void KFileDetailView::clearView()
00203 {
00204     m_resolver->m_lstPendingMimeIconItems.clear();
00205     KListView::clear();
00206 }
00207 
00208 void KFileDetailView::insertItem( KFileItem *i )
00209 {
00210     KFileView::insertItem( i );
00211 
00212     KFileListViewItem *item = new KFileListViewItem( (QListView*) this, i );
00213 
00214     setSortingKey( item, i );
00215 
00216     i->setExtraData( this, item );
00217 
00218     if ( !i->isMimeTypeKnown() )
00219         m_resolver->m_lstPendingMimeIconItems.append( item );
00220 }
00221 
00222 void KFileDetailView::slotActivate( QListViewItem *item )
00223 {
00224     if ( !item )
00225         return;
00226 
00227     const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo();
00228     if ( fi )
00229         sig->activate( fi );
00230 }
00231 
00232 void KFileDetailView::selected( QListViewItem *item )
00233 {
00234     if ( !item )
00235         return;
00236 
00237     if ( KGlobalSettings::singleClick() ) {
00238         const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo();
00239         if ( fi && (fi->isDir() || !onlyDoubleClickSelectsFiles()) )
00240             sig->activate( fi );
00241     }
00242 }
00243 
00244 void KFileDetailView::highlighted( QListViewItem *item )
00245 {
00246     if ( !item )
00247         return;
00248 
00249     const KFileItem *fi = ( (KFileListViewItem*)item )->fileInfo();
00250     if ( fi )
00251         sig->highlightFile( fi );
00252 }
00253 
00254 
00255 void KFileDetailView::setSelectionMode( KFile::SelectionMode sm )
00256 {
00257     disconnect( this, SIGNAL( selectionChanged() ));
00258     disconnect( this, SIGNAL( selectionChanged( QListViewItem * ) ));
00259 
00260     KFileView::setSelectionMode( sm );
00261 
00262     switch ( KFileView::selectionMode() ) {
00263     case KFile::Multi:
00264         QListView::setSelectionMode( QListView::Multi );
00265         break;
00266     case KFile::Extended:
00267         QListView::setSelectionMode( QListView::Extended );
00268         break;
00269     case KFile::NoSelection:
00270         QListView::setSelectionMode( QListView::NoSelection );
00271         break;
00272     default: // fall through
00273     case KFile::Single:
00274         QListView::setSelectionMode( QListView::Single );
00275         break;
00276     }
00277 
00278     if ( sm == KFile::Multi || sm == KFile::Extended )
00279         connect( this, SIGNAL( selectionChanged() ),
00280                  SLOT( slotSelectionChanged() ));
00281     else
00282         connect( this, SIGNAL( selectionChanged( QListViewItem * )),
00283                  SLOT( highlighted( QListViewItem * )));
00284 }
00285 
00286 bool KFileDetailView::isSelected( const KFileItem *i ) const
00287 {
00288     if ( !i )
00289         return false;
00290 
00291     KFileListViewItem *item = (KFileListViewItem*) i->extraData( this );
00292     return (item && item->isSelected());
00293 }
00294 
00295 
00296 void KFileDetailView::updateView( bool b )
00297 {
00298     if ( !b )
00299         return;
00300 
00301     QListViewItemIterator it( (QListView*)this );
00302     for ( ; it.current(); ++it ) {
00303         KFileListViewItem *item=static_cast<KFileListViewItem *>(it.current());
00304         item->setPixmap( 0, item->fileInfo()->pixmap(KIcon::SizeSmall) );
00305     }
00306 }
00307 
00308 void KFileDetailView::updateView( const KFileItem *i )
00309 {
00310     if ( !i )
00311         return;
00312 
00313     KFileListViewItem *item = (KFileListViewItem*) i->extraData( this );
00314     if ( !item )
00315         return;
00316 
00317     item->init();
00318     setSortingKey( item, i );
00319 
00320     //item->repaint(); // only repaints if visible
00321 }
00322 
00323 void KFileDetailView::setSortingKey( KFileListViewItem *item,
00324                                      const KFileItem *i )
00325 {
00326     // see also setSorting()
00327     QDir::SortSpec spec = KFileView::sorting();
00328 
00329     if ( spec & QDir::Time )
00330         item->setKey( sortingKey( i->time( KIO::UDS_MODIFICATION_TIME ),
00331                                   i->isDir(), spec ));
00332     else if ( spec & QDir::Size )
00333         item->setKey( sortingKey( i->size(), i->isDir(), spec ));
00334 
00335     else // Name or Unsorted
00336         item->setKey( sortingKey( i->text(), i->isDir(), spec ));
00337 }
00338 
00339 
00340 void KFileDetailView::removeItem( const KFileItem *i )
00341 {
00342     if ( !i )
00343         return;
00344 
00345     KFileListViewItem *item = (KFileListViewItem*) i->extraData( this );
00346     m_resolver->m_lstPendingMimeIconItems.remove( item );
00347     delete item;
00348 
00349     KFileView::removeItem( i );
00350 }
00351 
00352 void KFileDetailView::slotSortingChanged( int col )
00353 {
00354     // col is the section here, not the index!
00355     
00356     QDir::SortSpec sort = sorting();
00357     int sortSpec = -1;
00358     bool reversed = (col == m_sortingCol) && (sort & QDir::Reversed) == 0;
00359     m_sortingCol = col;
00360 
00361     switch( col ) {
00362         case COL_NAME:
00363             sortSpec = (sort & ~QDir::SortByMask | QDir::Name);
00364             break;
00365         case COL_SIZE:
00366             sortSpec = (sort & ~QDir::SortByMask | QDir::Size);
00367             break;
00368         case COL_DATE:
00369             sortSpec = (sort & ~QDir::SortByMask | QDir::Time);
00370             break;
00371 
00372         // the following columns have no equivalent in QDir, so we set it
00373         // to QDir::Unsorted and remember the column (m_sortingCol)
00374         case COL_OWNER:
00375         case COL_GROUP:
00376         case COL_PERM:
00377             // grmbl, QDir::Unsorted == SortByMask.
00378             sortSpec = (sort & ~QDir::SortByMask);// | QDir::Unsorted;
00379             break;
00380         default:
00381             break;
00382     }
00383 
00384     if ( reversed )
00385         sortSpec |= QDir::Reversed;
00386     else
00387         sortSpec &= ~QDir::Reversed;
00388 
00389     if ( sort & QDir::IgnoreCase )
00390         sortSpec |= QDir::IgnoreCase;
00391     else
00392         sortSpec &= ~QDir::IgnoreCase;
00393 
00394 
00395     KFileView::setSorting( static_cast<QDir::SortSpec>( sortSpec ) );
00396 
00397     KFileItem *item;
00398     KFileItemListIterator it( *items() );
00399 
00400     if ( sortSpec & QDir::Time ) {
00401         for ( ; (item = it.current()); ++it )
00402             viewItem(item)->setKey( sortingKey( item->time( KIO::UDS_MODIFICATION_TIME ), item->isDir(), sortSpec ));
00403     }
00404 
00405     else if ( sortSpec & QDir::Size ) {
00406         for ( ; (item = it.current()); ++it )
00407             viewItem(item)->setKey( sortingKey( item->size(), item->isDir(),
00408                                                 sortSpec ));
00409     }
00410     else { // Name or Unsorted -> use column text
00411         for ( ; (item = it.current()); ++it ) {
00412             KFileListViewItem *i = viewItem( item );
00413             i->setKey( sortingKey( i->text(m_sortingCol), item->isDir(),
00414                                    sortSpec ));
00415         }
00416     }
00417 
00418     KListView::setSorting( m_sortingCol, !reversed );
00419     KListView::sort();
00420 
00421     if ( !m_blockSortingSignal )
00422         sig->changeSorting( static_cast<QDir::SortSpec>( sortSpec ) );
00423 }
00424 
00425 
00426 void KFileDetailView::setSorting( QDir::SortSpec spec )
00427 {
00428     int col = 0;
00429     if ( spec & QDir::Time )
00430         col = COL_DATE;
00431     else if ( spec & QDir::Size )
00432         col = COL_SIZE;
00433     else if ( spec & QDir::Unsorted )
00434         col = m_sortingCol;
00435     else
00436         col = COL_NAME;
00437 
00438     // inversed, because slotSortingChanged will reverse it
00439     if ( spec & QDir::Reversed )
00440         spec = (QDir::SortSpec) (spec & ~QDir::Reversed);
00441     else
00442         spec = (QDir::SortSpec) (spec | QDir::Reversed);
00443 
00444     m_sortingCol = col;
00445     KFileView::setSorting( (QDir::SortSpec) spec );
00446 
00447 
00448     // don't emit sortingChanged() when called via setSorting()
00449     m_blockSortingSignal = true; // can't use blockSignals()
00450     slotSortingChanged( col );
00451     m_blockSortingSignal = false;
00452 }
00453 
00454 void KFileDetailView::ensureItemVisible( const KFileItem *i )
00455 {
00456     if ( !i )
00457         return;
00458 
00459     KFileListViewItem *item = (KFileListViewItem*) i->extraData( this );
00460 
00461     if ( item )
00462         KListView::ensureItemVisible( item );
00463 }
00464 
00465 // we're in multiselection mode
00466 void KFileDetailView::slotSelectionChanged()
00467 {
00468     sig->highlightFile( 0L );
00469 }
00470 
00471 KFileItem * KFileDetailView::firstFileItem() const
00472 {
00473     KFileListViewItem *item = static_cast<KFileListViewItem*>( firstChild() );
00474     if ( item )
00475         return item->fileInfo();
00476     return 0L;
00477 }
00478 
00479 KFileItem * KFileDetailView::nextItem( const KFileItem *fileItem ) const
00480 {
00481     if ( fileItem ) {
00482         KFileListViewItem *item = viewItem( fileItem );
00483         if ( item && item->itemBelow() )
00484             return ((KFileListViewItem*) item->itemBelow())->fileInfo();
00485         else
00486             return 0L;
00487     }
00488     else
00489         return firstFileItem();
00490 }
00491 
00492 KFileItem * KFileDetailView::prevItem( const KFileItem *fileItem ) const
00493 {
00494     if ( fileItem ) {
00495         KFileListViewItem *item = viewItem( fileItem );
00496         if ( item && item->itemAbove() )
00497             return ((KFileListViewItem*) item->itemAbove())->fileInfo();
00498         else
00499             return 0L;
00500     }
00501     else
00502         return firstFileItem();
00503 }
00504 
00505 void KFileDetailView::keyPressEvent( QKeyEvent *e )
00506 {
00507     KListView::keyPressEvent( e );
00508 
00509     if ( e->key() == Key_Return || e->key() == Key_Enter ) {
00510         if ( e->state() & ControlButton )
00511             e->ignore();
00512         else
00513             e->accept();
00514     }
00515 }
00516 
00517 //
00518 // mimetype determination on demand
00519 //
00520 void KFileDetailView::mimeTypeDeterminationFinished()
00521 {
00522     // anything to do?
00523 }
00524 
00525 void KFileDetailView::determineIcon( KFileListViewItem *item )
00526 {
00527     (void) item->fileInfo()->determineMimeType();
00528     updateView( item->fileInfo() );
00529 }
00530 
00531 void KFileDetailView::listingCompleted()
00532 {
00533     m_resolver->start();
00534 }
00535 
00536 QDragObject *KFileDetailView::dragObject()
00537 {
00538     // create a list of the URL:s that we want to drag
00539     KURL::List urls;
00540     KFileItemListIterator it( * KFileView::selectedItems() );
00541     for ( ; it.current(); ++it ){
00542         urls.append( (*it)->url() );
00543     }
00544     QPixmap pixmap;
00545     if( urls.count() > 1 )
00546         pixmap = DesktopIcon( "kmultiple", KIcon::SizeSmall );
00547     if( pixmap.isNull() )
00548         pixmap = currentFileItem()->pixmap( KIcon::SizeSmall );
00549 
00550     QPoint hotspot;
00551     hotspot.setX( pixmap.width() / 2 );
00552     hotspot.setY( pixmap.height() / 2 );
00553     QDragObject* myDragObject = new KURLDrag( urls, widget() );
00554     myDragObject->setPixmap( pixmap, hotspot );
00555     return myDragObject;
00556 }
00557 
00558 void KFileDetailView::slotAutoOpen()
00559 {
00560     d->autoOpenTimer.stop();
00561     if( !d->dropItem )
00562         return;
00563 
00564     KFileItem *fileItem = d->dropItem->fileInfo();
00565     if (!fileItem)
00566         return;
00567 
00568     if( fileItem->isFile() )
00569         return;
00570 
00571     if ( fileItem->isDir() || fileItem->isLink())
00572         sig->activate( fileItem );
00573 }
00574 
00575 bool KFileDetailView::acceptDrag(QDropEvent* e) const
00576 {
00577    return KURLDrag::canDecode( e ) &&
00578        (e->source()!= const_cast<KFileDetailView*>(this)) &&
00579        ( e->action() == QDropEvent::Copy
00580       || e->action() == QDropEvent::Move
00581       || e->action() == QDropEvent::Link );
00582 }
00583 
00584 void KFileDetailView::contentsDragEnterEvent( QDragEnterEvent *e )
00585 {
00586     if ( ! acceptDrag( e ) ) { // can we decode this ?
00587         e->ignore();            // No
00588         return;
00589     }
00590     e->acceptAction();     // Yes
00591 
00592     if ((dropOptions() & AutoOpenDirs) == 0)
00593        return;
00594 
00595     KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) ));
00596     if ( item ) {  // are we over an item ?
00597        d->dropItem = item;
00598        d->autoOpenTimer.start( autoOpenDelay() ); // restart timer
00599     }
00600     else
00601     {
00602        d->dropItem = 0;
00603        d->autoOpenTimer.stop();
00604     }
00605 }
00606 
00607 void KFileDetailView::contentsDragMoveEvent( QDragMoveEvent *e )
00608 {
00609     if ( ! acceptDrag( e ) ) { // can we decode this ?
00610         e->ignore();            // No
00611         return;
00612     }
00613     e->acceptAction();     // Yes
00614 
00615     if ((dropOptions() & AutoOpenDirs) == 0)
00616        return;
00617 
00618     KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) ));
00619     if ( item ) {  // are we over an item ?
00620        if (d->dropItem != item)
00621        {
00622            d->dropItem = item;
00623            d->autoOpenTimer.start( autoOpenDelay() ); // restart timer
00624        }
00625     }
00626     else
00627     {
00628        d->dropItem = 0;
00629        d->autoOpenTimer.stop();
00630     }
00631 }
00632 
00633 void KFileDetailView::contentsDragLeaveEvent( QDragLeaveEvent * )
00634 {
00635     d->dropItem = 0;
00636     d->autoOpenTimer.stop();
00637 }
00638 
00639 void KFileDetailView::contentsDropEvent( QDropEvent *e )
00640 {
00641     d->dropItem = 0;
00642     d->autoOpenTimer.stop();
00643 
00644     if ( ! acceptDrag( e ) ) { // can we decode this ?
00645         e->ignore();            // No
00646         return;
00647     }
00648     e->acceptAction();     // Yes
00649 
00650     KFileListViewItem *item = dynamic_cast<KFileListViewItem*>(itemAt( contentsToViewport( e->pos() ) ));
00651     KFileItem * fileItem = 0;
00652     if (item)
00653         fileItem = item->fileInfo();
00654 
00655     emit dropped(e, fileItem);
00656 
00657     KURL::List urls;
00658     if (KURLDrag::decode( e, urls ) && !urls.isEmpty())
00659     {
00660         emit dropped(e, urls, fileItem ? fileItem->url() : KURL());
00661         sig->dropURLs(fileItem, e, urls);
00662     }
00663 }
00664 
00665 
00667 
00668 
00669 void KFileListViewItem::init()
00670 {
00671     KFileListViewItem::setPixmap( COL_NAME, inf->pixmap(KIcon::SizeSmall));
00672 
00673     setText( COL_NAME, inf->text() );
00674     setText( COL_SIZE, KGlobal::locale()->formatNumber( inf->size(), 0));
00675     setText( COL_DATE,  inf->timeString() );
00676     setText( COL_PERM,  inf->permissionsString() );
00677     setText( COL_OWNER, inf->user() );
00678     setText( COL_GROUP, inf->group() );
00679 }
00680 
00681 
00682 void KFileDetailView::virtual_hook( int id, void* data )
00683 { KListView::virtual_hook( id, data );
00684   KFileView::virtual_hook( id, data ); }
00685 
00686 #include "kfiledetailview.moc"

kio

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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