/******************************************************************* * kfindtreeview.cpp * Copyright 2009 Dario Andres Rodriguez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ******************************************************************/ #include "kfindtreeview.h" #include "kfinddlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Permission strings static const char* const perm[4] = { I18N_NOOP( "Read-write" ), I18N_NOOP( "Read-only" ), I18N_NOOP( "Write-only" ), I18N_NOOP( "Inaccessible" ) }; #define RW 0 #define RO 1 #define WO 2 #define NA 3 //BEGIN KFindItemModel KFindItemModel::KFindItemModel( KFindTreeView * parentView ) : QAbstractTableModel( parentView ) { m_view = parentView; } QVariant KFindItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return i18nc("file name column","Name"); case 1: return i18nc("name of the containing folder","In Subfolder"); case 2: return i18nc("file size column","Size"); case 3: return i18nc("modified date column","Modified"); case 4: return i18nc("file permissions column","Permissions"); case 5: return i18nc("first matching line of the query string in this file", "First Matching Line"); default: return QVariant(); } } return QVariant(); } void KFindItemModel::insertFileItems( const QList< QPair > & pairs) { if ( pairs.size() > 0 ) { beginInsertRows( QModelIndex(), m_itemList.size(), m_itemList.size()+pairs.size()-1 ); QList< QPair >::const_iterator it = pairs.constBegin(); QList< QPair >::const_iterator end = pairs.constEnd(); for (; it != end; ++it) { QPair pair = *it; QString subDir = m_view->reducedDir(pair.first.url().directory(KUrl::AddTrailingSlash)); m_itemList.append( KFindItem( pair.first, subDir, pair.second ) ); } endInsertRows(); } } int KFindItemModel::rowCount ( const QModelIndex & parent ) const { if( !parent.isValid() ) return m_itemList.count(); //Return itemcount for toplevel else return 0; } KFindItem KFindItemModel::itemAtIndex( const QModelIndex & index ) const { if ( index.isValid() && m_itemList.size() >= index.row() ) return m_itemList.at( index.row() ); return KFindItem(); } QVariant KFindItemModel::data ( const QModelIndex & index, int role ) const { if (!index.isValid()) return QVariant(); if (index.column() > 6 || index.row() >= m_itemList.count() ) return QVariant(); switch( role ) { case Qt::DisplayRole: case Qt::DecorationRole: case Qt::UserRole: return m_itemList.at( index.row() ).data( index.column(), role ); default: return QVariant(); } return QVariant(); } void KFindItemModel::removeItem( const KUrl & url ) { int itemCount = m_itemList.size(); for ( int i = 0; i < itemCount; i++) { KFindItem item = m_itemList.at(i); if ( item.getFileItem().url() == url ) { beginRemoveRows( QModelIndex(), i, i ); m_itemList.removeAt( i ); endRemoveRows(); return; } } } bool KFindItemModel::isInserted( const KUrl & url ) { int itemCount = m_itemList.size(); for ( int i = 0; i < itemCount; i++) { KFindItem item = m_itemList.at(i); if ( item.getFileItem().url() == url ) { return true; } } return false; } void KFindItemModel::clear() { beginRemoveRows( QModelIndex(), 0, m_itemList.size() ); m_itemList.clear(); endRemoveRows(); } Qt::ItemFlags KFindItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.isValid()) return Qt::ItemIsDragEnabled | defaultFlags; return defaultFlags; } QMimeData * KFindItemModel::mimeData(const QModelIndexList &indexes) const { KUrl::List uris; foreach ( const QModelIndex & index, indexes ) { if( index.isValid()) { if( index.column() == 0 ) //Only use the first column item { uris.append( m_itemList.at( index.row() ).getFileItem().url() ); } } } if ( uris.count() <= 0 ) return 0; QMimeData * mimeData = new QMimeData(); uris.populateMimeData( mimeData ); return mimeData; } //END KFindItemModel //BEGIN KFindItem KFindItem::KFindItem( const KFileItem & _fileItem, const QString & subDir, const QString & matchingLine ) { m_fileItem = _fileItem; m_subDir = subDir; m_matchingLine = matchingLine; //TODO more caching ? if ( !m_fileItem.isNull() && m_fileItem.isLocalFile() ) { QFileInfo fileInfo(m_fileItem.url().toLocalFile()); int perm_index; if(fileInfo.isReadable()) perm_index = fileInfo.isWritable() ? RW : RO; else perm_index = fileInfo.isWritable() ? WO : NA; m_permission = i18n(perm[perm_index]); m_icon = KIcon( m_fileItem.iconName() ); } } QVariant KFindItem::data( int column, int role ) const { if ( m_fileItem.isNull() ) return QVariant(); if( role == Qt::DecorationRole ) { if (column == 0) return m_icon; else return QVariant(); } if( role == Qt::DisplayRole ) switch( column ) { case 0: return m_fileItem.url().fileName(KUrl::LeaveTrailingSlash); case 1: return m_subDir; case 2: return KIO::convertSize( m_fileItem.size() ); case 3: return m_fileItem.timeString(KFileItem::ModificationTime); case 4: return m_permission; case 5: return m_matchingLine; default: return QVariant(); } if( role == Qt::UserRole ) switch( column ) { case 2: return m_fileItem.size(); case 3: return m_fileItem.time(KFileItem::ModificationTime).toTime_t(); default: return QVariant(); } return QVariant(); } //END KFindItem //BEGIN KFindSortFilterProxyModel bool KFindSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { //Order by UserData size in bytes or unix date if( left.column() == 2 || left.column() == 3) { qulonglong leftData = sourceModel()->data( left, Qt::UserRole ).toULongLong(); qulonglong rightData = sourceModel()->data( right, Qt::UserRole ).toULongLong(); return leftData < rightData; } // Default sorting rules for string values else { return QSortFilterProxyModel::lessThan( left, right ); } } //END KFindSortFilterProxyModel //BEGIN KFindTreeView KFindTreeView::KFindTreeView( QWidget *parent, KfindDlg * findDialog ) : QTreeView( parent ) , m_contextMenu(0), m_kfindDialog(findDialog) { //Configure model and proxy model m_model = new KFindItemModel( this ); m_proxyModel = new KFindSortFilterProxyModel(); m_proxyModel->setSourceModel( m_model ); setModel( m_proxyModel ); //Configure QTreeView setRootIsDecorated( false ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSortingEnabled( true ); setDragEnabled( true ); setContextMenuPolicy( Qt::CustomContextMenu ); connect( this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); //Mouse single/double click settings connect( KGlobalSettings::self(), SIGNAL(mouseChanged()), this, SLOT(reconfigureMouseSettings()) ); reconfigureMouseSettings(); // TODO: this is a workaround until Qt-issue 176832 has been fixed (from Dolphin) connect(this, SIGNAL(pressed(QModelIndex)), this, SLOT(updateMouseButtons())); //Generate popup menu actions m_actionCollection = new KActionCollection( this ); m_actionCollection->addAssociatedWidget(this); KAction * open = KStandardAction::open(this, SLOT(slotExecuteSelected()), this); m_actionCollection->addAction( "file_open", open ); KAction * copy = KStandardAction::copy(this, SLOT(copySelection()), this); m_actionCollection->addAction( "edit_copy", copy ); KAction * openFolder = new KAction( KIcon("window-new"), i18n("&Open containing folder(s)"), this ); connect( openFolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()) ); m_actionCollection->addAction( "openfolder", openFolder ); KAction * del = new KAction( KIcon("edit-delete"), i18n("&Delete"), this ); connect( del, SIGNAL(triggered()), this, SLOT(deleteSelectedFiles()) ); del->setShortcut(Qt::SHIFT + Qt::Key_Delete); m_actionCollection->addAction( "del", del ); KAction * trash = new KAction( KIcon("user-trash"), i18n("&Move to Trash"), this ); connect( trash, SIGNAL(triggered()), this, SLOT(moveToTrashSelectedFiles()) ); trash->setShortcut(Qt::Key_Delete); m_actionCollection->addAction( "trash", trash ); header()->setStretchLastSection( true ); sortByColumn( 0, Qt::AscendingOrder ); } KFindTreeView::~KFindTreeView() { delete m_model; delete m_proxyModel; delete m_actionCollection; } void KFindTreeView::resizeToContents() { resizeColumnToContents( 0 ); resizeColumnToContents( 1 ); resizeColumnToContents( 2 ); resizeColumnToContents( 3 ); } QString KFindTreeView::reducedDir(const QString& fullDir) { if (fullDir.indexOf(m_baseDir)==0) { QString tmp=fullDir.mid(m_baseDir.length()); return tmp; }; return fullDir; } void KFindTreeView::beginSearch(const KUrl& baseUrl) { kDebug() << QString("beginSearch in: %1").arg(baseUrl.path()); m_baseDir = baseUrl.path(KUrl::AddTrailingSlash); m_model->clear(); } void KFindTreeView::endSearch() { resizeToContents(); } void KFindTreeView::insertItems (const QList< QPair > & pairs) { m_model->insertFileItems( pairs ); } void KFindTreeView::removeItem(const KUrl & url) { KUrl::List list = selectedUrls(); if ( list.contains(url) ) { //Close menu if( m_contextMenu ) { m_contextMenu->hide(); delete m_contextMenu; m_contextMenu = 0; } } m_model->removeItem( url ); } // copy to clipboard void KFindTreeView::copySelection() { QMimeData * mime = m_model->mimeData( m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes() ); if (mime) { QClipboard * cb = kapp->clipboard(); cb->setMimeData( mime ); } } void KFindTreeView::saveResults() { KFileDialog *dlg = new KFileDialog(QString(), QString(), this); dlg->setOperationMode (KFileDialog::Saving); dlg->setCaption( i18nc("@title:window", "Save Results As") ); dlg->setFilter( QString("*.html|%1\n*.txt|%2").arg( i18n("HTML page"), i18n("Text file") ) ); dlg->setConfirmOverwrite(true); dlg->exec(); KUrl u = dlg->selectedUrl(); QString filter = dlg->currentFilter(); delete dlg; if (!u.isValid() || !u.isLocalFile()) return; QString filename = u.toLocalFile(); QFile file(filename); if ( !file.open(QIODevice::WriteOnly) ) { KMessageBox::error(parentWidget(), i18n("Unable to save results.")); } else { QTextStream stream( &file ); stream.setCodec( QTextCodec::codecForLocale() ); QList itemList = m_model->getItemList(); if ( filter == "*.html" ) { stream << QString::fromLatin1("\n" "\n" "%2\n" "\n" "\n

%2

\n" "
\n") .arg(QString::fromLatin1(QTextCodec::codecForLocale()->name())) .arg(i18n("KFind Results File")); Q_FOREACH( const KFindItem & item, itemList ) { const KFileItem fileItem = item.getFileItem(); stream << QString::fromLatin1("
%2
\n").arg( fileItem.url().url(), fileItem.url().prettyUrl() ); } stream << QString::fromLatin1("
\n\n\n"); } else { Q_FOREACH( const KFindItem & item, itemList ) { stream << item.getFileItem().url().url() << endl; } } file.close(); m_kfindDialog->setStatusMsg(i18nc("%1=filename", "Results were saved to: %1", filename)); } } void KFindTreeView::openContainingFolder() { KUrl::List uris = selectedUrls(); QMap folderMaps; //Generate *unique* folders Q_FOREACH( const KUrl & url, uris ) { KUrl dir = url; dir.setFileName( QString() ); folderMaps.insert( dir, 0 ); } //TODO if >1 add a warn ? Q_FOREACH( const KUrl & url, folderMaps.keys() ) { KToolInvocation::self()->startServiceForUrl(url.url(), this); } } void KFindTreeView::slotExecuteSelected() { QModelIndexList selected = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); if ( selected.size() == 0 ) return; //TODO if >X add a warn ? Q_FOREACH( const QModelIndex & index, selected ) { if( index.column() == 0 ) { KFindItem item = m_model->itemAtIndex( index ); if ( item.isValid() ) item.getFileItem().run(); } } } void KFindTreeView::slotExecute( const QModelIndex & index ) { if( (m_mouseButtons & Qt::LeftButton) && QApplication::keyboardModifiers() == Qt::NoModifier ) { if ( !index.isValid() ) return; QModelIndex realIndex = m_proxyModel->mapToSource( index ); if ( !realIndex.isValid() ) return; KFindItem item = m_model->itemAtIndex( realIndex ); if ( item.isValid() ) item.getFileItem().run(); } } void KFindTreeView::contextMenuRequested( const QPoint & p) { KFileItemList fileList; QModelIndexList selected = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); if ( selected.size() == 0 ) return; Q_FOREACH( const QModelIndex & index, selected ) { if( index.column() == 0 ) { const KFindItem item = m_model->itemAtIndex( index ); if( item.isValid() ) fileList.append( item.getFileItem() ); } } KonqPopupMenu::PopupFlags flags = KonqPopupMenu::ShowProperties; // | KonqPopupMenu::ShowUrlOperations; QList editActions; editActions.append(m_actionCollection->action("file_open")); editActions.append(m_actionCollection->action("openfolder")); editActions.append(m_actionCollection->action("edit_copy")); editActions.append(m_actionCollection->action("del")); editActions.append(m_actionCollection->action("trash")); KonqPopupMenu::ActionGroupMap actionGroups; actionGroups.insert("editactions", editActions); if( m_contextMenu ) { m_contextMenu->hide(); delete m_contextMenu; m_contextMenu = 0; } m_contextMenu = new KonqPopupMenu( fileList, KUrl(), *m_actionCollection, new KNewFileMenu( m_actionCollection, "new_menu", this), flags, this, 0, actionGroups); m_contextMenu->exec( this->mapToGlobal( p ) ); } KUrl::List KFindTreeView::selectedUrls() { KUrl::List uris; QModelIndexList indexes = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); Q_FOREACH( const QModelIndex & index, indexes ) { if( index.column() == 0 && index.isValid() ) { KFindItem item = m_model->itemAtIndex( index ); if( item.isValid() ) uris.append( item.getFileItem().url() ); } } return uris; } void KFindTreeView::deleteSelectedFiles() { KUrl::List uris = selectedUrls(); if ( uris.isEmpty() ) { return; } bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::DEL, KonqOperations::FORCE_CONFIRMATION, this ); if ( done ) { KJob * deleteJob = KIO::del( uris ); deleteJob->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void KFindTreeView::moveToTrashSelectedFiles() { KUrl::List uris = selectedUrls(); if ( uris.isEmpty() ) { return; } bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::TRASH, KonqOperations::FORCE_CONFIRMATION, this ); if ( done ) { KJob * trashJob = KIO::trash( uris ); trashJob->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void KFindTreeView::reconfigureMouseSettings() { disconnect( SIGNAL(clicked(QModelIndex)) ); disconnect( SIGNAL(doubleClicked(QModelIndex)) ); if ( KGlobalSettings::singleClick() ) { connect( this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotExecute(QModelIndex)) ); } else { connect( this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotExecute(QModelIndex)) ); } } void KFindTreeView::updateMouseButtons() { m_mouseButtons = QApplication::mouseButtons(); } //END KFindTreeView #include "moc_kfindtreeview.cpp"