cool#10610 Ensure the parent-child relations of comments.
[LibreOffice.git] / fpicker / source / office / fileview.cxx
bloba5756f5d28d41f53c87241dc646fb3e6bc637ca3
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::sdbc;
68 using namespace ::com::sun::star::task;
69 using namespace ::com::sun::star::ucb;
70 using namespace ::com::sun::star::uno;
71 using namespace ::com::sun::star::io;
72 using namespace ::com::sun::star::beans;
73 using namespace ::comphelper;
74 using ::svt::SortingData_Impl;
75 using ::svt::FolderDescriptor;
77 constexpr OUStringLiteral ALL_FILES_FILTER = u"*.*";
79 #define COLUMN_TITLE 1
80 #define COLUMN_TYPE 2
81 #define COLUMN_SIZE 3
82 #define COLUMN_DATE 4
84 #define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reset
86 namespace {
88 enum class FileViewFlags
90 NONE = 0x00,
91 MULTISELECTION = 0x02,
92 SHOW_TYPE = 0x04,
93 SHOW_NONE = 0x20,
98 namespace o3tl
100 template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x26> {};
103 namespace
106 //= CallbackTimer
108 class CallbackTimer : public ::salhelper::Timer
110 protected:
111 SvtFileView_Impl* const m_pTimeoutHandler;
113 public:
114 explicit CallbackTimer( SvtFileView_Impl* _pHandler ) : m_pTimeoutHandler( _pHandler ) { }
116 protected:
117 virtual void SAL_CALL onShot() override;
120 class ViewTabListBox_Impl
122 private:
123 rtl::Reference< ::ucbhelper::CommandEnvironment > mxCmdEnv;
124 std::unique_ptr<weld::TreeView> mxTreeView;
125 std::unique_ptr<weld::TreeIter> mxScratchIter;
127 ::osl::Mutex maMutex;
128 SvtFileView_Impl* mpParent;
129 Timer maResetQuickSearch { "fpicker SvtFileView_Impl maResetQuickSearch" };
130 OUString maQuickSearchText;
131 sal_uInt32 mnSearchIndex;
132 bool mbEnableDelete;
133 bool mbEditing;
134 bool const mbShowType;
136 void DeleteEntries();
137 void DoQuickSearch( sal_Unicode rChar );
138 bool Kill( const OUString& rURL );
140 public:
141 ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView, weld::Window* pTopLevel, SvtFileView_Impl* pParent, FileViewFlags nFlags);
143 std::unique_ptr<weld::TreeIter> make_iterator() const { return mxTreeView->make_iterator(); }
144 void insert(const OUString &rEntry, const OUString& rId, const OUString& rImage, weld::TreeIter& rIter)
146 mxTreeView->insert(nullptr, -1, &rEntry, &rId, nullptr, nullptr, false, &rIter);
147 mxTreeView->set_image(rIter, rImage);
149 void append(const OUString& rId, const OUString& rStr, const OUString& rType, const OUString& rSize, const OUString& rDate, const OUString& rImage)
151 mxTreeView->insert(nullptr, -1, &rStr, &rId, nullptr, nullptr, false, mxScratchIter.get());
152 mxTreeView->set_image(*mxScratchIter, rImage);
153 int nCol = 1;
154 if (mbShowType)
155 mxTreeView->set_text(*mxScratchIter, rType, nCol++);
156 mxTreeView->set_text(*mxScratchIter, rSize, nCol++);
157 mxTreeView->set_text(*mxScratchIter, rDate, nCol++);
160 void scroll_to_row(const weld::TreeIter& rIter) { mxTreeView->scroll_to_row(rIter); }
161 void set_cursor(int nPos) { mxTreeView->set_cursor(nPos); }
162 void set_cursor(const weld::TreeIter& rIter) { mxTreeView->set_cursor(rIter); }
163 bool get_cursor(weld::TreeIter* pIter) const { return mxTreeView->get_cursor(pIter); }
164 bool get_iter_first(weld::TreeIter& rIter) const { return mxTreeView->get_iter_first(rIter); }
165 bool get_selected(weld::TreeIter* pIter) const { return mxTreeView->get_selected(pIter); }
167 OUString get_selected_text() const
169 // tdf#131898 only care about column 0
170 int nIndex = mxTreeView->get_selected_index();
171 return nIndex != -1 ? mxTreeView->get_text(nIndex, 0) : OUString();
174 void unselect_all() { mxTreeView->unselect_all(); }
176 OUString get_id(const weld::TreeIter& rIter) { return mxTreeView->get_id(rIter); }
178 void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxTreeView->connect_row_activated(rLink); }
179 void connect_changed(const Link<weld::TreeView&, void>& rLink)
181 mxTreeView->connect_selection_changed(rLink);
184 int n_children() const { return mxTreeView->n_children(); }
186 void freeze() { mxTreeView->freeze(); }
187 void thaw() { mxTreeView->thaw(); }
189 void show() { mxTreeView->show(); }
190 void hide() { mxTreeView->hide(); }
191 bool get_visible() const { return mxTreeView->get_visible(); }
193 int count_selected_rows() const { return mxTreeView->count_selected_rows(); }
195 void grab_focus() { mxTreeView->grab_focus(); }
196 bool has_focus() const { return mxTreeView->has_focus(); }
198 void set_help_id(const OUString& rHelpId) { mxTreeView->set_help_id(rHelpId); }
199 OUString get_help_id() const { return mxTreeView->get_help_id(); }
201 bool IsEditingActive() const { return mbEditing; }
203 void end_editing()
205 mxTreeView->end_editing();
206 mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
207 mbEditing = false;
210 void selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
212 mxTreeView->selected_foreach(func);
215 weld::TreeView* getWidget() const
217 return mxTreeView.get();
220 void clear() { mxTreeView->clear(); }
222 void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
223 bool TypeColumnVisible() const { return mbShowType; }
225 const rtl::Reference< ::ucbhelper::CommandEnvironment >& GetCommandEnvironment() const { return mxCmdEnv; }
227 DECL_LINK(ResetQuickSearch_Impl, Timer *, void);
228 DECL_LINK(CommandHdl, const CommandEvent&, bool);
229 DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
230 typedef std::pair<const weld::TreeIter&, OUString> IterString;
231 DECL_LINK(EditedEntryHdl, const IterString&, bool);
232 DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
234 void ExecuteContextMenuAction(std::u16string_view rSelectedPopentry);
239 //= SvtFileView_Impl
240 class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
242 protected:
243 SvtFileView* m_pAntiImpl;
244 Link<SvtFileView*,void> m_aSelectHandler;
246 ::rtl::Reference< ::svt::FileViewContentEnumerator >
247 m_xContentEnumerator;
248 Link<void*,void> m_aCurrentAsyncActionHandler;
249 ::osl::Condition m_aAsyncActionFinished;
250 ::rtl::Reference< ::salhelper::Timer > m_xCancelAsyncTimer;
251 ::svt::EnumerationResult m_eAsyncActionResult;
252 bool m_bRunningAsyncAction;
253 bool m_bAsyncActionCancelled;
255 public:
257 ::std::vector<std::unique_ptr<SortingData_Impl>> maContent;
258 ::std::vector<std::unique_ptr<SvtContentEntry>> maEntries;
259 ::osl::Mutex maMutex;
261 weld::Window* m_pTopLevel;
262 std::unique_ptr<ViewTabListBox_Impl> mxView;
263 std::unique_ptr<weld::IconView> mxIconView;
264 sal_uInt16 mnSortColumn;
265 bool mbAscending : 1;
266 bool const mbOnlyFolder : 1;
267 sal_Int16 mnSuspendSelectCallback : 1;
268 bool mbIsFirstResort : 1;
270 IntlWrapper const aIntlWrapper;
272 OUString maViewURL;
273 OUString maCurrentFilter;
274 OUString maFolderImage;
275 Link<SvtFileView*,void> maOpenDoneLink;
276 Link<SvtFileView*,bool> maDoubleClickHandler;
278 Reference< XCommandEnvironment > mxCmdEnv;
280 SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
281 std::unique_ptr<weld::TreeView> xTreeView,
282 std::unique_ptr<weld::IconView> xIconView,
283 Reference < XCommandEnvironment > const & xEnv,
284 FileViewFlags nFlags,
285 bool bOnlyFolder);
287 virtual ~SvtFileView_Impl();
289 void Clear();
291 FileViewResult GetFolderContent_Impl(
292 std::u16string_view rFolder,
293 const FileViewAsyncAction* pAsyncDescriptor,
294 const css::uno::Sequence< OUString >& rDenyList );
296 FileViewResult GetFolderContent_Impl(
297 const FolderDescriptor& _rFolder,
298 const FileViewAsyncAction* pAsyncDescriptor,
299 const css::uno::Sequence< OUString >& rDenyList );
300 void FilterFolderContent_Impl( std::u16string_view rFilter );
301 void CancelRunningAsyncAction();
303 void OpenFolder_Impl();
304 static OUString ReplaceTabWithString(const OUString& rValue);
305 void CreateDisplayText_Impl();
306 void SortFolderContent_Impl();
308 void EntryRemoved( std::u16string_view rURL );
309 void EntryRenamed( OUString& rURL,
310 const OUString& rName );
311 const SortingData_Impl& FolderInserted( const OUString& rURL,
312 const OUString& rTitle );
314 int GetEntryPos( std::u16string_view rURL );
316 void SetViewMode( FileViewMode eMode );
318 inline void EnableDelete( bool bEnable );
320 void Resort_Impl( sal_Int16 nColumn, bool bAscending );
321 bool SearchNextEntry( sal_uInt32 &nIndex,
322 std::u16string_view rTitle,
323 bool bWrapAround );
325 void SetSelectHandler( const Link<SvtFileView*,void>& rHdl );
326 void SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl);
328 void ResetCursor();
330 void EndEditing()
332 if (mxView->IsEditingActive())
333 mxView->end_editing();
336 void onTimeout();
338 void grab_focus()
340 if (mxView->get_visible())
341 mxView->grab_focus();
342 else
343 mxIconView->grab_focus();
346 bool has_focus() const
348 return mxView->has_focus() || mxIconView->has_focus();
351 int GetSortColumn() const
353 sal_uInt16 nOldSortID = mnSortColumn;
354 // skip "TYPE"
355 if (!mxView->TypeColumnVisible() && nOldSortID != COLUMN_TITLE)
356 --nOldSortID;
357 return nOldSortID - 1;
360 protected:
361 DECL_LINK(ChangedHdl, weld::TreeView&, void);
362 DECL_LINK(SelectionChangedHdl, weld::IconView&, void);
363 DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
364 DECL_LINK(ItemActivatedHdl, weld::IconView&, bool);
366 // IEnumerationResultHandler overridables
367 virtual void enumerationDone( ::svt::EnumerationResult eResult ) override;
368 void implEnumerationSuccess();
371 inline void SvtFileView_Impl::EnableDelete( bool bEnable )
373 mxView->EnableDelete( bEnable );
376 namespace
378 // functions -------------------------------------------------------------
380 OUString CreateExactSizeText( sal_Int64 nSize )
382 double fSize( static_cast<double>(nSize) );
383 int nDec;
385 tools::Long nMega = 1024 * 1024;
386 tools::Long nGiga = nMega * 1024;
388 OUString aUnitStr(' ');
390 if ( nSize < 10000 )
392 aUnitStr += SvtResId(STR_SVT_BYTES );
393 nDec = 0;
395 else if ( nSize < nMega )
397 fSize /= 1024;
398 aUnitStr += SvtResId(STR_SVT_KB);
399 nDec = 1;
401 else if ( nSize < nGiga )
403 fSize /= nMega;
404 aUnitStr += SvtResId(STR_SVT_MB);
405 nDec = 2;
407 else
409 fSize /= nGiga;
410 aUnitStr += SvtResId(STR_SVT_GB);
411 nDec = 3;
414 OUString aSizeStr =
415 ::rtl::math::doubleToUString( fSize,
416 rtl_math_StringFormat_F, nDec,
417 SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) +
418 aUnitStr;
420 return aSizeStr;
424 ViewTabListBox_Impl::ViewTabListBox_Impl(std::unique_ptr<weld::TreeView> xTreeView,
425 weld::Window* pTopLevel,
426 SvtFileView_Impl* pParent,
427 FileViewFlags nFlags)
428 : mxTreeView(std::move(xTreeView))
429 , mxScratchIter(mxTreeView->make_iterator())
430 , mpParent( pParent )
431 , mnSearchIndex( 0 )
432 , mbEnableDelete( false )
433 , mbEditing( false )
434 , mbShowType(nFlags & FileViewFlags::SHOW_TYPE)
436 std::vector<int> aWidths { 180 };
437 if (nFlags & FileViewFlags::SHOW_TYPE)
438 aWidths.push_back(140);
439 aWidths.push_back(80);
440 mxTreeView->set_column_fixed_widths(aWidths);
442 if (nFlags & FileViewFlags::MULTISELECTION)
443 mxTreeView->set_selection_mode(SelectionMode::Multiple);
445 maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
446 maResetQuickSearch.SetInvokeHandler( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
448 const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
449 Reference< XInteractionHandler > xInteractionHandler(
450 InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
452 mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
454 mxTreeView->connect_popup_menu(LINK(this, ViewTabListBox_Impl, CommandHdl));
455 mxTreeView->connect_key_press(LINK(this, ViewTabListBox_Impl, KeyInputHdl));
458 IMPL_LINK_NOARG(ViewTabListBox_Impl, EditingEntryHdl, const weld::TreeIter&, bool)
460 return mbEditing;
463 IMPL_LINK_NOARG(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
465 ::osl::MutexGuard aGuard( maMutex );
467 maQuickSearchText.clear();
468 mnSearchIndex = 0;
471 IMPL_LINK(ViewTabListBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
473 if (mbEditing)
474 return false;
476 bool bHandled = false;
478 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
479 if ( 0 == rKeyCode.GetModifier() )
481 if ( ( rKeyCode.GetCode() == KEY_DELETE ) &&
482 mbEnableDelete )
484 ResetQuickSearch_Impl( nullptr );
485 DeleteEntries();
486 bHandled = true;
488 else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) ||
489 ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) )
491 DoQuickSearch( rKEvt.GetCharCode() );
492 bHandled = true;
496 if (!bHandled)
497 ResetQuickSearch_Impl( nullptr );
498 return bHandled;
501 IMPL_LINK(ViewTabListBox_Impl, CommandHdl, const CommandEvent&, rCEvt, bool)
503 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
504 return false;
506 bool bEnableDelete = mbEnableDelete;
507 bool bEnableRename = true;
509 int nCount = 0;
510 mxTreeView->selected_foreach([this, &nCount, &bEnableDelete, &bEnableRename](weld::TreeIter& rEntry){
511 ++nCount;
513 ::ucbhelper::Content aCnt;
516 OUString aURL(weld::fromId<SvtContentEntry*>(
517 mxTreeView->get_id(rEntry))->maURL);
518 aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
520 catch( Exception const & )
522 bEnableDelete = bEnableRename = false;
525 if ( bEnableDelete )
529 Reference< XCommandInfo > aCommands = aCnt.getCommands();
530 if ( aCommands.is() )
531 bEnableDelete = aCommands->hasCommandByName( u"delete"_ustr );
532 else
533 bEnableDelete = false;
535 catch( Exception const & )
537 bEnableDelete = false;
541 if ( bEnableRename )
545 Reference< XPropertySetInfo > aProps = aCnt.getProperties();
546 if ( aProps.is() )
548 Property aProp = aProps->getPropertyByName(u"Title"_ustr);
549 bEnableRename
550 = !( aProp.Attributes & PropertyAttribute::READONLY );
552 else
553 bEnableRename = false;
555 catch( Exception const & )
557 bEnableRename = false;
561 bool bStop = !bEnableDelete && !bEnableRename;
562 return bStop;
565 if (nCount == 0)
566 bEnableDelete = false;
567 if (nCount != 1)
568 bEnableRename = false;
570 if (bEnableDelete || bEnableRename)
572 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), u"svt/ui/fileviewmenu.ui"_ustr));
573 auto xContextMenu = xBuilder->weld_menu(u"menu"_ustr);
574 xContextMenu->set_visible(u"delete"_ustr, bEnableDelete);
575 xContextMenu->set_visible(u"rename"_ustr, bEnableRename);
576 OUString sCommand(xContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
577 ExecuteContextMenuAction(sCommand);
580 return true;
583 void ViewTabListBox_Impl::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry)
585 if (rSelectedPopupEntry == u"delete")
586 DeleteEntries();
587 else if (rSelectedPopupEntry == u"rename")
589 std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator();
590 if (mxTreeView->get_selected(xEntry.get()))
592 mbEditing = true;
594 mxTreeView->connect_editing(LINK(this, ViewTabListBox_Impl, EditingEntryHdl),
595 LINK(this, ViewTabListBox_Impl, EditedEntryHdl));
597 mxTreeView->start_editing(*xEntry);
602 void ViewTabListBox_Impl::DeleteEntries()
604 short eResult = svtools::QUERYDELETE_YES;
606 mxTreeView->selected_foreach([this, &eResult](weld::TreeIter& rCurEntry){
607 OUString aURL;
608 if (!mxTreeView->get_id(rCurEntry).isEmpty())
609 aURL = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rCurEntry))->maURL;
610 if (aURL.isEmpty())
612 mxTreeView->unselect(rCurEntry);
613 return false;
616 bool canDelete = true;
619 ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
620 Reference< XCommandInfo > aCommands = aCnt.getCommands();
621 if ( aCommands.is() )
622 canDelete = aCommands->hasCommandByName( u"delete"_ustr );
623 else
624 canDelete = false;
626 catch( Exception const & )
628 canDelete = false;
631 if (!canDelete)
633 mxTreeView->unselect(rCurEntry);
634 return false; // process next entry
637 if ( eResult != svtools::QUERYDELETE_ALL )
639 INetURLObject aObj( aURL );
640 svtools::QueryDeleteDlg_Impl aDlg(
641 mxTreeView.get(), aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
643 if (mxTreeView->count_selected_rows() > 1)
644 aDlg.EnableAllButton();
646 eResult = aDlg.run();
649 bool bDeleted = false;
651 if (eResult == svtools::QUERYDELETE_ALL || eResult == svtools::QUERYDELETE_YES)
653 if ( Kill( aURL ) )
655 mpParent->EntryRemoved( aURL );
656 bDeleted = true;
660 if (!bDeleted)
661 mxTreeView->unselect(rCurEntry);
663 return false;
666 mxTreeView->remove_selection();
669 IMPL_LINK(ViewTabListBox_Impl, EditedEntryHdl, const IterString&, rIterString, bool)
671 mbEditing = false;
673 mxTreeView->connect_editing(Link<const weld::TreeIter&, bool>(), Link<const IterString&, bool>());
675 const weld::TreeIter& rEntry = rIterString.first;
676 OUString sNewText = rIterString.second;
678 if (sNewText.isEmpty())
679 return false;
681 bool bRet = false;
683 OUString aURL;
684 SvtContentEntry* pData = weld::fromId<SvtContentEntry*>(mxTreeView->get_id(rEntry));
686 if ( pData )
687 aURL = pData->maURL;
689 if ( aURL.isEmpty() )
690 return bRet;
694 OUString aPropName( u"Title"_ustr );
695 bool canRename = true;
696 ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
700 Reference< XPropertySetInfo > aProps = aContent.getProperties();
701 if ( aProps.is() )
703 Property aProp = aProps->getPropertyByName( aPropName );
704 canRename = !( aProp.Attributes & PropertyAttribute::READONLY );
706 else
708 canRename = false;
711 catch ( Exception const & )
713 canRename = false;
716 if ( canRename )
718 Any aValue;
719 aValue <<= sNewText;
720 aContent.setPropertyValue( aPropName, aValue );
721 mpParent->EntryRenamed(aURL, sNewText);
723 if (pData)
724 pData->maURL = aURL;
726 mxTreeView->set_id(rEntry, weld::toId(pData));
728 bRet = true;
731 catch( Exception const & )
735 return bRet;
738 void ViewTabListBox_Impl::DoQuickSearch( sal_Unicode rChar )
740 ::osl::MutexGuard aGuard( maMutex );
742 maResetQuickSearch.Stop();
744 OUString aLastText = maQuickSearchText;
745 sal_uInt32 aLastPos = mnSearchIndex;
747 maQuickSearchText += OUString(rChar).toAsciiLowerCase();
749 bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false );
751 if ( !bFound && ( aLastText.getLength() == 1 ) &&
752 ( aLastText == OUStringChar(rChar) ) )
754 mnSearchIndex = aLastPos + 1;
755 maQuickSearchText = aLastText;
756 bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
759 if (bFound)
761 mxTreeView->unselect_all();
762 mxTreeView->select(mnSearchIndex);
763 mxTreeView->set_cursor(mnSearchIndex);
764 mxTreeView->scroll_to_row(mnSearchIndex);
767 maResetQuickSearch.Start();
770 bool ViewTabListBox_Impl::Kill( const OUString& rContent )
772 bool bRet = true;
776 ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
777 aCnt.executeCommand( u"delete"_ustr, Any( true ) );
779 catch( css::ucb::CommandAbortedException const & )
781 SAL_INFO( "svtools.contnr", "CommandAbortedException" );
782 bRet = false;
784 catch( Exception const & )
786 SAL_INFO( "svtools.contnr", "Any other exception" );
787 bRet = false;
790 return bRet;
793 SvtFileView::SvtFileView(weld::Window* pTopLevel,
794 std::unique_ptr<weld::TreeView> xTreeView,
795 std::unique_ptr<weld::IconView> xIconView,
796 bool bOnlyFolder, bool bMultiSelection, bool bShowType )
798 FileViewFlags nFlags = FileViewFlags::NONE;
799 if ( bMultiSelection )
800 nFlags |= FileViewFlags::MULTISELECTION;
801 if ( bShowType )
802 nFlags |= FileViewFlags::SHOW_TYPE;
804 const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
805 Reference< XInteractionHandler > xInteractionHandler(
806 InteractionHandler::createWithParent(xContext, pTopLevel->GetXWindow()), UNO_QUERY_THROW);
807 Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
809 mpImpl.reset(new SvtFileView_Impl(this, pTopLevel, std::move(xTreeView), std::move(xIconView), xCmdEnv, nFlags, bOnlyFolder));
811 weld::TreeView* pView = mpImpl->mxView->getWidget();
812 pView->connect_column_clicked(LINK(this, SvtFileView, HeaderSelect_Impl));
815 void SvtFileView::grab_focus()
817 mpImpl->grab_focus();
820 bool SvtFileView::has_focus() const
822 return mpImpl->has_focus();
825 SvtFileView::~SvtFileView()
829 void SvtFileView::SetViewMode( FileViewMode eMode )
831 mpImpl->SetViewMode( eMode );
834 OUString SvtFileView::GetURL(const weld::TreeIter& rEntry) const
836 SvtContentEntry* pEntry;
837 if (mpImpl->mxView->get_visible())
838 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(rEntry));
839 else
840 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(rEntry));
841 if (pEntry)
842 return pEntry->maURL;
843 return OUString();
846 OUString SvtFileView::GetCurrentURL() const
848 SvtContentEntry* pEntry = nullptr;
849 OUString aURL;
850 if (mpImpl->mxView->get_visible())
852 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
853 if (mpImpl->mxView->get_selected(xEntry.get()))
854 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
856 else
858 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
859 if (mpImpl->mxIconView->get_selected(xEntry.get()))
860 pEntry = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
862 if (pEntry)
863 aURL = pEntry->maURL;
864 return aURL;
867 void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
869 const SortingData_Impl& rEntry = mpImpl->FolderInserted( rUrl, rNewFolder );
871 mpImpl->maEntries.emplace_back(std::make_unique<SvtContentEntry>(rUrl, true));
872 OUString sId(weld::toId(mpImpl->maEntries.back().get()));
874 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
875 mpImpl->mxView->insert(rEntry.maDisplayName, sId, mpImpl->maFolderImage, *xEntry);
876 mpImpl->mxView->scroll_to_row(*xEntry);
878 std::unique_ptr<weld::TreeIter> xIconEntry = mpImpl->mxIconView->make_iterator();
879 mpImpl->mxIconView->insert(-1, &rEntry.maDisplayName, &sId, &mpImpl->maFolderImage, xIconEntry.get());
880 mpImpl->mxIconView->scroll_to_item(*xIconEntry);
883 FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
885 FileViewResult eResult = eFailure;
887 OUString sParentURL;
888 if ( GetParentURL( sParentURL ) )
889 eResult = Initialize( sParentURL, mpImpl->maCurrentFilter, pAsyncDescriptor, maDenyList );
891 return eResult;
894 bool SvtFileView::GetParentURL( OUString& rParentURL ) const
896 bool bRet = false;
899 ::ucbhelper::Content aCnt( mpImpl->maViewURL, mpImpl->mxCmdEnv, comphelper::getProcessComponentContext() );
900 Reference< XContent > xContent( aCnt.get() );
901 Reference< css::container::XChild > xChild( xContent, UNO_QUERY );
902 if ( xChild.is() )
904 Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
905 if ( xParent.is() )
907 rParentURL = xParent->getIdentifier()->getContentIdentifier();
908 bRet = !rParentURL.isEmpty() && rParentURL != mpImpl->maViewURL;
912 catch( Exception const & )
914 // perhaps an unknown url protocol (e.g. "private:newdoc")
917 return bRet;
920 OUString SvtFileView::get_help_id() const
922 return mpImpl->mxView->get_help_id();
925 void SvtFileView::set_help_id(const OUString& rHelpId)
927 mpImpl->mxView->set_help_id(rHelpId);
930 OUString SvtFileView::get_selected_text() const
932 if (mpImpl->mxView->get_visible())
933 return mpImpl->mxView->get_selected_text();
934 return mpImpl->mxIconView->get_selected_text();
937 FileViewResult SvtFileView::Initialize(
938 const OUString& rURL,
939 const OUString& rFilter,
940 const FileViewAsyncAction* pAsyncDescriptor,
941 const css::uno::Sequence< OUString >& rDenyList )
943 weld::WaitObject aWaitCursor(mpImpl->m_pTopLevel);
944 maDenyList = rDenyList;
946 OUString sPushURL( mpImpl->maViewURL );
948 mpImpl->maViewURL = rURL;
949 FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor );
950 switch ( eResult )
952 case eFailure:
953 case eTimeout:
954 mpImpl->maViewURL = sPushURL;
955 return eResult;
957 case eStillRunning:
958 OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" );
959 [[fallthrough]];
960 case eSuccess:
961 return eResult;
964 OSL_FAIL( "SvtFileView::Initialize: unreachable!" );
965 return eFailure;
968 FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor )
970 mpImpl->maCurrentFilter = rFilter.toAsciiLowerCase();
972 mpImpl->Clear();
973 FileViewResult eResult = mpImpl->GetFolderContent_Impl(mpImpl->maViewURL, pAsyncDescriptor, maDenyList);
974 OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
975 return eResult;
978 void SvtFileView::CancelRunningAsyncAction()
980 mpImpl->CancelRunningAsyncAction();
983 void SvtFileView::SetNoSelection()
985 mpImpl->mxView->unselect_all();
986 mpImpl->mxIconView->unselect_all();
989 void SvtFileView::SetSelectHdl(const Link<SvtFileView*,void>& rHdl)
991 mpImpl->SetSelectHandler(rHdl);
994 void SvtFileView::SetDoubleClickHdl(const Link<SvtFileView*,bool>& rHdl)
996 mpImpl->SetDoubleClickHandler(rHdl);
999 sal_uInt32 SvtFileView::GetSelectionCount() const
1001 if (mpImpl->mxView->get_visible())
1002 return mpImpl->mxView->count_selected_rows();
1003 return mpImpl->mxIconView->count_selected_items();
1006 SvtContentEntry* SvtFileView::FirstSelected() const
1008 if (mpImpl->mxView->get_visible())
1010 SvtContentEntry* pRet = nullptr;
1011 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxView->make_iterator();
1012 if (mpImpl->mxView->get_selected(xEntry.get()))
1013 pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxView->get_id(*xEntry));
1014 return pRet;
1017 SvtContentEntry* pRet = nullptr;
1018 std::unique_ptr<weld::TreeIter> xEntry = mpImpl->mxIconView->make_iterator();
1019 if (mpImpl->mxIconView->get_selected(xEntry.get()))
1020 pRet = weld::fromId<SvtContentEntry*>(mpImpl->mxIconView->get_id(*xEntry));
1021 return pRet;
1024 const OUString& SvtFileView::GetViewURL() const
1026 return mpImpl->maViewURL;
1029 void SvtFileView::SetOpenDoneHdl( const Link<SvtFileView*,void>& rHdl )
1031 mpImpl->maOpenDoneLink = rHdl;
1034 void SvtFileView::EnableDelete( bool bEnable )
1036 mpImpl->EnableDelete( bEnable );
1039 void SvtFileView::EndInplaceEditing()
1041 return mpImpl->EndEditing();
1044 IMPL_LINK(SvtFileView, HeaderSelect_Impl, int, nColumn, void)
1046 sal_uInt16 nItemID = nColumn + 1;
1047 // skip "TYPE"
1048 if (!mpImpl->mxView->TypeColumnVisible() && nItemID != COLUMN_TITLE)
1049 ++nItemID;
1051 weld::TreeView* pView = mpImpl->mxView->getWidget();
1052 bool bSortAtoZ = mpImpl->mbAscending;
1054 //set new arrow positions in headerbar
1055 if (nItemID != mpImpl->mnSortColumn)
1057 // remove old indicator, new will be created in OpenFolder_Impl
1058 pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
1060 else
1061 bSortAtoZ = !bSortAtoZ;
1063 mpImpl->Resort_Impl(nItemID, bSortAtoZ);
1066 OUString SvtFileView::GetConfigString() const
1068 // sort order
1069 OUString sRet = OUString::number( mpImpl->mnSortColumn ) + ";";
1071 bool bUp = mpImpl->mbAscending;
1072 sRet += OUString::Concat(bUp ? std::u16string_view(u"1") : std::u16string_view(u"0")) + ";";
1074 weld::TreeView* pView = mpImpl->mxView->getWidget();
1075 sal_uInt16 nCount = mpImpl->mxView->TypeColumnVisible() ? 4 : 3;
1076 for (sal_uInt16 i = 0; i < nCount; ++i)
1078 sal_uInt16 nId = i + 1;
1079 // skip "TYPE"
1080 if (!mpImpl->mxView->TypeColumnVisible() && nId != COLUMN_TITLE)
1081 ++nId;
1083 sRet += OUString::number( nId )
1084 + ";"
1085 + OUString::number(pView->get_column_width(i))
1086 + ";";
1089 return comphelper::string::stripEnd(sRet, ';');
1092 ::std::vector< SvtContentEntry > SvtFileView::GetContent()
1094 ::std::vector< SvtContentEntry > aContent;
1096 for(auto const& elem : mpImpl->maContent)
1098 SvtContentEntry aEntry( elem->maTargetURL, elem->mbIsFolder );
1099 aContent.push_back( aEntry );
1102 return aContent;
1105 void SvtFileView::SetConfigString(std::u16string_view rCfgStr)
1107 sal_Int32 nIdx = 0;
1108 sal_uInt16 nSortColumn = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx )));
1109 bool bAscending = static_cast<bool>(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ))));
1111 std::vector<int> aWidths(mpImpl->mxView->TypeColumnVisible() ? 4 : 3, -1);
1113 while ( nIdx != -1 )
1115 int nItemId = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
1117 int nWidth = o3tl::toInt32(o3tl::getToken(rCfgStr, 0, ';', nIdx ));
1119 // skip "TYPE"
1120 if (!mpImpl->mxView->TypeColumnVisible() && nItemId != COLUMN_TITLE)
1121 --nItemId;
1122 int nColumn = nItemId - 1;
1124 if (nColumn >= 0 && o3tl::make_unsigned(nColumn) < aWidths.size())
1125 aWidths[nColumn] = nWidth;
1128 weld::TreeView* pView = mpImpl->mxView->getWidget();
1129 pView->set_column_fixed_widths(aWidths);
1130 if (mpImpl->mnSortColumn != nSortColumn)
1131 pView->set_sort_indicator(TRISTATE_INDET, mpImpl->GetSortColumn());
1132 mpImpl->Resort_Impl(nSortColumn, bAscending);
1135 SvtFileView_Impl::SvtFileView_Impl(SvtFileView* pAntiImpl, weld::Window* pTopLevel,
1136 std::unique_ptr<weld::TreeView> xTreeView,
1137 std::unique_ptr<weld::IconView> xIconView,
1138 Reference < XCommandEnvironment > const & xEnv,
1139 FileViewFlags nFlags, bool bOnlyFolder)
1140 : m_pAntiImpl ( pAntiImpl )
1141 , m_eAsyncActionResult ( ::svt::EnumerationResult::ERROR )
1142 , m_bRunningAsyncAction ( false )
1143 , m_bAsyncActionCancelled ( false )
1144 , m_pTopLevel ( pTopLevel )
1145 , mxView(new ViewTabListBox_Impl(std::move(xTreeView), pTopLevel, this, nFlags))
1146 , mxIconView(std::move(xIconView))
1147 , mnSortColumn ( COLUMN_TITLE )
1148 , mbAscending ( true )
1149 , mbOnlyFolder ( bOnlyFolder )
1150 , mnSuspendSelectCallback ( 0 )
1151 , mbIsFirstResort ( true )
1152 , aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
1153 , maFolderImage (RID_BMP_FOLDER)
1154 , mxCmdEnv ( xEnv )
1156 weld::TreeView* pWidget = mxView->getWidget();
1158 // set the width to something small so it's the parent that decides the final
1159 // width
1160 Size aSize(42, pWidget->get_height_rows(7));
1161 pWidget->set_size_request(aSize.Width(), aSize.Height());
1162 mxIconView->set_size_request(aSize.Width(), aSize.Height());
1165 SvtFileView_Impl::~SvtFileView_Impl()
1167 Clear();
1170 void SvtFileView_Impl::Clear()
1172 ::osl::MutexGuard aGuard( maMutex );
1174 maContent.clear();
1177 FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1178 std::u16string_view rFolder,
1179 const FileViewAsyncAction* pAsyncDescriptor,
1180 const css::uno::Sequence< OUString >& rDenyList )
1182 ::osl::ClearableMutexGuard aGuard( maMutex );
1183 INetURLObject aFolderObj( rFolder );
1184 DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
1186 FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1188 aGuard.clear();
1189 return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rDenyList );
1192 FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1193 const FolderDescriptor& _rFolder,
1194 const FileViewAsyncAction* pAsyncDescriptor,
1195 const css::uno::Sequence< OUString >& rDenyList )
1197 DBG_TESTSOLARMUTEX();
1198 ::osl::ClearableMutexGuard aGuard( maMutex );
1200 OSL_ENSURE( !m_xContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" );
1201 m_xContentEnumerator.set(new ::svt::FileViewContentEnumerator(
1202 mxView->GetCommandEnvironment(), maContent, maMutex));
1203 // TODO: should we cache and re-use this thread?
1205 if ( !pAsyncDescriptor )
1207 ::svt::EnumerationResult eResult = m_xContentEnumerator->enumerateFolderContentSync( _rFolder, rDenyList );
1208 if ( ::svt::EnumerationResult::SUCCESS == eResult )
1210 implEnumerationSuccess();
1211 m_xContentEnumerator.clear();
1212 return eSuccess;
1214 m_xContentEnumerator.clear();
1215 return eFailure;
1218 m_bRunningAsyncAction = true;
1219 m_bAsyncActionCancelled = false;
1220 m_eAsyncActionResult = ::svt::EnumerationResult::ERROR;
1221 m_aAsyncActionFinished.reset();
1223 // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler.
1224 // By definition, this handler *only* gets called when the result cannot be obtained
1225 // during the minimum wait time, so it is only set below, when needed.
1226 m_aCurrentAsyncActionHandler = Link<void*,void>();
1228 // minimum time to wait
1229 TimeValue aTimeout;
1230 sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
1231 OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
1232 if ( nMinTimeout <= 0 )
1233 nMinTimeout = sal_Int32( 1000 );
1234 aTimeout.Seconds = nMinTimeout / 1000;
1235 aTimeout.Nanosec = ( nMinTimeout % 1000 ) * 1000000;
1237 m_xContentEnumerator->enumerateFolderContent( _rFolder, this );
1239 // wait until the enumeration is finished
1240 // for this, release our own mutex (which is used by the enumerator thread)
1241 aGuard.clear();
1243 ::osl::Condition::Result eResult = ::osl::Condition::result_ok;
1245 // also release the SolarMutex. Not all code which is needed during the enumeration
1246 // is Solar-Thread-Safe, in particular there is some code which needs to access
1247 // string resources (and our resource system relies on the SolarMutex :()
1248 SolarMutexReleaser aSolarRelease;
1250 // now wait. Note that if we didn't get a pAsyncDescriptor, then this is an infinite wait.
1251 eResult = m_aAsyncActionFinished.wait( &aTimeout );
1254 ::osl::MutexGuard aGuard2( maMutex );
1255 if ( ::osl::Condition::result_timeout == eResult )
1257 // maximum time to wait
1258 OSL_ENSURE(!m_xCancelAsyncTimer,
1259 "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!");
1260 m_xCancelAsyncTimer.set(new CallbackTimer(this));
1261 sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout;
1262 OSL_ENSURE( nMaxTimeout > nMinTimeout,
1263 "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" );
1264 if ( nMaxTimeout <= nMinTimeout )
1265 nMaxTimeout = nMinTimeout + 5000;
1266 m_xCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) );
1267 // we already waited for nMinTimeout milliseconds, so take this into account
1268 m_xCancelAsyncTimer->start();
1270 m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler;
1271 DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" );
1272 maEntries.clear();
1273 mxView->clear();
1274 mxIconView->clear();
1275 return eStillRunning;
1278 m_bRunningAsyncAction = false;
1279 switch ( m_eAsyncActionResult )
1281 case ::svt::EnumerationResult::SUCCESS:
1282 return eSuccess;
1284 case ::svt::EnumerationResult::ERROR:
1285 return eFailure;
1288 SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" );
1289 return eFailure;
1292 void SvtFileView_Impl::FilterFolderContent_Impl( std::u16string_view rFilter )
1294 if ( rFilter.empty() || ( rFilter == ALL_FILES_FILTER ) )
1295 // when replacing names, there is always something to filter (no view of ".nametranslation.table")
1296 return;
1298 ::osl::MutexGuard aGuard( maMutex );
1300 if ( maContent.empty() )
1301 return;
1303 // collect the filter tokens
1304 ::std::vector< WildCard > aFilters;
1305 FilterMatch::createWildCardFilterList(rFilter,aFilters);
1308 // do the filtering
1309 std::erase_if(maContent,
1310 [&aFilters](const std::unique_ptr<SortingData_Impl>& rxContent) {
1311 if (rxContent->mbIsFolder)
1312 return false;
1313 // normalize the content title (we always match case-insensitive)
1314 // 91872 - 11.09.2001 - frank.schoenheit@sun.com
1315 OUString sCompareString = rxContent->GetFileName(); // filter works on file name, not on title!
1316 return std::none_of(aFilters.begin(), aFilters.end(), FilterMatch(sCompareString));
1320 IMPL_LINK_NOARG(SvtFileView_Impl, ChangedHdl, weld::TreeView&, void)
1322 if (!mnSuspendSelectCallback)
1323 m_aSelectHandler.Call(m_pAntiImpl);
1326 IMPL_LINK_NOARG(SvtFileView_Impl, SelectionChangedHdl, weld::IconView&, void)
1328 if (!mnSuspendSelectCallback)
1329 m_aSelectHandler.Call(m_pAntiImpl);
1332 void SvtFileView_Impl::SetSelectHandler(const Link<SvtFileView*,void>& rHdl)
1334 m_aSelectHandler = rHdl;
1336 mxView->connect_changed(LINK(this, SvtFileView_Impl, ChangedHdl));
1337 mxIconView->connect_selection_changed(LINK(this, SvtFileView_Impl, SelectionChangedHdl));
1340 IMPL_LINK_NOARG(SvtFileView_Impl, RowActivatedHdl, weld::TreeView&, bool)
1342 return maDoubleClickHandler.Call(m_pAntiImpl);
1345 IMPL_LINK_NOARG(SvtFileView_Impl, ItemActivatedHdl, weld::IconView&, bool)
1347 return maDoubleClickHandler.Call(m_pAntiImpl);
1350 void SvtFileView_Impl::SetDoubleClickHandler(const Link<SvtFileView*,bool>& rHdl)
1352 maDoubleClickHandler = rHdl;
1354 mxView->connect_row_activated(LINK(this, SvtFileView_Impl, RowActivatedHdl));
1355 mxIconView->connect_item_activated(LINK(this, SvtFileView_Impl, ItemActivatedHdl));
1358 void SvtFileView_Impl::OpenFolder_Impl()
1360 ::osl::MutexGuard aGuard( maMutex );
1362 mxView->freeze();
1363 mxIconView->freeze();
1364 maEntries.clear();
1365 mxView->clear();
1366 mxIconView->clear();
1368 for (auto const& elem : maContent)
1370 if (mbOnlyFolder && !elem->mbIsFolder)
1371 continue;
1373 // insert entry and set user data
1374 maEntries.emplace_back(std::make_unique<SvtContentEntry>(elem->maTargetURL, elem->mbIsFolder));
1375 OUString sId(weld::toId(maEntries.back().get()));
1376 mxView->append(sId, elem->maDisplayName, elem->maType, elem->maDisplaySize, elem->maDisplayDate, elem->maImage);
1377 mxIconView->append(sId, elem->maDisplayName, elem->maImage);
1380 ++mnSuspendSelectCallback;
1381 mxView->thaw();
1383 //set sort indicator
1384 weld::TreeView* pView = mxView->getWidget();
1385 pView->set_sort_indicator(mbAscending ? TRISTATE_TRUE : TRISTATE_FALSE, GetSortColumn());
1387 mxIconView->thaw();
1388 --mnSuspendSelectCallback;
1390 ResetCursor();
1393 void SvtFileView_Impl::ResetCursor()
1395 if (mxView->get_visible())
1397 std::unique_ptr<weld::TreeIter> xFirst = mxView->make_iterator();
1398 if (mxView->get_iter_first(*xFirst))
1400 // set cursor to the first entry
1401 mxView->set_cursor(*xFirst);
1403 // deselect
1404 mxView->unselect_all();
1406 else
1408 std::unique_ptr<weld::TreeIter> xFirst = mxIconView->make_iterator();
1409 if (mxIconView->get_iter_first(*xFirst))
1411 // set cursor to the first entry
1412 mxIconView->set_cursor(*xFirst);
1414 // deselect
1415 mxIconView->unselect_all();
1419 void SvtFileView_Impl::CancelRunningAsyncAction()
1421 DBG_TESTSOLARMUTEX();
1422 ::osl::MutexGuard aGuard( maMutex );
1423 if ( !m_xContentEnumerator.is() )
1424 return;
1426 m_bAsyncActionCancelled = true;
1427 m_xContentEnumerator->cancel();
1428 m_bRunningAsyncAction = false;
1430 m_xContentEnumerator.clear();
1431 if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
1432 m_xCancelAsyncTimer->stop();
1433 m_xCancelAsyncTimer.clear();
1437 void SvtFileView_Impl::onTimeout()
1439 SolarMutexGuard aSolarGuard;
1440 ::osl::MutexGuard aGuard( maMutex );
1441 if ( !m_bRunningAsyncAction )
1442 // there might have been a race condition while we waited for the mutex
1443 return;
1445 CancelRunningAsyncAction();
1447 if ( m_aCurrentAsyncActionHandler.IsSet() )
1449 Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) );
1450 m_aCurrentAsyncActionHandler = Link<void*,void>();
1455 void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult )
1457 SolarMutexGuard aSolarGuard;
1458 ::osl::MutexGuard aGuard( maMutex );
1460 m_xContentEnumerator.clear();
1461 if ( m_xCancelAsyncTimer.is() && m_xCancelAsyncTimer->isTicking() )
1462 m_xCancelAsyncTimer->stop();
1463 m_xCancelAsyncTimer.clear();
1465 if ( m_bAsyncActionCancelled )
1466 // this is to prevent race conditions
1467 return;
1469 m_eAsyncActionResult = eResult;
1470 m_bRunningAsyncAction = false;
1472 m_aAsyncActionFinished.set();
1474 if ( svt::EnumerationResult::SUCCESS == eResult )
1475 implEnumerationSuccess();
1477 if ( m_aCurrentAsyncActionHandler.IsSet() )
1479 Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) );
1480 m_aCurrentAsyncActionHandler = Link<void*,void>();
1485 void SvtFileView_Impl::implEnumerationSuccess()
1487 FilterFolderContent_Impl( maCurrentFilter );
1488 SortFolderContent_Impl();
1489 CreateDisplayText_Impl();
1490 OpenFolder_Impl();
1491 maOpenDoneLink.Call( m_pAntiImpl );
1494 OUString SvtFileView_Impl::ReplaceTabWithString(const OUString& rValue)
1496 return rValue.replaceAll(u"\t", u"%09");
1499 void SvtFileView_Impl::CreateDisplayText_Impl()
1501 ::osl::MutexGuard aGuard( maMutex );
1503 for (auto const& elem : maContent)
1505 // title, type, size, date
1506 elem->maDisplayName = ReplaceTabWithString(elem->GetTitle());
1507 // folders don't have a size
1508 if ( ! elem->mbIsFolder )
1509 elem->maDisplaySize = CreateExactSizeText( elem->maSize );
1510 // set the date, but volumes have no date
1511 if ( ! elem->mbIsFolder || ! elem->mbIsVolume )
1513 SvtSysLocale aSysLocale;
1514 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1515 elem->maDisplayDate = rLocaleData.getDate( elem->maModDate )
1516 + ", "
1517 + rLocaleData.getTime( elem->maModDate, false );
1520 // detect image
1521 if ( elem->mbIsFolder )
1523 ::svtools::VolumeInfo aVolInfo( elem->mbIsVolume, elem->mbIsRemote,
1524 elem->mbIsRemoveable, elem->mbIsFloppy,
1525 elem->mbIsCompactDisc );
1526 elem->maImage = SvFileInformationManager::GetFolderImageId(aVolInfo);
1528 else
1529 elem->maImage = SvFileInformationManager::GetFileImageId(INetURLObject(elem->maTargetURL));
1533 void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending )
1535 // TODO: IconView ()
1536 ::osl::MutexGuard aGuard( maMutex );
1538 if ( ( nColumn == mnSortColumn ) &&
1539 ( bAscending == mbAscending ) )
1540 return;
1542 // reset the quick search index
1543 mxView->ResetQuickSearch_Impl( nullptr );
1545 std::unique_ptr<weld::TreeIter> xEntry(mxView->make_iterator());
1546 bool bEntry = mxView->get_cursor(xEntry.get());
1548 OUString aEntryURL;
1549 if (bEntry && !mxView->get_id(*xEntry).isEmpty())
1550 aEntryURL = weld::fromId<SvtContentEntry*>(mxView->get_id(*xEntry))->maURL;
1552 mnSortColumn = nColumn;
1553 mbAscending = bAscending;
1555 SortFolderContent_Impl();
1556 OpenFolder_Impl();
1558 if ( !mbIsFirstResort )
1560 int nPos = GetEntryPos( aEntryURL );
1561 if (nPos != -1 && nPos < mxView->n_children())
1563 ++mnSuspendSelectCallback; // #i15668#
1564 mxView->set_cursor(nPos);
1565 --mnSuspendSelectCallback;
1568 else
1569 mbIsFirstResort = false;
1572 static bool gbAscending = true;
1573 static sal_Int16 gnColumn = COLUMN_TITLE;
1574 static const CollatorWrapper* pCollatorWrapper = nullptr;
1576 /* this function returns true, if aOne is less than aTwo
1578 static bool CompareSortingData_Impl( std::unique_ptr<SortingData_Impl> const & aOne, std::unique_ptr<SortingData_Impl> const & aTwo )
1580 assert(pCollatorWrapper && "*CompareSortingData_Impl(): Can't work this way!");
1582 sal_Int32 nComp;
1583 bool bRet = false;
1584 bool bEqual = false;
1586 if ( aOne->mbIsFolder != aTwo->mbIsFolder )
1588 bRet = aOne->mbIsFolder;
1590 // !!! pb: #100376# folder always on top
1591 if ( !gbAscending )
1592 bRet = !bRet;
1594 else
1596 switch ( gnColumn )
1598 case COLUMN_TITLE:
1599 // compare case insensitive first
1600 nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() );
1602 if ( nComp == 0 )
1603 nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() );
1605 if ( nComp < 0 )
1606 bRet = true;
1607 else if ( nComp > 0 )
1608 bRet = false;
1609 else
1610 bEqual = true;
1611 break;
1612 case COLUMN_TYPE:
1613 nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType );
1614 if ( nComp < 0 )
1615 bRet = true;
1616 else if ( nComp > 0 )
1617 bRet = false;
1618 else
1619 bEqual = true;
1620 break;
1621 case COLUMN_SIZE:
1622 if ( aOne->maSize < aTwo->maSize )
1623 bRet = true;
1624 else if ( aOne->maSize > aTwo->maSize )
1625 bRet = false;
1626 else
1627 bEqual = true;
1628 break;
1629 case COLUMN_DATE:
1630 if ( aOne->maModDate < aTwo->maModDate )
1631 bRet = true;
1632 else if ( aOne->maModDate > aTwo->maModDate )
1633 bRet = false;
1634 else
1635 bEqual = true;
1636 break;
1637 default:
1638 SAL_INFO( "svtools.contnr", "CompareSortingData_Impl: Compare unknown type!" );
1639 bRet = false;
1643 // when the two elements are equal, we must not return sal_True (which would
1644 // happen if we just return ! ( a < b ) when not sorting ascending )
1645 if ( bEqual )
1646 return false;
1648 return gbAscending ? bRet : !bRet;
1652 void SvtFileView_Impl::SortFolderContent_Impl()
1654 ::osl::MutexGuard aGuard( maMutex );
1656 if ( maContent.size() > 1 )
1658 gbAscending = mbAscending;
1659 gnColumn = mnSortColumn;
1660 pCollatorWrapper = aIntlWrapper.getCaseCollator();
1662 std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl );
1664 pCollatorWrapper = nullptr;
1669 void SvtFileView_Impl::EntryRemoved( std::u16string_view rURL )
1671 ::osl::MutexGuard aGuard( maMutex );
1673 maContent.erase(std::find_if(maContent.begin(), maContent.end(),
1674 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; }));
1678 void SvtFileView_Impl::EntryRenamed( OUString& rURL,
1679 const OUString& rTitle )
1681 ::osl::MutexGuard aGuard( maMutex );
1683 auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
1684 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
1685 if (aFoundElem != maContent.end())
1687 (*aFoundElem)->SetNewTitle( rTitle );
1688 (*aFoundElem)->maDisplayName = ReplaceTabWithString(rTitle);
1690 INetURLObject aURLObj( rURL );
1691 aURLObj.setName( rTitle, INetURLObject::EncodeMechanism::All );
1693 rURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1695 (*aFoundElem)->maTargetURL = rURL;
1699 const SortingData_Impl& SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
1701 ::osl::MutexGuard aGuard( maMutex );
1703 std::unique_ptr<SortingData_Impl> pData(new SortingData_Impl);
1705 pData->SetNewTitle( rTitle );
1706 pData->maSize = 0;
1707 pData->mbIsFolder = true;
1708 pData->maTargetURL = rURL;
1710 ::svtools::VolumeInfo aVolInfo;
1711 pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
1712 pData->maImage = SvFileInformationManager::GetFolderImageId( aVolInfo );
1714 // title, type, size, date
1715 pData->maDisplayName = ReplaceTabWithString(pData->GetTitle());
1716 // set the date
1717 SvtSysLocale aSysLocale;
1718 const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
1719 pData->maDisplayDate = rLocaleData.getDate( pData->maModDate )
1720 + ", "
1721 + rLocaleData.getTime( pData->maModDate );
1723 maContent.push_back( std::move(pData) );
1725 return *maContent.back();
1728 int SvtFileView_Impl::GetEntryPos(std::u16string_view rURL)
1730 ::osl::MutexGuard aGuard( maMutex );
1732 auto aFoundElem = std::find_if(maContent.begin(), maContent.end(),
1733 [&](const std::unique_ptr<SortingData_Impl> & data) { return data->maTargetURL == rURL; });
1734 return aFoundElem != maContent.end() ? std::distance(maContent.begin(), aFoundElem) : -1;
1737 void SvtFileView_Impl::SetViewMode( FileViewMode eMode )
1739 switch ( eMode )
1741 case eDetailedList:
1742 mxView->show();
1743 mxIconView->hide();
1744 break;
1746 case eIcon:
1747 mxView->hide();
1748 mxIconView->show();
1749 break;
1751 default:
1752 mxView->show();
1753 mxIconView->hide();
1757 bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, std::u16string_view rTitle, bool bWrapAround )
1759 ::osl::MutexGuard aGuard( maMutex );
1761 sal_uInt32 nEnd = maContent.size();
1762 sal_uInt32 nStart = nIndex;
1763 while ( nIndex < nEnd )
1765 SortingData_Impl* pData = maContent[ nIndex ].get();
1766 if ( pData->GetLowerTitle().startsWith( rTitle ) )
1767 return true;
1768 ++nIndex;
1771 if ( bWrapAround )
1773 nIndex = 0;
1774 while ( nIndex < nEnd && nIndex <= nStart )
1776 SortingData_Impl* pData = maContent[ nIndex ].get();
1777 if ( pData->GetLowerTitle().startsWith( rTitle ) )
1778 return true;
1779 ++nIndex;
1783 return false;
1786 namespace {
1787 void SAL_CALL CallbackTimer::onShot()
1789 OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" );
1790 SvtFileView_Impl* pHandler( m_pTimeoutHandler );
1791 if ( pHandler )
1792 pHandler->onTimeout();
1796 void SvtFileView::selected_foreach(const std::function<bool(weld::TreeIter&)>& func)
1798 if (mpImpl->mxView->get_visible())
1799 mpImpl->mxView->selected_foreach(func);
1800 else
1801 mpImpl->mxIconView->selected_foreach(func);
1804 weld::Widget* SvtFileView::identifier() const
1806 return mpImpl->mxView->getWidget();
1809 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */