Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "fileview.hxx"
21 : #include <sal/config.h>
22 : #include <svtools/treelistentry.hxx>
23 : #include <svtools/fileview.hxx>
24 : #include <svtools/svtresid.hxx>
25 : #include <svtools/imagemgr.hxx>
26 : #include <svtools/headbar.hxx>
27 : #include <svtools/svtabbx.hxx>
28 : #include <svtools/svtools.hrc>
29 : #include <svtools/viewdataentry.hxx>
30 : #include "fileview.hrc"
31 : #include "contentenumeration.hxx"
32 : #include <svtools/AccessibleBrowseBoxObjType.hxx>
33 : #include <com/sun/star/util/DateTime.hpp>
34 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
35 : #include <com/sun/star/task/InteractionHandler.hpp>
36 : #include <com/sun/star/ucb/XProgressHandler.hpp>
37 : #include <com/sun/star/sdbc/XResultSet.hpp>
38 : #include <com/sun/star/ucb/XAnyCompareFactory.hpp>
39 : #include <com/sun/star/ucb/XContentAccess.hpp>
40 : #include <com/sun/star/ucb/XDynamicResultSet.hpp>
41 : #include <com/sun/star/ucb/XSortedDynamicResultSetFactory.hpp>
42 : #include <com/sun/star/sdbc/XRow.hpp>
43 : #include <com/sun/star/container/XChild.hpp>
44 : #include <com/sun/star/ucb/CommandAbortedException.hpp>
45 : #include <com/sun/star/ucb/ContentCreationException.hpp>
46 : #include <vcl/waitobj.hxx>
47 : #include <vcl/settings.hxx>
48 : #include <com/sun/star/io/XPersist.hpp>
49 : #include <com/sun/star/beans/XPropertySet.hpp>
50 : #include <com/sun/star/ucb/XCommandInfo.hpp>
51 : #include <com/sun/star/beans/XPropertySetInfo.hpp>
52 : #include <com/sun/star/beans/PropertyAttribute.hpp>
53 :
54 : #include <algorithm>
55 : #include <vector>
56 : #include <tools/urlobj.hxx>
57 : #include <comphelper/processfactory.hxx>
58 : #include <comphelper/string.hxx>
59 : #include <unotools/localfilehelper.hxx>
60 : #include <ucbhelper/content.hxx>
61 : #include <ucbhelper/commandenvironment.hxx>
62 : #include <vcl/layout.hxx>
63 : #include <rtl/math.hxx>
64 : #include <tools/config.hxx>
65 : #include <osl/mutex.hxx>
66 : #include <osl/conditn.hxx>
67 : #include <salhelper/timer.hxx>
68 : #include <vcl/svapp.hxx>
69 : #include <vcl/builderfactory.hxx>
70 : #include <unotools/ucbhelper.hxx>
71 : #include <unotools/intlwrapper.hxx>
72 : #include <unotools/syslocale.hxx>
73 : #include <svl/urlfilter.hxx>
74 : #include <boost/ptr_container/ptr_set.hpp>
75 : #include <boost/scoped_ptr.hpp>
76 : #include <o3tl/typed_flags_set.hxx>
77 :
78 : using namespace ::com::sun::star::lang;
79 : using namespace ::com::sun::star::sdbc;
80 : using namespace ::com::sun::star::task;
81 : using namespace ::com::sun::star::ucb;
82 : using namespace ::com::sun::star::uno;
83 : using namespace ::com::sun::star::io;
84 : using namespace ::com::sun::star::beans;
85 : using namespace ::comphelper;
86 : using ::svt::SortingData_Impl;
87 : using ::svt::FolderDescriptor;
88 :
89 : #define ALL_FILES_FILTER "*.*"
90 :
91 : #define COLUMN_TITLE 1
92 : #define COLUMN_TYPE 2
93 : #define COLUMN_SIZE 3
94 : #define COLUMN_DATE 4
95 :
96 : #define ROW_HEIGHT 17 // the height of a row has to be a little higher than the bitmap
97 : #define QUICK_SEARCH_TIMEOUT 1500 // time in mSec before the quicksearch string will be reseted
98 :
99 : enum class FileViewFlags
100 : {
101 : NONE = 0x00,
102 : ONLYFOLDER = 0x01,
103 : MULTISELECTION = 0x02,
104 : SHOW_ONLYTITLE = 0x10,
105 : SHOW_NONE = 0x20,
106 : };
107 : namespace o3tl
108 : {
109 : template<> struct typed_flags<FileViewFlags> : is_typed_flags<FileViewFlags, 0x1f> {};
110 : }
111 :
112 : namespace
113 : {
114 :
115 : //= ITimeoutHandler
116 :
117 : class CallbackTimer;
118 0 : class ITimeoutHandler
119 : {
120 : public:
121 : virtual void onTimeout( CallbackTimer* _pInstigator ) = 0;
122 :
123 : protected:
124 0 : ~ITimeoutHandler() {}
125 : };
126 :
127 :
128 : //= CallbackTimer
129 :
130 0 : class CallbackTimer : public ::salhelper::Timer
131 : {
132 : protected:
133 : ITimeoutHandler* m_pTimeoutHandler;
134 :
135 : public:
136 0 : CallbackTimer( ITimeoutHandler* _pHandler ) : m_pTimeoutHandler( _pHandler ) { }
137 :
138 : protected:
139 : virtual void SAL_CALL onShot() SAL_OVERRIDE;
140 : };
141 :
142 :
143 0 : void SAL_CALL CallbackTimer::onShot()
144 : {
145 : OSL_ENSURE( m_pTimeoutHandler, "CallbackTimer::onShot: nobody interested in?" );
146 0 : ITimeoutHandler* pHandler( m_pTimeoutHandler );
147 0 : if ( pHandler )
148 0 : pHandler->onTimeout( this );
149 0 : }
150 :
151 : }
152 :
153 :
154 :
155 0 : void FilterMatch::createWildCardFilterList(const OUString& _rFilterList,::std::vector< WildCard >& _rFilters)
156 : {
157 0 : if( _rFilterList.getLength() )
158 : {
159 : // filter is given
160 0 : sal_Int32 nIndex = 0;
161 0 : OUString sToken;
162 0 : do
163 : {
164 0 : sToken = _rFilterList.getToken( 0, ';', nIndex );
165 0 : if ( !sToken.isEmpty() )
166 : {
167 0 : _rFilters.push_back( WildCard( sToken.toAsciiUpperCase() ) );
168 : }
169 : }
170 0 : while ( nIndex >= 0 );
171 : }
172 : else
173 : {
174 : // no filter is given -> match all
175 0 : _rFilters.push_back( WildCard(OUString("*")) );
176 : }
177 0 : }
178 : // class ViewTabListBox_Impl ---------------------------------------------
179 :
180 : class ViewTabListBox_Impl : public SvHeaderTabListBox
181 : {
182 : private:
183 : Reference< XCommandEnvironment > mxCmdEnv;
184 :
185 : ::osl::Mutex maMutex;
186 : VclPtr<HeaderBar> mpHeaderBar;
187 : SvtFileView_Impl* mpParent;
188 : Timer maResetQuickSearch;
189 : OUString maQuickSearchText;
190 : OUString msAccessibleDescText;
191 : OUString msFolder;
192 : OUString msFile;
193 : sal_uInt32 mnSearchIndex;
194 : bool mbResizeDisabled : 1;
195 : bool mbAutoResize : 1;
196 : bool mbEnableDelete : 1;
197 : bool mbEnableRename : 1;
198 : bool mbShowHeader;
199 :
200 : void DeleteEntries();
201 : void DoQuickSearch( const sal_Unicode& rChar );
202 : bool Kill( const OUString& rURL );
203 :
204 : protected:
205 : virtual bool DoubleClickHdl() SAL_OVERRIDE;
206 : virtual OUString GetAccessibleObjectDescription( ::svt::AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos ) const SAL_OVERRIDE;
207 :
208 : public:
209 : ViewTabListBox_Impl( vcl::Window* pParentWin, SvtFileView_Impl* pParent, FileViewFlags nFlags );
210 : virtual ~ViewTabListBox_Impl();
211 : virtual void dispose() SAL_OVERRIDE;
212 :
213 : virtual void Resize() SAL_OVERRIDE;
214 : virtual void KeyInput( const KeyEvent& rKEvt ) SAL_OVERRIDE;
215 : virtual bool EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText ) SAL_OVERRIDE;
216 :
217 : void ClearAll();
218 0 : HeaderBar* GetHeaderBar() const { return mpHeaderBar; }
219 :
220 0 : void EnableAutoResize() { mbAutoResize = true; }
221 0 : void EnableDelete( bool bEnable ) { mbEnableDelete = bEnable; }
222 :
223 0 : Reference< XCommandEnvironment > GetCommandEnvironment() const { return mxCmdEnv; }
224 :
225 : DECL_LINK_TYPED(ResetQuickSearch_Impl, Timer *, void);
226 :
227 : virtual PopupMenu* CreateContextMenu() SAL_OVERRIDE;
228 : virtual void ExcecuteContextMenuAction( sal_uInt16 nSelectedPopentry ) SAL_OVERRIDE;
229 : };
230 :
231 : // class HashedEntry --------------------------------------------------
232 :
233 : class HashedEntry
234 : { // just a special String which can be compared on equality much faster
235 : protected:
236 : OUString maName;
237 : sal_Int32 mnHashCode;
238 : public:
239 : inline HashedEntry( const OUString& rName );
240 : inline HashedEntry( const INetURLObject& rURL );
241 : virtual ~HashedEntry();
242 :
243 : inline bool operator ==( const HashedEntry& rRef ) const;
244 : inline bool operator !=( const HashedEntry& rRef ) const;
245 : inline bool operator <( const HashedEntry& rRef ) const;
246 : };
247 :
248 0 : inline HashedEntry::HashedEntry( const OUString& rName ): maName( rName ), mnHashCode( rName.hashCode() )
249 : {
250 0 : }
251 :
252 0 : inline HashedEntry::HashedEntry( const INetURLObject& rURL ):
253 : maName( rURL.GetMainURL( INetURLObject::NO_DECODE ) ),
254 0 : mnHashCode( maName.hashCode() )
255 : {
256 0 : }
257 :
258 0 : HashedEntry::~HashedEntry()
259 : {
260 0 : }
261 :
262 0 : inline bool HashedEntry::operator ==( const HashedEntry& rRef ) const
263 : {
264 0 : return mnHashCode == rRef.mnHashCode && maName == rRef.maName;
265 : }
266 :
267 0 : inline bool HashedEntry::operator !=( const HashedEntry& rRef ) const
268 : {
269 0 : return mnHashCode != rRef.mnHashCode || maName != rRef.maName;
270 : }
271 :
272 0 : inline bool HashedEntry::operator <( const HashedEntry& rRef ) const
273 : {
274 0 : if( mnHashCode == rRef.mnHashCode )
275 0 : return maName.reverseCompareTo( rRef.maName ) < 0;
276 : else
277 0 : return mnHashCode < rRef.mnHashCode;
278 : }
279 :
280 : // class NameTranslationEntry -----------------------------------------
281 :
282 0 : class NameTranslationEntry : public HashedEntry
283 : {// a fast comparable String and another String, which is used to get a substitution for a given String
284 : protected:
285 : OUString maTranslatedName;
286 : public:
287 : inline NameTranslationEntry( const OString& rOriginalName, const OString& rTranslatedName );
288 :
289 : inline const OUString& GetTranslation() const;
290 : };
291 :
292 0 : inline NameTranslationEntry::NameTranslationEntry( const OString& rOrg, const OString& rTrans )
293 : : HashedEntry(OStringToOUString(rOrg, RTL_TEXTENCODING_ASCII_US))
294 0 : , maTranslatedName(OStringToOUString(rTrans, RTL_TEXTENCODING_UTF8))
295 : {
296 0 : }
297 :
298 0 : inline const OUString& NameTranslationEntry::GetTranslation() const
299 : {
300 0 : return maTranslatedName;
301 : }
302 :
303 : // class NameTranslationList -----------------------------------------
304 : // provides a list of _unique_ Entries
305 0 : class NameTranslationList : protected boost::ptr_set<HashedEntry>
306 : { // contains a list of substitutes of strings for a given folder (as URL)
307 : // explanation of the circumstances see in remarks for Init();
308 : protected:
309 : INetURLObject maTransFile; // URL of file with translation entries
310 : HashedEntry maHashedURL; // for future purposes when dealing with a set of cached
311 : // NameTranslationLists
312 : private:
313 : const OUString maTransFileName;
314 : void Init(); // reads the translation file and fills the (internal) list
315 :
316 : public:
317 : NameTranslationList( const INetURLObject& rBaseURL );
318 : // rBaseURL: path to folder for which the translation of the entries
319 : // should be done
320 :
321 : using boost::ptr_set<HashedEntry>::operator==;
322 : using boost::ptr_set<HashedEntry>::operator!=;
323 : inline bool operator !=( const HashedEntry& rRef ) const;
324 :
325 : const OUString* Translate( const OUString& rName ) const;
326 : // returns NULL, if rName can't be found
327 :
328 : inline const OUString& GetTransTableFileName() const;
329 : // returns the name for the file, which contains the translation strings
330 : };
331 :
332 0 : inline const OUString& NameTranslationList::GetTransTableFileName() const
333 : {
334 0 : return maTransFileName;
335 : }
336 :
337 0 : void NameTranslationList::Init()
338 : {
339 : // Tries to read the file ".nametranslation.table" in the base folder. Complete path/name is in maTransFile.
340 : // Further on, the found entries in the section "TRANSLATIONNAMES" are used to replace names in the
341 : // base folder by translated ones. The translation must be given in UTF8
342 : // See examples of such a files in the samples-folder of an Office installation
343 :
344 : try
345 : {
346 0 : ::ucbhelper::Content aTestContent( maTransFile.GetMainURL( INetURLObject::NO_DECODE ), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
347 :
348 0 : if( aTestContent.isDocument() )
349 : {
350 : // ... also tests the existence of maTransFile by throwing an Exception
351 0 : OUString aFsysName( maTransFile.getFSysPath( INetURLObject::FSYS_DETECT ) );
352 0 : Config aConfig( aFsysName );
353 :
354 0 : aConfig.SetGroup( OString("TRANSLATIONNAMES") );
355 :
356 0 : sal_uInt16 nKeyCnt = aConfig.GetKeyCount();
357 :
358 0 : for( sal_uInt16 nCnt = 0 ; nCnt < nKeyCnt ; ++nCnt )
359 0 : insert( new NameTranslationEntry( aConfig.GetKeyName( nCnt ), aConfig.ReadKey( nCnt ) ) );
360 0 : }
361 : }
362 0 : catch( Exception const & ) {}
363 0 : }
364 :
365 0 : NameTranslationList::NameTranslationList( const INetURLObject& rBaseURL ):
366 : maTransFile( rBaseURL ),
367 : maHashedURL( rBaseURL ),
368 0 : maTransFileName( OUString(".nametranslation.table") )
369 : {
370 0 : maTransFile.insertName( maTransFileName );
371 0 : Init();
372 0 : }
373 :
374 0 : inline bool NameTranslationList::operator !=( const HashedEntry& rRef ) const
375 : {
376 0 : return maHashedURL != rRef;
377 : }
378 :
379 0 : const OUString* NameTranslationList::Translate( const OUString& rName ) const
380 : {
381 0 : HashedEntry aRef( rName );
382 0 : const NameTranslationEntry* pSearch = NULL;
383 0 : for( const_iterator it = begin(); it != end(); ++it )
384 0 : if( (*it) == aRef )
385 : {
386 0 : pSearch = static_cast<const NameTranslationEntry*>(&*it);
387 : }
388 :
389 0 : return pSearch ? &pSearch->GetTranslation() : NULL;
390 : }
391 :
392 : // class NameTranslator_Impl ------------------------------------------
393 :
394 : // enables the user to get string substitutions (translations for the content) for a given folder
395 : // see more explanations above in the description for NameTranslationList
396 : class NameTranslator_Impl : public ::svt::IContentTitleTranslation
397 : {
398 : private:
399 : NameTranslationList* mpActFolder;
400 : public:
401 : NameTranslator_Impl( const INetURLObject& rActualFolder );
402 : virtual ~NameTranslator_Impl();
403 :
404 : // IContentTitleTranslation
405 : virtual bool GetTranslation( const OUString& rOriginalName, OUString& rTranslatedName ) const SAL_OVERRIDE;
406 :
407 : void SetActualFolder( const INetURLObject& rActualFolder );
408 : const OUString* GetTransTableFileName() const;
409 : // returns the name for the file, which contains the translation strings
410 : };
411 :
412 :
413 : //= SvtFileView_Impl
414 :
415 :
416 : class SvtFileView_Impl :public ::svt::IEnumerationResultHandler
417 : ,public ITimeoutHandler
418 : {
419 : protected:
420 : VclPtr<SvtFileView> mpAntiImpl;
421 : Link<> m_aSelectHandler;
422 :
423 : ::rtl::Reference< ::svt::FileViewContentEnumerator >
424 : m_pContentEnumerator;
425 : Link<> m_aCurrentAsyncActionHandler;
426 : ::osl::Condition m_aAsyncActionFinished;
427 : ::rtl::Reference< ::salhelper::Timer > m_pCancelAsyncTimer;
428 : ::svt::EnumerationResult m_eAsyncActionResult;
429 : bool m_bRunningAsyncAction;
430 : bool m_bAsyncActionCancelled;
431 :
432 :
433 : public:
434 :
435 : ::std::vector< SortingData_Impl* > maContent;
436 : ::osl::Mutex maMutex;
437 :
438 : VclPtr<ViewTabListBox_Impl> mpView;
439 : NameTranslator_Impl* mpNameTrans;
440 : sal_uInt16 mnSortColumn;
441 : bool mbAscending : 1;
442 : bool mbOnlyFolder : 1;
443 : bool mbReplaceNames : 1; // translate folder names or display doc-title instead of file name
444 : sal_Int16 mnSuspendSelectCallback : 1;
445 : bool mbIsFirstResort : 1;
446 :
447 : IntlWrapper aIntlWrapper;
448 :
449 : OUString maViewURL;
450 : OUString maAllFilter;
451 : OUString maCurrentFilter;
452 : Image maFolderImage;
453 : Link<> maOpenDoneLink;
454 : Reference< XCommandEnvironment > mxCmdEnv;
455 :
456 : SvtFileView_Impl( SvtFileView* pAntiImpl, Reference < XCommandEnvironment > xEnv,
457 : FileViewFlags nFlags,
458 : bool bOnlyFolder );
459 : virtual ~SvtFileView_Impl();
460 :
461 : void Clear();
462 :
463 : FileViewResult GetFolderContent_Impl(
464 : const OUString& rFolder,
465 : const FileViewAsyncAction* pAsyncDescriptor,
466 : const ::com::sun::star::uno::Sequence< OUString >& rBlackList = ::com::sun::star::uno::Sequence< OUString >() );
467 :
468 : FileViewResult GetFolderContent_Impl(
469 : const FolderDescriptor& _rFolder,
470 : const FileViewAsyncAction* pAsyncDescriptor,
471 : const ::com::sun::star::uno::Sequence< OUString >& rBlackList = ::com::sun::star::uno::Sequence< OUString >());
472 : void FilterFolderContent_Impl( const OUString &rFilter );
473 : void CancelRunningAsyncAction();
474 :
475 : void OpenFolder_Impl();
476 : // #83004# -------
477 : static void ReplaceTabWithString( OUString& aValue );
478 : void CreateDisplayText_Impl();
479 : void SortFolderContent_Impl();
480 :
481 : void EntryRemoved( const OUString& rURL );
482 : void EntryRenamed( OUString& rURL,
483 : const OUString& rName );
484 : OUString FolderInserted( const OUString& rURL,
485 : const OUString& rTitle );
486 :
487 : sal_uLong GetEntryPos( const OUString& rURL );
488 :
489 : inline void EnableDelete( bool bEnable );
490 :
491 : void Resort_Impl( sal_Int16 nColumn, bool bAscending );
492 : bool SearchNextEntry( sal_uInt32 &nIndex,
493 : const OUString& rTitle,
494 : bool bWrapAround );
495 :
496 : void SetActualFolder( const INetURLObject& rActualFolder );
497 :
498 : void SetSelectHandler( const Link<>& _rHdl );
499 :
500 : void InitSelection();
501 : void ResetCursor();
502 :
503 : inline void EndEditing( bool _bCancel );
504 :
505 : protected:
506 : DECL_LINK( SelectionMultiplexer, void* );
507 :
508 : protected:
509 : // IEnumerationResultHandler overridables
510 : virtual void enumerationDone( ::svt::EnumerationResult eResult ) SAL_OVERRIDE;
511 : void implEnumerationSuccess();
512 :
513 : // ITimeoutHandler
514 : virtual void onTimeout( CallbackTimer* _pInstigator ) SAL_OVERRIDE;
515 : };
516 :
517 0 : inline void SvtFileView_Impl::EnableDelete( bool bEnable )
518 : {
519 0 : mpView->EnableDelete( bEnable );
520 0 : if( bEnable )
521 0 : mbReplaceNames = false;
522 0 : }
523 :
524 0 : inline void SvtFileView_Impl::EndEditing( bool _bCancel )
525 : {
526 0 : if ( mpView->IsEditingActive() )
527 0 : mpView->EndEditing(_bCancel);
528 0 : }
529 :
530 : // functions -------------------------------------------------------------
531 :
532 0 : OUString CreateExactSizeText( sal_Int64 nSize )
533 : {
534 0 : double fSize( ( double ) nSize );
535 : int nDec;
536 :
537 0 : long nMega = 1024 * 1024;
538 0 : long nGiga = nMega * 1024;
539 :
540 0 : OUString aUnitStr(' ');
541 :
542 0 : if ( nSize < 10000 )
543 : {
544 0 : aUnitStr += SVT_RESSTR(STR_SVT_BYTES );
545 0 : nDec = 0;
546 : }
547 0 : else if ( nSize < nMega )
548 : {
549 0 : fSize /= 1024;
550 0 : aUnitStr += SVT_RESSTR(STR_SVT_KB);
551 0 : nDec = 1;
552 : }
553 0 : else if ( nSize < nGiga )
554 : {
555 0 : fSize /= nMega;
556 0 : aUnitStr += SVT_RESSTR(STR_SVT_MB);
557 0 : nDec = 2;
558 : }
559 : else
560 : {
561 0 : fSize /= nGiga;
562 0 : aUnitStr += SVT_RESSTR(STR_SVT_GB);
563 0 : nDec = 3;
564 : }
565 :
566 : OUString aSizeStr( ::rtl::math::doubleToUString( fSize,
567 : rtl_math_StringFormat_F, nDec,
568 0 : SvtSysLocale().GetLocaleData().getNumDecimalSep()[0]) );
569 0 : aSizeStr += aUnitStr;
570 :
571 0 : return aSizeStr;
572 : }
573 :
574 :
575 : // class ViewTabListBox_Impl ---------------------------------------------
576 :
577 :
578 0 : ViewTabListBox_Impl::ViewTabListBox_Impl( vcl::Window* pParentWin,
579 : SvtFileView_Impl* pParent,
580 : FileViewFlags nFlags ) :
581 :
582 : SvHeaderTabListBox( pParentWin, WB_TABSTOP ),
583 :
584 : mpHeaderBar ( NULL ),
585 : mpParent ( pParent ),
586 : msAccessibleDescText( SVT_RESSTR(STR_SVT_ACC_DESC_FILEVIEW) ),
587 : msFolder ( SVT_RESSTR(STR_SVT_ACC_DESC_FOLDER) ),
588 : msFile ( SVT_RESSTR(STR_SVT_ACC_DESC_FILE) ),
589 : mnSearchIndex ( 0 ),
590 : mbResizeDisabled ( false ),
591 : mbAutoResize ( false ),
592 : mbEnableDelete ( false ),
593 : mbEnableRename ( true ),
594 0 : mbShowHeader ( !(nFlags & FileViewFlags::SHOW_NONE) )
595 : {
596 0 : Size aBoxSize = pParentWin->GetSizePixel();
597 0 : mpHeaderBar = VclPtr<HeaderBar>::Create( pParentWin, WB_BUTTONSTYLE | WB_BOTTOMBORDER );
598 0 : mpHeaderBar->SetPosSizePixel( Point( 0, 0 ), mpHeaderBar->CalcWindowSizePixel() );
599 :
600 0 : HeaderBarItemBits nBits = ( HeaderBarItemBits::LEFT | HeaderBarItemBits::VCENTER | HeaderBarItemBits::CLICKABLE );
601 0 : if (nFlags & FileViewFlags::SHOW_ONLYTITLE)
602 : {
603 0 : long pTabs[] = { 2, 20, 600 };
604 0 : SetTabs(&pTabs[0], MAP_PIXEL);
605 :
606 0 : mpHeaderBar->InsertItem(COLUMN_TITLE, SVT_RESSTR(STR_SVT_FILEVIEW_COLUMN_TITLE), 600, nBits | HeaderBarItemBits::UPARROW);
607 : }
608 : else
609 : {
610 0 : long pTabs[] = { 5, 20, 180, 320, 400, 600 };
611 0 : SetTabs(&pTabs[0], MAP_PIXEL);
612 0 : SetTabJustify(2, AdjustRight); // column "Size"
613 :
614 0 : mpHeaderBar->InsertItem(COLUMN_TITLE, SVT_RESSTR(STR_SVT_FILEVIEW_COLUMN_TITLE), 180, nBits | HeaderBarItemBits::UPARROW);
615 0 : mpHeaderBar->InsertItem(COLUMN_TYPE, SVT_RESSTR(STR_SVT_FILEVIEW_COLUMN_TYPE), 140, nBits);
616 0 : mpHeaderBar->InsertItem(COLUMN_SIZE, SVT_RESSTR(STR_SVT_FILEVIEW_COLUMN_SIZE), 80, nBits);
617 0 : mpHeaderBar->InsertItem(COLUMN_DATE, SVT_RESSTR(STR_SVT_FILEVIEW_COLUMN_DATE), 500, nBits);
618 : }
619 :
620 0 : Size aHeadSize = mpHeaderBar->GetSizePixel();
621 0 : SetPosSizePixel( Point( 0, aHeadSize.Height() ),
622 0 : Size( aBoxSize.Width(), aBoxSize.Height() - aHeadSize.Height() ) );
623 0 : InitHeaderBar( mpHeaderBar );
624 0 : SetHighlightRange();
625 0 : SetEntryHeight( ROW_HEIGHT );
626 0 : if (nFlags & FileViewFlags::MULTISELECTION)
627 0 : SetSelectionMode( MULTIPLE_SELECTION );
628 :
629 0 : Show();
630 0 : if( mbShowHeader )
631 0 : mpHeaderBar->Show();
632 :
633 0 : maResetQuickSearch.SetTimeout( QUICK_SEARCH_TIMEOUT );
634 0 : maResetQuickSearch.SetTimeoutHdl( LINK( this, ViewTabListBox_Impl, ResetQuickSearch_Impl ) );
635 :
636 0 : Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
637 : Reference< XInteractionHandler > xInteractionHandler(
638 0 : InteractionHandler::createWithParent(xContext, 0), UNO_QUERY_THROW );
639 :
640 0 : mxCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
641 :
642 0 : EnableContextMenuHandling();
643 0 : }
644 :
645 :
646 :
647 0 : ViewTabListBox_Impl::~ViewTabListBox_Impl()
648 : {
649 0 : disposeOnce();
650 0 : }
651 :
652 0 : void ViewTabListBox_Impl::dispose()
653 : {
654 0 : maResetQuickSearch.Stop();
655 :
656 0 : mpHeaderBar.disposeAndClear();
657 0 : SvHeaderTabListBox::dispose();
658 0 : }
659 :
660 :
661 :
662 0 : IMPL_LINK_NOARG_TYPED(ViewTabListBox_Impl, ResetQuickSearch_Impl, Timer *, void)
663 : {
664 0 : ::osl::MutexGuard aGuard( maMutex );
665 :
666 0 : maQuickSearchText.clear();
667 0 : mnSearchIndex = 0;
668 0 : }
669 :
670 :
671 :
672 0 : void ViewTabListBox_Impl::Resize()
673 : {
674 0 : SvTabListBox::Resize();
675 0 : Size aBoxSize = Control::GetParent()->GetOutputSizePixel();
676 :
677 0 : if ( mbResizeDisabled || !aBoxSize.Width() )
678 0 : return;
679 :
680 0 : Size aBarSize;
681 0 : if ( mbShowHeader )
682 : {
683 0 : aBarSize = mpHeaderBar->GetSizePixel();
684 0 : aBarSize.Width() = mbAutoResize ? aBoxSize.Width() : GetSizePixel().Width();
685 0 : mpHeaderBar->SetSizePixel( aBarSize );
686 : }
687 :
688 0 : if ( mbAutoResize )
689 : {
690 0 : mbResizeDisabled = true;
691 0 : SetPosSizePixel( Point( 0, aBarSize.Height() ),
692 0 : Size( aBoxSize.Width(), aBoxSize.Height() - aBarSize.Height() ) );
693 0 : mbResizeDisabled = false;
694 : }
695 : }
696 :
697 :
698 :
699 0 : void ViewTabListBox_Impl::KeyInput( const KeyEvent& rKEvt )
700 : {
701 0 : bool bHandled = false;
702 :
703 0 : const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
704 0 : if ( 0 == rKeyCode.GetModifier() )
705 : {
706 0 : if ( rKeyCode.GetCode() == KEY_RETURN )
707 : {
708 0 : ResetQuickSearch_Impl( NULL );
709 0 : GetDoubleClickHdl().Call( this );
710 0 : bHandled = true;
711 : }
712 0 : else if ( ( rKeyCode.GetCode() == KEY_DELETE ) &&
713 : mbEnableDelete )
714 : {
715 0 : ResetQuickSearch_Impl( NULL );
716 0 : DeleteEntries();
717 0 : bHandled = true;
718 : }
719 0 : else if ( ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) ||
720 0 : ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ) )
721 : {
722 0 : DoQuickSearch( rKEvt.GetCharCode() );
723 0 : bHandled = true;
724 : }
725 : }
726 :
727 0 : if ( !bHandled )
728 : {
729 0 : ResetQuickSearch_Impl( NULL );
730 0 : SvHeaderTabListBox::KeyInput( rKEvt );
731 : }
732 0 : }
733 :
734 :
735 :
736 0 : PopupMenu* ViewTabListBox_Impl::CreateContextMenu()
737 : {
738 0 : bool bEnableDelete = mbEnableDelete;
739 0 : bool bEnableRename = mbEnableRename;
740 :
741 0 : if ( bEnableDelete || bEnableRename )
742 : {
743 0 : sal_Int32 nSelectedEntries = GetSelectionCount();
744 0 : bEnableDelete &= nSelectedEntries > 0;
745 0 : bEnableRename &= nSelectedEntries == 1;
746 : }
747 :
748 0 : if ( bEnableDelete || bEnableRename )
749 : {
750 0 : SvTreeListEntry* pEntry = FirstSelected();
751 0 : while ( pEntry )
752 : {
753 0 : ::ucbhelper::Content aCnt;
754 : try
755 : {
756 : OUString aURL( static_cast< SvtContentEntry * >(
757 0 : pEntry->GetUserData() )->maURL );
758 0 : aCnt = ::ucbhelper::Content( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
759 : }
760 0 : catch( Exception const & )
761 : {
762 0 : bEnableDelete = bEnableRename = false;
763 : }
764 :
765 0 : if ( bEnableDelete )
766 : {
767 : try
768 : {
769 0 : Reference< XCommandInfo > aCommands = aCnt.getCommands();
770 0 : if ( aCommands.is() )
771 : bEnableDelete
772 0 : = aCommands->hasCommandByName(
773 0 : OUString( "delete" ) );
774 : else
775 0 : bEnableDelete = false;
776 : }
777 0 : catch( Exception const & )
778 : {
779 0 : bEnableDelete = false;
780 : }
781 : }
782 :
783 0 : if ( bEnableRename )
784 : {
785 : try
786 : {
787 0 : Reference< XPropertySetInfo > aProps = aCnt.getProperties();
788 0 : if ( aProps.is() )
789 : {
790 : Property aProp
791 0 : = aProps->getPropertyByName(
792 0 : OUString( "Title" ) );
793 : bEnableRename
794 0 : = !( aProp.Attributes & PropertyAttribute::READONLY );
795 : }
796 : else
797 0 : bEnableRename = false;
798 : }
799 0 : catch( Exception const & )
800 : {
801 0 : bEnableRename = false;
802 : }
803 : }
804 :
805 0 : pEntry = ( bEnableDelete || bEnableRename )
806 0 : ? NextSelected( pEntry )
807 0 : : 0;
808 0 : }
809 : }
810 :
811 0 : if ( bEnableDelete || bEnableRename )
812 : {
813 : PopupMenu * pRet
814 0 : = new PopupMenu( SvtResId( RID_FILEVIEW_CONTEXTMENU ) );
815 0 : pRet->EnableItem( MID_FILEVIEW_DELETE, bEnableDelete );
816 0 : pRet->EnableItem( MID_FILEVIEW_RENAME, bEnableRename );
817 0 : pRet->RemoveDisabledEntries( true, true );
818 0 : return pRet;
819 : }
820 :
821 0 : return NULL;
822 : }
823 :
824 :
825 :
826 0 : void ViewTabListBox_Impl::ExcecuteContextMenuAction( sal_uInt16 nSelectedPopupEntry )
827 : {
828 0 : switch ( nSelectedPopupEntry )
829 : {
830 : case MID_FILEVIEW_DELETE :
831 0 : DeleteEntries();
832 0 : break;
833 :
834 : case MID_FILEVIEW_RENAME :
835 0 : EditEntry( FirstSelected() );
836 0 : break;
837 : }
838 0 : }
839 :
840 :
841 :
842 0 : void ViewTabListBox_Impl::ClearAll()
843 : {
844 0 : for ( sal_uLong i = 0; i < GetEntryCount(); ++i )
845 0 : delete static_cast<SvtContentEntry*>(GetEntry(i)->GetUserData());
846 0 : Clear();
847 0 : }
848 :
849 :
850 0 : void ViewTabListBox_Impl::DeleteEntries()
851 : {
852 0 : short eResult = svtools::QUERYDELETE_YES;
853 0 : SvTreeListEntry* pEntry = FirstSelected();
854 0 : OUString aURL;
855 :
856 0 : OString sDialogPosition;
857 0 : while ( pEntry && ( eResult != svtools::QUERYDELETE_CANCEL ) )
858 : {
859 0 : SvTreeListEntry *pCurEntry = pEntry;
860 0 : pEntry = NextSelected( pEntry );
861 :
862 0 : if ( pCurEntry->GetUserData() )
863 0 : aURL = static_cast<SvtContentEntry*>(pCurEntry->GetUserData())->maURL;
864 :
865 0 : if ( aURL.isEmpty() )
866 0 : continue;
867 :
868 0 : bool canDelete = true;
869 : try
870 : {
871 0 : ::ucbhelper::Content aCnt( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
872 0 : Reference< XCommandInfo > aCommands = aCnt.getCommands();
873 0 : if ( aCommands.is() )
874 : canDelete
875 0 : = aCommands->hasCommandByName(
876 0 : OUString( "delete" ) );
877 : else
878 0 : canDelete = false;
879 : }
880 0 : catch( Exception const & )
881 : {
882 0 : canDelete = false;
883 : }
884 :
885 0 : if (!canDelete)
886 0 : continue; // process next entry
887 :
888 0 : if ( eResult != svtools::QUERYDELETE_ALL )
889 : {
890 0 : INetURLObject aObj( aURL );
891 0 : ScopedVclPtrInstance< svtools::QueryDeleteDlg_Impl > aDlg(nullptr, aObj.GetName( INetURLObject::DECODE_WITH_CHARSET ) );
892 0 : if ( sDialogPosition.getLength() )
893 0 : aDlg->SetWindowState( sDialogPosition );
894 :
895 0 : if ( GetSelectionCount() > 1 )
896 0 : aDlg->EnableAllButton();
897 :
898 0 : eResult = aDlg->Execute();
899 :
900 0 : sDialogPosition = aDlg->GetWindowState( );
901 : }
902 :
903 0 : if ( ( eResult == svtools::QUERYDELETE_ALL ) ||
904 : ( eResult == svtools::QUERYDELETE_YES ) )
905 : {
906 0 : if ( Kill( aURL ) )
907 : {
908 0 : delete static_cast<SvtContentEntry*>(pCurEntry->GetUserData());
909 0 : GetModel()->Remove( pCurEntry );
910 0 : mpParent->EntryRemoved( aURL );
911 : }
912 : }
913 0 : }
914 0 : }
915 :
916 :
917 0 : bool ViewTabListBox_Impl::EditedEntry( SvTreeListEntry* pEntry,
918 : const OUString& rNewText )
919 : {
920 0 : bool bRet = false;
921 :
922 0 : OUString aURL;
923 0 : SvtContentEntry* pData = static_cast<SvtContentEntry*>(pEntry->GetUserData());
924 :
925 0 : if ( pData )
926 0 : aURL = pData->maURL;
927 :
928 0 : if ( aURL.isEmpty() )
929 0 : return bRet;
930 :
931 : try
932 : {
933 0 : OUString aPropName( "Title" );
934 0 : bool canRename = true;
935 0 : ::ucbhelper::Content aContent( aURL, mxCmdEnv, comphelper::getProcessComponentContext() );
936 :
937 : try
938 : {
939 0 : Reference< XPropertySetInfo > aProps = aContent.getProperties();
940 0 : if ( aProps.is() )
941 : {
942 0 : Property aProp = aProps->getPropertyByName( aPropName );
943 0 : canRename = !( aProp.Attributes & PropertyAttribute::READONLY );
944 : }
945 : else
946 : {
947 0 : canRename = false;
948 0 : }
949 : }
950 0 : catch ( Exception const & )
951 : {
952 0 : canRename = false;
953 : }
954 :
955 0 : if ( canRename )
956 : {
957 0 : Any aValue;
958 0 : aValue <<= rNewText;
959 0 : aContent.setPropertyValue( aPropName, aValue );
960 0 : mpParent->EntryRenamed( aURL, rNewText );
961 :
962 0 : if (pData)
963 0 : pData->maURL = aURL;
964 :
965 0 : pEntry->SetUserData( pData );
966 :
967 0 : bRet = true;
968 0 : }
969 : }
970 0 : catch( Exception const & )
971 : {
972 : }
973 :
974 0 : return bRet;
975 : }
976 :
977 :
978 0 : void ViewTabListBox_Impl::DoQuickSearch( const sal_Unicode& rChar )
979 : {
980 0 : ::osl::MutexGuard aGuard( maMutex );
981 :
982 0 : maResetQuickSearch.Stop();
983 :
984 0 : OUString aLastText = maQuickSearchText;
985 0 : sal_uInt32 aLastPos = mnSearchIndex;
986 :
987 0 : maQuickSearchText += OUString(rChar).toAsciiLowerCase();
988 :
989 0 : bool bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, false );
990 :
991 0 : if ( !bFound && ( aLastText.getLength() == 1 ) &&
992 0 : ( aLastText == OUString(rChar) ) )
993 : {
994 0 : mnSearchIndex = aLastPos + 1;
995 0 : maQuickSearchText = aLastText;
996 0 : bFound = mpParent->SearchNextEntry( mnSearchIndex, maQuickSearchText, true );
997 : }
998 :
999 0 : if ( bFound )
1000 : {
1001 0 : SvTreeListEntry* pEntry = GetEntry( mnSearchIndex );
1002 0 : if ( pEntry )
1003 : {
1004 0 : SelectAll( false );
1005 0 : Select( pEntry );
1006 0 : SetCurEntry( pEntry );
1007 0 : MakeVisible( pEntry );
1008 : }
1009 : }
1010 :
1011 0 : maResetQuickSearch.Start();
1012 0 : }
1013 :
1014 :
1015 0 : bool ViewTabListBox_Impl::DoubleClickHdl()
1016 : {
1017 0 : SvHeaderTabListBox::DoubleClickHdl();
1018 0 : return false;
1019 : // this means "do no additional handling". Especially this means that the SvImpLBox does not
1020 : // recognize that the entry at the double click position change after the handler call (which is
1021 : // the case if in the handler, our content was replaced)
1022 : // If it _would_ recognize this change, it would take this as a reason to select the entry, again
1023 : // - which is not what in the case of content replace
1024 : // (I really doubt that this behaviour of the SvImpLBox does make any sense at all, but
1025 : // who knows ...)
1026 : }
1027 :
1028 0 : OUString ViewTabListBox_Impl::GetAccessibleObjectDescription( ::svt::AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos ) const
1029 : {
1030 0 : OUString sRet = SvHeaderTabListBox::GetAccessibleObjectDescription( _eType, _nPos );
1031 0 : if ( ::svt::BBTYPE_TABLECELL == _eType )
1032 : {
1033 0 : sal_Int32 nRow = -1;
1034 0 : const sal_uInt16 nColumnCount = GetColumnCount();
1035 0 : if (nColumnCount > 0)
1036 0 : nRow = _nPos / nColumnCount;
1037 0 : SvTreeListEntry* pEntry = GetEntry( nRow );
1038 0 : if ( pEntry )
1039 : {
1040 0 : SvtContentEntry* pData = static_cast<SvtContentEntry*>(pEntry->GetUserData());
1041 0 : if ( pData )
1042 : {
1043 0 : const OUString sVar1( "%1" );
1044 0 : const OUString sVar2( "%2" );
1045 0 : OUString aText( msAccessibleDescText );
1046 0 : aText = aText.replaceAll( sVar1, pData->mbIsFolder ? msFolder : msFile );
1047 0 : aText = aText.replaceAll( sVar2, pData->maURL );
1048 0 : sRet += aText;
1049 : }
1050 : }
1051 : }
1052 :
1053 0 : return sRet;
1054 : }
1055 :
1056 :
1057 0 : bool ViewTabListBox_Impl::Kill( const OUString& rContent )
1058 : {
1059 0 : bool bRet = true;
1060 :
1061 : try
1062 : {
1063 0 : ::ucbhelper::Content aCnt( rContent, mxCmdEnv, comphelper::getProcessComponentContext() );
1064 0 : aCnt.executeCommand( OUString( "delete" ), makeAny( true ) );
1065 : }
1066 0 : catch( ::com::sun::star::ucb::CommandAbortedException const & )
1067 : {
1068 : DBG_WARNING( "CommandAbortedException" );
1069 0 : bRet = false;
1070 : }
1071 0 : catch( Exception const & )
1072 : {
1073 : DBG_WARNING( "Any other exception" );
1074 0 : bRet = false;
1075 : }
1076 :
1077 0 : return bRet;
1078 : }
1079 :
1080 : // class SvtFileView -----------------------------------------------------
1081 0 : SvtFileView::SvtFileView( vcl::Window* pParent, WinBits nBits,
1082 : bool bOnlyFolder, bool bMultiSelection ) :
1083 :
1084 0 : Control( pParent, nBits )
1085 : {
1086 0 : FileViewFlags nFlags = FileViewFlags::NONE;
1087 0 : if ( bOnlyFolder )
1088 0 : nFlags |= FileViewFlags::ONLYFOLDER;
1089 0 : if ( bMultiSelection )
1090 0 : nFlags |= FileViewFlags::MULTISELECTION;
1091 :
1092 0 : Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1093 : Reference< XInteractionHandler > xInteractionHandler(
1094 0 : InteractionHandler::createWithParent(xContext, 0), UNO_QUERY_THROW );
1095 0 : Reference < XCommandEnvironment > xCmdEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler, Reference< XProgressHandler >() );
1096 :
1097 0 : mpImp = new SvtFileView_Impl( this, xCmdEnv, nFlags, bOnlyFolder );
1098 0 : mpImp->mpView->ForbidEmptyText();
1099 0 : SetSortColumn( true );
1100 :
1101 0 : HeaderBar* pHeaderBar = mpImp->mpView->GetHeaderBar();
1102 0 : pHeaderBar->SetSelectHdl( LINK( this, SvtFileView, HeaderSelect_Impl ) );
1103 0 : pHeaderBar->SetEndDragHdl( LINK( this, SvtFileView, HeaderEndDrag_Impl ) );
1104 0 : }
1105 :
1106 0 : SvtFileView::~SvtFileView()
1107 : {
1108 0 : disposeOnce();
1109 0 : }
1110 :
1111 0 : void SvtFileView::dispose()
1112 : {
1113 : // use temp pointer to prevent access of deleted member (GetFocus())
1114 0 : SvtFileView_Impl* pTemp = mpImp;
1115 0 : mpImp = NULL;
1116 0 : delete pTemp;
1117 0 : Control::dispose();
1118 0 : }
1119 :
1120 0 : VCL_BUILDER_DECL_FACTORY(SvtFileView)
1121 : {
1122 0 : WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1123 :
1124 0 : bool bDropdown = VclBuilder::extractDropdown(rMap);
1125 :
1126 0 : if (bDropdown)
1127 0 : nBits |= WB_DROPDOWN;
1128 :
1129 0 : rRet = VclPtr<SvtFileView>::Create(pParent, nBits, true, true);
1130 0 : }
1131 :
1132 0 : Size SvtFileView::GetOptimalSize() const
1133 : {
1134 0 : return LogicToPixel(Size(208, 50), MAP_APPFONT);
1135 : }
1136 :
1137 0 : OUString SvtFileView::GetURL( SvTreeListEntry* pEntry )
1138 : {
1139 0 : OUString aURL;
1140 0 : if ( pEntry && pEntry->GetUserData() )
1141 0 : aURL = static_cast<SvtContentEntry*>(pEntry->GetUserData())->maURL;
1142 0 : return aURL;
1143 : }
1144 :
1145 :
1146 :
1147 0 : OUString SvtFileView::GetCurrentURL() const
1148 : {
1149 0 : OUString aURL;
1150 0 : SvTreeListEntry* pEntry = mpImp->mpView->FirstSelected();
1151 0 : if ( pEntry && pEntry->GetUserData() )
1152 0 : aURL = static_cast<SvtContentEntry*>(pEntry->GetUserData())->maURL;
1153 0 : return aURL;
1154 : }
1155 :
1156 :
1157 0 : void SvtFileView::CreatedFolder( const OUString& rUrl, const OUString& rNewFolder )
1158 : {
1159 0 : OUString sEntry = mpImp->FolderInserted( rUrl, rNewFolder );
1160 0 : SvTreeListEntry* pEntry = mpImp->mpView->InsertEntry( sEntry, mpImp->maFolderImage, mpImp->maFolderImage );
1161 0 : SvtContentEntry* pUserData = new SvtContentEntry( rUrl, true );
1162 0 : pEntry->SetUserData( pUserData );
1163 0 : mpImp->mpView->MakeVisible( pEntry );
1164 0 : }
1165 :
1166 :
1167 :
1168 0 : FileViewResult SvtFileView::PreviousLevel( const FileViewAsyncAction* pAsyncDescriptor )
1169 : {
1170 0 : FileViewResult eResult = eFailure;
1171 :
1172 0 : OUString sParentURL;
1173 0 : if ( GetParentURL( sParentURL ) )
1174 0 : eResult = Initialize( sParentURL, mpImp->maCurrentFilter, pAsyncDescriptor, mpBlackList );
1175 :
1176 0 : return eResult;
1177 : }
1178 :
1179 :
1180 :
1181 0 : bool SvtFileView::GetParentURL( OUString& rParentURL ) const
1182 : {
1183 0 : bool bRet = false;
1184 : try
1185 : {
1186 0 : ::ucbhelper::Content aCnt( mpImp->maViewURL, mpImp->mxCmdEnv, comphelper::getProcessComponentContext() );
1187 0 : Reference< XContent > xContent( aCnt.get() );
1188 0 : Reference< com::sun::star::container::XChild > xChild( xContent, UNO_QUERY );
1189 0 : if ( xChild.is() )
1190 : {
1191 0 : Reference< XContent > xParent( xChild->getParent(), UNO_QUERY );
1192 0 : if ( xParent.is() )
1193 : {
1194 0 : rParentURL = xParent->getIdentifier()->getContentIdentifier();
1195 0 : bRet = !rParentURL.isEmpty() && rParentURL != mpImp->maViewURL;
1196 0 : }
1197 0 : }
1198 : }
1199 0 : catch( Exception const & )
1200 : {
1201 : // perhaps an unknown url protocol (e.g. "private:newdoc")
1202 : }
1203 :
1204 0 : return bRet;
1205 : }
1206 :
1207 :
1208 :
1209 0 : const OString& SvtFileView::GetHelpId( ) const
1210 : {
1211 0 : return mpImp->mpView->GetHelpId( );
1212 : }
1213 :
1214 :
1215 :
1216 0 : void SvtFileView::SetHelpId( const OString& rHelpId )
1217 : {
1218 0 : mpImp->mpView->SetHelpId( rHelpId );
1219 0 : }
1220 :
1221 :
1222 :
1223 0 : void SvtFileView::SetSizePixel( const Size& rNewSize )
1224 : {
1225 0 : Control::SetSizePixel( rNewSize );
1226 0 : mpImp->mpView->SetSizePixel( rNewSize );
1227 0 : }
1228 :
1229 :
1230 :
1231 0 : void SvtFileView::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize )
1232 : {
1233 0 : SetPosPixel( rNewPos );
1234 0 : SetSizePixel( rNewSize );
1235 0 : }
1236 :
1237 :
1238 0 : bool SvtFileView::Initialize( const ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XContent>& _xContent, const OUString& rFilter )
1239 : {
1240 0 : WaitObject aWaitCursor( this );
1241 :
1242 0 : mpImp->Clear();
1243 0 : ::ucbhelper::Content aContent(_xContent, mpImp->mxCmdEnv, comphelper::getProcessComponentContext() );
1244 0 : FileViewResult eResult = mpImp->GetFolderContent_Impl( FolderDescriptor( aContent ), NULL );
1245 : OSL_ENSURE( eResult != eStillRunning, "SvtFileView::Initialize: this was expected to be synchronous!" );
1246 0 : if ( eResult != eSuccess )
1247 0 : return false;
1248 :
1249 0 : mpImp->FilterFolderContent_Impl( rFilter );
1250 :
1251 0 : mpImp->SortFolderContent_Impl(); // possibly not necessary!!!!!!!!!!
1252 0 : mpImp->CreateDisplayText_Impl();
1253 0 : mpImp->OpenFolder_Impl();
1254 :
1255 0 : mpImp->maOpenDoneLink.Call( this );
1256 0 : return true;
1257 : }
1258 :
1259 :
1260 0 : FileViewResult SvtFileView::Initialize(
1261 : const OUString& rURL,
1262 : const OUString& rFilter,
1263 : const FileViewAsyncAction* pAsyncDescriptor,
1264 : const ::com::sun::star::uno::Sequence< OUString >& rBlackList )
1265 : {
1266 0 : WaitObject aWaitCursor( this );
1267 0 : mpBlackList = rBlackList;
1268 :
1269 0 : OUString sPushURL( mpImp->maViewURL );
1270 :
1271 0 : mpImp->maViewURL = rURL;
1272 0 : FileViewResult eResult = ExecuteFilter( rFilter, pAsyncDescriptor );
1273 0 : switch ( eResult )
1274 : {
1275 : case eFailure:
1276 : case eTimeout:
1277 0 : mpImp->maViewURL = sPushURL;
1278 0 : return eResult;
1279 :
1280 : case eStillRunning:
1281 : OSL_ENSURE( pAsyncDescriptor, "SvtFileView::Initialize: we told it to read synchronously!" );
1282 : case eSuccess:
1283 0 : return eResult;
1284 : }
1285 :
1286 : OSL_FAIL( "SvtFileView::Initialize: unreachable!" );
1287 0 : return eFailure;
1288 : }
1289 :
1290 0 : FileViewResult SvtFileView::ExecuteFilter( const OUString& rFilter, const FileViewAsyncAction* pAsyncDescriptor )
1291 : {
1292 0 : mpImp->maCurrentFilter = rFilter.toAsciiLowerCase();
1293 :
1294 0 : mpImp->Clear();
1295 0 : FileViewResult eResult = mpImp->GetFolderContent_Impl( mpImp->maViewURL, pAsyncDescriptor, mpBlackList );
1296 : OSL_ENSURE( ( eResult != eStillRunning ) || pAsyncDescriptor, "SvtFileView::ExecuteFilter: we told it to read synchronously!" );
1297 0 : return eResult;
1298 : }
1299 :
1300 0 : void SvtFileView::CancelRunningAsyncAction()
1301 : {
1302 0 : mpImp->CancelRunningAsyncAction();
1303 0 : }
1304 :
1305 0 : void SvtFileView::SetNoSelection()
1306 : {
1307 0 : mpImp->mpView->SelectAll( false );
1308 0 : }
1309 :
1310 :
1311 :
1312 0 : void SvtFileView::GetFocus()
1313 : {
1314 0 : Control::GetFocus();
1315 0 : if ( mpImp && mpImp->mpView )
1316 0 : mpImp->mpView->GrabFocus();
1317 0 : }
1318 :
1319 :
1320 :
1321 0 : void SvtFileView::SetSelectHdl( const Link<>& rHdl )
1322 : {
1323 0 : mpImp->SetSelectHandler( rHdl );
1324 0 : }
1325 :
1326 :
1327 :
1328 0 : void SvtFileView::SetDoubleClickHdl( const Link<>& rHdl )
1329 : {
1330 0 : mpImp->mpView->SetDoubleClickHdl( rHdl );
1331 0 : }
1332 :
1333 :
1334 :
1335 0 : sal_uLong SvtFileView::GetSelectionCount() const
1336 : {
1337 0 : return mpImp->mpView->GetSelectionCount();
1338 : }
1339 :
1340 :
1341 :
1342 0 : SvTreeListEntry* SvtFileView::FirstSelected() const
1343 : {
1344 0 : return mpImp->mpView->FirstSelected();
1345 : }
1346 :
1347 :
1348 :
1349 0 : SvTreeListEntry* SvtFileView::NextSelected( SvTreeListEntry* pEntry ) const
1350 : {
1351 0 : return mpImp->mpView->NextSelected( pEntry );
1352 : }
1353 :
1354 0 : void SvtFileView::EnableAutoResize()
1355 : {
1356 0 : mpImp->mpView->EnableAutoResize();
1357 0 : }
1358 :
1359 0 : const OUString& SvtFileView::GetViewURL() const
1360 : {
1361 0 : return mpImp->maViewURL;
1362 : }
1363 :
1364 0 : void SvtFileView::SetOpenDoneHdl( const Link<>& rHdl )
1365 : {
1366 0 : mpImp->maOpenDoneLink = rHdl;
1367 0 : }
1368 :
1369 0 : void SvtFileView::EnableDelete( bool bEnable )
1370 : {
1371 0 : mpImp->EnableDelete( bEnable );
1372 0 : }
1373 :
1374 0 : void SvtFileView::EndInplaceEditing( bool _bCancel )
1375 : {
1376 0 : return mpImp->EndEditing( _bCancel );
1377 : }
1378 :
1379 0 : IMPL_LINK( SvtFileView, HeaderSelect_Impl, HeaderBar*, pBar )
1380 : {
1381 : DBG_ASSERT( pBar, "no headerbar" );
1382 0 : sal_uInt16 nItemID = pBar->GetCurItemId();
1383 :
1384 : HeaderBarItemBits nBits;
1385 :
1386 : // clear the arrow of the recently used column
1387 0 : if ( nItemID != mpImp->mnSortColumn )
1388 : {
1389 0 : if ( !nItemID )
1390 : {
1391 : // first call -> remove arrow from title column,
1392 : // because another column is the sort column
1393 0 : nItemID = mpImp->mnSortColumn;
1394 0 : mpImp->mnSortColumn = COLUMN_TITLE;
1395 : }
1396 0 : nBits = pBar->GetItemBits( mpImp->mnSortColumn );
1397 0 : nBits &= ~HeaderBarItemBits( HeaderBarItemBits::UPARROW | HeaderBarItemBits::DOWNARROW );
1398 0 : pBar->SetItemBits( mpImp->mnSortColumn, nBits );
1399 : }
1400 :
1401 0 : nBits = pBar->GetItemBits( nItemID );
1402 :
1403 0 : bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW );
1404 :
1405 0 : if ( bUp )
1406 : {
1407 0 : nBits &= ~HeaderBarItemBits::UPARROW;
1408 0 : nBits |= HeaderBarItemBits::DOWNARROW;
1409 : }
1410 : else
1411 : {
1412 0 : nBits &= ~HeaderBarItemBits::DOWNARROW;
1413 0 : nBits |= HeaderBarItemBits::UPARROW;
1414 : }
1415 :
1416 0 : pBar->SetItemBits( nItemID, nBits );
1417 0 : mpImp->Resort_Impl( nItemID, !bUp );
1418 0 : return 1;
1419 : }
1420 :
1421 :
1422 0 : IMPL_LINK( SvtFileView, HeaderEndDrag_Impl, HeaderBar*, pBar )
1423 : {
1424 0 : if ( !pBar->IsItemMode() )
1425 : {
1426 0 : Size aSize;
1427 0 : sal_uInt16 nTabs = pBar->GetItemCount();
1428 0 : long nTmpSize = 0;
1429 :
1430 0 : for ( sal_uInt16 i = 1; i <= nTabs; ++i )
1431 : {
1432 0 : long nWidth = pBar->GetItemSize(i);
1433 0 : aSize.Width() = nWidth + nTmpSize;
1434 0 : nTmpSize += nWidth;
1435 0 : mpImp->mpView->SetTab( i, aSize.Width(), MAP_PIXEL );
1436 : }
1437 : }
1438 :
1439 0 : return 0;
1440 : }
1441 :
1442 :
1443 0 : OUString SvtFileView::GetConfigString() const
1444 : {
1445 0 : OUString sRet;
1446 0 : HeaderBar* pBar = mpImp->mpView->GetHeaderBar();
1447 : DBG_ASSERT( pBar, "invalid headerbar" );
1448 :
1449 : // sort order
1450 0 : sRet += OUString::number( mpImp->mnSortColumn );
1451 0 : sRet += ";";
1452 0 : HeaderBarItemBits nBits = pBar->GetItemBits( mpImp->mnSortColumn );
1453 0 : bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW );
1454 0 : sRet += bUp ? OUString("1") : OUString("0");
1455 0 : sRet += ";";
1456 :
1457 0 : sal_uInt16 nCount = pBar->GetItemCount();
1458 0 : for ( sal_uInt16 i = 0; i < nCount; ++i )
1459 : {
1460 0 : sal_uInt16 nId = pBar->GetItemId(i);
1461 0 : sRet += OUString::number( nId );
1462 0 : sRet += ";";
1463 0 : sRet += OUString::number( pBar->GetItemSize( nId ) );
1464 0 : sRet += ";";
1465 : }
1466 :
1467 0 : sRet = comphelper::string::stripEnd(sRet, ';');
1468 0 : return sRet;
1469 : }
1470 :
1471 :
1472 0 : void SvtFileView::SetConfigString( const OUString& rCfgStr )
1473 : {
1474 0 : HeaderBar* pBar = mpImp->mpView->GetHeaderBar();
1475 : DBG_ASSERT( pBar, "invalid headerbar" );
1476 :
1477 0 : sal_Int32 nIdx = 0;
1478 0 : mpImp->mnSortColumn = (sal_uInt16)rCfgStr.getToken( 0, ';', nIdx ).toInt32();
1479 0 : bool bUp = (bool)(sal_uInt16)rCfgStr.getToken( 0, ';', nIdx ).toInt32();
1480 0 : HeaderBarItemBits nBits = pBar->GetItemBits( mpImp->mnSortColumn );
1481 :
1482 0 : if ( bUp )
1483 : {
1484 0 : nBits &= ~HeaderBarItemBits::UPARROW;
1485 0 : nBits |= HeaderBarItemBits::DOWNARROW;
1486 : }
1487 : else
1488 : {
1489 0 : nBits &= ~HeaderBarItemBits::DOWNARROW;
1490 0 : nBits |= HeaderBarItemBits::UPARROW;
1491 : }
1492 0 : pBar->SetItemBits( mpImp->mnSortColumn, nBits );
1493 :
1494 0 : while ( nIdx != -1 )
1495 : {
1496 0 : sal_uInt16 nItemId = (sal_uInt16)rCfgStr.getToken( 0, ';', nIdx ).toInt32();
1497 0 : pBar->SetItemSize( nItemId, rCfgStr.getToken( 0, ';', nIdx ).toInt32() );
1498 : }
1499 :
1500 0 : HeaderSelect_Impl( pBar );
1501 0 : HeaderEndDrag_Impl( pBar );
1502 0 : }
1503 :
1504 :
1505 0 : void SvtFileView::StateChanged( StateChangedType nStateChange )
1506 : {
1507 0 : if ( nStateChange == StateChangedType::Enable )
1508 0 : Invalidate();
1509 0 : Control::StateChanged( nStateChange );
1510 0 : }
1511 :
1512 :
1513 : // class NameTranslator_Impl
1514 :
1515 :
1516 0 : NameTranslator_Impl::NameTranslator_Impl( const INetURLObject& rActualFolder )
1517 : {
1518 0 : mpActFolder = new NameTranslationList( rActualFolder );
1519 0 : }
1520 :
1521 0 : NameTranslator_Impl::~NameTranslator_Impl()
1522 : {
1523 0 : if( mpActFolder )
1524 0 : delete mpActFolder;
1525 0 : }
1526 :
1527 0 : void NameTranslator_Impl::SetActualFolder( const INetURLObject& rActualFolder )
1528 : {
1529 0 : HashedEntry aActFolder( rActualFolder );
1530 :
1531 0 : if( mpActFolder )
1532 : {
1533 0 : if( *mpActFolder != aActFolder )
1534 : {
1535 0 : delete mpActFolder;
1536 0 : mpActFolder = new NameTranslationList( rActualFolder );
1537 : }
1538 : }
1539 : else
1540 0 : mpActFolder = new NameTranslationList( rActualFolder );
1541 0 : }
1542 :
1543 0 : bool NameTranslator_Impl::GetTranslation( const OUString& rOrg, OUString& rTrans ) const
1544 : {
1545 0 : bool bRet = false;
1546 :
1547 0 : if( mpActFolder )
1548 : {
1549 0 : const OUString* pTrans = mpActFolder->Translate( rOrg );
1550 0 : if( pTrans )
1551 : {
1552 0 : rTrans = *pTrans;
1553 0 : bRet = true;
1554 : }
1555 : }
1556 :
1557 0 : return bRet;
1558 : }
1559 :
1560 0 : const OUString* NameTranslator_Impl::GetTransTableFileName() const
1561 : {
1562 0 : return mpActFolder? &mpActFolder->GetTransTableFileName() : NULL;
1563 : }
1564 :
1565 :
1566 : // class SvtFileView_Impl
1567 :
1568 :
1569 0 : SvtFileView_Impl::SvtFileView_Impl( SvtFileView* pAntiImpl, Reference < XCommandEnvironment > xEnv, FileViewFlags nFlags, bool bOnlyFolder )
1570 :
1571 : :mpAntiImpl ( pAntiImpl )
1572 : ,m_eAsyncActionResult ( ::svt::ERROR )
1573 : ,m_bRunningAsyncAction ( false )
1574 : ,m_bAsyncActionCancelled ( false )
1575 : ,mpNameTrans ( NULL )
1576 : ,mnSortColumn ( COLUMN_TITLE )
1577 : ,mbAscending ( true )
1578 : ,mbOnlyFolder ( bOnlyFolder )
1579 : ,mbReplaceNames ( false )
1580 : ,mnSuspendSelectCallback ( 0 )
1581 : ,mbIsFirstResort ( true )
1582 0 : ,aIntlWrapper ( Application::GetSettings().GetLanguageTag() )
1583 : ,maFolderImage ( SvtResId( IMG_SVT_FOLDER ) )
1584 0 : ,mxCmdEnv ( xEnv )
1585 :
1586 : {
1587 0 : maAllFilter = "*.*";
1588 0 : mpView = VclPtr<ViewTabListBox_Impl>::Create( mpAntiImpl, this, nFlags );
1589 0 : mpView->EnableCellFocus();
1590 0 : }
1591 :
1592 :
1593 0 : SvtFileView_Impl::~SvtFileView_Impl()
1594 : {
1595 0 : Clear();
1596 0 : mpView.disposeAndClear();
1597 0 : }
1598 :
1599 :
1600 0 : void SvtFileView_Impl::Clear()
1601 : {
1602 0 : ::osl::MutexGuard aGuard( maMutex );
1603 :
1604 0 : std::vector< SortingData_Impl* >::iterator aIt;
1605 :
1606 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
1607 0 : delete (*aIt);
1608 :
1609 0 : maContent.clear();
1610 :
1611 0 : if( mpNameTrans )
1612 0 : DELETEZ( mpNameTrans );
1613 0 : }
1614 :
1615 :
1616 0 : FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1617 : const OUString& rFolder,
1618 : const FileViewAsyncAction* pAsyncDescriptor,
1619 : const ::com::sun::star::uno::Sequence< OUString >& rBlackList )
1620 : {
1621 0 : ::osl::ClearableMutexGuard aGuard( maMutex );
1622 0 : INetURLObject aFolderObj( rFolder );
1623 : DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
1624 :
1625 : // prepare name translation
1626 0 : SetActualFolder( aFolderObj );
1627 :
1628 0 : FolderDescriptor aFolder( aFolderObj.GetMainURL( INetURLObject::NO_DECODE ) );
1629 :
1630 0 : aGuard.clear();
1631 0 : return GetFolderContent_Impl( aFolder, pAsyncDescriptor, rBlackList );
1632 : }
1633 :
1634 :
1635 0 : FileViewResult SvtFileView_Impl::GetFolderContent_Impl(
1636 : const FolderDescriptor& _rFolder,
1637 : const FileViewAsyncAction* pAsyncDescriptor,
1638 : const ::com::sun::star::uno::Sequence< OUString >& rBlackList )
1639 : {
1640 : DBG_TESTSOLARMUTEX();
1641 0 : ::osl::ClearableMutexGuard aGuard( maMutex );
1642 :
1643 : OSL_ENSURE( !m_pContentEnumerator.is(), "SvtFileView_Impl::GetFolderContent_Impl: still running another enumeration!" );
1644 0 : m_pContentEnumerator = new ::svt::FileViewContentEnumerator(
1645 0 : mpView->GetCommandEnvironment(), maContent, maMutex, mbReplaceNames ? mpNameTrans : NULL );
1646 : // TODO: should we cache and re-use this thread?
1647 :
1648 0 : if ( !pAsyncDescriptor )
1649 : {
1650 0 : ::svt::EnumerationResult eResult = m_pContentEnumerator->enumerateFolderContentSync( _rFolder, rBlackList );
1651 0 : if ( ::svt::SUCCESS == eResult )
1652 : {
1653 0 : implEnumerationSuccess();
1654 0 : m_pContentEnumerator.clear();
1655 0 : return eSuccess;
1656 : }
1657 0 : m_pContentEnumerator.clear();
1658 0 : return eFailure;
1659 : }
1660 :
1661 0 : m_bRunningAsyncAction = true;
1662 0 : m_bAsyncActionCancelled = false;
1663 0 : m_eAsyncActionResult = ::svt::ERROR;
1664 0 : m_aAsyncActionFinished.reset();
1665 :
1666 : // don't (yet) set m_aCurrentAsyncActionHandler to pTimeout->aFinishHandler.
1667 : // By definition, this handler *only* get's called when the result cannot be obtained
1668 : // during the minimum wait time, so it is only set below, when needed.
1669 0 : m_aCurrentAsyncActionHandler = Link<>();
1670 :
1671 : // minimum time to wait
1672 0 : boost::scoped_ptr< TimeValue > pTimeout( new TimeValue );
1673 0 : sal_Int32 nMinTimeout = pAsyncDescriptor->nMinTimeout;
1674 : OSL_ENSURE( nMinTimeout > 0, "SvtFileView_Impl::GetFolderContent_Impl: invalid minimum timeout!" );
1675 0 : if ( nMinTimeout <= 0 )
1676 0 : nMinTimeout = sal_Int32( 1000L );
1677 0 : pTimeout->Seconds = nMinTimeout / 1000L;
1678 0 : pTimeout->Nanosec = ( nMinTimeout % 1000L ) * 1000000L;
1679 :
1680 0 : m_pContentEnumerator->enumerateFolderContent( _rFolder, this );
1681 :
1682 : // wait until the enumeration is finished
1683 : // for this, release our own mutex (which is used by the enumerator thread)
1684 0 : aGuard.clear();
1685 :
1686 0 : ::osl::Condition::Result eResult = ::osl::Condition::result_ok;
1687 : {
1688 : // also release the SolarMutex. Not all code which is needed during the enumeration
1689 : // is Solar-Thread-Safe, in particular there is some code which needs to access
1690 : // string resources (and our resource system relies on the SolarMutex :()
1691 0 : SolarMutexReleaser aSolarRelease;
1692 :
1693 : // now wait. Note that if we didn't get an pAsyncDescriptor, then this is an infinite wait.
1694 0 : eResult = m_aAsyncActionFinished.wait( pTimeout.get() );
1695 : }
1696 :
1697 0 : ::osl::MutexGuard aGuard2( maMutex );
1698 0 : if ( ::osl::Condition::result_timeout == eResult )
1699 : {
1700 : // maximum time to wait
1701 : OSL_ENSURE( !m_pCancelAsyncTimer.get(), "SvtFileView_Impl::GetFolderContent_Impl: there's still a previous timer!" );
1702 0 : m_pCancelAsyncTimer = new CallbackTimer( this );
1703 0 : sal_Int32 nMaxTimeout = pAsyncDescriptor->nMaxTimeout;
1704 : OSL_ENSURE( nMaxTimeout > nMinTimeout,
1705 : "SvtFileView_Impl::GetFolderContent_Impl: invalid maximum timeout!" );
1706 0 : if ( nMaxTimeout <= nMinTimeout )
1707 0 : nMaxTimeout = nMinTimeout + 5000;
1708 0 : m_pCancelAsyncTimer->setRemainingTime( salhelper::TTimeValue( nMaxTimeout - nMinTimeout ) );
1709 : // we already waited for nMinTimeout milliseconds, so take this into account
1710 0 : m_pCancelAsyncTimer->start();
1711 :
1712 0 : m_aCurrentAsyncActionHandler = pAsyncDescriptor->aFinishHandler;
1713 : DBG_ASSERT( m_aCurrentAsyncActionHandler.IsSet(), "SvtFileView_Impl::GetFolderContent_Impl: nobody interested when it's finished?" );
1714 0 : mpView->ClearAll();
1715 0 : return eStillRunning;
1716 : }
1717 :
1718 0 : m_bRunningAsyncAction = false;
1719 0 : switch ( m_eAsyncActionResult )
1720 : {
1721 : case ::svt::SUCCESS:
1722 0 : return eSuccess;
1723 :
1724 : case ::svt::ERROR:
1725 0 : return eFailure;
1726 :
1727 : case ::svt::RUNNING:
1728 0 : return eStillRunning;
1729 : }
1730 :
1731 : SAL_WARN( "svtools.contnr", "SvtFileView_Impl::GetFolderContent_Impl: unreachable!" );
1732 0 : return eFailure;
1733 : }
1734 :
1735 :
1736 0 : void SvtFileView_Impl::FilterFolderContent_Impl( const OUString &rFilter )
1737 : {
1738 0 : bool bHideTransFile = mbReplaceNames && mpNameTrans;
1739 :
1740 0 : OUString sHideEntry;
1741 0 : if( bHideTransFile )
1742 : {
1743 0 : const OUString* pTransTableFileName = mpNameTrans->GetTransTableFileName();
1744 0 : if( pTransTableFileName )
1745 : {
1746 0 : sHideEntry = *pTransTableFileName;
1747 0 : sHideEntry = sHideEntry.toAsciiUpperCase();
1748 : }
1749 : else
1750 0 : bHideTransFile = false;
1751 : }
1752 :
1753 0 : if ( !bHideTransFile &&
1754 0 : ( rFilter.isEmpty() || ( rFilter == ALL_FILES_FILTER ) ) )
1755 : // when replacing names, there is always something to filter (no view of ".nametranslation.table")
1756 0 : return;
1757 :
1758 0 : ::osl::MutexGuard aGuard( maMutex );
1759 :
1760 0 : if ( maContent.empty() )
1761 0 : return;
1762 :
1763 : // count (estimate) the number of filter tokens
1764 0 : sal_Int32 nTokens=0;
1765 0 : const sal_Unicode* pStart = rFilter.getStr();
1766 0 : const sal_Unicode* pEnd = pStart + rFilter.getLength();
1767 0 : while ( pStart != pEnd )
1768 0 : if ( *pStart++ == ';' )
1769 0 : ++nTokens;
1770 :
1771 : // collect the filter tokens
1772 0 : ::std::vector< WildCard > aFilters;
1773 0 : FilterMatch::createWildCardFilterList(rFilter,aFilters);
1774 :
1775 :
1776 : // do the filtering
1777 0 : ::std::vector< SortingData_Impl* >::iterator aContentLoop = maContent.begin();
1778 0 : OUString sCompareString;
1779 0 : do
1780 : {
1781 0 : if ( (*aContentLoop)->mbIsFolder )
1782 0 : ++aContentLoop;
1783 : else
1784 : {
1785 : // normalize the content title (we always match case-insensitive)
1786 : // 91872 - 11.09.2001 - frank.schoenheit@sun.com
1787 0 : sCompareString = (*aContentLoop)->GetFileName(); // filter works on file name, not on title!
1788 : bool bDelete;
1789 :
1790 0 : if( bHideTransFile && sCompareString == sHideEntry )
1791 0 : bDelete = true;
1792 : else
1793 : {
1794 : bDelete = ::std::none_of( aFilters.begin(), aFilters.end(),
1795 0 : FilterMatch( sCompareString ) );
1796 : }
1797 :
1798 0 : if( bDelete )
1799 : {
1800 : // none of the filters did match
1801 0 : delete (*aContentLoop);
1802 :
1803 0 : if ( maContent.begin() == aContentLoop )
1804 : {
1805 0 : maContent.erase( aContentLoop );
1806 0 : aContentLoop = maContent.begin();
1807 : }
1808 : else
1809 : {
1810 0 : std::vector< SortingData_Impl* >::iterator aDelete = aContentLoop;
1811 0 : --aContentLoop; // move the iterator to a position which is not invalidated by the erase
1812 0 : maContent.erase( aDelete );
1813 0 : ++aContentLoop; // this is now the next one ....
1814 : }
1815 : }
1816 : else
1817 0 : ++aContentLoop;
1818 : }
1819 : }
1820 0 : while ( aContentLoop != maContent.end() );
1821 : }
1822 :
1823 :
1824 0 : IMPL_LINK( SvtFileView_Impl, SelectionMultiplexer, void*, _pSource )
1825 : {
1826 0 : return mnSuspendSelectCallback ? 0L : m_aSelectHandler.Call( _pSource );
1827 : }
1828 :
1829 :
1830 0 : void SvtFileView_Impl::SetSelectHandler( const Link<>& _rHdl )
1831 : {
1832 0 : m_aSelectHandler = _rHdl;
1833 :
1834 0 : Link<> aMasterHandler;
1835 0 : if ( m_aSelectHandler.IsSet() )
1836 0 : aMasterHandler = LINK( this, SvtFileView_Impl, SelectionMultiplexer );
1837 :
1838 0 : mpView->SetSelectHdl( aMasterHandler );
1839 0 : }
1840 :
1841 :
1842 0 : void SvtFileView_Impl::InitSelection()
1843 : {
1844 0 : mpView->SelectAll( false );
1845 0 : SvTreeListEntry* pFirst = mpView->First();
1846 0 : if ( pFirst )
1847 0 : mpView->SetCursor( pFirst, true );
1848 0 : }
1849 :
1850 :
1851 0 : void SvtFileView_Impl::OpenFolder_Impl()
1852 : {
1853 0 : ::osl::MutexGuard aGuard( maMutex );
1854 :
1855 0 : mpView->SetUpdateMode( false );
1856 0 : mpView->ClearAll();
1857 :
1858 0 : std::vector< SortingData_Impl* >::iterator aIt;
1859 :
1860 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
1861 : {
1862 0 : if ( mbOnlyFolder && ! (*aIt)->mbIsFolder )
1863 0 : continue;
1864 :
1865 : // insert entry and set user data
1866 0 : SvTreeListEntry* pEntry = mpView->InsertEntry( (*aIt)->maDisplayText,
1867 0 : (*aIt)->maImage,
1868 0 : (*aIt)->maImage );
1869 :
1870 0 : SvtContentEntry* pUserData = new SvtContentEntry( (*aIt)->maTargetURL,
1871 0 : (*aIt)->mbIsFolder );
1872 0 : pEntry->SetUserData( pUserData );
1873 : }
1874 :
1875 0 : InitSelection();
1876 :
1877 0 : ++mnSuspendSelectCallback;
1878 0 : mpView->SetUpdateMode( true );
1879 0 : --mnSuspendSelectCallback;
1880 :
1881 0 : ResetCursor();
1882 0 : }
1883 :
1884 :
1885 0 : void SvtFileView_Impl::ResetCursor()
1886 : {
1887 : // deselect
1888 0 : SvTreeListEntry* pEntry = mpView->FirstSelected();
1889 0 : if ( pEntry )
1890 0 : mpView->Select( pEntry, false );
1891 : // set cursor to the first entry
1892 0 : mpView->SetCursor( mpView->First(), true );
1893 0 : mpView->Update();
1894 0 : }
1895 :
1896 :
1897 0 : void SvtFileView_Impl::CancelRunningAsyncAction()
1898 : {
1899 : DBG_TESTSOLARMUTEX();
1900 0 : ::osl::MutexGuard aGuard( maMutex );
1901 0 : if ( !m_pContentEnumerator.is() )
1902 0 : return;
1903 :
1904 0 : m_bAsyncActionCancelled = true;
1905 0 : m_pContentEnumerator->cancel();
1906 0 : m_bRunningAsyncAction = false;
1907 :
1908 0 : m_pContentEnumerator.clear();
1909 0 : if ( m_pCancelAsyncTimer.is() && m_pCancelAsyncTimer->isTicking() )
1910 0 : m_pCancelAsyncTimer->stop();
1911 0 : m_pCancelAsyncTimer = NULL;
1912 : }
1913 :
1914 :
1915 0 : void SvtFileView_Impl::onTimeout( CallbackTimer* )
1916 : {
1917 0 : SolarMutexGuard aSolarGuard;
1918 0 : ::osl::MutexGuard aGuard( maMutex );
1919 0 : if ( !m_bRunningAsyncAction )
1920 : // there might have been a race condition while we waited for the mutex
1921 0 : return;
1922 :
1923 0 : CancelRunningAsyncAction();
1924 :
1925 0 : if ( m_aCurrentAsyncActionHandler.IsSet() )
1926 : {
1927 0 : Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( eTimeout ) );
1928 0 : m_aCurrentAsyncActionHandler = Link<>();
1929 0 : }
1930 : }
1931 :
1932 :
1933 0 : void SvtFileView_Impl::enumerationDone( ::svt::EnumerationResult eResult )
1934 : {
1935 0 : SolarMutexGuard aSolarGuard;
1936 0 : ::osl::MutexGuard aGuard( maMutex );
1937 :
1938 0 : m_pContentEnumerator.clear();
1939 0 : if ( m_pCancelAsyncTimer.is() && m_pCancelAsyncTimer->isTicking() )
1940 0 : m_pCancelAsyncTimer->stop();
1941 0 : m_pCancelAsyncTimer = NULL;
1942 :
1943 0 : if ( m_bAsyncActionCancelled )
1944 : // this is to prevent race conditions
1945 0 : return;
1946 :
1947 0 : m_eAsyncActionResult = eResult;
1948 0 : m_bRunningAsyncAction = false;
1949 :
1950 0 : m_aAsyncActionFinished.set();
1951 :
1952 0 : if ( svt::SUCCESS == eResult )
1953 0 : implEnumerationSuccess();
1954 :
1955 0 : if ( m_aCurrentAsyncActionHandler.IsSet() )
1956 : {
1957 0 : Application::PostUserEvent( m_aCurrentAsyncActionHandler, reinterpret_cast< void* >( m_eAsyncActionResult ) );
1958 0 : m_aCurrentAsyncActionHandler = Link<>();
1959 0 : }
1960 : }
1961 :
1962 :
1963 0 : void SvtFileView_Impl::implEnumerationSuccess()
1964 : {
1965 0 : FilterFolderContent_Impl( maCurrentFilter );
1966 0 : SortFolderContent_Impl();
1967 0 : CreateDisplayText_Impl();
1968 0 : OpenFolder_Impl();
1969 0 : maOpenDoneLink.Call( mpAntiImpl );
1970 0 : }
1971 :
1972 :
1973 0 : void SvtFileView_Impl::ReplaceTabWithString( OUString& aValue )
1974 : {
1975 0 : OUString aTab( "\t" );
1976 0 : OUString aTabString( "%09" );
1977 : sal_Int32 iPos;
1978 :
1979 0 : while ( ( iPos = aValue.indexOf( aTab ) ) >= 0 )
1980 0 : aValue = aValue.replaceAt( iPos, 1, aTabString );
1981 0 : }
1982 :
1983 :
1984 0 : void SvtFileView_Impl::CreateDisplayText_Impl()
1985 : {
1986 0 : ::osl::MutexGuard aGuard( maMutex );
1987 :
1988 0 : OUString aValue;
1989 0 : OUString aTab( "\t" );
1990 0 : OUString aDateSep( ", " );
1991 :
1992 0 : std::vector< SortingData_Impl* >::iterator aIt;
1993 :
1994 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
1995 : {
1996 : // title, type, size, date
1997 0 : aValue = (*aIt)->GetTitle();
1998 : // #83004# --------------------
1999 0 : ReplaceTabWithString( aValue );
2000 0 : aValue += aTab;
2001 0 : aValue += (*aIt)->maType;
2002 0 : aValue += aTab;
2003 : // folders don't have a size
2004 0 : if ( ! (*aIt)->mbIsFolder )
2005 0 : aValue += CreateExactSizeText( (*aIt)->maSize );
2006 0 : aValue += aTab;
2007 : // set the date, but volumes have no date
2008 0 : if ( ! (*aIt)->mbIsFolder || ! (*aIt)->mbIsVolume )
2009 : {
2010 0 : SvtSysLocale aSysLocale;
2011 0 : const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
2012 0 : aValue += rLocaleData.getDate( (*aIt)->maModDate );
2013 0 : aValue += aDateSep;
2014 0 : aValue += rLocaleData.getTime( (*aIt)->maModDate );
2015 : }
2016 0 : (*aIt)->maDisplayText = aValue;
2017 :
2018 : // detect image
2019 0 : if ( (*aIt)->mbIsFolder )
2020 : {
2021 0 : ::svtools::VolumeInfo aVolInfo( (*aIt)->mbIsVolume, (*aIt)->mbIsRemote,
2022 0 : (*aIt)->mbIsRemoveable, (*aIt)->mbIsFloppy,
2023 0 : (*aIt)->mbIsCompactDisc );
2024 0 : (*aIt)->maImage = SvFileInformationManager::GetFolderImage( aVolInfo, false );
2025 : }
2026 : else
2027 0 : (*aIt)->maImage = SvFileInformationManager::GetFileImage( INetURLObject( (*aIt)->maTargetURL ), false );
2028 0 : }
2029 0 : }
2030 :
2031 0 : void SvtFileView_Impl::Resort_Impl( sal_Int16 nColumn, bool bAscending )
2032 : {
2033 0 : ::osl::MutexGuard aGuard( maMutex );
2034 :
2035 0 : if ( ( nColumn == mnSortColumn ) &&
2036 0 : ( bAscending == mbAscending ) )
2037 0 : return;
2038 :
2039 : // reset the quick search index
2040 0 : mpView->ResetQuickSearch_Impl( NULL );
2041 :
2042 0 : OUString aEntryURL;
2043 0 : SvTreeListEntry* pEntry = mpView->GetCurEntry();
2044 0 : if ( pEntry && pEntry->GetUserData() )
2045 0 : aEntryURL = static_cast<SvtContentEntry*>(pEntry->GetUserData())->maURL;
2046 :
2047 0 : mnSortColumn = nColumn;
2048 0 : mbAscending = bAscending;
2049 :
2050 0 : SortFolderContent_Impl();
2051 0 : OpenFolder_Impl();
2052 :
2053 0 : if ( !mbIsFirstResort )
2054 : {
2055 0 : sal_uLong nPos = GetEntryPos( aEntryURL );
2056 0 : if ( nPos < mpView->GetEntryCount() )
2057 : {
2058 0 : pEntry = mpView->GetEntry( nPos );
2059 :
2060 0 : ++mnSuspendSelectCallback; // #i15668#
2061 0 : mpView->SetCurEntry( pEntry );
2062 0 : --mnSuspendSelectCallback;
2063 : }
2064 : }
2065 : else
2066 0 : mbIsFirstResort = false;
2067 : }
2068 :
2069 :
2070 : static bool gbAscending = true;
2071 : static sal_Int16 gnColumn = COLUMN_TITLE;
2072 : static const CollatorWrapper* pCollatorWrapper = NULL;
2073 :
2074 : /* this function returns true, if aOne is less then aTwo
2075 : */
2076 0 : bool CompareSortingData_Impl( SortingData_Impl* const aOne, SortingData_Impl* const aTwo )
2077 : {
2078 : DBG_ASSERT( pCollatorWrapper, "*CompareSortingData_Impl(): Can't work this way!" );
2079 :
2080 : sal_Int32 nComp;
2081 0 : bool bRet = false;
2082 0 : bool bEqual = false;
2083 :
2084 0 : if ( aOne->mbIsFolder != aTwo->mbIsFolder )
2085 : {
2086 0 : if ( aOne->mbIsFolder )
2087 0 : bRet = true;
2088 : else
2089 0 : bRet = false;
2090 :
2091 : // !!! pb: #100376# folder always on top
2092 0 : if ( !gbAscending )
2093 0 : bRet = !bRet;
2094 : }
2095 : else
2096 : {
2097 0 : switch ( gnColumn )
2098 : {
2099 : case COLUMN_TITLE:
2100 : // compare case insensitive first
2101 0 : nComp = pCollatorWrapper->compareString( aOne->GetLowerTitle(), aTwo->GetLowerTitle() );
2102 :
2103 0 : if ( nComp == 0 )
2104 0 : nComp = pCollatorWrapper->compareString( aOne->GetTitle(), aTwo->GetTitle() );
2105 :
2106 0 : if ( nComp < 0 )
2107 0 : bRet = true;
2108 0 : else if ( nComp > 0 )
2109 0 : bRet = false;
2110 : else
2111 0 : bEqual = true;
2112 0 : break;
2113 : case COLUMN_TYPE:
2114 0 : nComp = pCollatorWrapper->compareString( aOne->maType, aTwo->maType );
2115 0 : if ( nComp < 0 )
2116 0 : bRet = true;
2117 0 : else if ( nComp > 0 )
2118 0 : bRet = false;
2119 : else
2120 0 : bEqual = true;
2121 0 : break;
2122 : case COLUMN_SIZE:
2123 0 : if ( aOne->maSize < aTwo->maSize )
2124 0 : bRet = true;
2125 0 : else if ( aOne->maSize > aTwo->maSize )
2126 0 : bRet = false;
2127 : else
2128 0 : bEqual = true;
2129 0 : break;
2130 : case COLUMN_DATE:
2131 0 : if ( aOne->maModDate < aTwo->maModDate )
2132 0 : bRet = true;
2133 0 : else if ( aOne->maModDate > aTwo->maModDate )
2134 0 : bRet = false;
2135 : else
2136 0 : bEqual = true;
2137 0 : break;
2138 : default:
2139 : DBG_WARNING( "CompareSortingData_Impl: Compare unknown type!" );
2140 0 : bRet = false;
2141 : }
2142 : }
2143 :
2144 : // when the two elements are equal, we must not return sal_True (which would
2145 : // happen if we just return ! ( a < b ) when not sorting ascending )
2146 0 : if ( bEqual )
2147 0 : return false;
2148 :
2149 0 : return gbAscending ? bRet : !bRet;
2150 : }
2151 :
2152 :
2153 0 : void SvtFileView_Impl::SortFolderContent_Impl()
2154 : {
2155 0 : ::osl::MutexGuard aGuard( maMutex );
2156 :
2157 0 : sal_uInt32 nSize = maContent.size();
2158 :
2159 0 : if ( nSize > 1 )
2160 : {
2161 0 : gbAscending = mbAscending;
2162 0 : gnColumn = mnSortColumn;
2163 0 : pCollatorWrapper = aIntlWrapper.getCaseCollator();
2164 :
2165 0 : std::stable_sort( maContent.begin(), maContent.end(), CompareSortingData_Impl );
2166 :
2167 0 : pCollatorWrapper = NULL;
2168 0 : }
2169 0 : }
2170 :
2171 :
2172 0 : void SvtFileView_Impl::EntryRemoved( const OUString& rURL )
2173 : {
2174 0 : ::osl::MutexGuard aGuard( maMutex );
2175 :
2176 0 : std::vector< SortingData_Impl* >::iterator aIt;
2177 :
2178 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
2179 : {
2180 0 : if ( (*aIt)->maTargetURL == rURL )
2181 : {
2182 0 : maContent.erase( aIt );
2183 0 : break;
2184 : }
2185 0 : }
2186 0 : }
2187 :
2188 :
2189 0 : void SvtFileView_Impl::EntryRenamed( OUString& rURL,
2190 : const OUString& rTitle )
2191 : {
2192 0 : ::osl::MutexGuard aGuard( maMutex );
2193 :
2194 0 : std::vector< SortingData_Impl* >::iterator aIt;
2195 :
2196 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
2197 : {
2198 0 : if ( (*aIt)->maTargetURL == rURL )
2199 : {
2200 0 : (*aIt)->SetNewTitle( rTitle );
2201 0 : OUString aDisplayText = (*aIt)->maDisplayText;
2202 0 : sal_Int32 nIndex = aDisplayText.indexOf( '\t' );
2203 :
2204 0 : if ( nIndex > 0 )
2205 0 : (*aIt)->maDisplayText = aDisplayText.replaceAt( 0, nIndex, rTitle );
2206 :
2207 0 : INetURLObject aURLObj( rURL );
2208 0 : aURLObj.SetName( rTitle, INetURLObject::ENCODE_ALL );
2209 :
2210 0 : rURL = aURLObj.GetMainURL( INetURLObject::NO_DECODE );
2211 :
2212 0 : (*aIt)->maTargetURL = rURL;
2213 0 : break;
2214 : }
2215 0 : }
2216 0 : }
2217 :
2218 :
2219 0 : OUString SvtFileView_Impl::FolderInserted( const OUString& rURL, const OUString& rTitle )
2220 : {
2221 0 : ::osl::MutexGuard aGuard( maMutex );
2222 :
2223 0 : SortingData_Impl* pData = new SortingData_Impl;
2224 :
2225 0 : pData->SetNewTitle( rTitle );
2226 0 : pData->maSize = 0;
2227 0 : pData->mbIsFolder = true;
2228 0 : pData->maTargetURL = rURL;
2229 :
2230 0 : ::svtools::VolumeInfo aVolInfo;
2231 0 : pData->maType = SvFileInformationManager::GetFolderDescription( aVolInfo );
2232 0 : pData->maImage = SvFileInformationManager::GetFolderImage( aVolInfo, false );
2233 :
2234 0 : OUString aValue;
2235 0 : OUString aTab( "\t" );
2236 0 : OUString aDateSep( ", " );
2237 :
2238 : // title, type, size, date
2239 0 : aValue = pData->GetTitle();
2240 0 : ReplaceTabWithString( aValue );
2241 0 : aValue += aTab;
2242 0 : aValue += pData->maType;
2243 0 : aValue += aTab;
2244 : // folders don't have a size
2245 0 : aValue += aTab;
2246 : // set the date
2247 0 : SvtSysLocale aSysLocale;
2248 0 : const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
2249 0 : aValue += rLocaleData.getDate( pData->maModDate );
2250 0 : aValue += aDateSep;
2251 0 : aValue += rLocaleData.getTime( pData->maModDate );
2252 :
2253 0 : pData->maDisplayText = aValue;
2254 0 : maContent.push_back( pData );
2255 :
2256 0 : return aValue;
2257 : }
2258 :
2259 :
2260 0 : sal_uLong SvtFileView_Impl::GetEntryPos( const OUString& rURL )
2261 : {
2262 0 : ::osl::MutexGuard aGuard( maMutex );
2263 :
2264 0 : std::vector< SortingData_Impl* >::iterator aIt;
2265 0 : sal_uLong nPos = 0;
2266 :
2267 0 : for ( aIt = maContent.begin(); aIt != maContent.end(); ++aIt )
2268 : {
2269 0 : if ( (*aIt)->maTargetURL == rURL )
2270 0 : return nPos;
2271 0 : nPos += 1;
2272 : }
2273 :
2274 0 : return nPos;
2275 : }
2276 :
2277 :
2278 0 : bool SvtFileView_Impl::SearchNextEntry( sal_uInt32& nIndex, const OUString& rTitle, bool bWrapAround )
2279 : {
2280 0 : ::osl::MutexGuard aGuard( maMutex );
2281 :
2282 0 : sal_uInt32 nEnd = maContent.size();
2283 0 : sal_uInt32 nStart = nIndex;
2284 0 : while ( nIndex < nEnd )
2285 : {
2286 0 : SortingData_Impl* pData = maContent[ nIndex ];
2287 0 : if ( pData->GetLowerTitle().startsWith( rTitle ) )
2288 0 : return true;
2289 0 : nIndex += 1;
2290 : }
2291 :
2292 0 : if ( bWrapAround )
2293 : {
2294 0 : nIndex = 0;
2295 0 : while ( nIndex < nEnd && nIndex <= nStart )
2296 : {
2297 0 : SortingData_Impl* pData = maContent[ nIndex ];
2298 0 : if ( pData->GetLowerTitle().startsWith( rTitle ) )
2299 0 : return true;
2300 0 : nIndex += 1;
2301 : }
2302 : }
2303 :
2304 0 : return false;
2305 : }
2306 :
2307 :
2308 0 : void SvtFileView_Impl::SetActualFolder( const INetURLObject& rActualFolder )
2309 : {
2310 0 : if( mbReplaceNames )
2311 : {
2312 0 : if( mpNameTrans )
2313 0 : mpNameTrans->SetActualFolder( rActualFolder );
2314 : else
2315 0 : mpNameTrans = new NameTranslator_Impl( rActualFolder );
2316 : }
2317 0 : }
2318 :
2319 : namespace svtools {
2320 :
2321 0 : QueryDeleteDlg_Impl::QueryDeleteDlg_Impl(vcl::Window* pParent, const OUString& rName)
2322 0 : : MessageDialog(pParent, "QueryDeleteDialog", "svt/ui/querydeletedialog.ui")
2323 : {
2324 0 : get(m_pAllButton, "all");
2325 :
2326 : // display specified texts
2327 0 : set_secondary_text(get_secondary_text().replaceFirst("%s", rName));
2328 0 : }
2329 :
2330 0 : QueryDeleteDlg_Impl::~QueryDeleteDlg_Impl()
2331 : {
2332 0 : disposeOnce();
2333 0 : }
2334 :
2335 0 : void QueryDeleteDlg_Impl::dispose()
2336 : {
2337 0 : m_pAllButton.clear();
2338 0 : MessageDialog::dispose();
2339 0 : }
2340 :
2341 798 : }
2342 :
2343 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|