filemanager/src/filemanager/src/fmfilebrowsewidget.cpp
author hgs
Tue, 24 Aug 2010 10:24:14 +0800
changeset 37 15bc28c9dd51
parent 16 ada7962b4308
child 46 d58987eac7e8
permissions -rw-r--r--
201033

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*     Zhiqiang Yang <zhiqiang.yang@nokia.com>
*     Steven Yao <steven.yao@nokia.com>
*     Yong Zhang <yong.5.zhang@nokia.com>
* 
* Description:
*     The source file of the widget to browse files
*
*/

#include "fmfilebrowsewidget.h"
#include "fmviewdetailsdialog.h"
#include "fmviewmanager.h"
#include "fmfiledialog.h"
#include "fmdlgutils.h"
#include "fmfileiconprovider.h"

#include <QFile>
#include <QFileSystemModel>
#include <QSizePolicy>
#include <QGraphicsLinearLayout>

#include <hbmenu.h>
#include <hblistview.h>
#include <hbtreeview.h>
#include <hbabstractviewitem.h>
#include <hbaction.h>
#include <hbsearchpanel.h>
#include <hblabel.h>

// These define comes from implementation of QFileSystemModel
#define QFileSystemSortName 0
#define QFileSystemSortSize 1 
#define QFileSystemSortType 2
#define QFileSystemSortTime 3

FmFileBrowseWidget::FmFileBrowseWidget( HbWidget *parent, FmFileBrowseWidget::Style style )
    : HbWidget( parent ),
      mTreeView( 0 ),
      mListView( 0 ),
      mLayout( 0 ),
      mModel( 0 ),
      mSelectable( false ), 
      mStyle( NoStyle ), 
      mFileBrowseStyle( style ),
      mCurrentItem( 0 ),
      mOperationService( 0 ),
      mSearchPanel( 0 ),
      mListLongPressed( false )
{
    initFileModel();
    initListView();
    initTreeView();
    initSearchPanel();
    initEmptyTipsLabel();
    initLayout();
    
    mOperationService = FmViewManager::viewManager()->operationService();
    setStyle( style );
}

FmFileBrowseWidget::~FmFileBrowseWidget()
{
    //take the model out from view and delete it.
    //so that the model could be destroy earlier
    //since there is a thread running in background
    //if the model destroy later, the thread might not quit properly.

    mTreeView->setModel( 0 );
    mListView->setModel( 0 );
    delete mModel;
    
    delete mFileIconProvider;
}

QFileInfo FmFileBrowseWidget::currentPath() const
{
    QModelIndex index = mListView->rootIndex();
    if( !index.isValid() ) {
        return QFileInfo();
    }

    if (mStyle == ListStyle) {
        return mModel->fileInfo( mListView->rootIndex() );
    } else if ( mStyle == TreeStyle ) {
        return QFileInfo();
    } else {
        return QFileInfo();
    }
}

QList<QFileInfo> FmFileBrowseWidget::checkedItems() const
{
    QList<QFileInfo> infos;
    
    QItemSelection selection;
    if (mStyle == ListStyle) {
        selection = mListView->selectionModel()->selection();
    }

    QModelIndexList indexes = selection.indexes();
    for (int i = 0; i < indexes.size(); ++i) {
        infos.append( mModel->fileInfo( indexes.at(i) ) );
    }
 
    return infos;
}

void FmFileBrowseWidget::setRootPath( const QString &pathName )
{
    QString logString = "FmFileBrowseWidget::setRootPath(" + pathName + ')';
    FM_LOG( logString );

    int err = checkPathAndSetStyle( pathName );
    switch( err )
        {
        case FmErrNone:
            {
            mListView->setRootIndex( mModel->setRootPath( pathName ) );
            emit currentPathChanged( pathName );
            break;
            }
        case FmErrPathNotExist:
            {
            FmDlgUtils::information( hbTrId( "Path is not exist" ) );
            break;
            }
        case FmErrDriveNotAvailable:
            {
            // do not take any action as widget set note in label already. 
            break;
            }
        case FmErrDriveDenied:
        case FmErrPathDenied:
            {
            FmDlgUtils::information( hbTrId( "Can not access" ) );
            break;
            }
        default:
            Q_ASSERT_X( false, "setRootPath", "please handle all error from isPathAccessabel" );
            break;
        }
    mCurrentDrive = pathName.left( 3 );
}

void FmFileBrowseWidget::setStyle( FmFileBrowseWidget::Style style )
{
    if ( mStyle == style ) {
        return;
    }

    if ( mStyle == ListStyle ) {
        mLayout->removeItem( mListView );
        mListView->hide();
    } else if ( mStyle == TreeStyle ) {
        mLayout->removeItem( mTreeView );
        mTreeView->hide();
    } else if( mStyle == LabelStyle ){
        mLayout->removeItem( mEmptyTipLabel );
        mEmptyTipLabel->hide();
    }

    if ( style == ListStyle ) {
        mLayout->addItem( mListView );
        mListView->show();
        mFileBrowseStyle = ListStyle;
    } else if ( style == TreeStyle ) {
        mLayout->addItem( mTreeView );
        mTreeView->show();
        mFileBrowseStyle = TreeStyle;
    } else if ( style == LabelStyle ){
        mLayout->addItem( mEmptyTipLabel );
        mEmptyTipLabel->show();
    }
    
    mStyle = style;
}

bool FmFileBrowseWidget::selectable() const
{
    return mSelectable;
}

void FmFileBrowseWidget::setSelectable( bool enable )
{
    if (mSelectable != enable) {
        mSelectable = enable;
        if (mStyle == ListStyle) {
            mListView->setSelectionMode( mSelectable ? HbAbstractItemView::MultiSelection 
                                                    : HbAbstractItemView::NoSelection );
        }
    }
}

void FmFileBrowseWidget::clearSelection()
{
    QItemSelectionModel *selectionModel = 0;

    if (mStyle == ListStyle) {
        selectionModel = mListView->selectionModel();
    } else if (mStyle == TreeStyle) {
        selectionModel = mTreeView->selectionModel();
    }

    if( selectionModel ){
        selectionModel->clear();
    }

}


bool FmFileBrowseWidget::rename( const QString &oldName, const QString &newName )
{
    return QFile::rename( oldName, newName );
}




bool FmFileBrowseWidget::cdUp()
{
	if (mStyle == ListStyle) {
        QModelIndex parentIndex = mListView->rootIndex().parent();
		// QFileSystemModel will auto refresh for file/folder change
        if (parentIndex.isValid()) {
			changeRootIndex( parentIndex );
            return true;
        }
    }

    return false;
}

void FmFileBrowseWidget::on_list_activated( const QModelIndex &index )
{
    mActivatedModelIndex = index;
    emit listActivated();
}

void FmFileBrowseWidget::on_listActivated()
{
    FM_LOG("FmFileBrowseWidget::on_listActivated start");
    if( mListLongPressed ) {
        FM_LOG("FmFileBrowseWidget::on_list_activated end because longPressed");
        return;
    }
    if (!mSelectable) {
        if (mModel->isDir(mActivatedModelIndex) ) {
            FM_LOG("FmFileBrowseWidget::on_list_activated changeRootIndex>>");
            changeRootIndex( mActivatedModelIndex );
            FM_LOG("FmFileBrowseWidget::on_list_activated changeRootIndex<<");
        } else {
            QString filePath( mModel->filePath( mActivatedModelIndex ) );
            QFileInfo fileInfo( filePath );
            if ( fileInfo.isFile() ) {
                mOperationService->syncLaunchFileOpen( filePath );
            }
        }
    }
    FM_LOG("FmFileBrowseWidget::on_listActivated end");
}

void FmFileBrowseWidget::on_tree_activated( const QModelIndex &index )
{
    if (!mSelectable) {
        mTreeView->setExpanded( index, !mTreeView->isExpanded( index ) );
    }
}

void FmFileBrowseWidget::on_list_longPressed( HbAbstractViewItem *item, const QPointF &coords )
{
    mListLongPressed = true;
    HbMenu *contextMenu = new HbMenu();
    mCurrentItem = item;
    
    HbAction *viewAction = new HbAction();
    viewAction->setObjectName( "viewAction" );
    viewAction->setText( hbTrId( "txt_fmgr_menu_view_details_file" ) );
    contextMenu->addAction( viewAction );

    connect( viewAction, SIGNAL( triggered() ),
        this, SLOT( on_viewAction_triggered() ), Qt::QueuedConnection );

    //copy
    HbAction *copyAction = new HbAction();
    copyAction->setObjectName( "copyAction" );
    copyAction->setText( hbTrId( "txt_fmgr_menu_copy" ) );
    contextMenu->addAction( copyAction );

    connect( copyAction, SIGNAL( triggered() ),
    this, SLOT( on_copyAction_triggered() ), Qt::QueuedConnection );

    
    QString filePath( mModel->filePath( item->modelIndex() ) );
    QString formatFilePath( FmUtils::fillPathWithSplash( filePath ) );
    QFileInfo fileInfo( filePath );
    
    if( ( fileInfo.isFile() ) || ( fileInfo.isDir() && !( FmUtils::isDefaultFolder( formatFilePath ) ) ) ){
        //Move
        HbAction *moveAction = new HbAction();
        moveAction->setObjectName( "moveAction" );
        moveAction->setText( hbTrId( "txt_fmgr_menu_move" ) );
        contextMenu->addAction( moveAction );
    
        connect( moveAction, SIGNAL( triggered() ),
        this, SLOT( on_moveAction_triggered() ), Qt::QueuedConnection );
    
        //Delete
        HbAction *deleteAction = new HbAction();
        deleteAction->setObjectName( "deleteAction" );
        deleteAction->setText( hbTrId( "txt_fmgr_menu_delete" ) );
        contextMenu->addAction( deleteAction );
    
        connect( deleteAction, SIGNAL( triggered() ),
        this, SLOT( on_deleteAction_triggered() ), Qt::QueuedConnection );
    
        //rename
        HbAction *renameAction = new HbAction();
        renameAction->setObjectName( "renameAction" );
        renameAction->setText( hbTrId( "txt_fmgr_menu_rename" ) );
        contextMenu->addAction( renameAction );
    
        connect( renameAction, SIGNAL( triggered() ),
        this, SLOT( on_renameAction_triggered() ), Qt::QueuedConnection );
    }
    
//    if( fileInfo.isFile() ){
//        HbAction *sendAction = new HbAction();
//        sendAction->setObjectName( "sendAction" );
//        sendAction->setText( hbTrId( "txt_fmgr_menu_send" ) );
//        contextMenu->addAction( sendAction );
//        
//        connect( sendAction, SIGNAL( triggered() ),
//        this, SLOT( on_sendAction_triggered() ) );
//    }
    
    contextMenu->setPreferredPos( coords );
    contextMenu->open();
}

void FmFileBrowseWidget::on_list_pressed( const QModelIndex &  index )
{
    mListLongPressed = false;
}

void FmFileBrowseWidget::on_tree_longPressed( HbAbstractViewItem *item, const QPointF &coords )
{
    Q_UNUSED( item );
    Q_UNUSED( coords );
    /*
    HbMenu *contextMenu = new HbMenu( this );

    if ( isFolder( item->modelIndex() ) ) {
        contextMenu->addAction("View details");
        contextMenu->addAction("Copy");
        contextMenu->addAction("Rename");
        contextMenu->addAction("Delete");
    } else {
        contextMenu->addAction("View details");
        contextMenu->addAction("Open");
        contextMenu->addAction("Copy");
        contextMenu->addAction("Rename");
        contextMenu->addAction("Delete");
    }

    contextMenu->exec( coords );
    */
}

void FmFileBrowseWidget::initListView()
{
    mListView = new HbListView();
    mListView->setObjectName( "list" );
    mListView->setModel( mModel );

    connect( mListView, SIGNAL( activated( const QModelIndex& ) ),
        this, SLOT( on_list_activated( const QModelIndex& ) ) );
    connect( this, SIGNAL( listActivated() ),
        this, SLOT( on_listActivated() ), Qt::QueuedConnection );
    connect( mListView, SIGNAL( pressed( const QModelIndex & ) ),
        this, SLOT( on_list_pressed( const QModelIndex & ) ) );
    connect( mListView, SIGNAL( longPressed( HbAbstractViewItem *, const QPointF & ) ),
        this, SLOT( on_list_longPressed( HbAbstractViewItem *, const QPointF & ) ) );
}

void FmFileBrowseWidget::initTreeView()
{
    mTreeView = new HbTreeView();
    mTreeView->setObjectName( "tree" );
    mTreeView->setModel( mModel );

    connect( mTreeView, SIGNAL( activated( const QModelIndex& ) ),
        this, SLOT( on_tree_activated( const QModelIndex& ) ) );
    connect( mTreeView, SIGNAL( longPressed( HbAbstractViewItem *, const QPointF & ) ),
        this, SLOT( on_tree_longPressed( HbAbstractViewItem *, const QPointF & ) ) );
}

void FmFileBrowseWidget::initFileModel()
{
    mModel = new QFileSystemModel( this );
    mModel->setReadOnly( false );
    
    mFileIconProvider = new FmFileIconProvider();
    mModel->setIconProvider( mFileIconProvider );
}

void FmFileBrowseWidget::initLayout()
{
    mLayout = new QGraphicsLinearLayout( this );
    mLayout->setOrientation( Qt::Vertical );
    
    setLayout( mLayout );
}

void FmFileBrowseWidget::initSearchPanel()
{
    mSearchPanel = new HbSearchPanel( this );
    mSearchPanel->setObjectName( "searchPanel" );
    mSearchPanel->setSearchOptionsEnabled( true );
    mSearchPanel->setProgressive( false );
    mSearchPanel->hide();
    
    connect( mSearchPanel, SIGNAL( searchOptionsClicked() ),
        this, SLOT( on_searchPanel_searchOptionsClicked() ), Qt::QueuedConnection );
    
    connect( mSearchPanel, SIGNAL( criteriaChanged( const QString & ) ),
        this, SLOT( on_searchPanel_criteriaChanged( const QString & ) ) );
    
    connect( mSearchPanel, SIGNAL( exitClicked() ),
        this, SLOT( on_searchPanel_exitClicked() ) );
}

void FmFileBrowseWidget::initEmptyTipsLabel()
{
    mEmptyTipLabel = new HbLabel( this );
    mEmptyTipLabel->setObjectName( "searchPanel" );
    mEmptyTipLabel->hide();
}


void FmFileBrowseWidget::changeRootIndex( const QModelIndex &index )
{
	QString filePath = mModel->fileInfo( index ).absoluteFilePath();
	setRootPath( filePath );
}

bool FmFileBrowseWidget::isDriver(const QModelIndex &index) const
{
    QString path = mModel->filePath(index);
    return path.right(1) == ":";
}

bool FmFileBrowseWidget::isFolder(const QModelIndex &index) const
{
    if( isDriver( index ) )    {
        return false;
    }

    return mModel->isDir(index);
}

void FmFileBrowseWidget::setModelFilter( QDir::Filters filters ) 
{
    mModel->setFilter( filters );
}

void FmFileBrowseWidget::on_driveChanged()
{
    FM_LOG( "FmFileBrowseWidget::on_driveChanged start" );
    QString currPath( currentPath().absoluteFilePath() );
    
    if(style()==ListStyle) {
        // display normally, setRootPath again to check if drive is available 
        setRootPath( currPath );
    } else{
        // display label style, setRootPath to drive root
        setRootPath( mCurrentDrive );
    }
    emit setTitle( FmUtils::fillDriveVolume( mCurrentDrive, true ) );
    FM_LOG( "FmFileBrowseWidget::on_driveChanged end" );
}

int FmFileBrowseWidget::checkPathAndSetStyle( const QString& path )
{
    FM_LOG( "FmFileBrowseWidget::checkPathAndSetStyle start_" + path );
    int err = FmUtils::isPathAccessabel( path );
    switch( err )
        {
        case FmErrNone:
            {
            setStyle( mFileBrowseStyle );
            emit setEmptyMenu( false );
            break;
            }
        case FmErrDriveNotAvailable:
            {
            QString driveName = FmUtils::getDriveNameFromPath( path );
            FmDriverInfo::DriveState state = FmUtils::queryDriverInfo( driveName ).driveState();
            
            if( state & FmDriverInfo::EDriveLocked ) {
                mEmptyTipLabel->setPlainText( hbTrId( "Drive is locked" ) );       
            } else if( state & FmDriverInfo::EDriveNotPresent ) {
                mEmptyTipLabel->setPlainText( hbTrId( "Drive is not present" ) );
            } else if( state & FmDriverInfo::EDriveCorrupted ) {
                mEmptyTipLabel->setPlainText( hbTrId( "Drive is Corrupted" ) );
            } else {
                mEmptyTipLabel->setPlainText( hbTrId( "Drive can not be opened " ) );
            }
            setStyle( LabelStyle );
			//hide search panel when the drive is removed 
            if ( mSearchPanel->isVisible() ){
                mSearchPanel->hide();
                mLayout->removeItem( mSearchPanel );
            }    
            emit setEmptyMenu( true );
            break;
            }
        case FmErrPathNotExist:
        case FmErrDriveDenied:
        case FmErrPathDenied:
            {
            // do not tack any action, error note shoule be shown by invoker.
            // checkPathAndSetStyle just check path and set style.
            break;
            }
        default:
            Q_ASSERT_X( false, "checkPathAndSetStyle", "please handle all error from isPathAccessabel" );
            break;
        }
    return err;
}

void FmFileBrowseWidget::sortFiles( TSortType sortType )
{
    switch( sortType ){
        case ESortByName:{
			mModel->sort( QFileSystemSortName );
        }
            break;
        case ESortByTime:{
            mModel->sort( QFileSystemSortTime );
        }
            break;
        case ESortBySize:{
            mModel->sort( QFileSystemSortSize );
        }
            break;
        case ESortByType:{
            mModel->sort( QFileSystemSortType );
        }
            break;
        default:
            break;
    }
}

void FmFileBrowseWidget::activeSearchPanel()
{
    mFindTargetPath.clear();
    mLayout->addItem( mSearchPanel );
    mSearchPanel->show();
}

void FmFileBrowseWidget::on_searchPanel_searchOptionsClicked()
{
    mFindTargetPath = FmUtils::fillPathWithSplash( FmFileDialog::getExistingDirectory( 0, hbTrId( "Look in:" ), QString(""),
        QStringList() ) );
}

void FmFileBrowseWidget::on_searchPanel_criteriaChanged( const QString &criteria )
{
    if( mFindTargetPath.isEmpty() ){
        mFindTargetPath = currentPath().filePath();
    }
    emit startSearch( mFindTargetPath, criteria );
    
    mSearchPanel->hide();
    mLayout->removeItem( mSearchPanel );
}

void FmFileBrowseWidget::on_searchPanel_exitClicked()
{
    mSearchPanel->hide();
    mLayout->removeItem( mSearchPanel );
}

void FmFileBrowseWidget::on_sendAction_triggered()
{
    QString filePath = mModel->filePath( mCurrentItem->modelIndex() );
    QStringList list;
    list.append( filePath );
    FmUtils::sendFiles( list );
}

void FmFileBrowseWidget::on_viewAction_triggered()
{
    QString filePath = mModel->filePath( mCurrentItem->modelIndex() );
    QFileInfo fileInfo = mModel->fileInfo( mCurrentItem->modelIndex() );
    
    if( fileInfo.isDir() ){
        mOperationService->asyncViewFolderDetails( filePath );  
    }
    else if( fileInfo.isFile() ){
		FmViewDetailsDialog::showFileViewDetailsDialog( filePath, FmUtils::getDriveLetterFromPath( filePath ) );
    } 
}

void FmFileBrowseWidget::on_deleteAction_triggered()
{
    QStringList fileList;
    fileList.push_back( mModel->filePath( mCurrentItem->modelIndex() ) );
    if ( FmDlgUtils::question( hbTrId("Confirm Deletion?" ) )) {
        int ret = mOperationService->asyncRemove( fileList );
        switch( ret ) {
            case FmErrNone:
                // no error, do not show note to user
                break;
            case FmErrAlreadyStarted:
                // last operation have not finished
                FmDlgUtils::information( hbTrId( "Operatin already started!" ) );
                break;
            case FmErrWrongParam:
                FmDlgUtils::information( hbTrId( "Wrong parameters!" ) );
                break;
            default:
                FmDlgUtils::information( hbTrId( "Operation fail to start!" ) );
        }
    }
}

void FmFileBrowseWidget::on_copyAction_triggered()
{
    QStringList srcFileList;
    srcFileList.push_back( mModel->filePath( mCurrentItem->modelIndex() ) );

    QString targetPathName = FmFileDialog::getExistingDirectory( 0, hbTrId( "copy to" ),
    QString(""), QStringList() );
    if( !targetPathName.isEmpty() ) {
        targetPathName = FmUtils::fillPathWithSplash( targetPathName );

        int ret = mOperationService->asyncCopy(
            srcFileList, targetPathName );
        switch( ret ) {
            case FmErrNone:
                // no error, do not show note to user
                break;
            case FmErrAlreadyStarted:
                // last operation have not finished
                FmDlgUtils::information( hbTrId( "Operatin already started!" ) );
                break;
            case FmErrWrongParam:
                FmDlgUtils::information( hbTrId( "Wrong parameters!" ) );
                break;
            default:
                FmDlgUtils::information( hbTrId( "Operation fail to start!" ) );
        }
    }

}
 
void FmFileBrowseWidget::on_moveAction_triggered()
{
    QStringList fileList;
    fileList.push_back( mModel->filePath( mCurrentItem->modelIndex() ) );

    QString targetPathName = FmFileDialog::getExistingDirectory( 0, hbTrId( "move to" ),
            QString(""), QStringList() );

    if( !targetPathName.isEmpty() ) {
        targetPathName = FmUtils::fillPathWithSplash( targetPathName );

        int ret = mOperationService->asyncMove( fileList, targetPathName );
        switch( ret ) {
            case FmErrNone:
                // no error, do not show note to user
                break;
            case FmErrAlreadyStarted:
                // last operation have not finished
                FmDlgUtils::information( hbTrId( "Operatin already started!" ) );
                break;
            case FmErrWrongParam:
                FmDlgUtils::information( hbTrId( "Wrong parameters!" ) );
                break;
            default:
                FmDlgUtils::information( hbTrId( "Operation fail to start!" ) );
        }
    }
}


void FmFileBrowseWidget::on_renameAction_triggered()
{
    QString filePath = mModel->filePath( mCurrentItem->modelIndex() );
    QFileInfo fileInfo = mModel->fileInfo( mCurrentItem->modelIndex() );
    int maxFileNameLength = FmUtils::getMaxFileNameLength();
    // regExpList used to disable primary action of text query dialog if input text is not match
    QStringList regExpList = (QStringList() << Regex_ValidFileFolderName << Regex_ValidNotEndWithDot );

    QString oldSuffix( fileInfo.suffix() );
    QString newName( fileInfo.fileName() );
    while( FmDlgUtils::showTextQuery( hbTrId( "Enter new name for %1" ).arg( newName ), newName, regExpList,
            maxFileNameLength, QString() , true ) ){
        // remove whitespace from the start and the end.
        newName = newName.trimmed();
        QString newTargetPath = FmUtils::fillPathWithSplash(
            fileInfo.absolutePath() ) + newName;
        QFileInfo newFileInfo( newTargetPath );
        QString errString;
        // check if name/path is available for use
        // add new Name to check, in order to avoid problem of newName is empty
        if( !FmUtils::checkNewFolderOrFile( newName, newTargetPath, errString ) ) {
            FmDlgUtils::information( errString );
            continue;
        }
        if( !rename( fileInfo.absoluteFilePath(), newTargetPath ) ) {
            FmDlgUtils::information( hbTrId("Rename failed!") );
        }
        else {
            // Rename succeed
            if ( ( oldSuffix.compare( newFileInfo.suffix(), Qt::CaseInsensitive ) != 0 )
                && newFileInfo.isFile() ) {
                // popup warning when the suffix of file is changed.
                FmDlgUtils::information( hbTrId( "File may become unusable when file name extension is changed" ) );        
            }   
        }
        break;
    }
}