dolphin/kfind/kfindtreeview.cpp
David Faure c43f0a1c2c Deprecated KNewMenu and make it derive from KNewFileMenu (BIC, but SC; the only way to avoid duplicating code until kde5)
Port all of kdebase' use of KNewMenu to KNewFileMenu.
Also port from KonqNameAndUrlInputDialog to KNameAndUrlInputDialog,
and from KonqMenuActions to KFileItemActions in dolphincontextmenu.cpp

svn path=/trunk/KDE/kdebase/apps/; revision=1080547
2010-01-26 12:04:56 +00:00

667 lines
19 KiB
C++

/*******************************************************************
* kfindtreeview.cpp
* Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
******************************************************************/
#include "kfindtreeview.h"
#include <QtCore/QTextStream>
#include <QtCore/QTextCodec>
#include <QtCore/QFileInfo>
#include <QtGui/QClipboard>
#include <QtGui/QHeaderView>
#include <QtGui/QApplication>
#include <QtCore/QDate>
#include <kfiledialog.h>
#include <klocale.h>
#include <kapplication.h>
#include <krun.h>
#include <kmessagebox.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kglobalsettings.h>
#include <kio/netaccess.h>
#include <kio/copyjob.h>
#include <kio/deletejob.h>
#include <konq_operations.h>
#include <knewfilemenu.h>
// 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<KFileItem,QString> > & pairs)
{
if ( pairs.size() > 0 )
{
beginInsertRows( QModelIndex(), m_itemList.size(), m_itemList.size()+pairs.size()-1 );
QList< QPair<KFileItem,QString> >::const_iterator it = pairs.constBegin();
QList< QPair<KFileItem,QString> >::const_iterator end = pairs.constEnd();
for (; it != end; ++it)
{
QPair<KFileItem,QString> pair = *it;
QString subDir = m_view->reducedDir(pair.first.url().directory(KUrl::AppendTrailingSlash));
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::ObeyTrailingSlash);
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 )
: QTreeView( parent ) ,
m_contextMenu(0)
{
//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( const QPoint &) ),
this, SLOT( contextMenuRequested( const QPoint & )));
//Mouse single/double click settings
connect( KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(reconfigureMouseSettings()) );
reconfigureMouseSettings();
// TODO: this is a workaround until Qt-issue 176832 has been fixed (from Dolphin)
connect(this, SIGNAL(pressed(const QModelIndex&)), this, SLOT(updateMouseButtons()));
//Generate popup menu actions
m_actionCollection = new KActionCollection( 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<KFileItem,QString> > & 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( i18n("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<KFindItem> itemList = m_model->getItemList();
if ( filter == "*.html" )
{
stream << QString::fromLatin1("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
"<head>\n"
"<title>%2</title></head>\n"
"<body>\n<h1>%2</h1>\n"
"<dl>\n")
.arg(i18n("KFind Results File"));
Q_FOREACH( const KFindItem & item, itemList )
{
const KFileItem fileItem = item.getFileItem();
stream << QString::fromLatin1("<dt><a href=\"%1\">%2</a></dt>\n").arg(
fileItem.url().url(), fileItem.url().prettyUrl() );
}
stream << QString::fromLatin1("</dl>\n</body>\n</html>\n");
}
else
{
Q_FOREACH( const KFindItem & item, itemList )
{
stream << item.getFileItem().url().url() << endl;
}
}
file.close();
KMessageBox::information(parentWidget(),
i18n("Results were saved to file\n")+
filename);
}
}
void KFindTreeView::openContainingFolder()
{
KUrl::List uris = selectedUrls();
QMap<KUrl, int> 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() )
{
(void) new KRun(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() );
}
}
KParts::BrowserExtension::PopupFlags flags = KParts::BrowserExtension::ShowProperties; // | KParts::BrowserExtension::ShowUrlOperations;
QList<QAction*> 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"));
KParts::BrowserExtension::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), 0, 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();
bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::DEL, KonqOperations::FORCE_CONFIRMATION, this );
if ( done )
{
KIO::del( uris );
//This should be done by KDirWatch integration in the main dialog, but it could fail?
Q_FOREACH( const KUrl & url, uris )
m_model->removeItem( url );
}
}
void KFindTreeView::moveToTrashSelectedFiles()
{
KUrl::List uris = selectedUrls();
bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::TRASH, KonqOperations::FORCE_CONFIRMATION, this );
if ( done )
{
KIO::trash( uris );
//This should be done by KDirWatch integration in the main dialog, but it could fail?
Q_FOREACH( const KUrl & url, uris )
m_model->removeItem( url );
}
}
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 "kfindtreeview.moc"