/* -*- 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::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:
rtl::Reference< ::ucbhelper::CommandEnvironment > 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 OUString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
OUString 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 rtl::Reference< ::ucbhelper::CommandEnvironment >& 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::u16string_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( std::u16string_view 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]) +
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 ) );
const 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( u"delete"_ustr );
else
bEnableDelete = false;
}
catch( Exception const & )
{
bEnableDelete = false;
}
}
if ( bEnableRename )
{
try
{
Reference< XPropertySetInfo > aProps = aCnt.getProperties();
if ( aProps.is() )
{
Property aProp = aProps->getPropertyByName(u"Title"_ustr);
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(), u"svt/ui/fileviewmenu.ui"_ustr));
auto xContextMenu = xBuilder->weld_menu(u"menu"_ustr);
xContextMenu->set_visible(u"delete"_ustr, bEnableDelete);
xContextMenu->set_visible(u"rename"_ustr, bEnableRename);
OUString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
ExecuteContextMenuAction(sCommand);
}
return true;
}
void ViewTabListBox_Impl::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry)
{
if (rSelectedPopupEntry == u"delete")
DeleteEntries();
else if (rSelectedPopupEntry == u"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( u"delete"_ustr );
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( u"Title"_ustr );
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( u"delete"_ustr, 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;
const 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;
}
OUString SvtFileView::get_help_id() const
{
return mpImpl->mxView->get_help_id();
}
void SvtFileView::set_help_id(const OUString& 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 )
{
int nItemId = 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( std::u16string_view rFilter )
{
if ( rFilter.empty() || ( 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
std::erase_if(maContent,
[&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));
});
}
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)
{
return rValue.replaceAll(u"\t", u"%09");
}
void SvtFileView_Impl::CreateDisplayText_Impl()
{
::osl::MutexGuard aGuard( maMutex );
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 )
+ ", "
+ 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 )
{
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: */
↑ V657 It's odd that this function always returns one and the same value.