Bump version to 24.04.3.4
[LibreOffice.git] / fpicker / source / office / fileview.cxx
blob1fa1c4dafcd3154fafb2b4f895c3489d49b1e84a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
23 #include <svtools/svtresid.hxx>
24 #include <svtools/imagemgr.hxx>
25 #include <svtools/querydelete.hxx>
26 #include <svtools/strings.hrc>
27 #include <bitmaps.hlst>
28 #include "contentenumeration.hxx"
29 #include <com/sun/star/task/InteractionHandler.hpp>
30 #include <com/sun/star/ucb/XProgressHandler.hpp>
31 #include <com/sun/star/ucb/XContent.hpp>
32 #include <com/sun/star/container/XChild.hpp>
33 #include <com/sun/star/ucb/CommandAbortedException.hpp>
34 #include <com/sun/star/ucb/XCommandInfo.hpp>
35 #include <com/sun/star/beans/XPropertySetInfo.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
38 #include <algorithm>
39 #include <string_view>
40 #include <vector>
41 #include <tools/debug.hxx>
42 #include <tools/urlobj.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/string.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <ucbhelper/commandenvironment.hxx>
47 #include <rtl/math.hxx>
48 #include <o3tl/safeint.hxx>
49 #include <o3tl/typed_flags_set.hxx>
50 #include <o3tl/string_view.hxx>
51 #include <osl/mutex.hxx>
52 #include <osl/conditn.hxx>
53 #include <salhelper/timer.hxx>
54 #include <svtools/urlfilter.hxx>
55 #include <unotools/collatorwrapper.hxx>
56 #include <unotools/localedatawrapper.hxx>
57 #include <unotools/intlwrapper.hxx>
58 #include <unotools/syslocale.hxx>
59 #include <vcl/svapp.hxx>
60 #include <vcl/commandevent.hxx>
61 #include <vcl/event.hxx>
62 #include <vcl/settings.hxx>
63 #include <vcl/timer.hxx>
64 #include <memory>
65 #include "fileview.hxx"
67 using namespace ::com::sun::star::lang;
68 using namespace ::com::sun::star::sdbc;
69 using namespace ::com::sun::star::task;
70 using namespace ::com::sun::star::ucb;
71 using namespace ::com::sun::star::uno;
72 using namespace ::com::sun::star::io;
73 using namespace ::com::sun::star::beans;
74 using namespace ::comphelper;
75 using ::svt::SortingData_Impl;
76 using ::svt::FolderDescriptor;
78 constexpr OUStringLiteral ALL_FILES_FILTER = u"*.*";
80 #define COLUMN_TITLE 1
81 #define COLUMN_TYPE 2
82 #define COLUMN_SIZE 3
83 #define COLUMN_DATE 4
85 #define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset
87 namespace {
89 enum class FileViewFlags
91 NONE = 0x00,
92 MULTISELECTION = 0x02,
93 SHOW_TYPE = 0x04,
94 SHOW_NONE = 0x20,
99 namespace o3tl
101 template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x26> {};
104 namespace
107 //= CallbackTimer
109 class CallbackTimer : public ::salhelper::Timer
111 protected:
112 SvtFileView_Impl* const m_pTimeoutHandler;
114 public:
115 explicit CallbackTimer( SvtFileView_Impl* _pHandler ) : m_pTimeoutHandler( _pHandler ) { }
117 protected:
118 virtual void SAL_CALL onShot() override;
121 class ViewTabListBox_Impl
123 private:
124 Reference< XCommandEnvironment > mxCmdEnv;
125 std::unique_ptr<weld::TreeView> mxTreeView;
126 std::unique_ptr<weld::TreeIter> mxScratchIter;
128 ::osl::Mutex maMutex;
129 SvtFileView_Impl* mpParent;
130 Timer maResetQuickSearch { "fpicker SvtFileView_Impl maResetQuickSearch" };
131 OUString maQuickSearchText;
132 sal_uInt32 mnSearchIndex;
133 bool mbEnableDelete;
134 bool mbEditing;
135 bool const mbShowType;
137 void DeleteEntries();
138 void DoQuickSearch( sal_Unicode rChar );
139 bool Kill( const OUString& rURL );
141 public:
142 ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel, SvtFileView_Impl* pParent, FileViewFlags nFlags);
144 std::unique_ptr<weld::TreeIter> make_iterator() const { return mxTreeView->make_iterator(); }
145 void insert(const OUString &rEntry, const OUString& rId, const OUString& rImage, weld::TreeIter& rIter)
147 mxTreeView->insert(nullptr, -1, &rEntry, &rId, nullptr, nullptr, false, &rIter);
148 mxTreeView->set_image(rIter, rImage);
150 void append(const OUString& rId, const OUString& rStr, const OUString& rType, const OUString& rSize, const OUString& rDate, const OUString& rImage)
152 mxTreeView->insert(nullptr, -1, &rStr, &rId, nullptr, nullptr, false, mxScratchIter.get());
153 mxTreeView->set_image(*mxScratchIter, rImage);
154 int nCol = 1;
155 if (mbShowType)
156 mxTreeView->set_text(*mxScratchIter, rType, nCol++);
157 mxTreeView->set_text(*mxScratchIter, rSize, nCol++);
158 mxTreeView->set_text(*mxScratchIter, rDate, nCol++);
161 void scroll_to_row(const weld::TreeIter& rIter) { mxTreeView->scroll_to_row(rIter); }
162 void set_cursor(int nPos) { mxTreeView->set_cursor(nPos); }
163 void set_cursor(const weld::TreeIter& rIter) { mxTreeView->set_cursor(rIter); }
164 bool get_cursor(weld::TreeIter* pIter) const { return mxTreeView->get_cursor(pIter); }
165 bool get_iter_first(weld::TreeIter& rIter) const { return mxTreeView->get_iter_first(rIter); }
166 bool get_selected(weld::TreeIter* pIter) const { return mxTreeView->get_selected(pIter); }
168 OUString get_selected_text() const
170 // tdf#131898 only care about column 0
171 int nIndex = mxTreeView->get_selected_index();
172 return nIndex != -1 ? mxTreeView->get_text(nIndex, 0) : OUString();
175 void unselect_all() { mxTreeView->unselect_all(); }
177 OUString get_id(const weld::TreeIter& rIter) { return mxTreeView->get_id(rIter); }
179 void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxTreeView->connect_row_activated(rLink); }
180 void connect_changed(const Link<weld::TreeView&, void>& rLink) { mxTreeView->connect_changed(rLink); }
182 int n_children() const { return mxTreeView->n_children(); }
184 void freeze() { mxTreeView->freeze(); }
185 void thaw() { mxTreeView->thaw(); }
187 void show() { mxTreeView->show(); }
188 void hide() { mxTreeView->hide(); }
189 bool get_visible() const { return mxTreeView->get_visible(); }
191 int count_selected_rows() const { return mxTreeView->count_selected_rows(); }
193 void grab_focus() { mxTreeView->grab_focus(); }
194 bool has_focus() const { return mxTreeView->has_focus(); }
196 void set_help_id(const OUString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
197 OUString get_help_id() const { return mxTreeView->get_help_id(); }
199 bool IsEditingActive() const { return mbEditing; }
201 void end_editing()
203 mxTreeView->end_editing();
204 mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
205 mbEditing = false;
208 void selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
210 mxTreeView->selected_foreach(func);
213 weld::TreeView* getWidget() const
215 return mxTreeView.get();
218 void clear() { mxTreeView->clear(); }
220 void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
221 bool TypeColumnVisible() const { return mbShowType; }
223 const Reference< XCommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; }
225 DECL_LINK(ResetQuickSearch_Impl, Timer *, void);
226 DECL_LINK(CommandHdl, const CommandEvent&, bool);
227 DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
228 typedef std::pair<const weld::TreeIter&, OUString> IterString;
229 DECL_LINK(EditedEntryHdl, const IterString&, bool);
230 DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
232 void ExecuteContextMenuAction(std::u16string_view rSelectedPopentry);
237 //= SvtFileView_Impl
238 class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
240 protected:
241 SvtFileView* m_pAntiImpl;
242 Link<SvtFileView*,void> m_aSelectHandler;
244 ::rtl::Reference< ::svt::FileViewContentEnumerator >
245 m_xContentEnumerator;
246 Link<void*,void> m_aCurrentAsyncActionHandler;
247 ::osl::Condition m_aAsyncActionFinished;
248 ::rtl::Reference< ::salhelper::Timer > m_xCancelAsyncTimer;
249 ::svt::EnumerationResult m_eAsyncActionResult;
250 bool m_bRunningAsyncAction;
251 bool m_bAsyncActionCancelled;
253 public:
255 ::std::vector<std::unique_ptr<SortingData_Impl>> maContent;
256 ::std::vector<std::unique_ptr<SvtContentEntry>> maEntries;
257 ::osl::Mutex maMutex;
259 weld::Window* m_pTopLevel;
260 std::unique_ptr<ViewTabListBox_Impl> mxView;
261 std::unique_ptr<weld::IconView> mxIconView;
262 sal_uInt16 mnSortColumn;
263 bool mbAscending : 1;
264 bool const mbOnlyFolder : 1;
265 sal_Int16 mnSuspendSelectCallback : 1;
266 bool mbIsFirstResort : 1;
268 IntlWrapper const aIntlWrapper;
270 OUString maViewURL;
271 OUString maCurrentFilter;
272 OUString maFolderImage;
273 Link<SvtFileView*,void> maOpenDoneLink;
274 Link<SvtFileView*,bool> maDoubleClickHandler;
276 Reference< XCommandEnvironment > mxCmdEnv;
278 SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
279 std::unique_ptr<weld::TreeView> xTreeView,
280 std::unique_ptr<weld::IconView> xIconView,
281 Reference < XCommandEnvironment > const & xEnv,
282 FileViewFlags nFlags,
283 bool bOnlyFolder);
285 virtual ~SvtFileView_Impl();
287 void Clear();
289 FileViewResult GetFolderContent_Impl(
290 std::u16string_view rFolder,
291 const FileViewAsyncAction* pAsyncDescriptor,
292 const css::uno::Sequence< OUString >& rDenyList );
294 FileViewResult GetFolderContent_Impl(
295 const FolderDescriptor& _rFolder,
296 const FileViewAsyncAction* pAsyncDescriptor,
297 const css::uno::Sequence< OUString >& rDenyList );
298 void FilterFolderContent_Impl( std::u16string_view rFilter );
299 void CancelRunningAsyncAction();
301 void OpenFolder_Impl();
302 static OUString ReplaceTabWithString(const OUString& rValue);
303 void CreateDisplayText_Impl();
304 void SortFolderContent_Impl();
306 void EntryRemoved( std::u16string_view rURL );
307 void EntryRenamed( OUString& rURL,
308 const OUString& rName );
309 const SortingData_Impl& FolderInserted( const OUString& rURL,
310 const OUString& rTitle );
312 int GetEntryPos( std::u16string_view rURL );
314 void SetViewMode( FileViewMode eMode );
316 inline void EnableDelete( bool bEnable );
318 void Resort_Impl( sal_Int16 nColumn, bool bAscending );
319 bool SearchNextEntry( sal_uInt32 &nIndex,
320 std::u16string_view rTitle,
321 bool bWrapAround );
323 void SetSelectHandler( const Link<SvtFileView*,void>& rHdl );
324 void SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl);
326 void ResetCursor();
328 void EndEditing()
330 if (mxView->IsEditingActive())
331 mxView->end_editing();
334 void onTimeout();
336 void grab_focus()
338 if (mxView->get_visible())
339 mxView->grab_focus();
340 else
341 mxIconView->grab_focus();
344 bool has_focus() const
346 return mxView->has_focus() || mxIconView->has_focus();
349 int GetSortColumn() const
351 sal_uInt16 nOldSortID = mnSortColumn;
352 // skip "TYPE"
353 if (!mxView->TypeColumnVisible() && nOldSortID != COLUMN_TITLE)
354 --nOldSortID;
355 return nOldSortID - 1;
358 protected:
359 DECL_LINK(ChangedHdl, weld::TreeView&, void);
360 DECL_LINK(SelectionChangedHdl, weld::IconView&, void);
361 DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
362 DECL_LINK(ItemActivatedHdl, weld::IconView&, bool);
364 // IEnumerationResultHandler overridables
365 virtual void enumerationDone( ::svt::EnumerationResult eResult ) override;
366 void implEnumerationSuccess();
369 inline void SvtFileView_Impl::EnableDelete( bool bEnable )
371 mxView->EnableDelete( bEnable );
374 namespace
376 // functions -------------------------------------------------------------
378 OUString CreateExactSizeText( sal_Int64 nSize )
380 double fSize( static_cast<double>(nSize) );
381 int nDec;
383 tools::Long nMega = 1024 * 1024;
384 tools::Long nGiga = nMega * 1024;
386 OUString aUnitStr(' ');
388 if ( nSize < 10000 )
390 aUnitStr += SvtResId(STR_SVT_BYTES );
391 nDec = 0;
393 else if ( nSize < nMega )
395 fSize /= 1024;
396 aUnitStr += SvtResId(STR_SVT_KB);
397 nDec = 1;
399 else if ( nSize < nGiga )
401 fSize /= nMega;
402 aUnitStr += SvtResId(STR_SVT_MB);
403 nDec = 2;
405 else
407 fSize /= nGiga;
408 aUnitStr += SvtResId(STR_SVT_GB);
409 nDec = 3;
412 OUString aSizeStr =
413 ::rtl::math::doubleToUString( fSize,
414 rtl_math_StringFormat_F, nDec,
415 SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) +
416 aUnitStr;
418 return aSizeStr;
422 ViewTabListBox_Impl::ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView,
423 weld::Window* pTopLevel,
424 SvtFileView_Impl* pParent,
425 FileViewFlags nFlags)
426 : mxTreeView(std::move(xTreeView))
427 , mxScratchIter(mxTreeView->make_iterator())
428 , mpParent( pParent )
429 , mnSearchIndex( 0 )
430 , mbEnableDelete( false )
431 , mbEditing( false )
432 , mbShowType(nFlags & FileViewFlags::SHOW_TYPE)
434 std::vector<int> aWidths { 180 };
435 if (nFlags & FileViewFlags::SHOW_TYPE)
436 aWidths.push_back(140);
437 aWidths.push_back(80);
438 mxTreeView->set_column_fixed_widths(aWidths);
440 if (nFlags & FileViewFlags::MULTISELECTION)
441 mxTreeView->set_selection_mode(SelectionMode::Multiple);
443 maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
444 maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
446 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
447 Reference< XInteractionHandler > xInteractionHandler(
448 InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
450 mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
452 mxTreeView->connect_popup_menu(LINK(this, ViewTabListBox_Impl, CommandHdl));
453 mxTreeView->connect_key_press(LINK(this, ViewTabListBox_Impl, KeyInputHdl));
456 IMPL_LINK_NOARG(ViewTabListBox_Impl, EditingEntryHdl, const weld::TreeIter&, bool)
458 return mbEditing;
461 IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
463 ::osl::MutexGuard aGuard( maMutex );
465 maQuickSearchText.clear();
466 mnSearchIndex = 0;
469 IMPL_LINK(ViewTabListBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
471 if (mbEditing)
472 return false;
474 bool bHandled = false;
476 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
477 if ( 0 == rKeyCode.GetModifier() )
479 if ( ( rKeyCode.GetCode() == KEY_DELETE ) &&
480 mbEnableDelete )
482 ResetQuickSearch_Impl( nullptr );
483 DeleteEntries();
484 bHandled = true;
486 else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) ||
487 ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) )
489 DoQuickSearch( rKEvt.GetCharCode() );
490 bHandled = true;
494 if (!bHandled)
495 ResetQuickSearch_Impl( nullptr );
496 return bHandled;
499 IMPL_LINK(ViewTabListBox_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
501 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
502 return false;
504 bool bEnableDelete = mbEnableDelete;
505 bool bEnableRename = true;
507 int nCount = 0;
508 mxTreeView->selected_foreach([this, &nCount, &bEnableDelete, &bEnableRename](weld::TreeIter& rEntry){
509 ++nCount;
511 ::ucbhelper::Content aCnt;
514 OUString aURL(weld::fromId<SvtContentEntry*>(
515 mxTreeView->get_id(rEntry))->maURL);
516 aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
518 catch( Exception const & )
520 bEnableDelete = bEnableRename = false;
523 if ( bEnableDelete )
527 Reference< XCommandInfo > aCommands = aCnt.getCommands();
528 if ( aCommands.is() )
529 bEnableDelete = aCommands->hasCommandByName( "delete" );
530 else
531 bEnableDelete = false;
533 catch( Exception const & )
535 bEnableDelete = false;
539 if ( bEnableRename )
543 Reference< XPropertySetInfo > aProps = aCnt.getProperties();
544 if ( aProps.is() )
546 Property aProp = aProps->getPropertyByName("Title");
547 bEnableRename
548 = !( aProp.Attributes & PropertyAttribute::READONLY );
550 else
551 bEnableRename = false;
553 catch( Exception const & )
555 bEnableRename = false;
559 bool bStop = !bEnableDelete && !bEnableRename;
560 return bStop;
563 if (nCount == 0)
564 bEnableDelete = false;
565 if (nCount != 1)
566 bEnableRename = false;
568 if (bEnableDelete || bEnableRename)
570 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "svt/ui/fileviewmenu.ui"));
571 auto xContextMenu = xBuilder->weld_menu("menu");
572 xContextMenu->set_visible("delete", bEnableDelete);
573 xContextMenu->set_visible("rename", bEnableRename);
574 OUString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
575 ExecuteContextMenuAction(sCommand);
578 return true;
581 void ViewTabListBox_Impl::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry)
583 if (rSelectedPopupEntry == u"delete")
584 DeleteEntries();
585 else if (rSelectedPopupEntry == u"rename")
587 std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator();
588 if (mxTreeView->get_selected(xEntry.get()))
590 mbEditing = true;
592 mxTreeView->connect_editing(LINK(this, ViewTabListBox_Impl, EditingEntryHdl),
593 LINK(this, ViewTabListBox_Impl, EditedEntryHdl));
595 mxTreeView->start_editing(*xEntry);
600 void ViewTabListBox_Impl::DeleteEntries()
602 short eResult = svtools::QUERYDELETE_YES;
604 mxTreeView->selected_foreach([this, &eResult](weld::TreeIter& rCurEntry){
605 OUString aURL;
606 if (!mxTreeView->get_id(rCurEntry).isEmpty())
607 aURL = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rCurEntry))->maURL;
608 if (aURL.isEmpty())
610 mxTreeView->unselect(rCurEntry);
611 return false;
614 bool canDelete = true;
617 ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
618 Reference< XCommandInfo > aCommands = aCnt.getCommands();
619 if ( aCommands.is() )
620 canDelete = aCommands->hasCommandByName( "delete" );
621 else
622 canDelete = false;
624 catch( Exception const & )
626 canDelete = false;
629 if (!canDelete)
631 mxTreeView->unselect(rCurEntry);
632 return false; // process next entry
635 if ( eResult != svtools::QUERYDELETE_ALL )
637 INetURLObject aObj( aURL );
638 svtools::QueryDeleteDlg_Impl aDlg(
639 mxTreeView.get(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
641 if (mxTreeView->count_selected_rows() > 1)
642 aDlg.EnableAllButton();
644 eResult = aDlg.run();
647 bool bDeleted = false;
649 if (eResult == svtools::QUERYDELETE_ALL || eResult == svtools::QUERYDELETE_YES)
651 if ( Kill( aURL ) )
653 mpParent->EntryRemoved( aURL );
654 bDeleted = true;
658 if (!bDeleted)
659 mxTreeView->unselect(rCurEntry);
661 return false;
664 mxTreeView->remove_selection();
667 IMPL_LINK(ViewTabListBox_Impl, EditedEntryHdl, const IterString&, rIterString, bool)
669 mbEditing = false;
671 mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
673 const weld::TreeIter& rEntry = rIterString.first;
674 OUString sNewText = rIterString.second;
676 if (sNewText.isEmpty())
677 return false;
679 bool bRet = false;
681 OUString aURL;
682 SvtContentEntry* pData = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rEntry));
684 if ( pData )
685 aURL = pData->maURL;
687 if ( aURL.isEmpty() )
688 return bRet;
692 OUString aPropName( "Title" );
693 bool canRename = true;
694 ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
698 Reference< XPropertySetInfo > aProps = aContent.getProperties();
699 if ( aProps.is() )
701 Property aProp = aProps->getPropertyByName( aPropName );
702 canRename = !( aProp.Attributes & PropertyAttribute::READONLY );
704 else
706 canRename = false;
709 catch ( Exception const & )
711 canRename = false;
714 if ( canRename )
716 Any aValue;
717 aValue <<= sNewText;
718 aContent.setPropertyValue( aPropName, aValue );
719 mpParent->EntryRenamed(aURL, sNewText);
721 if (pData)
722 pData->maURL = aURL;
724 mxTreeView->set_id(rEntry, weld::toId(pData));
726 bRet = true;
729 catch( Exception const & )
733 return bRet;
736 void ViewTabListBox_Impl::DoQuickSearch( sal_Unicode rChar )
738 ::osl::MutexGuard aGuard( maMutex );
740 maResetQuickSearch.Stop();
742 OUString aLastText = maQuickSearchText;
743 sal_uInt32 aLastPos = mnSearchIndex;
745 maQuickSearchText += OUString(rChar).toAsciiLowerCase();
747 bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false );
749 if ( !bFound && ( aLastText.getLength() == 1 ) &&
750 ( aLastText == OUStringChar(rChar) ) )
752 mnSearchIndex = aLastPos + 1;
753 maQuickSearchText = aLastText;
754 bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
757 if (bFound)
759 mxTreeView->unselect_all();
760 mxTreeView->select(mnSearchIndex);
761 mxTreeView->set_cursor(mnSearchIndex);
762 mxTreeView->scroll_to_row(mnSearchIndex);
765 maResetQuickSearch.Start();
768 bool ViewTabListBox_Impl::Kill( const OUString& rContent )
770 bool bRet = true;
774 ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
775 aCnt.executeCommand( "delete", Any( true ) );
777 catch( css::ucb::CommandAbortedException const & )
779 SAL_INFO( "svtools.contnr", "CommandAbortedException" );
780 bRet = false;
782 catch( Exception const & )
784 SAL_INFO( "svtools.contnr", "Any other exception" );
785 bRet = false;
788 return bRet;
791 SvtFileView::SvtFileView(weld::Window* pTopLevel,
792 std::unique_ptr<weld::TreeView> xTreeView,
793 std::unique_ptr<weld::IconView> xIconView,
794 bool bOnlyFolder, bool bMultiSelection, bool bShowType )
796 FileViewFlags nFlags = FileViewFlags::NONE;
797 if ( bMultiSelection )
798 nFlags |= FileViewFlags::MULTISELECTION;
799 if ( bShowType )
800 nFlags |= FileViewFlags::SHOW_TYPE;
802 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
803 Reference< XInteractionHandler > xInteractionHandler(
804 InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
805 Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
807 mpImpl.reset(new SvtFileView_Impl(this, pTopLevel, std::move(xTreeView), std::move(xIconView), xCmdEnv, nFlags, bOnlyFolder));
809 weld::TreeView* pView = mpImpl->mxView->getWidget();
810 pView->connect_column_clicked(LINK(this, SvtFileView, HeaderSelect_Impl));
813 void SvtFileView::grab_focus()
815 mpImpl->grab_focus();
818 bool SvtFileView::has_focus() const
820 return mpImpl->has_focus();
823 SvtFileView::~SvtFileView()
827 void SvtFileView::SetViewMode( FileViewMode eMode )
829 mpImpl->SetViewMode( eMode );
832 OUString SvtFileView::GetURL(const weld::TreeIter& rEntry) const
834 SvtContentEntry* pEntry;
835 if (mpImpl->mxView->get_visible())
836 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(rEntry));
837 else
838 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(rEntry));
839 if (pEntry)
840 return pEntry->maURL;
841 return OUString();
844 OUString SvtFileView::GetCurrentURL() const
846 SvtContentEntry* pEntry = nullptr;
847 OUString aURL;
848 if (mpImpl->mxView->get_visible())
850 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
851 if (mpImpl->mxView->get_selected(xEntry.get()))
852 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
854 else
856 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
857 if (mpImpl->mxIconView->get_selected(xEntry.get()))
858 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
860 if (pEntry)
861 aURL = pEntry->maURL;
862 return aURL;
865 void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
867 const SortingData_Impl& rEntry = mpImpl->FolderInserted( rUrl, rNewFolder );
869 mpImpl->maEntries.emplace_back(std::make_unique<SvtContentEntry>(rUrl, true));
870 OUString sId(weld::toId(mpImpl->maEntries.back().get()));
872 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
873 mpImpl->mxView->insert(rEntry.maDisplayName, sId, mpImpl->maFolderImage, *xEntry);
874 mpImpl->mxView->scroll_to_row(*xEntry);
876 std::unique_ptr<weld::TreeIter> xIconEntry = mpImpl->mxIconView->make_iterator();
877 mpImpl->mxIconView->insert(-1, &rEntry.maDisplayName, &sId, &mpImpl->maFolderImage, xIconEntry.get());
878 mpImpl->mxIconView->scroll_to_item(*xIconEntry);
881 FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
883 FileViewResult eResult = eFailure;
885 OUString sParentURL;
886 if ( GetParentURL( sParentURL ) )
887 eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, maDenyList );
889 return eResult;
892 bool SvtFileView::GetParentURL( OUString& rParentURL ) const
894 bool bRet = false;
897 ::ucbhelper::Content aCnt( mpImpl->maViewURL, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() );
898 Reference< XContent > xContent( aCnt.get() );
899 Reference< css::container::XChild > xChild( xContent, UNO_QUERY );
900 if ( xChild.is() )
902 Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
903 if ( xParent.is() )
905 rParentURL = xParent->getIdentifier()->getContentIdentifier();
906 bRet = !rParentURL.isEmpty() && rParentURL != mpImpl->maViewURL;
910 catch( Exception const & )
912 // perhaps an unknown url protocol (e.g. "private:newdoc")
915 return bRet;
918 OUString SvtFileView::get_help_id() const
920 return mpImpl->mxView->get_help_id();
923 void SvtFileView::set_help_id(const OUString& rHelpId)
925 mpImpl->mxView->set_help_id(rHelpId);
928 OUString SvtFileView::get_selected_text() const
930 if (mpImpl->mxView->get_visible())
931 return mpImpl->mxView->get_selected_text();
932 return mpImpl->mxIconView->get_selected_text();
935 FileViewResult SvtFileView::Initialize(
936 const OUString& rURL,
937 const OUString& rFilter,
938 const FileViewAsyncAction* pAsyncDescriptor,
939 const css::uno::Sequence< OUString >& rDenyList )
941 weld::WaitObject aWaitCursor(mpImpl->m_pTopLevel);
942 maDenyList = rDenyList;
944 OUString sPushURL( mpImpl->maViewURL );
946 mpImpl->maViewURL = rURL;
947 FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor );
948 switch ( eResult )
950 case eFailure:
951 case eTimeout:
952 mpImpl->maViewURL = sPushURL;
953 return eResult;
955 case eStillRunning:
956 OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" );
957 [[fallthrough]];
958 case eSuccess:
959 return eResult;
962 OSL_FAIL( "SvtFileView::Initialize: unreachable!" );
963 return eFailure;
966 FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor )
968 mpImpl->maCurrentFilter = rFilter.toAsciiLowerCase();
970 mpImpl->Clear();
971 FileViewResult eResult = mpImpl->GetFolderContent_Impl(mpImpl->maViewURL, pAsyncDescriptor, maDenyList);
972 OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
973 return eResult;
976 void SvtFileView::CancelRunningAsyncAction()
978 mpImpl->CancelRunningAsyncAction();
981 void SvtFileView::SetNoSelection()
983 mpImpl->mxView->unselect_all();
984 mpImpl->mxIconView->unselect_all();
987 void SvtFileView::SetSelectHdl(const Link<SvtFileView*,void>& rHdl)
989 mpImpl->SetSelectHandler(rHdl);
992 void SvtFileView::SetDoubleClickHdl(const Link<SvtFileView*,bool>& rHdl)
994 mpImpl->SetDoubleClickHandler(rHdl);
997 sal_uInt32 SvtFileView::GetSelectionCount() const
999 if (mpImpl->mxView->get_visible())
1000 return mpImpl->mxView->count_selected_rows();
1001 return mpImpl->mxIconView->count_selected_items();
1004 SvtContentEntry* SvtFileView::FirstSelected() const
1006 if (mpImpl->mxView->get_visible())
1008 SvtContentEntry* pRet = nullptr;
1009 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
1010 if (mpImpl->mxView->get_selected(xEntry.get()))
1011 pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
1012 return pRet;
1015 SvtContentEntry* pRet = nullptr;
1016 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
1017 if (mpImpl->mxIconView->get_selected(xEntry.get()))
1018 pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
1019 return pRet;
1022 const OUString& SvtFileView::GetViewURL() const
1024 return mpImpl->maViewURL;
1027 void SvtFileView::SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl )
1029 mpImpl->maOpenDoneLink = rHdl;
1032 void SvtFileView::EnableDelete( bool bEnable )
1034 mpImpl->EnableDelete( bEnable );
1037 void SvtFileView::EndInplaceEditing()
1039 return mpImpl->EndEditing();
1042 IMPL_LINK(SvtFileView, HeaderSelect_Impl, int, nColumn, void)
1044 sal_uInt16 nItemID = nColumn + 1;
1045 // skip "TYPE"
1046 if (!mpImpl->mxView->TypeColumnVisible() && nItemID != COLUMN_TITLE)
1047 ++nItemID;
1049 weld::TreeView* pView = mpImpl->mxView->getWidget();
1050 bool bSortAtoZ = mpImpl->mbAscending;
1052 //set new arrow positions in headerbar
1053 if (nItemID != mpImpl->mnSortColumn)
1055 // remove old indicator, new will be created in OpenFolder_Impl
1056 pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
1058 else
1059 bSortAtoZ = !bSortAtoZ;
1061 mpImpl->Resort_Impl(nItemID, bSortAtoZ);
1064 OUString SvtFileView::GetConfigString() const
1066 // sort order
1067 OUString sRet = OUString::number( mpImpl->mnSortColumn ) + ";";
1069 bool bUp = mpImpl->mbAscending;
1070 sRet += OUString::Concat(bUp ? std::u16string_view(u"1") : std::u16string_view(u"0")) + ";";
1072 weld::TreeView* pView = mpImpl->mxView->getWidget();
1073 sal_uInt16 nCount = mpImpl->mxView->TypeColumnVisible() ? 4 : 3;
1074 for (sal_uInt16 i = 0; i < nCount; ++i)
1076 sal_uInt16 nId = i + 1;
1077 // skip "TYPE"
1078 if (!mpImpl->mxView->TypeColumnVisible() && nId != COLUMN_TITLE)
1079 ++nId;
1081 sRet += OUString::number( nId )
1082 + ";"
1083 + OUString::number(pView->get_column_width(i))
1084 + ";";
1087 return comphelper::string::stripEnd(sRet, ';');
1090 ::std::vector< SvtContentEntry > SvtFileView::GetContent()
1092 ::std::vector< SvtContentEntry > aContent;
1094 for(auto const& elem : mpImpl->maContent)
1096 SvtContentEntry aEntry( elem->maTargetURL, elem->mbIsFolder );
1097 aContent.push_back( aEntry );
1100 return aContent;
1103 void SvtFileView::SetConfigString(std::u16string_view rCfgStr)
1105 sal_Int32 nIdx = 0;
1106 sal_uInt16 nSortColumn = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
1107 bool bAscending = static_cast<bool>(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ))));
1109 std::vector<int> aWidths(mpImpl->mxView->TypeColumnVisible() ? 4 : 3, -1);
1111 while ( nIdx != -1 )
1113 sal_uInt16 nItemId = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
1115 int nWidth = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
1117 // skip "TYPE"
1118 if (!mpImpl->mxView->TypeColumnVisible() && nItemId != COLUMN_TITLE)
1119 --nItemId;
1120 int nColumn = nItemId - 1;
1122 if (nColumn >= 0 && o3tl::make_unsigned(nColumn) < aWidths.size())
1123 aWidths[nColumn] = nWidth;
1126 weld::TreeView* pView = mpImpl->mxView->getWidget();
1127 pView->set_column_fixed_widths(aWidths);
1128 if (mpImpl->mnSortColumn != nSortColumn)
1129 pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
1130 mpImpl->Resort_Impl(nSortColumn, bAscending);
1133 SvtFileView_Impl::SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
1134 std::unique_ptr<weld::TreeView> xTreeView,
1135 std::unique_ptr<weld::IconView> xIconView,
1136 Reference < XCommandEnvironment > const & xEnv,
1137 FileViewFlags nFlags, bool bOnlyFolder)
1138 : m_pAntiImpl ( pAntiImpl )
1139 , m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR )
1140 , m_bRunningAsyncAction ( false )
1141 , m_bAsyncActionCancelled ( false )
1142 , m_pTopLevel ( pTopLevel )
1143 , mxView(new ViewTabListBox_Impl(std::move(xTreeView), pTopLevel, this, nFlags))
1144 , mxIconView(std::move(xIconView))
1145 , mnSortColumn ( COLUMN_TITLE )
1146 , mbAscending ( true )
1147 , mbOnlyFolder ( bOnlyFolder )
1148 , mnSuspendSelectCallback ( 0 )
1149 , mbIsFirstResort ( true )
1150 , aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
1151 , maFolderImage (RID_BMP_FOLDER)
1152 , mxCmdEnv ( xEnv )
1154 weld::TreeView* pWidget = mxView->getWidget();
1156 // set the width to something small so it's the parent that decides the final
1157 // width
1158 Size aSize(42, pWidget->get_height_rows(7));
1159 pWidget->set_size_request(aSize.Width(), aSize.Height());
1160 mxIconView->set_size_request(aSize.Width(), aSize.Height());
1163 SvtFileView_Impl::~SvtFileView_Impl()
1165 Clear();
1168 void SvtFileView_Impl::Clear()
1170 ::osl::MutexGuard aGuard( maMutex );
1172 maContent.clear();
1175 FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1176 std::u16string_view rFolder,
1177 const FileViewAsyncAction* pAsyncDescriptor,
1178 const css::uno::Sequence< OUString >& rDenyList )
1180 ::osl::ClearableMutexGuard aGuard( maMutex );
1181 INetURLObject aFolderObj( rFolder );
1182 DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
1184 FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1186 aGuard.clear();
1187 return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rDenyList );
1190 FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1191 const FolderDescriptor& _rFolder,
1192 const FileViewAsyncAction* pAsyncDescriptor,
1193 const css::uno::Sequence< OUString >& rDenyList )
1195 DBG_TESTSOLARMUTEX();
1196 ::osl::ClearableMutexGuard aGuard( maMutex );
1198 OSL_ENSURE( !m_xContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" );
1199 m_xContentEnumerator.set(new ::svt::FileViewContentEnumerator(
1200 mxView->GetCommandEnvironment(), maContent, maMutex));
1201 // TODO: should we cache and re-use this thread?
1203 if ( !pAsyncDescriptor )
1205 ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rDenyList );
1206 if ( ::svt::EnumerationResult::SUCCESS == eResult )
1208 implEnumerationSuccess();
1209 m_xContentEnumerator.clear();
1210 return eSuccess;
1212 m_xContentEnumerator.clear();
1213 return eFailure;
1216 m_bRunningAsyncAction = true;
1217 m_bAsyncActionCancelled = false;
1218 m_eAsyncActionResult = ::svt::EnumerationResult::ERROR;
1219 m_aAsyncActionFinished.reset();
1221 // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler.
1222 // By definition, this handler *only* gets called when the result cannot be obtained
1223 // during the minimum wait time, so it is only set below, when needed.
1224 m_aCurrentAsyncActionHandler = Link<void*,void>();
1226 // minimum time to wait
1227 TimeValue aTimeout;
1228 sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
1229 OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
1230 if ( nMinTimeout <= 0 )
1231 nMinTimeout = sal_Int32( 1000 );
1232 aTimeout.Seconds = nMinTimeout / 1000;
1233 aTimeout.Nanosec = ( nMinTimeout % 1000 ) * 1000000;
1235 m_xContentEnumerator->enumerateFolderContent( _rFolder, this );
1237 // wait until the enumeration is finished
1238 // for this, release our own mutex (which is used by the enumerator thread)
1239 aGuard.clear();
1241 ::osl::Condition::Result eResult = ::osl::Condition::result_ok;
1243 // also release the SolarMutex. Not all code which is needed during the enumeration
1244 // is Solar-Thread-Safe, in particular there is some code which needs to access
1245 // string resources (and our resource system relies on the SolarMutex :()
1246 SolarMutexReleaser aSolarRelease;
1248 // now wait. Note that if we didn't get a pAsyncDescriptor, then this is an infinite wait.
1249 eResult = m_aAsyncActionFinished.wait( &aTimeout );
1252 ::osl::MutexGuard aGuard2( maMutex );
1253 if ( ::osl::Condition::result_timeout == eResult )
1255 // maximum time to wait
1256 OSL_ENSURE(!m_xCancelAsyncTimer,
1257 "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!");
1258 m_xCancelAsyncTimer.set(new CallbackTimer(this));
1259 sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout;
1260 OSL_ENSURE( nMaxTimeout > nMinTimeout,
1261 "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" );
1262 if ( nMaxTimeout <= nMinTimeout )
1263 nMaxTimeout = nMinTimeout + 5000;
1264 m_xCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) );
1265 // we already waited for nMinTimeout milliseconds, so take this into account
1266 m_xCancelAsyncTimer->start();
1268 m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler;
1269 DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" );
1270 maEntries.clear();
1271 mxView->clear();
1272 mxIconView->clear();
1273 return eStillRunning;
1276 m_bRunningAsyncAction = false;
1277 switch ( m_eAsyncActionResult )
1279 case ::svt::EnumerationResult::SUCCESS:
1280 return eSuccess;
1282 case ::svt::EnumerationResult::ERROR:
1283 return eFailure;
1286 SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" );
1287 return eFailure;
1290 void SvtFileView_Impl::FilterFolderContent_Impl( std::u16string_view rFilter )
1292 if ( rFilter.empty() || ( rFilter == ALL_FILES_FILTER ) )
1293 // when replacing names, there is always something to filter (no view of ".nametranslation.table")
1294 return;
1296 ::osl::MutexGuard aGuard( maMutex );
1298 if ( maContent.empty() )
1299 return;
1301 // collect the filter tokens
1302 ::std::vector< WildCard > aFilters;
1303 FilterMatch::createWildCardFilterList(rFilter,aFilters);
1306 // do the filtering
1307 std::erase_if(maContent,
1308 [&aFilters](const std::unique_ptr<SortingData_Impl>& rxContent) {
1309 if (rxContent->mbIsFolder)
1310 return false;
1311 // normalize the content title (we always match case-insensitive)
1312 // 91872 - 11.09.2001 - frank.schoenheit@sun.com
1313 OUString sCompareString = rxContent->GetFileName(); // filter works on file name, not on title!
1314 return std::none_of(aFilters.begin(), aFilters.end(), FilterMatch(sCompareString));
1318 IMPL_LINK_NOARG(SvtFileView_Impl, ChangedHdl, weld::TreeView&, void)
1320 if (!mnSuspendSelectCallback)
1321 m_aSelectHandler.Call(m_pAntiImpl);
1324 IMPL_LINK_NOARG(SvtFileView_Impl, SelectionChangedHdl, weld::IconView&, void)
1326 if (!mnSuspendSelectCallback)
1327 m_aSelectHandler.Call(m_pAntiImpl);
1330 void SvtFileView_Impl::SetSelectHandler(const Link<SvtFileView*,void>& rHdl)
1332 m_aSelectHandler = rHdl;
1334 mxView->connect_changed(LINK(this, SvtFileView_Impl, ChangedHdl));
1335 mxIconView->connect_selection_changed(LINK(this, SvtFileView_Impl, SelectionChangedHdl));
1338 IMPL_LINK_NOARG(SvtFileView_Impl, RowActivatedHdl, weld::TreeView&, bool)
1340 return maDoubleClickHandler.Call(m_pAntiImpl);
1343 IMPL_LINK_NOARG(SvtFileView_Impl, ItemActivatedHdl, weld::IconView&, bool)
1345 return maDoubleClickHandler.Call(m_pAntiImpl);
1348 void SvtFileView_Impl::SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl)
1350 maDoubleClickHandler = rHdl;
1352 mxView->connect_row_activated(LINK(this, SvtFileView_Impl, RowActivatedHdl));
1353 mxIconView->connect_item_activated(LINK(this, SvtFileView_Impl, ItemActivatedHdl));
1356 void SvtFileView_Impl::OpenFolder_Impl()
1358 ::osl::MutexGuard aGuard( maMutex );
1360 mxView->freeze();
1361 mxIconView->freeze();
1362 maEntries.clear();
1363 mxView->clear();
1364 mxIconView->clear();
1366 for (auto const& elem : maContent)
1368 if (mbOnlyFolder && !elem->mbIsFolder)
1369 continue;
1371 // insert entry and set user data
1372 maEntries.emplace_back(std::make_unique<SvtContentEntry>(elem->maTargetURL, elem->mbIsFolder));
1373 OUString sId(weld::toId(maEntries.back().get()));
1374 mxView->append(sId, elem->maDisplayName, elem->maType, elem->maDisplaySize, elem->maDisplayDate, elem->maImage);
1375 mxIconView->append(sId, elem->maDisplayName, elem->maImage);
1378 ++mnSuspendSelectCallback;
1379 mxView->thaw();
1381 //set sort indicator
1382 weld::TreeView* pView = mxView->getWidget();
1383 pView->set_sort_indicator(mbAscending ? TRISTATE_TRUE : TRISTATE_FALSE, GetSortColumn());
1385 mxIconView->thaw();
1386 --mnSuspendSelectCallback;
1388 ResetCursor();
1391 void SvtFileView_Impl::ResetCursor()
1393 if (mxView->get_visible())
1395 std::unique_ptr<weld::TreeIter> xFirst = mxView->make_iterator();
1396 if (mxView->get_iter_first(*xFirst))
1398 // set cursor to the first entry
1399 mxView->set_cursor(*xFirst);
1401 // deselect
1402 mxView->unselect_all();
1404 else
1406 std::unique_ptr<weld::TreeIter> xFirst = mxIconView->make_iterator();
1407 if (mxIconView->get_iter_first(*xFirst))
1409 // set cursor to the first entry
1410 mxIconView->set_cursor(*xFirst);
1412 // deselect
1413 mxIconView->unselect_all();
1417 void SvtFileView_Impl::CancelRunningAsyncAction()
1419 DBG_TESTSOLARMUTEX();
1420 ::osl::MutexGuard aGuard( maMutex );
1421 if ( !m_xContentEnumerator.is() )
1422 return;
1424 m_bAsyncActionCancelled = true;
1425 m_xContentEnumerator->cancel();
1426 m_bRunningAsyncAction = false;
1428 m_xContentEnumerator.clear();
1429 if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
1430 m_xCancelAsyncTimer->stop();
1431 m_xCancelAsyncTimer.clear();
1435 void SvtFileView_Impl::onTimeout()
1437 SolarMutexGuard aSolarGuard;
1438 ::osl::MutexGuard aGuard( maMutex );
1439 if ( !m_bRunningAsyncAction )
1440 // there might have been a race condition while we waited for the mutex
1441 return;
1443 CancelRunningAsyncAction();
1445 if ( m_aCurrentAsyncActionHandler.IsSet() )
1447 Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) );
1448 m_aCurrentAsyncActionHandler = Link<void*,void>();
1453 void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult )
1455 SolarMutexGuard aSolarGuard;
1456 ::osl::MutexGuard aGuard( maMutex );
1458 m_xContentEnumerator.clear();
1459 if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
1460 m_xCancelAsyncTimer->stop();
1461 m_xCancelAsyncTimer.clear();
1463 if ( m_bAsyncActionCancelled )
1464 // this is to prevent race conditions
1465 return;
1467 m_eAsyncActionResult = eResult;
1468 m_bRunningAsyncAction = false;
1470 m_aAsyncActionFinished.set();
1472 if ( svt::EnumerationResult::SUCCESS == eResult )
1473 implEnumerationSuccess();
1475 if ( m_aCurrentAsyncActionHandler.IsSet() )
1477 Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) );
1478 m_aCurrentAsyncActionHandler = Link<void*,void>();
1483 void SvtFileView_Impl::implEnumerationSuccess()
1485 FilterFolderContent_Impl( maCurrentFilter );
1486 SortFolderContent_Impl();
1487 CreateDisplayText_Impl();
1488 OpenFolder_Impl();
1489 maOpenDoneLink.Call( m_pAntiImpl );
1492 OUString SvtFileView_Impl::ReplaceTabWithString(const OUString& rValue)
1494 return rValue.replaceAll(u"\t", u"%09");
1497 void SvtFileView_Impl::CreateDisplayText_Impl()
1499 ::osl::MutexGuard aGuard( maMutex );
1501 for (auto const& elem : maContent)
1503 // title, type, size, date
1504 elem->maDisplayName = ReplaceTabWithString(elem->GetTitle());
1505 // folders don't have a size
1506 if ( ! elem->mbIsFolder )
1507 elem->maDisplaySize = CreateExactSizeText( elem->maSize );
1508 // set the date, but volumes have no date
1509 if ( ! elem->mbIsFolder || ! elem->mbIsVolume )
1511 SvtSysLocale aSysLocale;
1512 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1513 elem->maDisplayDate = rLocaleData.getDate( elem->maModDate )
1514 + ", "
1515 + rLocaleData.getTime( elem->maModDate, false );
1518 // detect image
1519 if ( elem->mbIsFolder )
1521 ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote,
1522 elem->mbIsRemoveable, elem->mbIsFloppy,
1523 elem->mbIsCompactDisc );
1524 elem->maImage = SvFileInformationManager::GetFolderImageId(aVolInfo);
1526 else
1527 elem->maImage = SvFileInformationManager::GetFileImageId(INetURLObject(elem->maTargetURL));
1531 void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending )
1533 // TODO: IconView ()
1534 ::osl::MutexGuard aGuard( maMutex );
1536 if ( ( nColumn == mnSortColumn ) &&
1537 ( bAscending == mbAscending ) )
1538 return;
1540 // reset the quick search index
1541 mxView->ResetQuickSearch_Impl( nullptr );
1543 std::unique_ptr<weld::TreeIter> xEntry(mxView->make_iterator());
1544 bool bEntry = mxView->get_cursor(xEntry.get());
1546 OUString aEntryURL;
1547 if (bEntry && !mxView->get_id(*xEntry).isEmpty())
1548 aEntryURL = weld::fromId<SvtContentEntry*>(mxView->get_id(*xEntry))->maURL;
1550 mnSortColumn = nColumn;
1551 mbAscending = bAscending;
1553 SortFolderContent_Impl();
1554 OpenFolder_Impl();
1556 if ( !mbIsFirstResort )
1558 int nPos = GetEntryPos( aEntryURL );
1559 if (nPos != -1 && nPos < mxView->n_children())
1561 ++mnSuspendSelectCallback; // #i15668#
1562 mxView->set_cursor(nPos);
1563 --mnSuspendSelectCallback;
1566 else
1567 mbIsFirstResort = false;
1570 static bool gbAscending = true;
1571 static sal_Int16 gnColumn = COLUMN_TITLE;
1572 static const CollatorWrapper* pCollatorWrapper = nullptr;
1574 /* this function returns true, if aOne is less than aTwo
1576 static bool CompareSortingData_Impl( std::unique_ptr<SortingData_Impl> const & aOne, std::unique_ptr<SortingData_Impl> const & aTwo )
1578 DBG_ASSERT( pCollatorWrapper, "*CompareSortingData_Impl(): Can't work this way!" );
1580 sal_Int32 nComp;
1581 bool bRet = false;
1582 bool bEqual = false;
1584 if ( aOne->mbIsFolder != aTwo->mbIsFolder )
1586 bRet = aOne->mbIsFolder;
1588 // !!! pb: #100376# folder always on top
1589 if ( !gbAscending )
1590 bRet = !bRet;
1592 else
1594 switch ( gnColumn )
1596 case COLUMN_TITLE:
1597 // compare case insensitive first
1598 nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() );
1600 if ( nComp == 0 )
1601 nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() );
1603 if ( nComp < 0 )
1604 bRet = true;
1605 else if ( nComp > 0 )
1606 bRet = false;
1607 else
1608 bEqual = true;
1609 break;
1610 case COLUMN_TYPE:
1611 nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType );
1612 if ( nComp < 0 )
1613 bRet = true;
1614 else if ( nComp > 0 )
1615 bRet = false;
1616 else
1617 bEqual = true;
1618 break;
1619 case COLUMN_SIZE:
1620 if ( aOne->maSize < aTwo->maSize )
1621 bRet = true;
1622 else if ( aOne->maSize > aTwo->maSize )
1623 bRet = false;
1624 else
1625 bEqual = true;
1626 break;
1627 case COLUMN_DATE:
1628 if ( aOne->maModDate < aTwo->maModDate )
1629 bRet = true;
1630 else if ( aOne->maModDate > aTwo->maModDate )
1631 bRet = false;
1632 else
1633 bEqual = true;
1634 break;
1635 default:
1636 SAL_INFO( "svtools.contnr", "CompareSortingData_Impl: Compare unknown type!" );
1637 bRet = false;
1641 // when the two elements are equal, we must not return sal_True (which would
1642 // happen if we just return ! ( a < b ) when not sorting ascending )
1643 if ( bEqual )
1644 return false;
1646 return gbAscending ? bRet : !bRet;
1650 void SvtFileView_Impl::SortFolderContent_Impl()
1652 ::osl::MutexGuard aGuard( maMutex );
1654 if ( maContent.size() > 1 )
1656 gbAscending = mbAscending;
1657 gnColumn = mnSortColumn;
1658 pCollatorWrapper = aIntlWrapper.getCaseCollator();
1660 std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl );
1662 pCollatorWrapper = nullptr;
1667 void SvtFileView_Impl::EntryRemoved( std::u16string_view rURL )
1669 ::osl::MutexGuard aGuard( maMutex );
1671 maContent.erase(std::find_if(maContent.begin(), maContent.end(),
1672 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; }));
1676 void SvtFileView_Impl::EntryRenamed( OUString& rURL,
1677 const OUString& rTitle )
1679 ::osl::MutexGuard aGuard( maMutex );
1681 auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
1682 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
1683 if (aFoundElem != maContent.end())
1685 (*aFoundElem)->SetNewTitle( rTitle );
1686 (*aFoundElem)->maDisplayName = ReplaceTabWithString(rTitle);
1688 INetURLObject aURLObj( rURL );
1689 aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All );
1691 rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1693 (*aFoundElem)->maTargetURL = rURL;
1697 const SortingData_Impl& SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
1699 ::osl::MutexGuard aGuard( maMutex );
1701 std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
1703 pData->SetNewTitle( rTitle );
1704 pData->maSize = 0;
1705 pData->mbIsFolder = true;
1706 pData->maTargetURL = rURL;
1708 ::svtools::VolumeInfo aVolInfo;
1709 pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
1710 pData->maImage = SvFileInformationManager::GetFolderImageId( aVolInfo );
1712 // title, type, size, date
1713 pData->maDisplayName = ReplaceTabWithString(pData->GetTitle());
1714 // set the date
1715 SvtSysLocale aSysLocale;
1716 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1717 pData->maDisplayDate = rLocaleData.getDate( pData->maModDate )
1718 + ", "
1719 + rLocaleData.getTime( pData->maModDate );
1721 maContent.push_back( std::move(pData) );
1723 return *maContent.back();
1726 int SvtFileView_Impl::GetEntryPos(std::u16string_view rURL)
1728 ::osl::MutexGuard aGuard( maMutex );
1730 auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
1731 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
1732 return aFoundElem != maContent.end() ? std::distance(maContent.begin(), aFoundElem) : -1;
1735 void SvtFileView_Impl::SetViewMode( FileViewMode eMode )
1737 switch ( eMode )
1739 case eDetailedList:
1740 mxView->show();
1741 mxIconView->hide();
1742 break;
1744 case eIcon:
1745 mxView->hide();
1746 mxIconView->show();
1747 break;
1749 default:
1750 mxView->show();
1751 mxIconView->hide();
1755 bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, std::u16string_view rTitle, bool bWrapAround )
1757 ::osl::MutexGuard aGuard( maMutex );
1759 sal_uInt32 nEnd = maContent.size();
1760 sal_uInt32 nStart = nIndex;
1761 while ( nIndex < nEnd )
1763 SortingData_Impl* pData = maContent[ nIndex ].get();
1764 if ( pData->GetLowerTitle().startsWith( rTitle ) )
1765 return true;
1766 ++nIndex;
1769 if ( bWrapAround )
1771 nIndex = 0;
1772 while ( nIndex < nEnd && nIndex <= nStart )
1774 SortingData_Impl* pData = maContent[ nIndex ].get();
1775 if ( pData->GetLowerTitle().startsWith( rTitle ) )
1776 return true;
1777 ++nIndex;
1781 return false;
1784 namespace {
1785 void SAL_CALL CallbackTimer::onShot()
1787 OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" );
1788 SvtFileView_Impl* pHandler( m_pTimeoutHandler );
1789 if ( pHandler )
1790 pHandler->onTimeout();
1794 void SvtFileView::selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
1796 if (mpImpl->mxView->get_visible())
1797 mpImpl->mxView->selected_foreach(func);
1798 else
1799 mpImpl->mxIconView->selected_foreach(func);
1802 weld::Widget* SvtFileView::identifier() const
1804 return mpImpl->mxView->getWidget();
1807 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */