summaryrefslogtreecommitdiffstats
path: root/fpicker/source/office/fileview.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'fpicker/source/office/fileview.cxx')
-rw-r--r--fpicker/source/office/fileview.cxx1816
1 files changed, 1816 insertions, 0 deletions
diff --git a/fpicker/source/office/fileview.cxx b/fpicker/source/office/fileview.cxx
new file mode 100644
index 000000000..7e78f773e
--- /dev/null
+++ b/fpicker/source/office/fileview.cxx
@@ -0,0 +1,1816 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svtools/svtresid.hxx>
+#include <svtools/imagemgr.hxx>
+#include <svtools/querydelete.hxx>
+#include <svtools/strings.hrc>
+#include <bitmaps.hlst>
+#include "contentenumeration.hxx"
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <algorithm>
+#include <string_view>
+#include <vector>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <osl/conditn.hxx>
+#include <salhelper/timer.hxx>
+#include <svtools/urlfilter.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/timer.hxx>
+#include <memory>
+#include "fileview.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;
+
+constexpr OUStringLiteral ALL_FILES_FILTER = u"*.*";
+
+#define COLUMN_TITLE 1
+#define COLUMN_TYPE 2
+#define COLUMN_SIZE 3
+#define COLUMN_DATE 4
+
+#define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset
+
+namespace {
+
+enum class FileViewFlags
+{
+ NONE = 0x00,
+ MULTISELECTION = 0x02,
+ SHOW_TYPE = 0x04,
+ SHOW_NONE = 0x20,
+};
+
+}
+
+namespace o3tl
+{
+ template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x26> {};
+}
+
+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
+{
+private:
+ Reference< XCommandEnvironment > mxCmdEnv;
+ std::unique_ptr<weld::TreeView> mxTreeView;
+ std::unique_ptr<weld::TreeIter> mxScratchIter;
+
+ ::osl::Mutex maMutex;
+ SvtFileView_Impl* mpParent;
+ Timer maResetQuickSearch { "fpicker SvtFileView_Impl maResetQuickSearch" };
+ OUString maQuickSearchText;
+ sal_uInt32 mnSearchIndex;
+ bool mbEnableDelete;
+ bool mbEditing;
+ bool const mbShowType;
+
+ void DeleteEntries();
+ void DoQuickSearch( sal_Unicode rChar );
+ bool Kill( const OUString& rURL );
+
+public:
+ ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel, SvtFileView_Impl* pParent, FileViewFlags nFlags);
+
+ std::unique_ptr<weld::TreeIter> make_iterator() const { return mxTreeView->make_iterator(); }
+ void insert(const OUString &rEntry, const OUString& rId, const OUString& rImage, weld::TreeIter& rIter)
+ {
+ mxTreeView->insert(nullptr, -1, &rEntry, &rId, nullptr, nullptr, false, &rIter);
+ mxTreeView->set_image(rIter, rImage);
+ }
+ void append(const OUString& rId, const OUString& rStr, const OUString& rType, const OUString& rSize, const OUString& rDate, const OUString& rImage)
+ {
+ mxTreeView->insert(nullptr, -1, &rStr, &rId, nullptr, nullptr, false, mxScratchIter.get());
+ mxTreeView->set_image(*mxScratchIter, rImage);
+ int nCol = 1;
+ if (mbShowType)
+ mxTreeView->set_text(*mxScratchIter, rType, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rSize, nCol++);
+ mxTreeView->set_text(*mxScratchIter, rDate, nCol++);
+ }
+
+ void scroll_to_row(const weld::TreeIter& rIter) { mxTreeView->scroll_to_row(rIter); }
+ void set_cursor(int nPos) { mxTreeView->set_cursor(nPos); }
+ void set_cursor(const weld::TreeIter& rIter) { mxTreeView->set_cursor(rIter); }
+ bool get_cursor(weld::TreeIter* pIter) const { return mxTreeView->get_cursor(pIter); }
+ bool get_iter_first(weld::TreeIter& rIter) const { return mxTreeView->get_iter_first(rIter); }
+ bool get_selected(weld::TreeIter* pIter) const { return mxTreeView->get_selected(pIter); }
+
+ OUString get_selected_text() const
+ {
+ // tdf#131898 only care about column 0
+ int nIndex = mxTreeView->get_selected_index();
+ return nIndex != -1 ? mxTreeView->get_text(nIndex, 0) : OUString();
+ }
+
+ void unselect_all() { mxTreeView->unselect_all(); }
+
+ OUString get_id(const weld::TreeIter& rIter) { return mxTreeView->get_id(rIter); }
+
+ void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxTreeView->connect_row_activated(rLink); }
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { mxTreeView->connect_changed(rLink); }
+
+ int n_children() const { return mxTreeView->n_children(); }
+
+ void freeze() { mxTreeView->freeze(); }
+ void thaw() { mxTreeView->thaw(); }
+
+ void show() { mxTreeView->show(); }
+ void hide() { mxTreeView->hide(); }
+ bool get_visible() const { return mxTreeView->get_visible(); }
+
+ int count_selected_rows() const { return mxTreeView->count_selected_rows(); }
+
+ void grab_focus() { mxTreeView->grab_focus(); }
+ bool has_focus() const { return mxTreeView->has_focus(); }
+
+ void set_help_id(const OString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
+ OString get_help_id() const { return mxTreeView->get_help_id(); }
+
+ bool IsEditingActive() const { return mbEditing; }
+
+ void end_editing()
+ {
+ mxTreeView->end_editing();
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+ mbEditing = false;
+ }
+
+ void selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+ {
+ mxTreeView->selected_foreach(func);
+ }
+
+ weld::TreeView* getWidget() const
+ {
+ return mxTreeView.get();
+ }
+
+ void clear() { mxTreeView->clear(); }
+
+ void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
+ bool TypeColumnVisible() const { return mbShowType; }
+
+ const Reference< XCommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; }
+
+ DECL_LINK(ResetQuickSearch_Impl, Timer *, void);
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+ DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
+ typedef std::pair<const weld::TreeIter&, OUString> IterString;
+ DECL_LINK(EditedEntryHdl, const IterString&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ void ExecuteContextMenuAction(std::string_view rSelectedPopentry);
+};
+
+}
+
+//= SvtFileView_Impl
+class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
+{
+protected:
+ SvtFileView* m_pAntiImpl;
+ Link<SvtFileView*,void> m_aSelectHandler;
+
+ ::rtl::Reference< ::svt::FileViewContentEnumerator >
+ m_xContentEnumerator;
+ Link<void*,void> 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<SortingData_Impl>> maContent;
+ ::std::vector<std::unique_ptr<SvtContentEntry>> maEntries;
+ ::osl::Mutex maMutex;
+
+ weld::Window* m_pTopLevel;
+ std::unique_ptr<ViewTabListBox_Impl> mxView;
+ std::unique_ptr<weld::IconView> mxIconView;
+ sal_uInt16 mnSortColumn;
+ bool mbAscending : 1;
+ bool const mbOnlyFolder : 1;
+ sal_Int16 mnSuspendSelectCallback : 1;
+ bool mbIsFirstResort : 1;
+
+ IntlWrapper const aIntlWrapper;
+
+ OUString maViewURL;
+ OUString maCurrentFilter;
+ OUString maFolderImage;
+ Link<SvtFileView*,void> maOpenDoneLink;
+ Link<SvtFileView*,bool> maDoubleClickHandler;
+
+ Reference< XCommandEnvironment > mxCmdEnv;
+
+ SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags,
+ bool bOnlyFolder);
+
+ virtual ~SvtFileView_Impl();
+
+ void Clear();
+
+ FileViewResult GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+
+ FileViewResult GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList );
+ void FilterFolderContent_Impl( const OUString &rFilter );
+ void CancelRunningAsyncAction();
+
+ void OpenFolder_Impl();
+ static OUString ReplaceTabWithString(const OUString& rValue);
+ void CreateDisplayText_Impl();
+ void SortFolderContent_Impl();
+
+ void EntryRemoved( std::u16string_view rURL );
+ void EntryRenamed( OUString& rURL,
+ const OUString& rName );
+ const SortingData_Impl& FolderInserted( const OUString& rURL,
+ const OUString& rTitle );
+
+ int GetEntryPos( std::u16string_view rURL );
+
+ void SetViewMode( FileViewMode eMode );
+
+ inline void EnableDelete( bool bEnable );
+
+ void Resort_Impl( sal_Int16 nColumn, bool bAscending );
+ bool SearchNextEntry( sal_uInt32 &nIndex,
+ std::u16string_view rTitle,
+ bool bWrapAround );
+
+ void SetSelectHandler( const Link<SvtFileView*,void>& rHdl );
+ void SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl);
+
+ void ResetCursor();
+
+ void EndEditing()
+ {
+ if (mxView->IsEditingActive())
+ mxView->end_editing();
+ }
+
+ void onTimeout();
+
+ void grab_focus()
+ {
+ if (mxView->get_visible())
+ mxView->grab_focus();
+ else
+ mxIconView->grab_focus();
+ }
+
+ bool has_focus() const
+ {
+ return mxView->has_focus() || mxIconView->has_focus();
+ }
+
+ int GetSortColumn() const
+ {
+ sal_uInt16 nOldSortID = mnSortColumn;
+ // skip "TYPE"
+ if (!mxView->TypeColumnVisible() && nOldSortID != COLUMN_TITLE)
+ --nOldSortID;
+ return nOldSortID - 1;
+ }
+
+protected:
+ DECL_LINK(ChangedHdl, weld::TreeView&, void);
+ DECL_LINK(SelectionChangedHdl, weld::IconView&, void);
+ DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
+ DECL_LINK(ItemActivatedHdl, weld::IconView&, bool);
+
+ // IEnumerationResultHandler overridables
+ virtual void enumerationDone( ::svt::EnumerationResult eResult ) override;
+ void implEnumerationSuccess();
+};
+
+inline void SvtFileView_Impl::EnableDelete( bool bEnable )
+{
+ mxView->EnableDelete( bEnable );
+}
+
+namespace
+{
+ // functions -------------------------------------------------------------
+
+ OUString CreateExactSizeText( sal_Int64 nSize )
+ {
+ double fSize( static_cast<double>(nSize) );
+ int nDec;
+
+ tools::Long nMega = 1024 * 1024;
+ tools::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(std::unique_ptr<weld::TreeView> xTreeView,
+ weld::Window* pTopLevel,
+ SvtFileView_Impl* pParent,
+ FileViewFlags nFlags)
+ : mxTreeView(std::move(xTreeView))
+ , mxScratchIter(mxTreeView->make_iterator())
+ , mpParent( pParent )
+ , mnSearchIndex( 0 )
+ , mbEnableDelete( false )
+ , mbEditing( false )
+ , mbShowType(nFlags & FileViewFlags::SHOW_TYPE)
+{
+ std::vector<int> aWidths { 180 };
+ if (nFlags & FileViewFlags::SHOW_TYPE)
+ aWidths.push_back(140);
+ aWidths.push_back(80);
+ mxTreeView->set_column_fixed_widths(aWidths);
+
+ if (nFlags & FileViewFlags::MULTISELECTION)
+ mxTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
+ maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+
+ mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mxTreeView->connect_popup_menu(LINK(this, ViewTabListBox_Impl, CommandHdl));
+ mxTreeView->connect_key_press(LINK(this, ViewTabListBox_Impl, KeyInputHdl));
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, EditingEntryHdl, const weld::TreeIter&, bool)
+{
+ return mbEditing;
+}
+
+IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maQuickSearchText.clear();
+ mnSearchIndex = 0;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ if (mbEditing)
+ return false;
+
+ bool bHandled = false;
+
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( 0 == rKeyCode.GetModifier() )
+ {
+ 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 );
+ return bHandled;
+}
+
+IMPL_LINK(ViewTabListBox_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ bool bEnableDelete = mbEnableDelete;
+ bool bEnableRename = true;
+
+ int nCount = 0;
+ mxTreeView->selected_foreach([this, &nCount, &bEnableDelete, &bEnableRename](weld::TreeIter& rEntry){
+ ++nCount;
+
+ ::ucbhelper::Content aCnt;
+ try
+ {
+ OUString aURL(weld::fromId<SvtContentEntry*>(
+ mxTreeView->get_id(rEntry))->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;
+ }
+ }
+
+ bool bStop = !bEnableDelete && !bEnableRename;
+ return bStop;
+ });
+
+ if (nCount == 0)
+ bEnableDelete = false;
+ if (nCount != 1)
+ bEnableRename = false;
+
+ if (bEnableDelete || bEnableRename)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "svt/ui/fileviewmenu.ui"));
+ auto xContextMenu = xBuilder->weld_menu("menu");
+ xContextMenu->set_visible("delete", bEnableDelete);
+ xContextMenu->set_visible("rename", bEnableRename);
+ OString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ ExecuteContextMenuAction(sCommand);
+ }
+
+ return true;
+}
+
+void ViewTabListBox_Impl::ExecuteContextMenuAction(std::string_view rSelectedPopupEntry)
+{
+ if (rSelectedPopupEntry == "delete")
+ DeleteEntries();
+ else if (rSelectedPopupEntry == "rename")
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator();
+ if (mxTreeView->get_selected(xEntry.get()))
+ {
+ mbEditing = true;
+
+ mxTreeView->connect_editing(LINK(this, ViewTabListBox_Impl, EditingEntryHdl),
+ LINK(this, ViewTabListBox_Impl, EditedEntryHdl));
+
+ mxTreeView->start_editing(*xEntry);
+ }
+ }
+}
+
+void ViewTabListBox_Impl::DeleteEntries()
+{
+ short eResult = svtools::QUERYDELETE_YES;
+
+ mxTreeView->selected_foreach([this, &eResult](weld::TreeIter& rCurEntry){
+ OUString aURL;
+ if (!mxTreeView->get_id(rCurEntry).isEmpty())
+ aURL = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rCurEntry))->maURL;
+ if (aURL.isEmpty())
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false;
+ }
+
+ 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)
+ {
+ mxTreeView->unselect(rCurEntry);
+ return false; // process next entry
+ }
+
+ if ( eResult != svtools::QUERYDELETE_ALL )
+ {
+ INetURLObject aObj( aURL );
+ svtools::QueryDeleteDlg_Impl aDlg(
+ mxTreeView.get(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+
+ if (mxTreeView->count_selected_rows() > 1)
+ aDlg.EnableAllButton();
+
+ eResult = aDlg.run();
+ }
+
+ bool bDeleted = false;
+
+ if (eResult == svtools::QUERYDELETE_ALL || eResult == svtools::QUERYDELETE_YES)
+ {
+ if ( Kill( aURL ) )
+ {
+ mpParent->EntryRemoved( aURL );
+ bDeleted = true;
+ }
+ }
+
+ if (!bDeleted)
+ mxTreeView->unselect(rCurEntry);
+
+ return false;
+ });
+
+ mxTreeView->remove_selection();
+}
+
+IMPL_LINK(ViewTabListBox_Impl, EditedEntryHdl, const IterString&, rIterString, bool)
+{
+ mbEditing = false;
+
+ mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
+
+ const weld::TreeIter& rEntry = rIterString.first;
+ OUString sNewText = rIterString.second;
+
+ if (sNewText.isEmpty())
+ return false;
+
+ bool bRet = false;
+
+ OUString aURL;
+ SvtContentEntry* pData = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rEntry));
+
+ 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 <<= sNewText;
+ aContent.setPropertyValue( aPropName, aValue );
+ mpParent->EntryRenamed(aURL, sNewText);
+
+ if (pData)
+ pData->maURL = aURL;
+
+ mxTreeView->set_id(rEntry, weld::toId(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 == OUStringChar(rChar) ) )
+ {
+ mnSearchIndex = aLastPos + 1;
+ maQuickSearchText = aLastText;
+ bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
+ }
+
+ if (bFound)
+ {
+ mxTreeView->unselect_all();
+ mxTreeView->select(mnSearchIndex);
+ mxTreeView->set_cursor(mnSearchIndex);
+ mxTreeView->scroll_to_row(mnSearchIndex);
+ }
+
+ maResetQuickSearch.Start();
+}
+
+bool ViewTabListBox_Impl::Kill( const OUString& rContent )
+{
+ bool bRet = true;
+
+ try
+ {
+ ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", Any( 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(weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ bool bOnlyFolder, bool bMultiSelection, bool bShowType )
+{
+ FileViewFlags nFlags = FileViewFlags::NONE;
+ if ( bMultiSelection )
+ nFlags |= FileViewFlags::MULTISELECTION;
+ if ( bShowType )
+ nFlags |= FileViewFlags::SHOW_TYPE;
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference< XInteractionHandler > xInteractionHandler(
+ InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
+ Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
+
+ mpImpl.reset(new SvtFileView_Impl(this, pTopLevel, std::move(xTreeView), std::move(xIconView), xCmdEnv, nFlags, bOnlyFolder));
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->connect_column_clicked(LINK(this, SvtFileView, HeaderSelect_Impl));
+}
+
+void SvtFileView::grab_focus()
+{
+ mpImpl->grab_focus();
+}
+
+bool SvtFileView::has_focus() const
+{
+ return mpImpl->has_focus();
+}
+
+SvtFileView::~SvtFileView()
+{
+}
+
+void SvtFileView::SetViewMode( FileViewMode eMode )
+{
+ mpImpl->SetViewMode( eMode );
+}
+
+OUString SvtFileView::GetURL(const weld::TreeIter& rEntry) const
+{
+ SvtContentEntry* pEntry;
+ if (mpImpl->mxView->get_visible())
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(rEntry));
+ else
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(rEntry));
+ if (pEntry)
+ return pEntry->maURL;
+ return OUString();
+}
+
+OUString SvtFileView::GetCurrentURL() const
+{
+ SvtContentEntry* pEntry = nullptr;
+ OUString aURL;
+ if (mpImpl->mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ }
+ if (pEntry)
+ aURL = pEntry->maURL;
+ return aURL;
+}
+
+void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
+{
+ const SortingData_Impl& rEntry = mpImpl->FolderInserted( rUrl, rNewFolder );
+
+ mpImpl->maEntries.emplace_back(std::make_unique<SvtContentEntry>(rUrl, true));
+ OUString sId(weld::toId(mpImpl->maEntries.back().get()));
+
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ mpImpl->mxView->insert(rEntry.maDisplayName, sId, mpImpl->maFolderImage, *xEntry);
+ mpImpl->mxView->scroll_to_row(*xEntry);
+
+ std::unique_ptr<weld::TreeIter> xIconEntry = mpImpl->mxIconView->make_iterator();
+ mpImpl->mxIconView->insert(-1, &rEntry.maDisplayName, &sId, &mpImpl->maFolderImage, xIconEntry.get());
+ mpImpl->mxIconView->scroll_to_item(*xIconEntry);
+}
+
+FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
+{
+ FileViewResult eResult = eFailure;
+
+ OUString sParentURL;
+ if ( GetParentURL( sParentURL ) )
+ eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, maDenyList );
+
+ 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;
+}
+
+OString SvtFileView::get_help_id() const
+{
+ return mpImpl->mxView->get_help_id();
+}
+
+void SvtFileView::set_help_id(const OString& rHelpId)
+{
+ mpImpl->mxView->set_help_id(rHelpId);
+}
+
+OUString SvtFileView::get_selected_text() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->get_selected_text();
+ return mpImpl->mxIconView->get_selected_text();
+}
+
+FileViewResult SvtFileView::Initialize(
+ const OUString& rURL,
+ const OUString& rFilter,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ weld::WaitObject aWaitCursor(mpImpl->m_pTopLevel);
+ maDenyList = rDenyList;
+
+ 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, maDenyList);
+ OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
+ return eResult;
+}
+
+void SvtFileView::CancelRunningAsyncAction()
+{
+ mpImpl->CancelRunningAsyncAction();
+}
+
+void SvtFileView::SetNoSelection()
+{
+ mpImpl->mxView->unselect_all();
+ mpImpl->mxIconView->unselect_all();
+}
+
+void SvtFileView::SetSelectHdl(const Link<SvtFileView*,void>& rHdl)
+{
+ mpImpl->SetSelectHandler(rHdl);
+}
+
+void SvtFileView::SetDoubleClickHdl(const Link<SvtFileView*,bool>& rHdl)
+{
+ mpImpl->SetDoubleClickHandler(rHdl);
+}
+
+sal_uInt32 SvtFileView::GetSelectionCount() const
+{
+ if (mpImpl->mxView->get_visible())
+ return mpImpl->mxView->count_selected_rows();
+ return mpImpl->mxIconView->count_selected_items();
+}
+
+SvtContentEntry* SvtFileView::FirstSelected() const
+{
+ if (mpImpl->mxView->get_visible())
+ {
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
+ if (mpImpl->mxView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
+ return pRet;
+ }
+
+ SvtContentEntry* pRet = nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
+ if (mpImpl->mxIconView->get_selected(xEntry.get()))
+ pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
+ return pRet;
+}
+
+const OUString& SvtFileView::GetViewURL() const
+{
+ return mpImpl->maViewURL;
+}
+
+void SvtFileView::SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl )
+{
+ mpImpl->maOpenDoneLink = rHdl;
+}
+
+void SvtFileView::EnableDelete( bool bEnable )
+{
+ mpImpl->EnableDelete( bEnable );
+}
+
+void SvtFileView::EndInplaceEditing()
+{
+ return mpImpl->EndEditing();
+}
+
+IMPL_LINK(SvtFileView, HeaderSelect_Impl, int, nColumn, void)
+{
+ sal_uInt16 nItemID = nColumn + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemID != COLUMN_TITLE)
+ ++nItemID;
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ bool bSortAtoZ = mpImpl->mbAscending;
+
+ //set new arrow positions in headerbar
+ if (nItemID != mpImpl->mnSortColumn)
+ {
+ // remove old indicator, new will be created in OpenFolder_Impl
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ }
+ else
+ bSortAtoZ = !bSortAtoZ;
+
+ mpImpl->Resort_Impl(nItemID, bSortAtoZ);
+}
+
+OUString SvtFileView::GetConfigString() const
+{
+ // sort order
+ OUString sRet = OUString::number( mpImpl->mnSortColumn ) + ";";
+
+ bool bUp = mpImpl->mbAscending;
+ sRet += OUString::Concat(bUp ? std::u16string_view(u"1") : std::u16string_view(u"0")) + ";";
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ sal_uInt16 nCount = mpImpl->mxView->TypeColumnVisible() ? 4 : 3;
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nId = i + 1;
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nId != COLUMN_TITLE)
+ ++nId;
+
+ sRet += OUString::number( nId )
+ + ";"
+ + OUString::number(pView->get_column_width(i))
+ + ";";
+ }
+
+ return comphelper::string::stripEnd(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(std::u16string_view rCfgStr)
+{
+ sal_Int32 nIdx = 0;
+ sal_uInt16 nSortColumn = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+ bool bAscending = static_cast<bool>(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ))));
+
+ std::vector<int> aWidths(mpImpl->mxView->TypeColumnVisible() ? 4 : 3, -1);
+
+ while ( nIdx != -1 )
+ {
+ sal_uInt16 nItemId = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
+
+ int nWidth = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
+
+ // skip "TYPE"
+ if (!mpImpl->mxView->TypeColumnVisible() && nItemId != COLUMN_TITLE)
+ --nItemId;
+ int nColumn = nItemId - 1;
+
+ if (nColumn >= 0 && o3tl::make_unsigned(nColumn) < aWidths.size())
+ aWidths[nColumn] = nWidth;
+ }
+
+ weld::TreeView* pView = mpImpl->mxView->getWidget();
+ pView->set_column_fixed_widths(aWidths);
+ if (mpImpl->mnSortColumn != nSortColumn)
+ pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
+ mpImpl->Resort_Impl(nSortColumn, bAscending);
+}
+
+SvtFileView_Impl::SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
+ std::unique_ptr<weld::TreeView> xTreeView,
+ std::unique_ptr<weld::IconView> xIconView,
+ Reference < XCommandEnvironment > const & xEnv,
+ FileViewFlags nFlags, bool bOnlyFolder)
+ : m_pAntiImpl ( pAntiImpl )
+ , m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR )
+ , m_bRunningAsyncAction ( false )
+ , m_bAsyncActionCancelled ( false )
+ , m_pTopLevel ( pTopLevel )
+ , mxView(new ViewTabListBox_Impl(std::move(xTreeView), pTopLevel, this, nFlags))
+ , mxIconView(std::move(xIconView))
+ , mnSortColumn ( COLUMN_TITLE )
+ , mbAscending ( true )
+ , mbOnlyFolder ( bOnlyFolder )
+ , mnSuspendSelectCallback ( 0 )
+ , mbIsFirstResort ( true )
+ , aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
+ , maFolderImage (RID_BMP_FOLDER)
+ , mxCmdEnv ( xEnv )
+{
+ weld::TreeView* pWidget = mxView->getWidget();
+
+ // set the width to something small so it's the parent that decides the final
+ // width
+ Size aSize(42, pWidget->get_height_rows(7));
+ pWidget->set_size_request(aSize.Width(), aSize.Height());
+ mxIconView->set_size_request(aSize.Width(), aSize.Height());
+}
+
+SvtFileView_Impl::~SvtFileView_Impl()
+{
+ Clear();
+}
+
+void SvtFileView_Impl::Clear()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.clear();
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ std::u16string_view rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ ::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, rDenyList );
+}
+
+FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
+ const FolderDescriptor& _rFolder,
+ const FileViewAsyncAction* pAsyncDescriptor,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ 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(
+ mxView->GetCommandEnvironment(), maContent, maMutex));
+ // TODO: should we cache and re-use this thread?
+
+ if ( !pAsyncDescriptor )
+ {
+ ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rDenyList );
+ 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<void*,void>();
+
+ // minimum time to wait
+ TimeValue aTimeout;
+ sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
+ OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
+ if ( nMinTimeout <= 0 )
+ nMinTimeout = sal_Int32( 1000 );
+ aTimeout.Seconds = nMinTimeout / 1000;
+ aTimeout.Nanosec = ( nMinTimeout % 1000 ) * 1000000;
+
+ 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( &aTimeout );
+ }
+
+ ::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?" );
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+ 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<SortingData_Impl>& 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_NOARG(SvtFileView_Impl, ChangedHdl, weld::TreeView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, SelectionChangedHdl, weld::IconView&, void)
+{
+ if (!mnSuspendSelectCallback)
+ m_aSelectHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetSelectHandler(const Link<SvtFileView*,void>& rHdl)
+{
+ m_aSelectHandler = rHdl;
+
+ mxView->connect_changed(LINK(this, SvtFileView_Impl, ChangedHdl));
+ mxIconView->connect_selection_changed(LINK(this, SvtFileView_Impl, SelectionChangedHdl));
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, RowActivatedHdl, weld::TreeView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+IMPL_LINK_NOARG(SvtFileView_Impl, ItemActivatedHdl, weld::IconView&, bool)
+{
+ return maDoubleClickHandler.Call(m_pAntiImpl);
+}
+
+void SvtFileView_Impl::SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl)
+{
+ maDoubleClickHandler = rHdl;
+
+ mxView->connect_row_activated(LINK(this, SvtFileView_Impl, RowActivatedHdl));
+ mxIconView->connect_item_activated(LINK(this, SvtFileView_Impl, ItemActivatedHdl));
+}
+
+void SvtFileView_Impl::OpenFolder_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ mxView->freeze();
+ mxIconView->freeze();
+ maEntries.clear();
+ mxView->clear();
+ mxIconView->clear();
+
+ for (auto const& elem : maContent)
+ {
+ if (mbOnlyFolder && !elem->mbIsFolder)
+ continue;
+
+ // insert entry and set user data
+ maEntries.emplace_back(std::make_unique<SvtContentEntry>(elem->maTargetURL, elem->mbIsFolder));
+ OUString sId(weld::toId(maEntries.back().get()));
+ mxView->append(sId, elem->maDisplayName, elem->maType, elem->maDisplaySize, elem->maDisplayDate, elem->maImage);
+ mxIconView->append(sId, elem->maDisplayName, elem->maImage);
+ }
+
+ ++mnSuspendSelectCallback;
+ mxView->thaw();
+
+ //set sort indicator
+ weld::TreeView* pView = mxView->getWidget();
+ pView->set_sort_indicator(mbAscending ? TRISTATE_TRUE : TRISTATE_FALSE, GetSortColumn());
+
+ mxIconView->thaw();
+ --mnSuspendSelectCallback;
+
+ ResetCursor();
+}
+
+void SvtFileView_Impl::ResetCursor()
+{
+ if (mxView->get_visible())
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxView->make_iterator();
+ if (mxView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxView->unselect_all();
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xFirst = mxIconView->make_iterator();
+ if (mxIconView->get_iter_first(*xFirst))
+ {
+ // set cursor to the first entry
+ mxIconView->set_cursor(*xFirst);
+ }
+ // deselect
+ mxIconView->unselect_all();
+ }
+}
+
+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*,void>();
+ }
+}
+
+
+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*,void>();
+ }
+}
+
+
+void SvtFileView_Impl::implEnumerationSuccess()
+{
+ FilterFolderContent_Impl( maCurrentFilter );
+ SortFolderContent_Impl();
+ CreateDisplayText_Impl();
+ OpenFolder_Impl();
+ maOpenDoneLink.Call( m_pAntiImpl );
+}
+
+OUString SvtFileView_Impl::ReplaceTabWithString(const OUString& rValue)
+{
+ OUString const aTab( "\t" );
+ OUString const aTabString( "%09" );
+
+ sal_Int32 iPos;
+ OUString aValue(rValue);
+ while ( ( iPos = aValue.indexOf( aTab ) ) >= 0 )
+ aValue = aValue.replaceAt( iPos, 1, aTabString );
+ return aValue;
+}
+
+void SvtFileView_Impl::CreateDisplayText_Impl()
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ OUString const aDateSep( ", " );
+
+ for (auto const& elem : maContent)
+ {
+ // title, type, size, date
+ elem->maDisplayName = ReplaceTabWithString(elem->GetTitle());
+ // folders don't have a size
+ if ( ! elem->mbIsFolder )
+ elem->maDisplaySize = CreateExactSizeText( elem->maSize );
+ // set the date, but volumes have no date
+ if ( ! elem->mbIsFolder || ! elem->mbIsVolume )
+ {
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ elem->maDisplayDate = rLocaleData.getDate( elem->maModDate )
+ + aDateSep
+ + rLocaleData.getTime( elem->maModDate, false );
+ }
+
+ // detect image
+ if ( elem->mbIsFolder )
+ {
+ ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote,
+ elem->mbIsRemoveable, elem->mbIsFloppy,
+ elem->mbIsCompactDisc );
+ elem->maImage = SvFileInformationManager::GetFolderImageId(aVolInfo);
+ }
+ else
+ elem->maImage = SvFileInformationManager::GetFileImageId(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
+ mxView->ResetQuickSearch_Impl( nullptr );
+
+ std::unique_ptr<weld::TreeIter> xEntry(mxView->make_iterator());
+ bool bEntry = mxView->get_cursor(xEntry.get());
+
+ OUString aEntryURL;
+ if (bEntry && !mxView->get_id(*xEntry).isEmpty())
+ aEntryURL = weld::fromId<SvtContentEntry*>(mxView->get_id(*xEntry))->maURL;
+
+ mnSortColumn = nColumn;
+ mbAscending = bAscending;
+
+ SortFolderContent_Impl();
+ OpenFolder_Impl();
+
+ if ( !mbIsFirstResort )
+ {
+ int nPos = GetEntryPos( aEntryURL );
+ if (nPos != -1 && nPos < mxView->n_children())
+ {
+ ++mnSuspendSelectCallback; // #i15668#
+ mxView->set_cursor(nPos);
+ --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<SortingData_Impl> const & aOne, std::unique_ptr<SortingData_Impl> 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( std::u16string_view rURL )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ maContent.erase(std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & 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<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ if (aFoundElem != maContent.end())
+ {
+ (*aFoundElem)->SetNewTitle( rTitle );
+ (*aFoundElem)->maDisplayName = ReplaceTabWithString(rTitle);
+
+ INetURLObject aURLObj( rURL );
+ aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All );
+
+ rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ (*aFoundElem)->maTargetURL = rURL;
+ }
+}
+
+const SortingData_Impl& SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ std::unique_ptr<SortingData_Impl> 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::GetFolderImageId( aVolInfo );
+
+ // title, type, size, date
+ pData->maDisplayName = ReplaceTabWithString(pData->GetTitle());
+ // set the date
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ pData->maDisplayDate = rLocaleData.getDate( pData->maModDate )
+ + ", "
+ + rLocaleData.getTime( pData->maModDate );
+
+ maContent.push_back( std::move(pData) );
+
+ return *maContent.back();
+}
+
+int SvtFileView_Impl::GetEntryPos(std::u16string_view rURL)
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
+ [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
+ return aFoundElem != maContent.end() ? std::distance(maContent.begin(), aFoundElem) : -1;
+}
+
+void SvtFileView_Impl::SetViewMode( FileViewMode eMode )
+{
+ switch ( eMode )
+ {
+ case eDetailedList:
+ mxView->show();
+ mxIconView->hide();
+ break;
+
+ case eIcon:
+ mxView->hide();
+ mxIconView->show();
+ break;
+
+ default:
+ mxView->show();
+ mxIconView->hide();
+ };
+}
+
+bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, std::u16string_view 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();
+ }
+}
+
+void SvtFileView::selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
+{
+ if (mpImpl->mxView->get_visible())
+ mpImpl->mxView->selected_foreach(func);
+ else
+ mpImpl->mxIconView->selected_foreach(func);
+}
+
+weld::Widget* SvtFileView::identifier() const
+{
+ return mpImpl->mxView->getWidget();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */