File indexing completed on 2024-05-05 16:39:00
0001 /* This file is part of the KDE project 0002 Copyright (C) 1998-2003 Carsten Pfeiffer <pfeiffer@kde.org> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation, version 2. 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; see the file COPYING. If not, write to 0015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0016 Boston, MA 02110-1301, USA. 0017 */ 0018 0019 #include "filewidget.h" 0020 0021 #include <KActionCollection> 0022 #include <KActionMenu> 0023 #include <KCompletion> 0024 #include <KConfigGroup> 0025 #include <KFileItemActions> 0026 #include <KFileItemListProperties> 0027 #include <KLocalizedString> 0028 0029 #include <QAbstractItemModel> 0030 #include <QAbstractItemView> 0031 #include <QEvent> 0032 #include <QKeyEvent> 0033 #include <QMenu> 0034 #include <QMimeDatabase> 0035 #include <QModelIndex> 0036 #include <QResizeEvent> 0037 #include <QTimer> 0038 0039 #include "filefinder.h" 0040 #include "kuickdata.h" 0041 #include "kuickshow_debug.h" 0042 #include "kuickshow.h" 0043 0044 0045 FileWidget::FileWidget( const QUrl& url, QWidget *parent ) 0046 : KDirOperator( url, parent ), 0047 m_validCompletion( false ), 0048 m_fileFinder( 0L ), 0049 m_fileItemActions( 0L ) 0050 { 0051 setEnableDirHighlighting( true ); 0052 0053 KConfigGroup group(KSharedConfig::openConfig(), "Filebrowser"); 0054 setViewConfig( group ); 0055 readConfig( group ); 0056 setView( KFile::Default ); 0057 0058 // setOnlyDoubleClickSelectsFiles( true ); 0059 reloadConfiguration(); 0060 0061 completionObject()->setCompletionMode( KCompletion::CompletionAuto ); 0062 dirCompletionObject()->setCompletionMode( KCompletion::CompletionAuto); 0063 0064 slotViewChanged(); 0065 connect( this, SIGNAL( viewChanged( QAbstractItemView * )), 0066 SLOT( slotViewChanged() )); 0067 0068 connect( dirLister(), SIGNAL( clear() ), SLOT( slotItemsCleared() )); 0069 connect( dirLister(), SIGNAL( itemsDeleted( const KFileItemList& ) ), 0070 SLOT( slotItemsDeleted( const KFileItemList& ) )); 0071 0072 connect( this, SIGNAL( fileHighlighted( const KFileItem& )), 0073 SLOT( slotHighlighted( const KFileItem& ))); 0074 0075 connect( this, SIGNAL(urlEntered(const QUrl&)), 0076 SLOT( slotURLEntered( const QUrl& ))); 0077 0078 // should actually be KDirOperator's job! 0079 connect( this, SIGNAL( finishedLoading() ), SLOT( slotFinishedLoading() )); 0080 0081 connect( this, SIGNAL( contextMenuAboutToShow( const KFileItem&, QMenu *) ), 0082 SLOT( slotContextMenu( const KFileItem&, QMenu *))); 0083 } 0084 0085 FileWidget::~FileWidget() 0086 { 0087 delete m_fileFinder; 0088 } 0089 0090 void FileWidget::initActions() 0091 { 0092 KActionCollection *coll = actionCollection(); 0093 KActionMenu *menu = static_cast<KActionMenu*>( coll->action("popupMenu") ); 0094 0095 menu->addAction(coll->action("kuick_showInOtherWindow")); 0096 menu->addAction(coll->action("kuick_showInSameWindow")); 0097 menu->addAction(coll->action("kuick_showFullscreen")); 0098 menu->addSeparator(); 0099 0100 // properties dialog is now in kfile, but not at the right position, 0101 // so we move it to the real bottom 0102 menu->menu()->removeAction( coll->action( "properties" ) ); 0103 /* 0104 KMenu *pMenu = menu->menu(); 0105 int lastItemId = pMenu->idAt( pMenu->count() - 1 ); 0106 QMenuItem *mItem = pMenu->findItem( lastItemId ); 0107 if ( mItem && !mItem->isSeparator() ) 0108 menu->addSeparator(); 0109 */ 0110 // those at the bottom 0111 menu->addAction(coll->action("kuick_print") ); 0112 menu->addSeparator(); 0113 menu->addAction(coll->action("properties") ); 0114 } 0115 0116 void FileWidget::reloadConfiguration() 0117 { 0118 if ( kdata->fileFilter != nameFilter() ) { 0119 // At first, our list must have folders 0120 QStringList mimes; 0121 mimes.append("inode/directory"); 0122 0123 // Then, all the images! 0124 QMimeDatabase mimedb; 0125 QList<QMimeType> l = mimedb.allMimeTypes(); 0126 for (QList<QMimeType>::const_iterator it = l.begin(); it != l.end(); ++it) 0127 if (it->name().startsWith( "image/" )) 0128 mimes.append( it->name() ); 0129 0130 // Ok, show what we've done 0131 setMimeFilter (mimes); 0132 updateDir(); 0133 } 0134 } 0135 0136 bool FileWidget::hasFiles() const 0137 { 0138 return (numFiles() > 0); 0139 } 0140 0141 void FileWidget::slotContextMenu( const KFileItem& item, QMenu *popupMenu ) 0142 { 0143 bool image = isImage( item ); 0144 actionCollection()->action("kuick_showInSameWindow")->setEnabled( image ); 0145 actionCollection()->action("kuick_showInOtherWindow")->setEnabled( image ); 0146 actionCollection()->action("kuick_showFullscreen")->setEnabled( image ); 0147 actionCollection()->action("kuick_print")->setEnabled( image ); 0148 0149 KActionCollection *coll = actionCollection(); 0150 KActionMenu *menu = static_cast<KActionMenu*>( coll->action("popupMenu") ); 0151 0152 menu->addAction(coll->action("kuick_showInOtherWindow")); 0153 menu->addAction(coll->action("kuick_showInSameWindow")); 0154 menu->addAction(coll->action("kuick_showFullscreen")); 0155 menu->addSeparator(); 0156 0157 if (!item.isNull()) { 0158 KFileItemList items; 0159 items.append(item); 0160 KFileItemListProperties properties( items ); 0161 if ( !m_fileItemActions ) { 0162 m_fileItemActions = new KFileItemActions( this ); 0163 m_fileItemActions->setParentWidget( this ); 0164 } 0165 m_fileItemActions->setItemListProperties( properties ); 0166 m_fileItemActions->addOpenWithActionsTo( menu->menu(), QString() ); 0167 } 0168 0169 // properties dialog is now in kfile, but not at the right position, 0170 // so we move it to the real bottom 0171 menu->menu()->removeAction( coll->action( "properties" ) ); 0172 0173 // those at the bottom 0174 menu->addAction(coll->action("kuick_print") ); 0175 menu->addSeparator(); 0176 menu->addAction(coll->action("properties") ); 0177 } 0178 0179 void FileWidget::findCompletion( const QString& text ) 0180 { 0181 if ( text.at(0) == '/' || text.at(0) == '~' || 0182 text.indexOf('/') != -1 ) { 0183 QString t = m_fileFinder->completion()->makeCompletion( text ); 0184 0185 if (m_fileFinder->completionMode() == KCompletion::CompletionPopup || 0186 m_fileFinder->completionMode() == KCompletion::CompletionPopupAuto) 0187 m_fileFinder->setCompletedItems( 0188 m_fileFinder->completion()->allMatches() ); 0189 else 0190 if ( !t.isNull() ) 0191 m_fileFinder->setCompletedText( t ); 0192 0193 return; 0194 } 0195 0196 QString file = makeDirCompletion( text ); 0197 if ( file.isNull() ) 0198 file = makeCompletion( text ); 0199 m_validCompletion = !file.isNull(); 0200 0201 if ( m_validCompletion ) { 0202 QUrl completeUrl = url(); 0203 completeUrl.setPath(completeUrl.adjusted(QUrl::RemoveFilename).path() + file); 0204 KDirOperator::setCurrentItem( completeUrl ); 0205 } 0206 } 0207 0208 bool FileWidget::eventFilter( QObject *o, QEvent *e ) 0209 { 0210 if ( e->type() == QEvent::KeyPress ) { 0211 QKeyEvent *k = static_cast<QKeyEvent*>( e ); 0212 0213 if ( (k->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) == 0 ) { 0214 int key = k->key(); 0215 if ( actionCollection()->action("delete")->shortcuts().contains( key ) ) 0216 { 0217 k->accept(); 0218 KFileItem item = getCurrentItem( false ); 0219 if ( !item.isNull() ) { 0220 KFileItemList list; 0221 list.append( item ); 0222 del( list, this, (k->modifiers() & Qt::ShiftModifier) == 0 ); 0223 } 0224 return true; 0225 } 0226 0227 const QString& text = k->text(); 0228 if ( !text.isEmpty() && text.unicode()->isPrint() ) { 0229 k->accept(); 0230 0231 if ( !m_fileFinder ) { 0232 m_fileFinder = new FileFinder( this ); 0233 m_fileFinder->setObjectName( "file finder" ); 0234 connect( m_fileFinder, SIGNAL( completion(const QString&)), 0235 SLOT( findCompletion( const QString& ))); 0236 connect( m_fileFinder, 0237 SIGNAL( enterDir( const QString& ) ), 0238 SLOT( slotReturnPressed( const QString& ))); 0239 m_fileFinder->move( width() - m_fileFinder->width(), 0240 height() - m_fileFinder->height() ); 0241 } 0242 0243 bool first = m_fileFinder->isHidden(); 0244 0245 m_fileFinder->setText( text ); 0246 m_fileFinder->raise(); 0247 m_fileFinder->show(); 0248 m_fileFinder->setFocus(); 0249 if ( first ) 0250 findCompletion( text ); 0251 0252 return true; 0253 } 0254 } 0255 0256 k->ignore(); 0257 } 0258 return KDirOperator::eventFilter( o, e ); 0259 } 0260 0261 0262 bool FileWidget::isImage( const KFileItem& item ) 0263 { 0264 // return item && !item.isDir(); 0265 if ( !item.isNull() ) 0266 { 0267 return item.isReadable() && item.mimetype().startsWith( "image/"); 0268 } 0269 return false; 0270 } 0271 0272 KFileItem FileWidget::gotoFirstImage() 0273 { 0274 QModelIndex modelIndex = view()->model()->index( 0, 0 ); 0275 while ( modelIndex.isValid() ) { 0276 KFileItem item = fileItemFor(modelIndex); 0277 if ( isImage( item ) ) { 0278 setCurrentItem( item ); 0279 return item; 0280 } 0281 0282 modelIndex = modelIndex.sibling( modelIndex.row() + 1, modelIndex.column() ); 0283 } 0284 return KFileItem(); 0285 } 0286 0287 KFileItem FileWidget::gotoLastImage() 0288 { 0289 QAbstractItemModel *model = view()->model(); 0290 int numRows = model->rowCount(); 0291 QModelIndex index = model->index(numRows - 1, 0); // last item 0292 while ( index.isValid() ) { 0293 KFileItem fileItem = fileItemFor( index ); 0294 if (isImage( fileItem )) { 0295 setCurrentItem( fileItem ); 0296 return fileItem; 0297 } 0298 0299 index = index.parent(); 0300 } 0301 0302 return KFileItem(); 0303 } 0304 0305 KFileItem FileWidget::getNext( bool go ) 0306 { 0307 KFileItem item = getItem( Next, true ); 0308 if ( !item.isNull() ) { 0309 if ( go ) 0310 // This needs to be done on a single shot timer because, 0311 // if the setCurrentItem() is done immediately, the current 0312 // index of the view() seems to be cleared soon afterwards by 0313 // a KCoreDirLister::completed() signal. It is not clear what 0314 // causes the sending of this signal, but the result is that 0315 // the current index is left cleared and the next call of 0316 // getItem() will fail. 0317 QTimer::singleShot(0, this, [item, this]() { setCurrentItem(item); }); 0318 return item; 0319 } 0320 0321 return KFileItem(); 0322 } 0323 0324 KFileItem FileWidget::getPrevious( bool go ) 0325 { 0326 KFileItem item = getItem( Previous, true ); 0327 if ( !item.isNull() ) { 0328 if ( go ) 0329 QTimer::singleShot(0, this, [item, this]() { setCurrentItem(item); }); 0330 return item; 0331 } 0332 0333 return KFileItem(); 0334 } 0335 0336 // returns a null item when there is no previous/next item/image 0337 // this sucks! Use KFileView::currentFileItem() when implemented 0338 KFileItem FileWidget::getItem( WhichItem which, bool onlyImage ) const 0339 { 0340 QModelIndex currentIndex = view()->currentIndex(); 0341 if ( !currentIndex.isValid() ) { 0342 qDebug("no current index"); 0343 return KFileItem(); 0344 } 0345 0346 QModelIndex index = currentIndex; 0347 KFileItem item; 0348 const int column = index.column(); 0349 item = fileItemFor( currentIndex ); 0350 if ( item.isNull() ) 0351 qDebug("### current item is null: %s, %s", index.data().typeName(), qUtf8Printable(currentIndex.data().value<QString>())); 0352 0353 switch( which ) { 0354 case Previous: { 0355 index = index.sibling( index.row() - 1, column ); 0356 while ( index.isValid() ) { 0357 item = fileItemFor(index); 0358 if ( !item.isNull() && (!onlyImage || isImage( item )) ) { 0359 return item; 0360 } 0361 index = index.sibling( index.row() - 1, column ); 0362 } 0363 0364 return KFileItem(); // no previous item / image 0365 } 0366 0367 case Next: { 0368 index = index.sibling( index.row() + 1, column ); 0369 while ( index.isValid() ) { 0370 item = fileItemFor(index); 0371 if ( !item.isNull() && (!onlyImage || isImage( item )) ) { 0372 return item; 0373 } 0374 index = index.sibling( index.row() + 1, column ); 0375 } 0376 0377 return KFileItem(); // no further item / image 0378 } 0379 0380 case Current: 0381 default: 0382 return fileItemFor(currentIndex); 0383 } 0384 return KFileItem(); 0385 } 0386 0387 void FileWidget::slotViewChanged() 0388 { 0389 view()->installEventFilter( this ); 0390 } 0391 0392 void FileWidget::slotItemsCleared() 0393 { 0394 m_currentURL.clear(); 0395 } 0396 0397 void FileWidget::slotItemsDeleted( const KFileItemList& items ) 0398 { 0399 KFileItem current = getCurrentItem( false ); 0400 if ( !items.contains(current) ) { 0401 return; // all ok, we already have a new current item 0402 } 0403 0404 KFileItem next = getNext(); 0405 if ( next.isNull() ) 0406 next = getPrevious(); 0407 0408 if ( !next.isNull() ) 0409 m_currentURL = next.url().url(); 0410 } 0411 0412 void FileWidget::slotHighlighted( const KFileItem& item ) 0413 { 0414 if ( !item.isNull() ) { 0415 m_currentURL = item.url().url(); 0416 } 0417 else { 0418 m_currentURL = QString(); 0419 } 0420 } 0421 0422 void FileWidget::slotReturnPressed( const QString& t ) 0423 { 0424 // we need a / at the end, otherwise replacedPath() will cut off the dir, 0425 // assuming it is a filename 0426 QString text = t; 0427 if ( text.at( text.length()-1 ) != '/' ) 0428 text += '/'; 0429 0430 if ( text.at(0) == '/' || text.at(0) == '~' ) { 0431 QString dir = m_fileFinder->completion()->replacedPath( text ); 0432 setUrl( QUrl::fromLocalFile(dir), true ); 0433 } 0434 0435 else if ( text.indexOf('/') != (int) text.length() -1 ) { // relative path 0436 QString dir = m_fileFinder->completion()->replacedPath( text ); 0437 QUrl u = url().resolved(QUrl(dir)); 0438 setUrl( u, true ); 0439 } 0440 0441 else if ( m_validCompletion ) { 0442 KFileItem item = getCurrentItem( true ); 0443 0444 if ( !item.isNull() ) { 0445 if ( item.isDir() ) 0446 setUrl( item.url(), true ); 0447 else 0448 emit fileSelected( item ); 0449 } 0450 } 0451 } 0452 0453 void FileWidget::setInitialItem( const QUrl& url ) 0454 { 0455 m_initialName = url; 0456 } 0457 0458 void FileWidget::slotURLEntered( const QUrl& url ) 0459 { 0460 if ( m_fileFinder ) 0461 m_fileFinder->completion()->setDir( url ); 0462 } 0463 0464 void FileWidget::slotFinishedLoading() 0465 { 0466 const KFileItem& current = getCurrentItem( false ); 0467 if ( !m_initialName.isEmpty() ) 0468 setCurrentItem( m_initialName ); 0469 else if ( current.isNull() ) { 0470 QModelIndex first = view()->model()->index(0, 0); 0471 if (first.isValid()) { 0472 KFileItem item = first.data(KDirModel::FileItemRole).value<KFileItem>(); 0473 if (!item.isNull()) { 0474 setCurrentItem( item ); 0475 } 0476 } 0477 } 0478 0479 m_initialName = QUrl(); 0480 emit finished(); 0481 } 0482 0483 QSize FileWidget::sizeHint() const 0484 { 0485 return QSize( 300, 300 ); 0486 } 0487 0488 void FileWidget::resizeEvent( QResizeEvent *e ) 0489 { 0490 KDirOperator::resizeEvent( e ); 0491 if ( m_fileFinder ) 0492 m_fileFinder->move( width() - m_fileFinder->width(), 0493 height() - m_fileFinder->height() ); 0494 }