From 76c5f33e65e9a36c94c46f1ed0b6d33721d21cfb Mon Sep 17 00:00:00 2001 From: Caolán McNamara Date: Fri, 4 Oct 2019 13:23:55 +0100 Subject: move file picker only code to fpicker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I47cc2cb7db396a06a2abeffe4a5d40a039f21c58 Reviewed-on: https://gerrit.libreoffice.org/80222 Tested-by: Jenkins Reviewed-by: Caolán McNamara Tested-by: Caolán McNamara --- fpicker/Library_fps_office.mk | 5 + fpicker/inc/bitmaps.hlst | 2 + fpicker/source/office/OfficeControlAccess.hxx | 2 +- fpicker/source/office/RemoteFilesDialog.hxx | 4 +- fpicker/source/office/asyncfilepicker.cxx | 2 +- fpicker/source/office/contentenumeration.cxx | 330 +++++ fpicker/source/office/contentenumeration.hxx | 240 +++ fpicker/source/office/fileview.cxx | 1922 +++++++++++++++++++++++++ fpicker/source/office/fileview.hxx | 197 +++ fpicker/source/office/foldertree.cxx | 170 +++ fpicker/source/office/foldertree.hxx | 47 + fpicker/source/office/iconview.cxx | 233 +++ fpicker/source/office/iconview.hxx | 41 + fpicker/source/office/iconviewimpl.cxx | 662 +++++++++ fpicker/source/office/iconviewimpl.hxx | 68 + fpicker/source/office/iodlg.cxx | 2 +- fpicker/source/office/iodlgimp.cxx | 2 +- 17 files changed, 3923 insertions(+), 6 deletions(-) create mode 100644 fpicker/source/office/contentenumeration.cxx create mode 100644 fpicker/source/office/contentenumeration.hxx create mode 100644 fpicker/source/office/fileview.cxx create mode 100644 fpicker/source/office/fileview.hxx create mode 100644 fpicker/source/office/foldertree.cxx create mode 100644 fpicker/source/office/foldertree.hxx create mode 100644 fpicker/source/office/iconview.cxx create mode 100644 fpicker/source/office/iconview.hxx create mode 100644 fpicker/source/office/iconviewimpl.cxx create mode 100644 fpicker/source/office/iconviewimpl.hxx (limited to 'fpicker') diff --git a/fpicker/Library_fps_office.mk b/fpicker/Library_fps_office.mk index 996e5994d07a..626f357a98b3 100644 --- a/fpicker/Library_fps_office.mk +++ b/fpicker/Library_fps_office.mk @@ -45,9 +45,14 @@ $(eval $(call gb_Library_add_exception_objects,fps_office,\ fpicker/source/office/asyncfilepicker \ fpicker/source/office/breadcrumb \ fpicker/source/office/commonpicker \ + fpicker/source/office/contentenumeration \ + fpicker/source/office/fileview \ + fpicker/source/office/foldertree \ fpicker/source/office/fpinteraction \ fpicker/source/office/fpsmartcontent \ fpicker/source/office/fps_office \ + fpicker/source/office/iconview \ + fpicker/source/office/iconviewimpl \ fpicker/source/office/iodlg \ fpicker/source/office/iodlgimp \ fpicker/source/office/OfficeControlAccess \ diff --git a/fpicker/inc/bitmaps.hlst b/fpicker/inc/bitmaps.hlst index 61d5b71186de..878058154fb6 100644 --- a/fpicker/inc/bitmaps.hlst +++ b/fpicker/inc/bitmaps.hlst @@ -10,10 +10,12 @@ #ifndef INCLUDED_FPICKER_INC_BITMAPS_HRC #define INCLUDED_FPICKER_INC_BITMAPS_HRC +#define RID_BMP_FOLDER_OPEN "res/folderop.png" #define BMP_FILEDLG_BTN_UP "res/fp010.png" #define BMP_FILEDLG_CREATEFOLDER "fpicker/res/fp014.png" #define BMP_FILEDLG_PLACE_LOCAL "fpicker/res/fp015.png" #define BMP_FILEDLG_PLACE_REMOTE "fpicker/res/fp016.png" +#define RID_BMP_FOLDER "svtools/res/folder.png" #endif diff --git a/fpicker/source/office/OfficeControlAccess.hxx b/fpicker/source/office/OfficeControlAccess.hxx index 7f1600bd587c..fcba48674dc9 100644 --- a/fpicker/source/office/OfficeControlAccess.hxx +++ b/fpicker/source/office/OfficeControlAccess.hxx @@ -20,9 +20,9 @@ #ifndef INCLUDED_FPICKER_SOURCE_OFFICE_OFFICECONTROLACCESS_HXX #define INCLUDED_FPICKER_SOURCE_OFFICE_OFFICECONTROLACCESS_HXX -#include #include #include +#include "fileview.hxx" #include "pickercallbacks.hxx" #include diff --git a/fpicker/source/office/RemoteFilesDialog.hxx b/fpicker/source/office/RemoteFilesDialog.hxx index 140dd65622ec..5331107018d5 100644 --- a/fpicker/source/office/RemoteFilesDialog.hxx +++ b/fpicker/source/office/RemoteFilesDialog.hxx @@ -11,10 +11,8 @@ #define INCLUDED_SVTOOLS_REMOTEFILESDIALOG_HXX #include -#include #include #include -#include #include @@ -38,6 +36,8 @@ #include "fpdialogbase.hxx" #include "breadcrumb.hxx" +#include "fileview.hxx" +#include "foldertree.hxx" #include "QueryFolderName.hxx" using namespace ::com::sun::star::beans; diff --git a/fpicker/source/office/asyncfilepicker.cxx b/fpicker/source/office/asyncfilepicker.cxx index f547b2fd634f..4dc64af3d4eb 100644 --- a/fpicker/source/office/asyncfilepicker.cxx +++ b/fpicker/source/office/asyncfilepicker.cxx @@ -19,8 +19,8 @@ #include "asyncfilepicker.hxx" +#include "fileview.hxx" #include "iodlg.hxx" -#include #include #include diff --git a/fpicker/source/office/contentenumeration.cxx b/fpicker/source/office/contentenumeration.cxx new file mode 100644 index 000000000000..2ca057036a64 --- /dev/null +++ b/fpicker/source/office/contentenumeration.cxx @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "contentenumeration.hxx" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace svt +{ + + +#define ROW_TITLE 1 +#define ROW_SIZE 2 +#define ROW_DATE_MOD 3 +#define ROW_DATE_CREATE 4 +#define ROW_IS_FOLDER 5 +#define ROW_TARGET_URL 6 +#define ROW_IS_HIDDEN 7 +#define ROW_IS_VOLUME 8 +#define ROW_IS_REMOTE 9 +#define ROW_IS_REMOVABLE 10 +#define ROW_IS_FLOPPY 11 +#define ROW_IS_COMPACTDISC 12 + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::util::DateTime; + using ::com::sun::star::sdbc::XResultSet; + using ::com::sun::star::sdbc::XRow; + using ::com::sun::star::ucb::XDynamicResultSet; + using ::com::sun::star::ucb::CommandAbortedException; + using ::com::sun::star::ucb::XContentAccess; + using ::com::sun::star::ucb::XCommandEnvironment; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::document::DocumentProperties; + using ::ucbhelper::ResultSetInclude; + using ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS; + + + //= FileViewContentEnumerator + + + FileViewContentEnumerator::FileViewContentEnumerator( + const Reference< XCommandEnvironment >& _rxCommandEnv, + ContentData& _rContentToFill, ::osl::Mutex& _rContentMutex ) + :Thread ( "FileViewContentEnumerator" ) + ,m_rContent ( _rContentToFill ) + ,m_rContentMutex ( _rContentMutex ) + ,m_xCommandEnv ( _rxCommandEnv ) + ,m_pResultHandler ( nullptr ) + ,m_bCancelled ( false ) + ,m_rBlackList ( css::uno::Sequence< OUString >() ) + { + } + + + FileViewContentEnumerator::~FileViewContentEnumerator() + { + } + + + void FileViewContentEnumerator::cancel() + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_bCancelled = true; + m_pResultHandler = nullptr; + m_aFolder.aContent = ::ucbhelper::Content(); + m_aFolder.sURL.clear(); + } + + + EnumerationResult FileViewContentEnumerator::enumerateFolderContentSync( + const FolderDescriptor& _rFolder, + const css::uno::Sequence< OUString >& rBlackList ) + { + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aFolder = _rFolder; + m_pResultHandler = nullptr; + m_rBlackList = rBlackList; + } + return enumerateFolderContent(); + } + + + void FileViewContentEnumerator::enumerateFolderContent( + const FolderDescriptor& _rFolder, IEnumerationResultHandler* _pResultHandler ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aFolder = _rFolder; + m_pResultHandler = _pResultHandler; + + OSL_ENSURE( m_aFolder.aContent.get().is() || !m_aFolder.sURL.isEmpty(), + "FileViewContentEnumerator::enumerateFolderContent: invalid folder descriptor!" ); + + launch(); + //TODO: a protocol is missing how to join with the launched thread + // before exit(3), to ensure the thread is no longer relying on any + // infrastructure while that infrastructure is being shut down in + // atexit handlers + } + + + EnumerationResult FileViewContentEnumerator::enumerateFolderContent() + { + EnumerationResult eResult = EnumerationResult::ERROR; + try + { + + Reference< XResultSet > xResultSet; + Sequence< OUString > aProps(12); + + aProps[0] = "Title"; + aProps[1] = "Size"; + aProps[2] = "DateModified"; + aProps[3] = "DateCreated"; + aProps[4] = "IsFolder"; + aProps[5] = "TargetURL"; + aProps[6] = "IsHidden"; + aProps[7] = "IsVolume"; + aProps[8] = "IsRemote"; + aProps[9] = "IsRemoveable"; + aProps[10] = "IsFloppy"; + aProps[11] = "IsCompactDisc"; + + Reference< XCommandEnvironment > xEnvironment; + try + { + FolderDescriptor aFolder; + { + ::osl::MutexGuard aGuard( m_aMutex ); + aFolder = m_aFolder; + xEnvironment = m_xCommandEnv; + } + if ( !aFolder.aContent.get().is() ) + { + aFolder.aContent = ::ucbhelper::Content( aFolder.sURL, xEnvironment, comphelper::getProcessComponentContext() ); + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aFolder.aContent = aFolder.aContent; + } + } + + Reference< XDynamicResultSet > xDynResultSet = aFolder.aContent.createDynamicCursor( aProps, INCLUDE_FOLDERS_AND_DOCUMENTS ); + + if ( xDynResultSet.is() ) + xResultSet = xDynResultSet->getStaticResultSet(); + } + catch( CommandAbortedException& ) + { + SAL_WARN( "svtools.contnr", "createCursor: CommandAbortedException" ); + } + catch( Exception& ) + { + } + + if ( xResultSet.is() ) + { + Reference< XRow > xRow( xResultSet, UNO_QUERY ); + Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); + + try + { + DateTime aDT; + + bool bCancelled = false; + while ( !bCancelled && xResultSet->next() ) + { + bool bIsHidden = xRow->getBoolean( ROW_IS_HIDDEN ); + // don't show hidden files + if ( !bIsHidden || xRow->wasNull() ) + { + aDT = xRow->getTimestamp( ROW_DATE_MOD ); + bool bContainsDate = !xRow->wasNull(); + if ( !bContainsDate ) + { + aDT = xRow->getTimestamp( ROW_DATE_CREATE ); + bContainsDate = !xRow->wasNull(); + } + + OUString aContentURL = xContentAccess->queryContentIdentifierString(); + OUString aTargetURL = xRow->getString( ROW_TARGET_URL ); + bool bHasTargetURL = !xRow->wasNull() && !aTargetURL.isEmpty(); + + OUString sRealURL = bHasTargetURL ? aTargetURL : aContentURL; + + // check for restrictions + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( /* m_rBlackList.hasElements() && */ URLOnBlackList ( sRealURL ) ) + continue; + } + + std::unique_ptr pData(new SortingData_Impl); + pData->maTargetURL = sRealURL; + + pData->mbIsFolder = xRow->getBoolean( ROW_IS_FOLDER ) && !xRow->wasNull(); + pData->mbIsVolume = xRow->getBoolean( ROW_IS_VOLUME ) && !xRow->wasNull(); + pData->mbIsRemote = xRow->getBoolean( ROW_IS_REMOTE ) && !xRow->wasNull(); + pData->mbIsRemoveable = xRow->getBoolean( ROW_IS_REMOVABLE ) && !xRow->wasNull(); + pData->mbIsFloppy = xRow->getBoolean( ROW_IS_FLOPPY ) && !xRow->wasNull(); + pData->mbIsCompactDisc = xRow->getBoolean( ROW_IS_COMPACTDISC ) && !xRow->wasNull(); + pData->SetNewTitle( xRow->getString( ROW_TITLE ) ); + pData->maSize = xRow->getLong( ROW_SIZE ); + + if ( bHasTargetURL && + INetURLObject( aContentURL ).GetProtocol() == INetProtocol::VndSunStarHier ) + { + ::ucbhelper::Content aCnt( aTargetURL, xEnvironment, comphelper::getProcessComponentContext() ); + try + { + aCnt.getPropertyValue("Size") >>= pData->maSize; + aCnt.getPropertyValue("DateModified") >>= aDT; + } + catch (...) {} + } + + if ( bContainsDate ) + { + pData->maModDate = ::DateTime( aDT ); + } + + if ( pData->mbIsFolder ) + { + SolarMutexGuard aGuard; + ::svtools::VolumeInfo aVolInfo( pData->mbIsVolume, pData->mbIsRemote, + pData->mbIsRemoveable, pData->mbIsFloppy, + pData->mbIsCompactDisc ); + pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo ); + } + else + pData->maType = SvFileInformationManager::GetFileDescription( + INetURLObject( pData->maTargetURL ) ); + + { + ::osl::MutexGuard aGuard( m_rContentMutex ); + m_rContent.push_back( std::move(pData) ); + } + } + + { + ::osl::MutexGuard aGuard( m_aMutex ); + bCancelled = m_bCancelled; + } + } + eResult = EnumerationResult::SUCCESS; + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent: caught an exception while enumerating"); + } + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "svtools.contnr", "FileViewContentEnumerator::enumerateFolderContent" ); + } + + IEnumerationResultHandler* pHandler = nullptr; + { + ::osl::MutexGuard aGuard( m_aMutex ); + pHandler = m_pResultHandler; + if ( m_bCancelled ) + return EnumerationResult::ERROR; + } + + { + ::osl::MutexGuard aGuard( m_rContentMutex ); + if ( eResult != EnumerationResult::SUCCESS ) + // clear any "intermediate" and unfinished result + m_rContent.clear(); + } + + if ( pHandler ) + pHandler->enumerationDone( eResult ); + return eResult; + } + + + bool FileViewContentEnumerator::URLOnBlackList ( const OUString& sRealURL ) + { + OUString entryName = sRealURL.copy( sRealURL.lastIndexOf( '/' ) + 1 ); + + return comphelper::findValue(m_rBlackList, entryName) != -1; + } + + + void FileViewContentEnumerator::execute() + { + enumerateFolderContent(); + } + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/contentenumeration.hxx b/fpicker/source/office/contentenumeration.hxx new file mode 100644 index 000000000000..0e6c529e65b3 --- /dev/null +++ b/fpicker/source/office/contentenumeration.hxx @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SVTOOLS_SOURCE_CONTNR_CONTENTENUMERATION_HXX +#define INCLUDED_SVTOOLS_SOURCE_CONTNR_CONTENTENUMERATION_HXX + +#include +#include +#include +#include +#include +#include + + +namespace svt +{ + + + //= SortingData_Impl + + struct SortingData_Impl + { + private: + OUString maFilename; // only filename in upper case - for compare purposes + OUString maTitle; // -> be careful when changing maTitle to update maFilename only when new + OUString maLowerTitle; + + + public: + OUString maType; + OUString maTargetURL; + OUString maDisplayText; + DateTime maModDate; + Image maImage; + sal_Int64 maSize; + bool mbIsFolder; + bool mbIsVolume; + bool mbIsRemote; + bool mbIsRemoveable; + bool mbIsFloppy; + bool mbIsCompactDisc; + + inline SortingData_Impl(); + inline const OUString& GetTitle() const; + inline const OUString& GetLowerTitle() const; + inline const OUString& GetFileName() const; + inline void SetNewTitle( const OUString& rNewTitle ); // new maTitle is set -> maFilename is set to same! + + private: + inline void SetTitles( const OUString& rNewTitle ); + }; + + inline SortingData_Impl::SortingData_Impl() : + maModDate ( DateTime::EMPTY ), + maSize ( 0 ), + mbIsFolder ( false ), + mbIsVolume ( false ), + mbIsRemote ( false ), + mbIsRemoveable ( false ), + mbIsFloppy ( false ), + mbIsCompactDisc ( false ) + { + } + + inline const OUString& SortingData_Impl::GetTitle() const + { + return maTitle; + } + + inline const OUString& SortingData_Impl::GetLowerTitle() const + { + return maLowerTitle; + } + + inline const OUString& SortingData_Impl::GetFileName() const + { + return maFilename; + } + + inline void SortingData_Impl::SetNewTitle( const OUString& rNewTitle ) + { + SetTitles( rNewTitle ); + maFilename = rNewTitle.toAsciiUpperCase(); + } + + inline void SortingData_Impl::SetTitles( const OUString& rNewTitle ) + { + maTitle = rNewTitle; + maLowerTitle = rNewTitle.toAsciiLowerCase(); + } + + + //= EnumerationResult + + enum class EnumerationResult + { + SUCCESS, /// the enumeration was successful + ERROR, /// the enumeration was unsuccessful + }; + + + //= FolderDescriptor + + struct FolderDescriptor + { + /** a content object describing the folder. Can be , in this case sURL + is relevant. + */ + ::ucbhelper::Content aContent; + /** the URL of a folder. Will be ignored if aContent is not . + */ + OUString sURL; + + FolderDescriptor() { } + + explicit FolderDescriptor( const ::ucbhelper::Content& _rContent ) + :aContent( _rContent ) + { + } + + explicit FolderDescriptor( const OUString& _rURL ) + :sURL( _rURL ) + { + } + }; + + + //= IEnumerationResultHandler + + class IEnumerationResultHandler + { + public: + virtual void enumerationDone( EnumerationResult _eResult ) = 0; + + protected: + ~IEnumerationResultHandler() {} + }; + + + //= FileViewContentEnumerator + + class FileViewContentEnumerator: public salhelper::Thread + { + public: + typedef ::std::vector< std::unique_ptr > ContentData; + + private: + ContentData& m_rContent; + ::osl::Mutex& m_rContentMutex; + + mutable ::osl::Mutex m_aMutex; + + FolderDescriptor m_aFolder; + css::uno::Reference< css::ucb::XCommandEnvironment > + m_xCommandEnv; + IEnumerationResultHandler* m_pResultHandler; + bool m_bCancelled; + + css::uno::Sequence< OUString > m_rBlackList; + + bool URLOnBlackList ( const OUString& sRealURL ); + + public: + /** constructs an enumerator instance + + @param _rContentToFill + the structure which is to be filled with the found content + @param _rContentMutex + the mutex which protects the access to _rContentToFill + @param _pTranslator + an instance which should be used to translate content titles. May be + */ + FileViewContentEnumerator( + const css::uno::Reference< css::ucb::XCommandEnvironment >& _rxCommandEnv, + ContentData& _rContentToFill, + ::osl::Mutex& _rContentMutex + ); + + /** enumerates the content of a given folder + + @param _rFolder + the folder whose content is to be enumerated + @param _pFilter + a filter to apply to the found contents + @param _pResultHandler + an instance which should handle the results of the enumeration + */ + void enumerateFolderContent( + const FolderDescriptor& _rFolder, + IEnumerationResultHandler* _pResultHandler + ); + + /** enumerates the content of a given folder synchronously + */ + EnumerationResult enumerateFolderContentSync( + const FolderDescriptor& _rFolder, + const css::uno::Sequence< OUString >& rBlackList + ); + + /** cancels the running operation. + + Note that "cancel" may mean that the operation is running, but its result + is simply disregarded later on. + */ + void cancel(); + + protected: + virtual ~FileViewContentEnumerator() override; + + private: + EnumerationResult enumerateFolderContent(); + + // Thread overridables + virtual void execute() override; + + }; + + +} // namespace svt + + +#endif // INCLUDED_SVTOOLS_SOURCE_CONTNR_CONTENTENUMERATION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/fileview.cxx b/fpicker/source/office/fileview.cxx new file mode 100644 index 000000000000..09ff753dd373 --- /dev/null +++ b/fpicker/source/office/fileview.cxx @@ -0,0 +1,1922 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "contentenumeration.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fileview.hxx" +#include "iconview.hxx" + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::comphelper; +using ::svt::SortingData_Impl; +using ::svt::FolderDescriptor; + +#define ALL_FILES_FILTER "*.*" + +#define COLUMN_TITLE 1 +#define COLUMN_TYPE 2 +#define COLUMN_SIZE 3 +#define COLUMN_DATE 4 + +#define ROW_HEIGHT 17 // the height of a row has to be a little higher than the bitmap +#define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset + +enum class FileViewFlags +{ + NONE = 0x00, + ONLYFOLDER = 0x01, + MULTISELECTION = 0x02, + SHOW_TYPE = 0x04, + SHOW_NONE = 0x20, +}; +namespace o3tl +{ + template<> struct typed_flags : is_typed_flags {}; +} + +namespace +{ + + //= CallbackTimer + + class CallbackTimer : public ::salhelper::Timer + { + protected: + SvtFileView_Impl* const m_pTimeoutHandler; + + public: + explicit CallbackTimer( SvtFileView_Impl* _pHandler ) : m_pTimeoutHandler( _pHandler ) { } + + protected: + virtual void SAL_CALL onShot() override; + }; + + +} + + +class ViewTabListBox_Impl : public SvHeaderTabListBox +{ +private: + Reference< XCommandEnvironment > mxCmdEnv; + std::unique_ptr mxBuilder; + VclPtr mxMenu; + + ::osl::Mutex maMutex; + VclPtr mpHeaderBar; + SvtFileView_Impl* mpParent; + Timer maResetQuickSearch; + OUString maQuickSearchText; + OUString const msAccessibleDescText; + OUString const msFolder; + OUString const msFile; + sal_uInt32 mnSearchIndex; + bool mbResizeDisabled : 1; + bool mbAutoResize : 1; + bool mbEnableDelete : 1; + bool const mbShowHeader; + + void DeleteEntries(); + void DoQuickSearch( sal_Unicode rChar ); + bool Kill( const OUString& rURL ); + +protected: + virtual bool DoubleClickHdl() override; + virtual OUString GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos = -1 ) const override; + +public: + ViewTabListBox_Impl( vcl::Window* pParentWin, SvtFileView_Impl* pParent, FileViewFlags nFlags ); + virtual ~ViewTabListBox_Impl() override; + virtual void dispose() override; + + virtual void Resize() override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + virtual bool EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) override; + + void ClearAll(); + HeaderBar* GetHeaderBar() const { return mpHeaderBar; } + + void EnableAutoResize() { mbAutoResize = true; } + void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; } + + const Reference< XCommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; } + + DECL_LINK(ResetQuickSearch_Impl, Timer *, void); + + virtual VclPtr CreateContextMenu() override; + virtual void ExecuteContextMenuAction( sal_uInt16 nSelectedPopentry ) override; +}; + + +//= SvtFileView_Impl + + +class SvtFileView_Impl :public ::svt::IEnumerationResultHandler +{ +protected: + VclPtr mpAntiImpl; + Link m_aSelectHandler; + + ::rtl::Reference< ::svt::FileViewContentEnumerator > + m_xContentEnumerator; + Link m_aCurrentAsyncActionHandler; + ::osl::Condition m_aAsyncActionFinished; + ::rtl::Reference< ::salhelper::Timer > m_xCancelAsyncTimer; + ::svt::EnumerationResult m_eAsyncActionResult; + bool m_bRunningAsyncAction; + bool m_bAsyncActionCancelled; + +public: + + ::std::vector< std::unique_ptr > maContent; + ::osl::Mutex maMutex; + + VclPtr mpCurView; + VclPtr mpView; + VclPtr mpIconView; + sal_uInt16 mnSortColumn; + bool mbAscending : 1; + bool const mbOnlyFolder : 1; + sal_Int16 mnSuspendSelectCallback : 1; + bool mbIsFirstResort : 1; + + IntlWrapper const aIntlWrapper; + + OUString maViewURL; + OUString maCurrentFilter; + Image const maFolderImage; + Link maOpenDoneLink; + Reference< XCommandEnvironment > mxCmdEnv; + + SvtFileView_Impl( SvtFileView* pAntiImpl, Reference < XCommandEnvironment > const & xEnv, + FileViewFlags nFlags, + bool bOnlyFolder ); + virtual ~SvtFileView_Impl(); + + void Clear(); + + FileViewResult GetFolderContent_Impl( + const OUString& rFolder, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList ); + + FileViewResult GetFolderContent_Impl( + const FolderDescriptor& _rFolder, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList ); + void FilterFolderContent_Impl( const OUString &rFilter ); + void CancelRunningAsyncAction(); + + void OpenFolder_Impl(); + static void ReplaceTabWithString( OUString& aValue ); + void CreateDisplayText_Impl(); + void SortFolderContent_Impl(); + + void EntryRemoved( const OUString& rURL ); + void EntryRenamed( OUString& rURL, + const OUString& rName ); + OUString FolderInserted( const OUString& rURL, + const OUString& rTitle ); + + sal_uLong GetEntryPos( const OUString& rURL ); + + void SetViewMode( FileViewMode eMode ); + + inline void EnableDelete( bool bEnable ); + + void Resort_Impl( sal_Int16 nColumn, bool bAscending ); + bool SearchNextEntry( sal_uInt32 &nIndex, + const OUString& rTitle, + bool bWrapAround ); + + void SetSelectHandler( const Link& _rHdl ); + + void InitSelection(); + void ResetCursor(); + + inline void EndEditing(); + + void onTimeout(); + +protected: + DECL_LINK( SelectionMultiplexer, SvTreeListBox*, void ); + + // IEnumerationResultHandler overridables + virtual void enumerationDone( ::svt::EnumerationResult eResult ) override; + void implEnumerationSuccess(); +}; + +inline void SvtFileView_Impl::EnableDelete( bool bEnable ) +{ + mpView->EnableDelete( bEnable ); +} + +inline void SvtFileView_Impl::EndEditing() +{ + if ( mpCurView->IsEditingActive() ) + mpCurView->EndEditing(); +} + +namespace +{ + // functions ------------------------------------------------------------- + + OUString CreateExactSizeText( sal_Int64 nSize ) + { + double fSize( static_cast(nSize) ); + int nDec; + + long nMega = 1024 * 1024; + long nGiga = nMega * 1024; + + OUString aUnitStr(' '); + + if ( nSize < 10000 ) + { + aUnitStr += SvtResId(STR_SVT_BYTES ); + nDec = 0; + } + else if ( nSize < nMega ) + { + fSize /= 1024; + aUnitStr += SvtResId(STR_SVT_KB); + nDec = 1; + } + else if ( nSize < nGiga ) + { + fSize /= nMega; + aUnitStr += SvtResId(STR_SVT_MB); + nDec = 2; + } + else + { + fSize /= nGiga; + aUnitStr += SvtResId(STR_SVT_GB); + nDec = 3; + } + + OUString aSizeStr( ::rtl::math::doubleToUString( fSize, + rtl_math_StringFormat_F, nDec, + SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) ); + aSizeStr += aUnitStr; + + return aSizeStr; + } +} + +ViewTabListBox_Impl::ViewTabListBox_Impl( vcl::Window* pParentWin, + SvtFileView_Impl* pParent, + FileViewFlags nFlags ) : + + SvHeaderTabListBox( pParentWin, WB_TABSTOP ), + + mpHeaderBar ( nullptr ), + mpParent ( pParent ), + msAccessibleDescText( SvtResId(STR_SVT_ACC_DESC_FILEVIEW) ), + msFolder ( SvtResId(STR_SVT_ACC_DESC_FOLDER) ), + msFile ( SvtResId(STR_SVT_ACC_DESC_FILE) ), + mnSearchIndex ( 0 ), + mbResizeDisabled ( false ), + mbAutoResize ( false ), + mbEnableDelete ( false ), + mbShowHeader ( !(nFlags & FileViewFlags::SHOW_NONE) ) +{ + Size aBoxSize = pParentWin->GetSizePixel(); + mpHeaderBar = VclPtr::Create( pParentWin, WB_BUTTONSTYLE | WB_BOTTOMBORDER ); + mpHeaderBar->SetPosSizePixel( Point( 0, 0 ), mpHeaderBar->CalcWindowSizePixel() ); + + HeaderBarItemBits nBits = HeaderBarItemBits::LEFT | HeaderBarItemBits::CLICKABLE; + + long aTabPositions[] = { 20, 180, 320, 400, 600 }; + SetTabs(SAL_N_ELEMENTS(aTabPositions), aTabPositions, MapUnit::MapPixel); + SetTabJustify(2, SvTabJustify::AdjustRight); // column "Size" + + mpHeaderBar->InsertItem(COLUMN_TITLE, SvtResId(STR_SVT_FILEVIEW_COLUMN_TITLE), 180, nBits | HeaderBarItemBits::UPARROW); + if (nFlags & FileViewFlags::SHOW_TYPE) + { + mpHeaderBar->InsertItem(COLUMN_TYPE, SvtResId(STR_SVT_FILEVIEW_COLUMN_TYPE), 140, nBits); + } + mpHeaderBar->InsertItem(COLUMN_SIZE, SvtResId(STR_SVT_FILEVIEW_COLUMN_SIZE), 80, nBits); + mpHeaderBar->InsertItem(COLUMN_DATE, SvtResId(STR_SVT_FILEVIEW_COLUMN_DATE), 500, nBits); + + Size aHeadSize = mpHeaderBar->GetSizePixel(); + SetPosSizePixel( Point( 0, aHeadSize.Height() ), + Size( aBoxSize.Width(), aBoxSize.Height() - aHeadSize.Height() ) ); + InitHeaderBar( mpHeaderBar ); + SetHighlightRange(); + SetEntryHeight( ROW_HEIGHT ); + if (nFlags & FileViewFlags::MULTISELECTION) + SetSelectionMode( SelectionMode::Multiple ); + + Show(); + if( mbShowHeader ) + mpHeaderBar->Show(); + + maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT ); + maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) ); + + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< XInteractionHandler > xInteractionHandler( + InteractionHandler::createWithParent(xContext, VCLUnoHelper::GetInterface(GetParentDialog())), UNO_QUERY_THROW ); + + mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() ); + + EnableContextMenuHandling(); +} + +ViewTabListBox_Impl::~ViewTabListBox_Impl() +{ + disposeOnce(); +} + +void ViewTabListBox_Impl::dispose() +{ + maResetQuickSearch.Stop(); + + mxMenu.disposeAndClear(); + mxBuilder.reset(); + + mpHeaderBar.disposeAndClear(); + SvHeaderTabListBox::dispose(); +} + +IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void) +{ + ::osl::MutexGuard aGuard( maMutex ); + + maQuickSearchText.clear(); + mnSearchIndex = 0; +} + + +void ViewTabListBox_Impl::Resize() +{ + SvTabListBox::Resize(); + Size aBoxSize = Control::GetParent()->GetOutputSizePixel(); + + if ( mbResizeDisabled || !aBoxSize.Width() ) + return; + + Size aBarSize; + if ( mbShowHeader ) + { + aBarSize = mpHeaderBar->GetSizePixel(); + aBarSize.setWidth( mbAutoResize ? aBoxSize.Width() : GetSizePixel().Width() ); + mpHeaderBar->SetSizePixel( aBarSize ); + } + + if ( mbAutoResize ) + { + mbResizeDisabled = true; + SetPosSizePixel( Point( 0, aBarSize.Height() ), + Size( aBoxSize.Width(), aBoxSize.Height() - aBarSize.Height() ) ); + mbResizeDisabled = false; + } +} + + +void ViewTabListBox_Impl::KeyInput( const KeyEvent& rKEvt ) +{ + bool bHandled = false; + + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + if ( 0 == rKeyCode.GetModifier() ) + { + if ( rKeyCode.GetCode() == KEY_RETURN ) + { + ResetQuickSearch_Impl( nullptr ); + GetDoubleClickHdl().Call( this ); + bHandled = true; + } + else if ( ( rKeyCode.GetCode() == KEY_DELETE ) && + mbEnableDelete ) + { + ResetQuickSearch_Impl( nullptr ); + DeleteEntries(); + bHandled = true; + } + else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) || + ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) ) + { + DoQuickSearch( rKEvt.GetCharCode() ); + bHandled = true; + } + } + + if ( !bHandled ) + { + ResetQuickSearch_Impl( nullptr ); + SvHeaderTabListBox::KeyInput( rKEvt ); + } +} + + +VclPtr ViewTabListBox_Impl::CreateContextMenu() +{ + bool bEnableDelete = mbEnableDelete; + bool bEnableRename = true; + + if ( bEnableDelete || bEnableRename ) + { + sal_Int32 nSelectedEntries = GetSelectionCount(); + bEnableDelete &= nSelectedEntries > 0; + bEnableRename &= nSelectedEntries == 1; + } + + if ( bEnableDelete || bEnableRename ) + { + SvTreeListEntry* pEntry = FirstSelected(); + while ( pEntry ) + { + ::ucbhelper::Content aCnt; + try + { + OUString aURL( static_cast< SvtContentEntry * >( + pEntry->GetUserData() )->maURL ); + aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() ); + } + catch( Exception const & ) + { + bEnableDelete = bEnableRename = false; + } + + if ( bEnableDelete ) + { + try + { + Reference< XCommandInfo > aCommands = aCnt.getCommands(); + if ( aCommands.is() ) + bEnableDelete = aCommands->hasCommandByName( "delete" ); + else + bEnableDelete = false; + } + catch( Exception const & ) + { + bEnableDelete = false; + } + } + + if ( bEnableRename ) + { + try + { + Reference< XPropertySetInfo > aProps = aCnt.getProperties(); + if ( aProps.is() ) + { + Property aProp = aProps->getPropertyByName("Title"); + bEnableRename + = !( aProp.Attributes & PropertyAttribute::READONLY ); + } + else + bEnableRename = false; + } + catch( Exception const & ) + { + bEnableRename = false; + } + } + + pEntry = ( bEnableDelete || bEnableRename ) + ? NextSelected( pEntry ) + : nullptr; + } + } + + if ( bEnableDelete || bEnableRename ) + { + mxMenu.disposeAndClear(); + mxBuilder.reset(new VclBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svt/ui/fileviewmenu.ui", "")); + mxMenu.set(mxBuilder->get_menu("menu")); + mxMenu->EnableItem(mxMenu->GetItemId("delete"), bEnableDelete); + mxMenu->EnableItem(mxMenu->GetItemId("rename"), bEnableRename); + mxMenu->RemoveDisabledEntries( true, true ); + return mxMenu; + } + + return nullptr; +} + +void ViewTabListBox_Impl::ExecuteContextMenuAction( sal_uInt16 nSelectedPopupEntry ) +{ + if (nSelectedPopupEntry == mxMenu->GetItemId("delete")) + DeleteEntries(); + else if (nSelectedPopupEntry == mxMenu->GetItemId("rename")) + EditEntry( FirstSelected() ); +} + +void ViewTabListBox_Impl::ClearAll() +{ + for ( sal_uLong i = 0; i < GetEntryCount(); ++i ) + delete static_cast(GetEntry(i)->GetUserData()); + Clear(); +} + + +void ViewTabListBox_Impl::DeleteEntries() +{ + short eResult = svtools::QUERYDELETE_YES; + SvTreeListEntry* pEntry = FirstSelected(); + OUString aURL; + + while ( pEntry ) + { + SvTreeListEntry *pCurEntry = pEntry; + pEntry = NextSelected( pEntry ); + + if ( pCurEntry->GetUserData() ) + aURL = static_cast(pCurEntry->GetUserData())->maURL; + + if ( aURL.isEmpty() ) + continue; + + bool canDelete = true; + try + { + ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() ); + Reference< XCommandInfo > aCommands = aCnt.getCommands(); + if ( aCommands.is() ) + canDelete = aCommands->hasCommandByName( "delete" ); + else + canDelete = false; + } + catch( Exception const & ) + { + canDelete = false; + } + + if (!canDelete) + continue; // process next entry + + if ( eResult != svtools::QUERYDELETE_ALL ) + { + INetURLObject aObj( aURL ); + svtools::QueryDeleteDlg_Impl aDlg( + GetFrameWeld(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset)); + + if ( GetSelectionCount() > 1 ) + aDlg.EnableAllButton(); + + eResult = aDlg.run(); + } + + if ( ( eResult == svtools::QUERYDELETE_ALL ) || + ( eResult == svtools::QUERYDELETE_YES ) ) + { + if ( Kill( aURL ) ) + { + delete static_cast(pCurEntry->GetUserData()); + GetModel()->Remove( pCurEntry ); + mpParent->EntryRemoved( aURL ); + } + } + } +} + + +bool ViewTabListBox_Impl::EditedEntry( SvTreeListEntry* pEntry, + const OUString& rNewText ) +{ + bool bRet = false; + + OUString aURL; + SvtContentEntry* pData = static_cast(pEntry->GetUserData()); + + if ( pData ) + aURL = pData->maURL; + + if ( aURL.isEmpty() ) + return bRet; + + try + { + OUString aPropName( "Title" ); + bool canRename = true; + ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() ); + + try + { + Reference< XPropertySetInfo > aProps = aContent.getProperties(); + if ( aProps.is() ) + { + Property aProp = aProps->getPropertyByName( aPropName ); + canRename = !( aProp.Attributes & PropertyAttribute::READONLY ); + } + else + { + canRename = false; + } + } + catch ( Exception const & ) + { + canRename = false; + } + + if ( canRename ) + { + Any aValue; + aValue <<= rNewText; + aContent.setPropertyValue( aPropName, aValue ); + mpParent->EntryRenamed( aURL, rNewText ); + + if (pData) + pData->maURL = aURL; + + pEntry->SetUserData( pData ); + + bRet = true; + } + } + catch( Exception const & ) + { + } + + return bRet; +} + + +void ViewTabListBox_Impl::DoQuickSearch( sal_Unicode rChar ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + maResetQuickSearch.Stop(); + + OUString aLastText = maQuickSearchText; + sal_uInt32 aLastPos = mnSearchIndex; + + maQuickSearchText += OUString(rChar).toAsciiLowerCase(); + + bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false ); + + if ( !bFound && ( aLastText.getLength() == 1 ) && + ( aLastText == OUStringLiteral1(rChar) ) ) + { + mnSearchIndex = aLastPos + 1; + maQuickSearchText = aLastText; + bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true ); + } + + if ( bFound ) + { + SvTreeListEntry* pEntry = GetEntry( mnSearchIndex ); + if ( pEntry ) + { + SelectAll( false ); + Select( pEntry ); + SetCurEntry( pEntry ); + MakeVisible( pEntry ); + } + } + + maResetQuickSearch.Start(); +} + + +bool ViewTabListBox_Impl::DoubleClickHdl() +{ + SvHeaderTabListBox::DoubleClickHdl(); + return false; + // this means "do no additional handling". Especially this means that the SvImpLBox does not + // recognize that the entry at the double click position change after the handler call (which is + // the case if in the handler, our content was replaced) + // If it _would_ recognize this change, it would take this as a reason to select the entry, again + // - which is not what in the case of content replace + // (I really doubt that this behaviour of the SvImpLBox does make any sense at all, but + // who knows ...) +} + +OUString ViewTabListBox_Impl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos ) const +{ + OUString sRet = SvHeaderTabListBox::GetAccessibleObjectDescription( _eType, _nPos ); + if ( ::vcl::BBTYPE_TABLECELL == _eType ) + { + sal_Int32 nRow = -1; + const sal_uInt16 nColumnCount = GetColumnCount(); + if (nColumnCount > 0) + nRow = _nPos / nColumnCount; + SvTreeListEntry* pEntry = GetEntry( nRow ); + if ( pEntry ) + { + SvtContentEntry* pData = static_cast(pEntry->GetUserData()); + if ( pData ) + { + const OUString sVar1( "%1" ); + const OUString sVar2( "%2" ); + OUString aText( msAccessibleDescText ); + aText = aText.replaceAll( sVar1, pData->mbIsFolder ? msFolder : msFile ); + aText = aText.replaceAll( sVar2, pData->maURL ); + sRet += aText; + } + } + } + + return sRet; +} + + +bool ViewTabListBox_Impl::Kill( const OUString& rContent ) +{ + bool bRet = true; + + try + { + ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() ); + aCnt.executeCommand( "delete", makeAny( true ) ); + } + catch( css::ucb::CommandAbortedException const & ) + { + SAL_INFO( "svtools.contnr", "CommandAbortedException" ); + bRet = false; + } + catch( Exception const & ) + { + SAL_INFO( "svtools.contnr", "Any other exception" ); + bRet = false; + } + + return bRet; +} + +SvtFileView::SvtFileView( vcl::Window* pParent, WinBits nBits, + bool bOnlyFolder, bool bMultiSelection, bool bShowType ) : + + Control( pParent, nBits ) +{ + FileViewFlags nFlags = FileViewFlags::NONE; + if ( bOnlyFolder ) + nFlags |= FileViewFlags::ONLYFOLDER; + if ( bMultiSelection ) + nFlags |= FileViewFlags::MULTISELECTION; + if ( bShowType ) + nFlags |= FileViewFlags::SHOW_TYPE; + + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< XInteractionHandler > xInteractionHandler( + InteractionHandler::createWithParent(xContext, VCLUnoHelper::GetInterface(GetParentDialog())), UNO_QUERY_THROW ); + Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() ); + + mpImpl.reset( new SvtFileView_Impl( this, xCmdEnv, nFlags, bOnlyFolder ) ); + mpImpl->mpView->ForbidEmptyText(); + + HeaderBar* pHeaderBar = mpImpl->mpView->GetHeaderBar(); + pHeaderBar->SetSelectHdl( LINK( this, SvtFileView, HeaderSelect_Impl ) ); + pHeaderBar->SetEndDragHdl( LINK( this, SvtFileView, HeaderEndDrag_Impl ) ); +} + +SvtFileView::~SvtFileView() +{ + disposeOnce(); +} + +void SvtFileView::dispose() +{ + mpImpl.reset(); + Control::dispose(); +} + +Size SvtFileView::GetOptimalSize() const +{ + return LogicToPixel(Size(208, 50), MapMode(MapUnit::MapAppFont)); +} + +void SvtFileView::SetViewMode( FileViewMode eMode ) +{ + mpImpl->SetViewMode( eMode ); +} + +OUString SvtFileView::GetURL( SvTreeListEntry const * pEntry ) +{ + OUString aURL; + if ( pEntry && pEntry->GetUserData() ) + aURL = static_cast(pEntry->GetUserData())->maURL; + return aURL; +} + + +OUString SvtFileView::GetCurrentURL() const +{ + OUString aURL; + SvTreeListEntry* pEntry = mpImpl->mpCurView->FirstSelected(); + if ( pEntry && pEntry->GetUserData() ) + aURL = static_cast(pEntry->GetUserData())->maURL; + return aURL; +} + + +void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder ) +{ + OUString sEntry = mpImpl->FolderInserted( rUrl, rNewFolder ); + + SvTreeListEntry* pEntry = mpImpl->mpView->InsertEntry( sEntry, mpImpl->maFolderImage, mpImpl->maFolderImage ); + SvtContentEntry* pUserData = new SvtContentEntry( rUrl, true ); + pEntry->SetUserData( pUserData ); + mpImpl->mpView->MakeVisible( pEntry ); + + SvTreeListEntry* pEntry2 = mpImpl->mpIconView->InsertEntry( sEntry.getToken( 0, '\t' ), mpImpl->maFolderImage, mpImpl->maFolderImage ); + SvtContentEntry* pUserData2 = new SvtContentEntry( rUrl, true ); + pEntry2->SetUserData( pUserData2 ); + mpImpl->mpIconView->MakeVisible( pEntry2 ); +} + + +FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor ) +{ + FileViewResult eResult = eFailure; + + OUString sParentURL; + if ( GetParentURL( sParentURL ) ) + eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, mpBlackList ); + + return eResult; +} + + +bool SvtFileView::GetParentURL( OUString& rParentURL ) const +{ + bool bRet = false; + try + { + ::ucbhelper::Content aCnt( mpImpl->maViewURL, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() ); + Reference< XContent > xContent( aCnt.get() ); + Reference< css::container::XChild > xChild( xContent, UNO_QUERY ); + if ( xChild.is() ) + { + Reference< XContent > xParent( xChild->getParent(), UNO_QUERY ); + if ( xParent.is() ) + { + rParentURL = xParent->getIdentifier()->getContentIdentifier(); + bRet = !rParentURL.isEmpty() && rParentURL != mpImpl->maViewURL; + } + } + } + catch( Exception const & ) + { + // perhaps an unknown url protocol (e.g. "private:newdoc") + } + + return bRet; +} + + +const OString& SvtFileView::GetHelpId( ) const +{ + return mpImpl->mpView->GetHelpId( ); +} + + +void SvtFileView::SetHelpId( const OString& rHelpId ) +{ + mpImpl->mpView->SetHelpId( rHelpId ); +} + + +void SvtFileView::SetSizePixel( const Size& rNewSize ) +{ + Control::SetSizePixel( rNewSize ); + mpImpl->mpView->SetSizePixel( rNewSize ); + mpImpl->mpIconView->SetSizePixel( rNewSize ); +} + + +void SvtFileView::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize ) +{ + SetPosPixel( rNewPos ); + SetSizePixel( rNewSize ); +} + + +bool SvtFileView::Initialize( const css::uno::Reference< css::ucb::XContent>& _xContent ) +{ + WaitObject aWaitCursor( this ); + + mpImpl->Clear(); + ::ucbhelper::Content aContent(_xContent, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() ); + FileViewResult eResult = mpImpl->GetFolderContent_Impl( FolderDescriptor( aContent ), nullptr, css::uno::Sequence< OUString >() ); + OSL_ENSURE( eResult != eStillRunning, "SvtFileView::Initialize: this was expected to be synchronous!" ); + if ( eResult != eSuccess ) + return false; + + mpImpl->FilterFolderContent_Impl( OUString() ); + + mpImpl->SortFolderContent_Impl(); // possibly not necessary!!!!!!!!!! + mpImpl->CreateDisplayText_Impl(); + mpImpl->OpenFolder_Impl(); + + mpImpl->maOpenDoneLink.Call( this ); + return true; +} + + +FileViewResult SvtFileView::Initialize( + const OUString& rURL, + const OUString& rFilter, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList ) +{ + WaitObject aWaitCursor( this ); + mpBlackList = rBlackList; + + OUString sPushURL( mpImpl->maViewURL ); + + mpImpl->maViewURL = rURL; + FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor ); + switch ( eResult ) + { + case eFailure: + case eTimeout: + mpImpl->maViewURL = sPushURL; + return eResult; + + case eStillRunning: + OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" ); + [[fallthrough]]; + case eSuccess: + return eResult; + } + + OSL_FAIL( "SvtFileView::Initialize: unreachable!" ); + return eFailure; +} + +FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor ) +{ + mpImpl->maCurrentFilter = rFilter.toAsciiLowerCase(); + + mpImpl->Clear(); + FileViewResult eResult = mpImpl->GetFolderContent_Impl( mpImpl->maViewURL, pAsyncDescriptor, mpBlackList ); + OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" ); + return eResult; +} + +void SvtFileView::CancelRunningAsyncAction() +{ + mpImpl->CancelRunningAsyncAction(); +} + +void SvtFileView::SetNoSelection() +{ + mpImpl->mpCurView->SelectAll( false ); +} + + +void SvtFileView::GetFocus() +{ + Control::GetFocus(); + if ( mpImpl && mpImpl->mpCurView ) + mpImpl->mpCurView->GrabFocus(); +} + + +void SvtFileView::SetSelectHdl( const Link& rHdl ) +{ + mpImpl->SetSelectHandler( rHdl ); +} + + +void SvtFileView::SetDoubleClickHdl( const Link& rHdl ) +{ + mpImpl->mpView->SetDoubleClickHdl( rHdl ); + mpImpl->mpIconView->SetDoubleClickHdl( rHdl ); +} + + +sal_uLong SvtFileView::GetSelectionCount() const +{ + return mpImpl->mpCurView->GetSelectionCount(); +} + + +SvTreeListEntry* SvtFileView::FirstSelected() const +{ + return mpImpl->mpCurView->FirstSelected(); +} + + +SvTreeListEntry* SvtFileView::NextSelected( SvTreeListEntry* pEntry ) const +{ + return mpImpl->mpCurView->NextSelected( pEntry ); +} + +void SvtFileView::EnableAutoResize() +{ + mpImpl->mpView->EnableAutoResize(); +} + +const OUString& SvtFileView::GetViewURL() const +{ + return mpImpl->maViewURL; +} + +void SvtFileView::SetOpenDoneHdl( const Link& rHdl ) +{ + mpImpl->maOpenDoneLink = rHdl; +} + +void SvtFileView::EnableDelete( bool bEnable ) +{ + mpImpl->EnableDelete( bEnable ); +} + +void SvtFileView::EndInplaceEditing() +{ + return mpImpl->EndEditing(); +} + +IMPL_LINK( SvtFileView, HeaderSelect_Impl, HeaderBar*, pBar, void ) +{ + DBG_ASSERT( pBar, "no headerbar" ); + sal_uInt16 nItemID = pBar->GetCurItemId(); + + HeaderBarItemBits nBits; + + // clear the arrow of the recently used column + if ( nItemID != mpImpl->mnSortColumn ) + { + if ( !nItemID ) + { + // first call -> remove arrow from title column, + // because another column is the sort column + nItemID = mpImpl->mnSortColumn; + mpImpl->mnSortColumn = COLUMN_TITLE; + } + nBits = pBar->GetItemBits( mpImpl->mnSortColumn ); + nBits &= ~HeaderBarItemBits( HeaderBarItemBits::UPARROW | HeaderBarItemBits::DOWNARROW ); + pBar->SetItemBits( mpImpl->mnSortColumn, nBits ); + } + + nBits = pBar->GetItemBits( nItemID ); + + bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW ); + + if ( bUp ) + { + nBits &= ~HeaderBarItemBits::UPARROW; + nBits |= HeaderBarItemBits::DOWNARROW; + } + else + { + nBits &= ~HeaderBarItemBits::DOWNARROW; + nBits |= HeaderBarItemBits::UPARROW; + } + + pBar->SetItemBits( nItemID, nBits ); + mpImpl->Resort_Impl( nItemID, !bUp ); +} + + +IMPL_LINK( SvtFileView, HeaderEndDrag_Impl, HeaderBar*, pBar, void ) +{ + if ( pBar->IsItemMode() ) + return; + + Size aSize; + sal_uInt16 nTabs = pBar->GetItemCount(); + long nTmpSize = 0; + + for ( sal_uInt16 i = 1; i <= nTabs; ++i ) + { + long nWidth = pBar->GetItemSize(i); + aSize.setWidth( nWidth + nTmpSize ); + nTmpSize += nWidth; + mpImpl->mpView->SetTab( i, aSize.Width(), MapUnit::MapPixel ); + } +} + + +OUString SvtFileView::GetConfigString() const +{ + OUString sRet; + HeaderBar* pBar = mpImpl->mpView->GetHeaderBar(); + DBG_ASSERT( pBar, "invalid headerbar" ); + + // sort order + sRet += OUString::number( mpImpl->mnSortColumn ) + ";"; + HeaderBarItemBits nBits = pBar->GetItemBits( mpImpl->mnSortColumn ); + bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW ); + sRet += bUp ? OUString("1") : OUString("0"); + sRet += ";"; + + sal_uInt16 nCount = pBar->GetItemCount(); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + { + sal_uInt16 nId = pBar->GetItemId(i); + sRet += OUString::number( nId ) + + ";" + + OUString::number( pBar->GetItemSize( nId ) ) + + ";"; + } + + sRet = comphelper::string::stripEnd(sRet, ';'); + return sRet; +} + +::std::vector< SvtContentEntry > SvtFileView::GetContent() +{ + ::std::vector< SvtContentEntry > aContent; + + for(auto const& elem : mpImpl->maContent) + { + SvtContentEntry aEntry( elem->maTargetURL, elem->mbIsFolder ); + aContent.push_back( aEntry ); + } + + return aContent; +} + +void SvtFileView::SetConfigString( const OUString& rCfgStr ) +{ + HeaderBar* pBar = mpImpl->mpView->GetHeaderBar(); + DBG_ASSERT( pBar, "invalid headerbar" ); + + sal_Int32 nIdx = 0; + mpImpl->mnSortColumn = static_cast(rCfgStr.getToken( 0, ';', nIdx ).toInt32()); + bool bUp = static_cast(static_cast(rCfgStr.getToken( 0, ';', nIdx ).toInt32())); + HeaderBarItemBits nBits = pBar->GetItemBits( mpImpl->mnSortColumn ); + + if ( bUp ) + { + nBits &= ~HeaderBarItemBits::UPARROW; + nBits |= HeaderBarItemBits::DOWNARROW; + } + else + { + nBits &= ~HeaderBarItemBits::DOWNARROW; + nBits |= HeaderBarItemBits::UPARROW; + } + pBar->SetItemBits( mpImpl->mnSortColumn, nBits ); + + while ( nIdx != -1 ) + { + sal_uInt16 nItemId = static_cast(rCfgStr.getToken( 0, ';', nIdx ).toInt32()); + pBar->SetItemSize( nItemId, rCfgStr.getToken( 0, ';', nIdx ).toInt32() ); + } + + HeaderSelect_Impl( pBar ); + HeaderEndDrag_Impl( pBar ); +} + + +void SvtFileView::StateChanged( StateChangedType nStateChange ) +{ + if ( nStateChange == StateChangedType::Enable ) + Invalidate(); + Control::StateChanged( nStateChange ); +} + + +// class SvtFileView_Impl + + +SvtFileView_Impl::SvtFileView_Impl( SvtFileView* pAntiImpl, Reference < XCommandEnvironment > const & xEnv, FileViewFlags nFlags, bool bOnlyFolder ) + + :mpAntiImpl ( pAntiImpl ) + ,m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR ) + ,m_bRunningAsyncAction ( false ) + ,m_bAsyncActionCancelled ( false ) + ,mnSortColumn ( COLUMN_TITLE ) + ,mbAscending ( true ) + ,mbOnlyFolder ( bOnlyFolder ) + ,mnSuspendSelectCallback ( 0 ) + ,mbIsFirstResort ( true ) + ,aIntlWrapper ( Application::GetSettings().GetLanguageTag() ) + ,maFolderImage (StockImage::Yes, RID_BMP_FOLDER) + ,mxCmdEnv ( xEnv ) + +{ + mpView = VclPtr::Create( mpAntiImpl, this, nFlags ); + mpCurView = mpView; + mpIconView = VclPtr::Create( mpAntiImpl, WB_TABSTOP ); + mpIconView->Hide(); + mpView->EnableCellFocus(); +} + + +SvtFileView_Impl::~SvtFileView_Impl() +{ + Clear(); + mpView.disposeAndClear(); + mpCurView.clear(); + mpIconView.disposeAndClear(); + mpAntiImpl.clear(); +} + + +void SvtFileView_Impl::Clear() +{ + ::osl::MutexGuard aGuard( maMutex ); + + maContent.clear(); +} + + +FileViewResult SvtFileView_Impl::GetFolderContent_Impl( + const OUString& rFolder, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList ) +{ + ::osl::ClearableMutexGuard aGuard( maMutex ); + INetURLObject aFolderObj( rFolder ); + DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" ); + + FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + aGuard.clear(); + return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rBlackList ); +} + + +FileViewResult SvtFileView_Impl::GetFolderContent_Impl( + const FolderDescriptor& _rFolder, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList ) +{ + DBG_TESTSOLARMUTEX(); + ::osl::ClearableMutexGuard aGuard( maMutex ); + + OSL_ENSURE( !m_xContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" ); + m_xContentEnumerator.set(new ::svt::FileViewContentEnumerator( + mpView->GetCommandEnvironment(), maContent, maMutex)); + // TODO: should we cache and re-use this thread? + + if ( !pAsyncDescriptor ) + { + ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rBlackList ); + if ( ::svt::EnumerationResult::SUCCESS == eResult ) + { + implEnumerationSuccess(); + m_xContentEnumerator.clear(); + return eSuccess; + } + m_xContentEnumerator.clear(); + return eFailure; + } + + m_bRunningAsyncAction = true; + m_bAsyncActionCancelled = false; + m_eAsyncActionResult = ::svt::EnumerationResult::ERROR; + m_aAsyncActionFinished.reset(); + + // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler. + // By definition, this handler *only* gets called when the result cannot be obtained + // during the minimum wait time, so it is only set below, when needed. + m_aCurrentAsyncActionHandler = Link(); + + // minimum time to wait + std::unique_ptr< TimeValue > pTimeout( new TimeValue ); + sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout; + OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" ); + if ( nMinTimeout <= 0 ) + nMinTimeout = sal_Int32( 1000 ); + pTimeout->Seconds = nMinTimeout / 1000L; + pTimeout->Nanosec = ( nMinTimeout % 1000L ) * 1000000L; + + m_xContentEnumerator->enumerateFolderContent( _rFolder, this ); + + // wait until the enumeration is finished + // for this, release our own mutex (which is used by the enumerator thread) + aGuard.clear(); + + ::osl::Condition::Result eResult = ::osl::Condition::result_ok; + { + // also release the SolarMutex. Not all code which is needed during the enumeration + // is Solar-Thread-Safe, in particular there is some code which needs to access + // string resources (and our resource system relies on the SolarMutex :() + SolarMutexReleaser aSolarRelease; + + // now wait. Note that if we didn't get a pAsyncDescriptor, then this is an infinite wait. + eResult = m_aAsyncActionFinished.wait( pTimeout.get() ); + } + + ::osl::MutexGuard aGuard2( maMutex ); + if ( ::osl::Condition::result_timeout == eResult ) + { + // maximum time to wait + OSL_ENSURE(!m_xCancelAsyncTimer, + "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!"); + m_xCancelAsyncTimer.set(new CallbackTimer(this)); + sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout; + OSL_ENSURE( nMaxTimeout > nMinTimeout, + "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" ); + if ( nMaxTimeout <= nMinTimeout ) + nMaxTimeout = nMinTimeout + 5000; + m_xCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) ); + // we already waited for nMinTimeout milliseconds, so take this into account + m_xCancelAsyncTimer->start(); + + m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler; + DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" ); + mpView->ClearAll(); + mpIconView->ClearAll(); + return eStillRunning; + } + + m_bRunningAsyncAction = false; + switch ( m_eAsyncActionResult ) + { + case ::svt::EnumerationResult::SUCCESS: + return eSuccess; + + case ::svt::EnumerationResult::ERROR: + return eFailure; + } + + SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" ); + return eFailure; +} + + +void SvtFileView_Impl::FilterFolderContent_Impl( const OUString &rFilter ) +{ + if ( rFilter.isEmpty() || ( rFilter == ALL_FILES_FILTER ) ) + // when replacing names, there is always something to filter (no view of ".nametranslation.table") + return; + + ::osl::MutexGuard aGuard( maMutex ); + + if ( maContent.empty() ) + return; + + // collect the filter tokens + ::std::vector< WildCard > aFilters; + FilterMatch::createWildCardFilterList(rFilter,aFilters); + + + // do the filtering + maContent.erase(std::remove_if(maContent.begin(), maContent.end(), + [&aFilters](const std::unique_ptr& rxContent) { + if (rxContent->mbIsFolder) + return false; + // normalize the content title (we always match case-insensitive) + // 91872 - 11.09.2001 - frank.schoenheit@sun.com + OUString sCompareString = rxContent->GetFileName(); // filter works on file name, not on title! + return std::none_of(aFilters.begin(), aFilters.end(), FilterMatch(sCompareString)); + }), + maContent.end()); +} + + +IMPL_LINK( SvtFileView_Impl, SelectionMultiplexer, SvTreeListBox*, _pSource, void ) +{ + if (!mnSuspendSelectCallback) + m_aSelectHandler.Call( _pSource ); +} + + +void SvtFileView_Impl::SetSelectHandler( const Link& _rHdl ) +{ + m_aSelectHandler = _rHdl; + + Link aMasterHandler; + if ( m_aSelectHandler.IsSet() ) + aMasterHandler = LINK( this, SvtFileView_Impl, SelectionMultiplexer ); + + mpView->SetSelectHdl( aMasterHandler ); + mpIconView->SetSelectHdl( aMasterHandler ); +} + + +void SvtFileView_Impl::InitSelection() +{ + mpCurView->SelectAll( false ); + SvTreeListEntry* pFirst = mpCurView->First(); + if ( pFirst ) + mpCurView->SetCursor( pFirst, true ); +} + + +void SvtFileView_Impl::OpenFolder_Impl() +{ + ::osl::MutexGuard aGuard( maMutex ); + + mpView->SetUpdateMode( false ); + mpIconView->SetUpdateMode( false ); + mpView->ClearAll(); + mpIconView->ClearAll(); + + for (auto const& elem : maContent) + { + if ( mbOnlyFolder && ! elem->mbIsFolder ) + continue; + + // insert entry and set user data + SvTreeListEntry* pEntry = mpView->InsertEntry( elem->maDisplayText, + elem->maImage, + elem->maImage ); + + SvTreeListEntry* pEntry2 = mpIconView->InsertEntry( elem->maDisplayText.getToken( 0, '\t' ), + elem->maImage, elem->maImage ); + + SvtContentEntry* pUserData = new SvtContentEntry( elem->maTargetURL, + elem->mbIsFolder ); + SvtContentEntry* pUserData2 = new SvtContentEntry( elem->maTargetURL, + elem->mbIsFolder ); + + pEntry->SetUserData( pUserData ); + pEntry2->SetUserData( pUserData2 ); + } + + InitSelection(); + + ++mnSuspendSelectCallback; + mpView->SetUpdateMode( true ); + mpIconView->SetUpdateMode( true ); + --mnSuspendSelectCallback; + + ResetCursor(); +} + + +void SvtFileView_Impl::ResetCursor() +{ + // deselect + SvTreeListEntry* pEntry = mpCurView->FirstSelected(); + if ( pEntry ) + mpCurView->Select( pEntry, false ); + // set cursor to the first entry + mpCurView->SetCursor( mpCurView->First(), true ); + mpCurView->Update(); +} + + +void SvtFileView_Impl::CancelRunningAsyncAction() +{ + DBG_TESTSOLARMUTEX(); + ::osl::MutexGuard aGuard( maMutex ); + if ( !m_xContentEnumerator.is() ) + return; + + m_bAsyncActionCancelled = true; + m_xContentEnumerator->cancel(); + m_bRunningAsyncAction = false; + + m_xContentEnumerator.clear(); + if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() ) + m_xCancelAsyncTimer->stop(); + m_xCancelAsyncTimer.clear(); +} + + +void SvtFileView_Impl::onTimeout() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( maMutex ); + if ( !m_bRunningAsyncAction ) + // there might have been a race condition while we waited for the mutex + return; + + CancelRunningAsyncAction(); + + if ( m_aCurrentAsyncActionHandler.IsSet() ) + { + Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) ); + m_aCurrentAsyncActionHandler = Link(); + } +} + + +void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( maMutex ); + + m_xContentEnumerator.clear(); + if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() ) + m_xCancelAsyncTimer->stop(); + m_xCancelAsyncTimer.clear(); + + if ( m_bAsyncActionCancelled ) + // this is to prevent race conditions + return; + + m_eAsyncActionResult = eResult; + m_bRunningAsyncAction = false; + + m_aAsyncActionFinished.set(); + + if ( svt::EnumerationResult::SUCCESS == eResult ) + implEnumerationSuccess(); + + if ( m_aCurrentAsyncActionHandler.IsSet() ) + { + Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) ); + m_aCurrentAsyncActionHandler = Link(); + } +} + + +void SvtFileView_Impl::implEnumerationSuccess() +{ + FilterFolderContent_Impl( maCurrentFilter ); + SortFolderContent_Impl(); + CreateDisplayText_Impl(); + OpenFolder_Impl(); + maOpenDoneLink.Call( mpAntiImpl ); +} + + +void SvtFileView_Impl::ReplaceTabWithString( OUString& aValue ) +{ + OUString const aTab( "\t" ); + OUString const aTabString( "%09" ); + sal_Int32 iPos; + + while ( ( iPos = aValue.indexOf( aTab ) ) >= 0 ) + aValue = aValue.replaceAt( iPos, 1, aTabString ); +} + + +void SvtFileView_Impl::CreateDisplayText_Impl() +{ + ::osl::MutexGuard aGuard( maMutex ); + + OUString const aTab( "\t" ); + OUString const aDateSep( ", " ); + + for (auto const& elem : maContent) + { + // title, type, size, date + OUString aValue = elem->GetTitle(); + ReplaceTabWithString( aValue ); + aValue += aTab + elem->maType + aTab; + // folders don't have a size + if ( ! elem->mbIsFolder ) + aValue += CreateExactSizeText( elem->maSize ); + aValue += aTab; + // set the date, but volumes have no date + if ( ! elem->mbIsFolder || ! elem->mbIsVolume ) + { + SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData(); + aValue += rLocaleData.getDate( elem->maModDate ) + + aDateSep + + rLocaleData.getTime( elem->maModDate, false ); + } + elem->maDisplayText = aValue; + + // detect image + if ( elem->mbIsFolder ) + { + ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote, + elem->mbIsRemoveable, elem->mbIsFloppy, + elem->mbIsCompactDisc ); + elem->maImage = SvFileInformationManager::GetFolderImage( aVolInfo ); + } + else + elem->maImage = SvFileInformationManager::GetFileImage( INetURLObject( elem->maTargetURL ) ); + } +} + +void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending ) +{ + // TODO: IconView () + ::osl::MutexGuard aGuard( maMutex ); + + if ( ( nColumn == mnSortColumn ) && + ( bAscending == mbAscending ) ) + return; + + // reset the quick search index + mpView->ResetQuickSearch_Impl( nullptr ); + + OUString aEntryURL; + SvTreeListEntry* pEntry = mpView->GetCurEntry(); + if ( pEntry && pEntry->GetUserData() ) + aEntryURL = static_cast(pEntry->GetUserData())->maURL; + + mnSortColumn = nColumn; + mbAscending = bAscending; + + SortFolderContent_Impl(); + OpenFolder_Impl(); + + if ( !mbIsFirstResort ) + { + sal_uLong nPos = GetEntryPos( aEntryURL ); + if ( nPos < mpView->GetEntryCount() ) + { + pEntry = mpView->GetEntry( nPos ); + + ++mnSuspendSelectCallback; // #i15668# + mpView->SetCurEntry( pEntry ); + --mnSuspendSelectCallback; + } + } + else + mbIsFirstResort = false; +} + + +static bool gbAscending = true; +static sal_Int16 gnColumn = COLUMN_TITLE; +static const CollatorWrapper* pCollatorWrapper = nullptr; + +/* this function returns true, if aOne is less than aTwo +*/ +static bool CompareSortingData_Impl( std::unique_ptr const & aOne, std::unique_ptr const & aTwo ) +{ + DBG_ASSERT( pCollatorWrapper, "*CompareSortingData_Impl(): Can't work this way!" ); + + sal_Int32 nComp; + bool bRet = false; + bool bEqual = false; + + if ( aOne->mbIsFolder != aTwo->mbIsFolder ) + { + bRet = aOne->mbIsFolder; + + // !!! pb: #100376# folder always on top + if ( !gbAscending ) + bRet = !bRet; + } + else + { + switch ( gnColumn ) + { + case COLUMN_TITLE: + // compare case insensitive first + nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() ); + + if ( nComp == 0 ) + nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() ); + + if ( nComp < 0 ) + bRet = true; + else if ( nComp > 0 ) + bRet = false; + else + bEqual = true; + break; + case COLUMN_TYPE: + nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType ); + if ( nComp < 0 ) + bRet = true; + else if ( nComp > 0 ) + bRet = false; + else + bEqual = true; + break; + case COLUMN_SIZE: + if ( aOne->maSize < aTwo->maSize ) + bRet = true; + else if ( aOne->maSize > aTwo->maSize ) + bRet = false; + else + bEqual = true; + break; + case COLUMN_DATE: + if ( aOne->maModDate < aTwo->maModDate ) + bRet = true; + else if ( aOne->maModDate > aTwo->maModDate ) + bRet = false; + else + bEqual = true; + break; + default: + SAL_INFO( "svtools.contnr", "CompareSortingData_Impl: Compare unknown type!" ); + bRet = false; + } + } + + // when the two elements are equal, we must not return sal_True (which would + // happen if we just return ! ( a < b ) when not sorting ascending ) + if ( bEqual ) + return false; + + return gbAscending ? bRet : !bRet; +} + + +void SvtFileView_Impl::SortFolderContent_Impl() +{ + ::osl::MutexGuard aGuard( maMutex ); + + if ( maContent.size() > 1 ) + { + gbAscending = mbAscending; + gnColumn = mnSortColumn; + pCollatorWrapper = aIntlWrapper.getCaseCollator(); + + std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl ); + + pCollatorWrapper = nullptr; + } +} + + +void SvtFileView_Impl::EntryRemoved( const OUString& rURL ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + maContent.erase(std::find_if(maContent.begin(), maContent.end(), + [&](const std::unique_ptr & data) { return data->maTargetURL == rURL; })); +} + + +void SvtFileView_Impl::EntryRenamed( OUString& rURL, + const OUString& rTitle ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + auto aFoundElem = std::find_if(maContent.begin(), maContent.end(), + [&](const std::unique_ptr & data) { return data->maTargetURL == rURL; }); + if (aFoundElem != maContent.end()) + { + (*aFoundElem)->SetNewTitle( rTitle ); + OUString aDisplayText = (*aFoundElem)->maDisplayText; + sal_Int32 nIndex = aDisplayText.indexOf( '\t' ); + + if ( nIndex > 0 ) + (*aFoundElem)->maDisplayText = aDisplayText.replaceAt( 0, nIndex, rTitle ); + + INetURLObject aURLObj( rURL ); + aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All ); + + rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + (*aFoundElem)->maTargetURL = rURL; + } +} + + +OUString SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + std::unique_ptr pData(new SortingData_Impl); + + pData->SetNewTitle( rTitle ); + pData->maSize = 0; + pData->mbIsFolder = true; + pData->maTargetURL = rURL; + + ::svtools::VolumeInfo aVolInfo; + pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo ); + pData->maImage = SvFileInformationManager::GetFolderImage( aVolInfo ); + + OUString aValue; + OUString const aTab( "\t" ); + OUString const aDateSep( ", " ); + + // title, type, size, date + aValue = pData->GetTitle(); + ReplaceTabWithString( aValue ); + aValue += aTab + pData->maType + aTab + + // folders don't have a size + aTab; + // set the date + SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData(); + aValue += rLocaleData.getDate( pData->maModDate ) + + aDateSep + + rLocaleData.getTime( pData->maModDate ); + + pData->maDisplayText = aValue; + maContent.push_back( std::move(pData) ); + + return aValue; +} + + +sal_uLong SvtFileView_Impl::GetEntryPos( const OUString& rURL ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + auto aFoundElem = std::find_if(maContent.begin(), maContent.end(), + [&](const std::unique_ptr & data) { return data->maTargetURL == rURL; }); + return aFoundElem != maContent.end()?std::distance(maContent.begin(), aFoundElem):0; +} + + +void SvtFileView_Impl::SetViewMode( FileViewMode eMode ) +{ + switch ( eMode ) + { + case eDetailedList: + mpCurView = mpView; + mpView->Show(); + mpView->GetHeaderBar()->Show(); + mpIconView->Hide(); + break; + + case eIcon: + mpCurView = mpIconView; + mpView->Hide(); + mpView->GetHeaderBar()->Hide(); + mpIconView->Show(); + break; + + default: + mpCurView = mpView; + mpView->Show(); + mpView->GetHeaderBar()->Show(); + mpIconView->Hide(); + }; +} + + +bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, const OUString& rTitle, bool bWrapAround ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + sal_uInt32 nEnd = maContent.size(); + sal_uInt32 nStart = nIndex; + while ( nIndex < nEnd ) + { + SortingData_Impl* pData = maContent[ nIndex ].get(); + if ( pData->GetLowerTitle().startsWith( rTitle ) ) + return true; + ++nIndex; + } + + if ( bWrapAround ) + { + nIndex = 0; + while ( nIndex < nEnd && nIndex <= nStart ) + { + SortingData_Impl* pData = maContent[ nIndex ].get(); + if ( pData->GetLowerTitle().startsWith( rTitle ) ) + return true; + ++nIndex; + } + } + + return false; +} + +namespace { + void SAL_CALL CallbackTimer::onShot() + { + OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" ); + SvtFileView_Impl* pHandler( m_pTimeoutHandler ); + if ( pHandler ) + pHandler->onTimeout(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/fileview.hxx b/fpicker/source/office/fileview.hxx new file mode 100644 index 000000000000..e655b8c4dcd3 --- /dev/null +++ b/fpicker/source/office/fileview.hxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_VCL_FILEVIEW_HXX +#define INCLUDED_VCL_FILEVIEW_HXX + +#include +#include +#include +#include +#include + +namespace com :: sun :: star :: ucb { class XContent; } + +// class SvtFileView ----------------------------------------------------- + +class SvtFileView_Impl; +class SvTreeListEntry; +class HeaderBar; +struct SvtContentEntry; +class SvTreeListBox; + +/// the result of an action in the FileView +enum FileViewResult +{ + eSuccess, + eFailure, + eTimeout, + eStillRunning +}; + +enum FileViewMode +{ + eDetailedList, + eIcon +}; + +/// describes parameters for doing an action on the FileView asynchronously +struct FileViewAsyncAction +{ + sal_uInt32 nMinTimeout; /// minimum time to wait for a result, in milliseconds + sal_uInt32 nMaxTimeout; /// maximum time to wait for a result, in milliseconds, until eTimeout is returned + Link aFinishHandler; /// the handler to be called when the action is finished. Called in every case, no matter of the result + + FileViewAsyncAction() + { + nMinTimeout = nMaxTimeout = 0; + } +}; + +class SvtFileView : public Control +{ +private: + std::unique_ptr mpImpl; + css::uno::Sequence< OUString > mpBlackList; + + DECL_LINK( HeaderSelect_Impl, HeaderBar*, void ); + DECL_LINK( HeaderEndDrag_Impl, HeaderBar*, void ); + +protected: + virtual void GetFocus() override; + +public: + SvtFileView( vcl::Window* pParent, WinBits nBits, bool bOnlyFolder, bool bMultiSelection, bool bShowType = true ); + virtual ~SvtFileView() override; + virtual void dispose() override; + + virtual Size GetOptimalSize() const override; + + void SetViewMode( FileViewMode eMode ); + + const OUString& GetViewURL() const; + static OUString GetURL( SvTreeListEntry const * pEntry ); + OUString GetCurrentURL() const; + + bool GetParentURL( OUString& _rParentURL ) const; + void CreatedFolder( const OUString& rUrl, const OUString& rNewFolder ); + + void SetHelpId( const OString& rHelpId ); + const OString& GetHelpId( ) const; + void SetSizePixel( const Size& rNewSize ) override; + virtual void SetPosSizePixel( const Point& rNewPos, const Size& rNewSize ) override; + + /** initialize the view with the content of a folder given by URL, and apply an immediate filter + + @param rFolderURL + the URL of the folder whose content is to be read + @param rFilter + the initial filter to be applied + @param pAsyncDescriptor + If not , this struct describes the parameters for doing the + action asynchronously. + */ + FileViewResult Initialize( + const OUString& rFolderURL, + const OUString& rFilter, + const FileViewAsyncAction* pAsyncDescriptor, + const css::uno::Sequence< OUString >& rBlackList + ); + + /** initializes the view with the content of a folder given by a UCB content + */ + bool Initialize( const css::uno::Reference< css::ucb::XContent>& _xContent ); + + /** reads the current content of the current folder again, and applies the given filter to it + + Note 1: The folder is really read a second time. This implies that any new elements (which were + not present when you called Initialize the last time) are now displayed. + + Note 2: This method must not be called when you previously initialized the view from a sequence + of strings, or a UNO content object. + + @param rFilter + the filter to be applied + @param pAsyncDescriptor + If not , this struct describes the parameters for doing the + action asynchronously. + */ + FileViewResult ExecuteFilter( + const OUString& rFilter, + const FileViewAsyncAction* pAsyncDescriptor + ); + + /** cancels a running async action (if any) + + @seealso Initialize + @seealso ExecuteFilter + @seealso FileViewAsyncAction + */ + void CancelRunningAsyncAction(); + + /** initializes the view with the parent folder of the current folder + + @param rNewURL + the URL of the folder which we just navigated to + @param pAsyncDescriptor + If not , this struct describes the parameters for doing the + action asynchronously. + */ + FileViewResult PreviousLevel( + const FileViewAsyncAction* pAsyncDescriptor + ); + + void SetNoSelection(); + + void SetSelectHdl( const Link& rHdl ); + void SetDoubleClickHdl( const Link& rHdl ); + void SetOpenDoneHdl( const Link& rHdl ); + + sal_uLong GetSelectionCount() const; + SvTreeListEntry* FirstSelected() const; + SvTreeListEntry* NextSelected( SvTreeListEntry* pEntry ) const; + void EnableAutoResize(); + + void EnableDelete( bool bEnable ); + + // save and load column size and sort order + OUString GetConfigString() const; + void SetConfigString( const OUString& rCfgStr ); + + void EndInplaceEditing(); + + ::std::vector< SvtContentEntry > GetContent(); + +protected: + virtual void StateChanged( StateChangedType nStateChange ) override; +}; + +// struct SvtContentEntry ------------------------------------------------ + +struct SvtContentEntry +{ + bool const mbIsFolder; + OUString maURL; + + SvtContentEntry( const OUString& rURL, bool bIsFolder ) : + mbIsFolder( bIsFolder ), maURL( rURL ) {} +}; + +#endif // INCLUDED_VCL_FILEVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/foldertree.cxx b/fpicker/source/office/foldertree.cxx new file mode 100644 index 000000000000..a2cc56aca385 --- /dev/null +++ b/fpicker/source/office/foldertree.cxx @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "contentenumeration.hxx" +#include "foldertree.hxx" +#include + +using namespace ::com::sun::star::task; + +FolderTree::FolderTree( vcl::Window* pParent, WinBits nBits ) + : SvTreeListBox( pParent, nBits | WB_SORT | WB_TABSTOP ) +{ + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< XInteractionHandler > xInteractionHandler( + InteractionHandler::createWithParent(xContext, VCLUnoHelper::GetInterface(GetParentDialog())), UNO_QUERY_THROW ); + m_xEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() ); + + Image aFolderImage(StockImage::Yes, RID_BMP_FOLDER); + Image aFolderExpandedImage(StockImage::Yes, RID_BMP_FOLDER_OPEN); + SetDefaultCollapsedEntryBmp( aFolderImage ); + SetDefaultExpandedEntryBmp( aFolderExpandedImage ); +} + +void FolderTree::RequestingChildren( SvTreeListEntry* pEntry ) +{ + EnableChildPointerOverwrite( true ); + SetPointer( PointerStyle::Wait ); + Invalidate(InvalidateFlags::Update); + + FillTreeEntry( pEntry ); + + SetPointer( PointerStyle::Arrow ); + EnableChildPointerOverwrite( false ); +} + +void FolderTree::FillTreeEntry( SvTreeListEntry* pEntry ) +{ + if( !pEntry ) + return; + + OUString* pURL = static_cast< OUString* >( pEntry->GetUserData() ); + + if( pURL && m_sLastUpdatedDir != *pURL ) + { + while (SvTreeListEntry* pChild = FirstChild(pEntry)) + { + GetModel()->Remove(pChild); + } + + ::std::vector< std::unique_ptr > aContent; + + ::rtl::Reference< ::svt::FileViewContentEnumerator > + xContentEnumerator(new FileViewContentEnumerator( + m_xEnv, aContent, m_aMutex)); + + FolderDescriptor aFolder( *pURL ); + + EnumerationResult eResult = + xContentEnumerator->enumerateFolderContentSync( aFolder, m_aBlackList ); + + if ( EnumerationResult::SUCCESS == eResult ) + { + for(const auto & i : aContent) + { + if( i->mbIsFolder ) + { + SvTreeListEntry* pNewEntry = InsertEntry( i->GetTitle(), pEntry, true ); + + OUString* sData = new OUString( i->maTargetURL ); + pNewEntry->SetUserData( static_cast< void* >( sData ) ); + } + } + } + } + else + { + // this dir was updated recently + // next time read this remote folder + m_sLastUpdatedDir.clear(); + } +} + +void FolderTree::FillTreeEntry( const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders ) +{ + SetTreePath( rUrl ); + + SvTreeListEntry* pParent = GetCurEntry(); + + if( !(pParent && !IsExpanded( pParent )) ) + return; + + while (SvTreeListEntry* pChild = FirstChild(pParent)) + { + GetModel()->Remove(pChild); + } + + for (auto const& folder : rFolders) + { + SvTreeListEntry* pNewEntry = InsertEntry( folder.first, pParent, true ); + OUString* sData = new OUString( folder.second ); + pNewEntry->SetUserData( static_cast< void* >( sData ) ); + } + + m_sLastUpdatedDir = rUrl; + Expand( pParent ); +} + +void FolderTree::SetTreePath( OUString const & sUrl ) +{ + INetURLObject aUrl( sUrl ); + aUrl.setFinalSlash(); + + OUString sPath = aUrl.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ); + + SvTreeListEntry* pEntry = First(); + bool end = false; + + while( pEntry && !end ) + { + if( pEntry->GetUserData() ) + { + OUString sNodeUrl = *static_cast< OUString* >( pEntry->GetUserData() ); + + INetURLObject aUrlObj( sNodeUrl ); + aUrlObj.setFinalSlash(); + + sNodeUrl = aUrlObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ); + + if( sPath == sNodeUrl ) + { + Select( pEntry ); + end = true; + } + else if( sPath.startsWith( sNodeUrl ) ) + { + if( !IsExpanded( pEntry ) ) + Expand( pEntry ); + + pEntry = FirstChild( pEntry ); + } + else + { + pEntry = pEntry->NextSibling(); + } + } + else + break; + } +} + +void FolderTree::SetBlackList( const css::uno::Sequence< OUString >& rBlackList ) +{ + m_aBlackList = rBlackList; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/foldertree.hxx b/fpicker/source/office/foldertree.hxx new file mode 100644 index 000000000000..03710efb2076 --- /dev/null +++ b/fpicker/source/office/foldertree.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_SVTOOLS_FOLDERTREE_HXX +#define INCLUDED_SVTOOLS_FOLDERTREE_HXX + +#include +#include +#include + +namespace com :: sun :: star :: ucb { class XCommandEnvironment; } + +class SvTreeListEntry; + +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::uno; +using namespace ::svt; + +class FolderTree : public SvTreeListBox +{ +private: + Reference< XCommandEnvironment > m_xEnv; + ::osl::Mutex m_aMutex; + Sequence< OUString > m_aBlackList; + + OUString m_sLastUpdatedDir; + +public: + FolderTree( vcl::Window* pParent, WinBits nBits ); + + virtual void RequestingChildren( SvTreeListEntry* pEntry ) override; + + void FillTreeEntry( SvTreeListEntry* pEntry ); + void FillTreeEntry( const OUString & rUrl, const ::std::vector< std::pair< OUString, OUString > >& rFolders ); + void SetTreePath( OUString const & sUrl ); + void SetBlackList( const css::uno::Sequence< OUString >& rBlackList ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/iconview.cxx b/fpicker/source/office/iconview.cxx new file mode 100644 index 000000000000..b6571b72d1da --- /dev/null +++ b/fpicker/source/office/iconview.cxx @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include "fileview.hxx" +#include "iconview.hxx" +#include "iconviewimpl.hxx" + +IconView::IconView( vcl::Window* pParent, WinBits nBits ) +: SvTreeListBox( pParent, nBits ) +{ + nColumns = 1; + mbCenterAndClipText = true; + SetEntryHeight( 100 ); + SetEntryWidth( 100 ); + + pImpl.reset( new IconViewImpl( this, GetModel(), GetStyle() ) ); +} + +void IconView::Resize() +{ + Size aBoxSize = Control::GetParent()->GetOutputSizePixel(); + + if ( !aBoxSize.Width() ) + return; + + SetSizePixel( aBoxSize ); + + nColumns = aBoxSize.Width() / nEntryWidth; + + SvTreeListBox::Resize(); +} + +tools::Rectangle IconView::GetFocusRect( SvTreeListEntry*, long nEntryPos ) +{ + Size aSize; + aSize.setHeight( nEntryHeight ); + aSize.setWidth( nEntryWidth ); + + Point aPos; + aPos.setX( 0 ); + aPos.setY( 0 ); + + tools::Rectangle aRect; + + short nCols = GetColumnsCount(); + + if(nCols) + { + aPos.setY( ( nEntryPos / nCols ) * nEntryHeight ); + aPos.setX( ( nEntryPos % nCols ) * nEntryWidth ); + } + + aRect.SetPos( aPos ); + aRect.SetSize( aSize ); + + return aRect; +} + +void IconView::ClearAll() +{ + for ( sal_uLong i = 0; i < GetEntryCount(); ++i ) + delete static_cast(GetEntry(i)->GetUserData()); + + Clear(); +} + +void IconView::PaintEntry(SvTreeListEntry& rEntry, long nX, long nY, + vcl::RenderContext& rRenderContext) +{ + + tools::Rectangle aRect; // multi purpose + + PreparePaint(rRenderContext, rEntry); + + pImpl->UpdateContextBmpWidthMax(&rEntry); + + short nTempEntryHeight = GetEntryHeight(); + short nTempEntryWidth = GetEntryWidth(); + + Point aEntryPos; + + Color aBackupTextColor(rRenderContext.GetTextColor()); + vcl::Font aBackupFont(rRenderContext.GetFont()); + Color aBackupColor = rRenderContext.GetFillColor(); + + bool bCurFontIsSel = false; + const WinBits nWindowStyle = GetStyle(); + const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus(); + const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); + + vcl::Font aHighlightFont(rRenderContext.GetFont()); + const Color aHighlightTextColor(rSettings.GetHighlightTextColor()); + aHighlightFont.SetColor(aHighlightTextColor); + + Size aRectSize(nTempEntryWidth, nTempEntryHeight); + + SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry ); + + sal_uInt16 nItemCount = rEntry.ItemCount(); + sal_uInt16 nCurItem = 0; + sal_uInt16 nIconItem = nItemCount; + + while (nCurItem < nItemCount) + { + SvLBoxItem* pItem = nCurItem < nItemCount ? &rEntry.GetItem(nCurItem) : nullptr; + SvLBoxItemType nItemType = pItem->GetType(); + + if (nItemType == SvLBoxItemType::ContextBmp) + { + nIconItem = nCurItem; + nCurItem++; + continue; + } + + auto nItemHeight = SvLBoxItem::GetHeight(pViewDataEntry, nCurItem); + + aEntryPos.setX( nX ); + aEntryPos.setY( nY ); + + // set background pattern/color + + Wallpaper aWallpaper = rRenderContext.GetBackground(); + + if (pViewDataEntry->IsHighlighted()) + { + Color aNewWallColor = rSettings.GetHighlightColor(); + // if the face color is bright then the deactivate color is also bright + // -> so you can't see any deactivate selection + if (bHideSelection && !rSettings.GetFaceColor().IsBright() + && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright()) + { + aNewWallColor = rSettings.GetDeactiveColor(); + } + // set font color to highlight + if (!bCurFontIsSel) + { + rRenderContext.SetTextColor(aHighlightTextColor); + rRenderContext.SetFont(aHighlightFont); + bCurFontIsSel = true; + } + aWallpaper.SetColor(aNewWallColor); + } + else // no selection + { + if (bCurFontIsSel) + { + bCurFontIsSel = false; + rRenderContext.SetTextColor(aBackupTextColor); + rRenderContext.SetFont(aBackupFont); + } + else + { + aWallpaper.SetColor(rEntry.GetBackColor()); + } + } + + // draw background + if (!(nTreeFlags & SvTreeFlags::USESEL)) + { + aRect.SetPos(aEntryPos); + aRect.SetSize(aRectSize); + + Color aBackgroundColor = aWallpaper.GetColor(); + if (aBackgroundColor != COL_TRANSPARENT) + { + rRenderContext.SetFillColor(aBackgroundColor); + // this case may occur for smaller horizontal resizes + if (aRect.Left() < aRect.Right()) + rRenderContext.DrawRect(aRect); + } + } + + // center vertically + aEntryPos.AdjustY((nTempEntryHeight - nItemHeight) / 2 ); + + // draw item + pViewDataEntry->SetPaintRectangle(aRect); + + aEntryPos.AdjustY(15 ); + + pItem->Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry); + + rRenderContext.SetFillColor(aBackupColor); + + nCurItem++; + } + + // draw icon + if(nIconItem != nItemCount && nIconItem < nItemCount) + { + SvLBoxItem* pItem = &rEntry.GetItem(nIconItem); + auto nItemWidth = pItem->GetWidth(this, pViewDataEntry, nIconItem); + auto nItemHeight = SvLBoxItem::GetHeight(pViewDataEntry, nIconItem); + + aEntryPos.setX( nX ); + aEntryPos.setY( nY ); + + // center horizontally + aEntryPos.AdjustX((nTempEntryWidth - nItemWidth) / 2 ); + // center vertically + aEntryPos.AdjustY((nTempEntryHeight - nItemHeight) / 2 ); + + aEntryPos.AdjustY( -10 ); + + pItem->Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry); + } + + if (bCurFontIsSel) + { + rRenderContext.SetTextColor(aBackupTextColor); + rRenderContext.SetFont(aBackupFont); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/iconview.hxx b/fpicker/source/office/iconview.hxx new file mode 100644 index 000000000000..17a9103e9c2f --- /dev/null +++ b/fpicker/source/office/iconview.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SVTOOLS_ICONVIEW_HXX +#define INCLUDED_SVTOOLS_ICONVIEW_HXX + +#include + +class IconView : public SvTreeListBox +{ +public: + IconView( vcl::Window* pParent, WinBits nBits ); + + virtual void Resize() override; + + virtual tools::Rectangle GetFocusRect( SvTreeListEntry*, long nEntryPos ) override; + + void ClearAll(); + + void PaintEntry( SvTreeListEntry&, long nX, long nY, vcl::RenderContext& rRenderContext); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/iconviewimpl.cxx b/fpicker/source/office/iconviewimpl.cxx new file mode 100644 index 000000000000..ff3bdfcd9e76 --- /dev/null +++ b/fpicker/source/office/iconviewimpl.cxx @@ -0,0 +1,662 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include "iconview.hxx" +#include "iconviewimpl.hxx" + +IconViewImpl::IconViewImpl( SvTreeListBox* pTreeListBox, SvTreeList* pTreeList, WinBits nWinStyle ) +: SvImpLBox( pTreeListBox, pTreeList, nWinStyle ) +{ +} + +void IconViewImpl::CursorUp() +{ + if (!m_pStartEntry) + return; + + SvTreeListEntry* pPrevFirstToDraw = m_pStartEntry; + + for(short i = 0; i < m_pView->GetColumnsCount() && pPrevFirstToDraw; i++) + pPrevFirstToDraw = m_pView->PrevVisible(pPrevFirstToDraw); + + if( !pPrevFirstToDraw ) + return; + + m_nFlags &= ~LBoxFlags::Filling; + long nEntryHeight = m_pView->GetEntryHeight(); + ShowCursor( false ); + m_pView->Update(); + m_pStartEntry = pPrevFirstToDraw; + tools::Rectangle aArea( GetVisibleArea() ); + aArea.AdjustBottom( -nEntryHeight ); + m_pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren ); + m_pView->Update(); + ShowCursor( true ); + m_pView->NotifyScrolled(); +} + +void IconViewImpl::CursorDown() +{ + if (!m_pStartEntry) + return; + + SvTreeListEntry* pNextFirstToDraw = m_pStartEntry; + + for(short i = 0; i < m_pView->GetColumnsCount(); i++) + pNextFirstToDraw = m_pView->NextVisible(pNextFirstToDraw); + + if( pNextFirstToDraw ) + { + m_nFlags &= ~LBoxFlags::Filling; + ShowCursor( false ); + m_pView->Update(); + m_pStartEntry = pNextFirstToDraw; + tools::Rectangle aArea( GetVisibleArea() ); + m_pView->Scroll( 0, -(m_pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren ); + m_pView->Update(); + ShowCursor( true ); + m_pView->NotifyScrolled(); + } +} + +void IconViewImpl::PageDown( sal_uInt16 nDelta ) +{ + sal_uInt16 nRealDelta = nDelta * m_pView->GetColumnsCount(); + + if( !nDelta ) + return; + + if (!m_pStartEntry) + return; + + SvTreeListEntry* pNext = m_pView->NextVisible(m_pStartEntry, nRealDelta); + if( pNext == m_pStartEntry ) + return; + + ShowCursor( false ); + + m_nFlags &= ~LBoxFlags::Filling; + m_pView->Update(); + m_pStartEntry = pNext; + + if( nRealDelta >= m_nVisibleCount ) + { + m_pView->Invalidate( GetVisibleArea() ); + m_pView->Update(); + } + else + { + tools::Rectangle aArea( GetVisibleArea() ); + long nScroll = m_pView->GetEntryHeight() * static_cast(nRealDelta); + nScroll = -nScroll; + m_pView->Update(); + m_pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren ); + m_pView->Update(); + m_pView->NotifyScrolled(); + } + + ShowCursor( true ); +} + +void IconViewImpl::PageUp( sal_uInt16 nDelta ) +{ + sal_uInt16 nRealDelta = nDelta * m_pView->GetColumnsCount(); + if( !nDelta ) + return; + + if (!m_pStartEntry) + return; + + SvTreeListEntry* pPrev = m_pView->PrevVisible(m_pStartEntry, nRealDelta); + if( pPrev == m_pStartEntry ) + return; + + m_nFlags &= ~LBoxFlags::Filling; + ShowCursor( false ); + + m_pView->Update(); + m_pStartEntry = pPrev; + if( nRealDelta >= m_nVisibleCount ) + { + m_pView->Invalidate( GetVisibleArea() ); + m_pView->Update(); + } + else + { + long nEntryHeight = m_pView->GetEntryHeight(); + tools::Rectangle aArea( GetVisibleArea() ); + m_pView->Update(); + m_pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren ); + m_pView->Update(); + m_pView->NotifyScrolled(); + } + + ShowCursor( true ); +} + +void IconViewImpl::KeyDown( bool bPageDown ) +{ + if( !m_aVerSBar->IsVisible() ) + return; + + long nDelta; + if( bPageDown ) + nDelta = m_aVerSBar->GetPageSize(); + else + nDelta = 1; + + long nThumbPos = m_aVerSBar->GetThumbPos(); + + if( nDelta <= 0 ) + return; + + m_nFlags &= ~LBoxFlags::Filling; + BeginScroll(); + + m_aVerSBar->SetThumbPos( nThumbPos+nDelta ); + if( bPageDown ) + PageDown( static_cast(nDelta) ); + else + CursorDown(); + + EndScroll(); +} + +void IconViewImpl::KeyUp( bool bPageUp ) +{ + if( !m_aVerSBar->IsVisible() ) + return; + + long nDelta; + if( bPageUp ) + nDelta = m_aVerSBar->GetPageSize(); + else + nDelta = 1; + + long nThumbPos = m_aVerSBar->GetThumbPos(); + + if( nThumbPos < nDelta ) + nDelta = nThumbPos; + + if( nDelta < 0 ) + return; + + m_nFlags &= ~LBoxFlags::Filling; + BeginScroll(); + + m_aVerSBar->SetThumbPos( nThumbPos - nDelta ); + if( bPageUp ) + PageUp( static_cast(nDelta) ); + else + CursorUp(); + + EndScroll(); +} + +long IconViewImpl::GetEntryLine( SvTreeListEntry* pEntry ) const +{ + if(!m_pStartEntry ) + return -1; // invisible position + + long nFirstVisPos = m_pView->GetVisiblePos( m_pStartEntry ); + long nEntryVisPos = m_pView->GetVisiblePos( pEntry ); + nFirstVisPos = nEntryVisPos - nFirstVisPos; + + return nFirstVisPos; +} + +Point IconViewImpl::GetEntryPosition( SvTreeListEntry* pEntry ) const +{ + const int pos = m_pView->GetAbsPos( pEntry ); + + return Point( ( pos % m_pView->GetColumnsCount() ) * m_pView->GetEntryWidth(), + ( pos / m_pView->GetColumnsCount() ) * m_pView->GetEntryHeight() ); +} + +SvTreeListEntry* IconViewImpl::GetClickedEntry( const Point& rPoint ) const +{ + DBG_ASSERT( m_pView->GetModel(), "IconViewImpl::GetClickedEntry: how can this ever happen?" ); + if ( !m_pView->GetModel() ) + return nullptr; + if( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight() || !m_pView->GetEntryWidth()) + return nullptr; + + sal_uInt16 nY = static_cast(rPoint.Y() / m_pView->GetEntryHeight() ); + sal_uInt16 nX = static_cast(rPoint.X() / m_pView->GetEntryWidth() ); + sal_uInt16 nTemp = nY * m_pView->GetColumnsCount() + nX; + + SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp); + + return pEntry; +} + +bool IconViewImpl::IsEntryInView( SvTreeListEntry* pEntry ) const +{ + // parent collapsed + if( !m_pView->IsEntryVisible(pEntry) ) + return false; + + long nY = GetEntryLine( pEntry ) / m_pView->GetColumnsCount() * m_pView->GetEntryHeight(); + if( nY < 0 ) + return false; + + long nMax = m_nVisibleCount / m_pView->GetColumnsCount() * m_pView->GetEntryHeight(); + if( nY >= nMax ) + return false; + + long nStart = GetEntryLine( pEntry ) - GetEntryLine( m_pStartEntry ); + return nStart >= 0; +} + +void IconViewImpl::AdjustScrollBars( Size& rSize ) +{ + long nEntryHeight = m_pView->GetEntryHeight(); + if( !nEntryHeight ) + return; + + sal_uInt16 nResult = 0; + + Size aOSize( m_pView->Control::GetOutputSizePixel() ); + + const WinBits nWindowStyle = m_pView->GetStyle(); + bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0; + + // number of entries that are not collapsed + sal_uLong nTotalCount = m_pView->GetVisibleCount(); + + // number of entries visible within the view + m_nVisibleCount = aOSize.Height() / nEntryHeight * m_pView->GetColumnsCount(); + + long nRows = ( nTotalCount / m_pView->GetColumnsCount() ) + 1; + + // do we need a vertical scrollbar? + if( bVerSBar || nTotalCount > m_nVisibleCount ) + { + nResult = 1; + } + + PositionScrollBars( aOSize, nResult ); + + // adapt Range, VisibleRange etc. + + // refresh output size, in case we have to scroll + tools::Rectangle aRect; + aRect.SetSize( aOSize ); + m_aSelEng.SetVisibleArea( aRect ); + + // vertical scrollbar + if( !m_bInVScrollHdl ) + { + m_aVerSBar->SetPageSize( nTotalCount ); + m_aVerSBar->SetVisibleSize( nTotalCount - nRows ); + } + else + { + m_nFlags |= LBoxFlags::EndScrollSetVisSize; + } + + if( nResult & 0x0001 ) + m_aVerSBar->Show(); + else + m_aVerSBar->Hide(); + + rSize = aOSize; +} + +// returns 0 if position is just past the last entry +SvTreeListEntry* IconViewImpl::GetEntry( const Point& rPoint ) const +{ + if( (m_pView->GetEntryCount() == 0) || !m_pStartEntry || + (rPoint.Y() > m_aOutputSize.Height()) + || !m_pView->GetEntryHeight() + || !m_pView->GetEntryWidth()) + return nullptr; + + sal_uInt16 nClickedEntry = static_cast(rPoint.Y() / m_pView->GetEntryHeight() * m_pView->GetColumnsCount() + rPoint.X() / m_pView->GetEntryWidth() ); + sal_uInt16 nTemp = nClickedEntry; + SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp); + if( nTemp != nClickedEntry ) + pEntry = nullptr; + return pEntry; +} + +void IconViewImpl::SyncVerThumb() +{ + if( m_pStartEntry ) + { + long nEntryPos = m_pView->GetVisiblePos( m_pStartEntry ); + m_aVerSBar->SetThumbPos( nEntryPos ); + } + else + m_aVerSBar->SetThumbPos( 0 ); +} + +void IconViewImpl::UpdateAll( bool bInvalidateCompleteView ) +{ + FindMostRight( nullptr ); + m_aVerSBar->SetRange( Range( 0, m_pView->GetVisibleCount() ) ); + SyncVerThumb(); + FillView(); + ShowVerSBar(); + if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() ) + m_pView->Select( m_pCursor ); + ShowCursor( true ); + if( bInvalidateCompleteView ) + m_pView->Invalidate(); + else + m_pView->Invalidate( GetVisibleArea() ); +} + +void IconViewImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (!m_pView->GetVisibleCount()) + return; + + m_nFlags |= LBoxFlags::InPaint; + + if (m_nFlags & LBoxFlags::Filling) + { + SvTreeListEntry* pFirst = m_pView->First(); + if (pFirst != m_pStartEntry) + { + ShowCursor(false); + m_pStartEntry = m_pView->First(); + m_aVerSBar->SetThumbPos( 0 ); + StopUserEvent(); + ShowCursor(true); + m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent), + reinterpret_cast(1)); + return; + } + } + + if (!m_pStartEntry) + { + m_pStartEntry = m_pView->First(); + } + + long nRectHeight = rRect.GetHeight(); + long nRectWidth = rRect.GetWidth(); + long nEntryHeight = m_pView->GetEntryHeight(); + long nEntryWidth = m_pView->GetEntryWidth(); + + // calculate area for the entries we want to draw + sal_uInt16 nStartId = static_cast(rRect.Top() / nEntryHeight * m_pView->GetColumnsCount() + (rRect.Left() / nEntryWidth)); + sal_uInt16 nCount = static_cast(( nRectHeight / nEntryHeight + 1 ) * nRectWidth / nEntryWidth); + nCount += 2; // don't miss an entry + + long nY = nStartId / m_pView->GetColumnsCount() * nEntryHeight; + long nX = 0; + SvTreeListEntry* pEntry = m_pStartEntry; + while (nStartId && pEntry) + { + pEntry = m_pView->NextVisible(pEntry); + nStartId--; + } + + vcl::Region aClipRegion(GetClipRegionRect()); + + if (!m_pCursor && !mbNoAutoCurEntry) + { + // do not select if multiselection or explicit set + bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION); + SetCursor(m_pStartEntry, bNotSelect); + } + + for(sal_uInt16 n = 0; n< nCount && pEntry; n++) + { + static_cast(m_pView.get())->PaintEntry(*pEntry, nX, nY, rRenderContext); + nX += nEntryWidth; + + if(nX + m_pView->GetEntryWidth() > nEntryWidth * m_pView->GetColumnsCount()) + { + nY += nEntryHeight; + nX = 0; + } + pEntry = m_pView->NextVisible(pEntry); + } + + m_nFlags &= ~LBoxFlags::DeselectAll; + rRenderContext.SetClipRegion(); + m_nFlags &= ~LBoxFlags::InPaint; +} + +void IconViewImpl::InvalidateEntry( long nId ) const +{ + if( m_nFlags & LBoxFlags::InPaint ) + return; + + tools::Rectangle aRect( GetVisibleArea() ); + long nMaxBottom = aRect.Bottom(); + aRect.SetTop( nId / m_pView->GetColumnsCount() * m_pView->GetEntryHeight() ); + aRect.SetBottom( aRect.Top() ); aRect.AdjustBottom(m_pView->GetEntryHeight() ); + + if( aRect.Top() > nMaxBottom ) + return; + if( aRect.Bottom() > nMaxBottom ) + aRect.SetBottom( nMaxBottom ); + m_pView->Invalidate( aRect ); +} + +bool IconViewImpl::KeyInput( const KeyEvent& rKEvt ) +{ + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + + if( rKeyCode.IsMod2() ) + return false; // don't evaluate Alt key + + m_nFlags &= ~LBoxFlags::Filling; + + if( !m_pCursor ) + m_pCursor = m_pStartEntry; + if( !m_pCursor ) + return false; + + sal_uInt16 aCode = rKeyCode.GetCode(); + + bool bShift = rKeyCode.IsShift(); + bool bMod1 = rKeyCode.IsMod1(); + + SvTreeListEntry* pNewCursor; + + bool bHandled = true; + + long i; + long nColumns = m_pView->GetColumnsCount(); + + switch( aCode ) + { + case KEY_LEFT: + if( !IsEntryInView( m_pCursor ) ) + MakeVisible( m_pCursor ); + + pNewCursor = m_pCursor; + do + { + pNewCursor = m_pView->PrevVisible(pNewCursor); + } while( pNewCursor && !IsSelectable(pNewCursor) ); + + // if there is no next entry, take the current one + // this ensures that in case of _one_ entry in the list, this entry is selected when pressing + // the cursor key + if (!pNewCursor) + pNewCursor = m_pCursor; + + m_aSelEng.CursorPosChanging( bShift, bMod1 ); + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + if( !IsEntryInView( pNewCursor ) ) + KeyUp( false ); + break; + + case KEY_RIGHT: + if( !IsEntryInView( m_pCursor ) ) + MakeVisible( m_pCursor ); + + pNewCursor = m_pCursor; + do + { + pNewCursor = m_pView->NextVisible(pNewCursor); + } while( pNewCursor && !IsSelectable(pNewCursor) ); + + // if there is no next entry, take the current one + // this ensures that in case of _one_ entry in the list, this entry is selected when pressing + // the cursor key + if ( !pNewCursor && m_pCursor ) + pNewCursor = m_pCursor; + + if( pNewCursor ) + { + m_aSelEng.CursorPosChanging( bShift, bMod1 ); + if( IsEntryInView( pNewCursor ) ) + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + else + { + if( m_pCursor ) + m_pView->Select( m_pCursor, false ); + KeyDown( false ); + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + } + } + else + KeyDown( false ); // because scrollbar range might still + // allow scrolling + break; + + case KEY_UP: + { + if( !IsEntryInView( m_pCursor ) ) + MakeVisible( m_pCursor ); + + pNewCursor = m_pCursor; + for( i = 0; i < nColumns && pNewCursor; i++) + { + do + { + pNewCursor = m_pView->PrevVisible(pNewCursor); + } while( pNewCursor && !IsSelectable(pNewCursor) ); + } + + // if there is no next entry, take the current one + // this ensures that in case of _one_ entry in the list, this entry is selected when pressing + // the cursor key + if ( !pNewCursor && m_pCursor ) + pNewCursor = m_pCursor; + + if( pNewCursor ) + { + m_aSelEng.CursorPosChanging( bShift, bMod1 ); + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + if( !IsEntryInView( pNewCursor ) ) + KeyUp( false ); + } + break; + } + + case KEY_DOWN: + { + if( !IsEntryInView( m_pCursor ) ) + MakeVisible( m_pCursor ); + + pNewCursor = m_pCursor; + for( i = 0; i < nColumns && pNewCursor; i++) + { + do + { + pNewCursor = m_pView->NextVisible(pNewCursor); + } while( pNewCursor && !IsSelectable(pNewCursor) ); + } + + // if there is no next entry, take the current one + // this ensures that in case of _one_ entry in the list, this entry is selected when pressing + // the cursor key + if ( !pNewCursor && m_pCursor ) + pNewCursor = m_pCursor; + + if( pNewCursor ) + { + m_aSelEng.CursorPosChanging( bShift, bMod1 ); + if( IsEntryInView( pNewCursor ) ) + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + else + { + if( m_pCursor ) + m_pView->Select( m_pCursor, false ); + KeyDown( false ); + SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on + } + } + else + KeyDown( false ); // because scrollbar range might still + // allow scrolling + break; + } + + case KEY_RETURN: + { + m_pView->aDoubleClickHdl.Call( m_pView ); + bHandled = true; + + break; + } + + case KEY_END: + { + pNewCursor = m_pView->GetModel()->Last(); + + while( pNewCursor && !IsSelectable(pNewCursor) ) + { + pNewCursor = m_pView->PrevVisible(pNewCursor); + } + + m_pStartEntry = pNewCursor; + + while( m_pStartEntry && m_pView->GetAbsPos( m_pStartEntry ) % m_pView->GetColumnsCount() != 0 ) + { + m_pStartEntry = m_pView->PrevVisible(m_pStartEntry); + } + + if( pNewCursor && pNewCursor != m_pCursor) + { +// SelAllDestrAnch( false ); + m_aSelEng.CursorPosChanging( bShift, bMod1 ); + SetCursor( pNewCursor ); + SyncVerThumb(); + } + + bHandled = true; + + break; + } + + default: + { + bHandled = false; + break; + } + } + + if(!bHandled) + return SvImpLBox::KeyInput( rKEvt ); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/iconviewimpl.hxx b/fpicker/source/office/iconviewimpl.hxx new file mode 100644 index 000000000000..df11f5952426 --- /dev/null +++ b/fpicker/source/office/iconviewimpl.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX +#define INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX + +#include + +class SvTreeListBox; +class Point; + +class IconViewImpl : public SvImpLBox +{ +public: + IconViewImpl( SvTreeListBox* pTreeListBox, SvTreeList* pTreeList, WinBits nWinStyle ); + + void KeyDown( bool bPageDown ) override; + + void KeyUp( bool bPageUp ) override; + + Point GetEntryPosition( SvTreeListEntry* pEntry ) const override; + + SvTreeListEntry* GetClickedEntry( const Point& rPoint ) const override; + + bool IsEntryInView( SvTreeListEntry* pEntry ) const override; + + void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + + // returns 0 if position is just past the last entry + SvTreeListEntry* GetEntry( const Point& rPoint ) const override; + + void UpdateAll( bool bInvalidateCompleteView ) override; + + bool KeyInput( const KeyEvent& ) override; + + void InvalidateEntry( long nId ) const override; + +protected: + long GetEntryLine( SvTreeListEntry* pEntry ) const override; + + void CursorUp() override; + void CursorDown() override; + void PageDown( sal_uInt16 nDelta ) override; + void PageUp( sal_uInt16 nDelta ) override; + + void SyncVerThumb() override; + void AdjustScrollBars( Size& rSize ) override; +}; + +#endif // INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/fpicker/source/office/iodlg.cxx b/fpicker/source/office/iodlg.cxx index abd187f13a24..ed097017dc5e 100644 --- a/fpicker/source/office/iodlg.cxx +++ b/fpicker/source/office/iodlg.cxx @@ -21,6 +21,7 @@ #include #include +#include "fileview.hxx" #include "iodlg.hxx" #include #include "PlacesListBox.hxx" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/fpicker/source/office/iodlgimp.cxx b/fpicker/source/office/iodlgimp.cxx index a19f55c9319f..b78603711847 100644 --- a/fpicker/source/office/iodlgimp.cxx +++ b/fpicker/source/office/iodlgimp.cxx @@ -17,6 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include "fileview.hxx" #include "iodlgimp.hxx" #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include "iodlg.hxx" #include -- cgit