kfiledetailview.cpp
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"