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 #ifdef UNX
21 #include <pwd.h>
22 #endif
23
24 #include <svtools/inettbc.hxx>
25 #include <tools/diagnose_ex.h>
26 #include <com/sun/star/uno/Any.hxx>
27 #include <com/sun/star/uno/Reference.hxx>
28 #include <com/sun/star/beans/Property.hpp>
29 #include <com/sun/star/beans/PropertyValue.hpp>
30 #include <com/sun/star/sdbc/XResultSet.hpp>
31 #include <com/sun/star/sdbc/XRow.hpp>
32 #include <com/sun/star/task/XInteractionHandler.hpp>
33 #include <com/sun/star/ucb/NumberedSortingInfo.hpp>
34 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
35 #include <com/sun/star/ucb/XAnyCompareFactory.hpp>
36 #include <com/sun/star/ucb/XCommandProcessor2.hpp>
37 #include <com/sun/star/ucb/XProgressHandler.hpp>
38 #include <com/sun/star/ucb/XContentAccess.hpp>
39 #include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp>
40 #include <comphelper/processfactory.hxx>
41 #include <comphelper/string.hxx>
42 #include <rtl/instance.hxx>
43 #include <salhelper/thread.hxx>
44 #include <tools/debug.hxx>
45 #include <osl/file.hxx>
46 #include <osl/mutex.hxx>
47 #include <vcl/builder.hxx>
48 #include <vcl/event.hxx>
49 #include <vcl/svapp.hxx>
50 #include <unotools/historyoptions.hxx>
51 #include <unotools/pathoptions.hxx>
52 #include <ucbhelper/commandenvironment.hxx>
53 #include <ucbhelper/content.hxx>
54 #include <unotools/ucbhelper.hxx>
55 #include <svtools/asynclink.hxx>
56 #include <svtools/urlfilter.hxx>
57
58 #include <vector>
59 #include <algorithm>
60
61 using namespace ::ucbhelper;
62 using namespace ::utl;
63 using namespace ::com::sun::star;
64 using namespace ::com::sun::star::beans;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::sdbc;
67 using namespace ::com::sun::star::task;
68 using namespace ::com::sun::star::ucb;
69 using namespace ::com::sun::star::uno;
70
71 class SvtURLBox_Impl
72 {
73 public:
74 std::vector<OUString> aURLs;
75 std::vector<OUString> aCompletions;
76 std::vector<WildCard> m_aFilters;
77
78 static bool TildeParsing( OUString& aText, OUString& aBaseUrl );
79
SvtURLBox_Impl()80 SvtURLBox_Impl( )
81 {
82 FilterMatch::createWildCardFilterList(OUString(),m_aFilters);
83 }
84 };
85
86 class SvtMatchContext_Impl: public salhelper::Thread
87 {
88 static ::osl::Mutex* pDirMutex;
89
90 std::vector<OUString> aPickList;
91 std::vector<OUString> aCompletions;
92 std::vector<OUString> aURLs;
93 svtools::AsynchronLink aLink;
94 OUString const aBaseURL;
95 OUString const aText;
96 VclPtr<SvtURLBox> pBox;
97 bool const bOnlyDirectories;
98 bool const bNoSelection;
99
100 osl::Mutex mutex_;
101 bool stopped_;
102 css::uno::Reference< css::ucb::XCommandProcessor > processor_;
103 sal_Int32 commandId_;
104
105 DECL_LINK( Select_Impl, void*, void );
106
107 virtual ~SvtMatchContext_Impl() override;
108 virtual void execute() override;
109 void doExecute();
110 void Insert( const OUString& rCompletion, const OUString& rURL, bool bForce = false);
111 void ReadFolder( const OUString& rURL, const OUString& rMatch, bool bSmart );
112 static void FillPicklist(std::vector<OUString>& rPickList);
113
114 public:
115 SvtMatchContext_Impl( SvtURLBox* pBoxP, const OUString& rText );
116 void Stop();
117 };
118
119 class MatchContext_Impl: public salhelper::Thread
120 {
121 static ::osl::Mutex* pDirMutex;
122
123 std::vector<OUString> aPickList;
124 std::vector<OUString> aCompletions;
125 std::vector<OUString> aURLs;
126 svtools::AsynchronLink aLink;
127 OUString const aText;
128 URLBox* pBox;
129 bool const bOnlyDirectories;
130 bool const bNoSelection;
131
132 osl::Mutex mutex_;
133 bool stopped_;
134 css::uno::Reference< css::ucb::XCommandProcessor > processor_;
135 sal_Int32 commandId_;
136
137 DECL_LINK( Select_Impl, void*, void );
138
139 virtual ~MatchContext_Impl() override;
140 virtual void execute() override;
141 void doExecute();
142 void Insert( const OUString& rCompletion, const OUString& rURL, bool bForce = false);
143 void ReadFolder( const OUString& rURL, const OUString& rMatch, bool bSmart );
144 static void FillPicklist(std::vector<OUString>& rPickList);
145
146 public:
147 MatchContext_Impl( URLBox* pBoxP, const OUString& rText );
148 void Stop();
149 };
150
151
152 namespace
153 {
154 struct theSvtMatchContextMutex
155 : public rtl::Static< ::osl::Mutex, theSvtMatchContextMutex > {};
156 }
157
SvtMatchContext_Impl(SvtURLBox * pBoxP,const OUString & rText)158 SvtMatchContext_Impl::SvtMatchContext_Impl(
159 SvtURLBox* pBoxP, const OUString& rText )
160 : Thread( "SvtMatchContext_Impl" )
161 , aLink( LINK( this, SvtMatchContext_Impl, Select_Impl ) )
162 , aBaseURL( pBoxP->aBaseURL )
163 , aText( rText )
164 , pBox( pBoxP )
165 , bOnlyDirectories( pBoxP->bOnlyDirectories )
166 , bNoSelection( pBoxP->bNoSelection )
167 , stopped_(false)
168 , commandId_(0)
169 {
170 aLink.CreateMutex();
171
172 FillPicklist( aPickList );
173 }
174
~SvtMatchContext_Impl()175 SvtMatchContext_Impl::~SvtMatchContext_Impl()
176 {
177 aLink.ClearPendingCall();
178 }
179
FillPicklist(std::vector<OUString> & rPickList)180 void SvtMatchContext_Impl::FillPicklist(std::vector<OUString>& rPickList)
181 {
182 // Read the history of picks
183 Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
184 sal_uInt32 nCount = seqPicklist.getLength();
185
186 for( sal_uInt32 nItem=0; nItem < nCount; nItem++ )
187 {
188 Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ];
189
190 auto pProperty = std::find_if(seqPropertySet.begin(), seqPropertySet.end(),
191 [](const PropertyValue& rProperty) { return rProperty.Name == HISTORY_PROPERTYNAME_TITLE; });
192 if (pProperty != seqPropertySet.end())
193 {
194 OUString sTitle;
195 INetURLObject aURL;
196
197 pProperty->Value >>= sTitle;
198 aURL.SetURL( sTitle );
199 rPickList.insert(rPickList.begin() + nItem, aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset));
200 }
201 }
202 }
203
Stop()204 void SvtMatchContext_Impl::Stop()
205 {
206 css::uno::Reference< css::ucb::XCommandProcessor > proc;
207 sal_Int32 id(0);
208 {
209 osl::MutexGuard g(mutex_);
210 if (!stopped_) {
211 stopped_ = true;
212 proc = processor_;
213 id = commandId_;
214 }
215 }
216 if (proc.is()) {
217 proc->abort(id);
218 }
219 terminate();
220 }
221
execute()222 void SvtMatchContext_Impl::execute( )
223 {
224 doExecute();
225 aLink.Call( this );
226 }
227
228
229 // This method is called via AsynchronLink, so it has the SolarMutex and
230 // calling solar code ( VCL ... ) is safe. It is called when the thread is
231 // terminated ( finished work or stopped ). Cancelling the thread via
232 // Cancellable does not discard the information gained so far, it
233 // inserts all collected completions into the listbox.
234
IMPL_LINK_NOARG(SvtMatchContext_Impl,Select_Impl,void *,void)235 IMPL_LINK_NOARG( SvtMatchContext_Impl, Select_Impl, void*, void )
236 {
237 // avoid recursion through cancel button
238 {
239 osl::MutexGuard g(mutex_);
240 if (stopped_) {
241 // Completion was stopped, no display:
242 return;
243 }
244 }
245
246 pBox->bAutoCompleteMode = true;
247
248 // did we filter completions which otherwise would have been valid?
249 // (to be filled below)
250 bool bValidCompletionsFiltered = false;
251
252 // insert all completed strings into the listbox
253 pBox->Clear();
254
255 for (auto const& completion : aCompletions)
256 {
257 // convert the file into a URL
258 OUString sURL;
259 osl::FileBase::getFileURLFromSystemPath(completion, sURL);
260 // note: if this doesn't work, we're not interested in: we're checking the
261 // untouched sCompletion then
262
263 if ( !sURL.isEmpty() && !sURL.endsWith("/") )
264 {
265 OUString sUpperURL( sURL.toAsciiUpperCase() );
266
267 if ( ::std::none_of( pBox->pImpl->m_aFilters.begin(),
268 pBox->pImpl->m_aFilters.end(),
269 FilterMatch( sUpperURL ) ) )
270 { // this URL is not allowed
271 bValidCompletionsFiltered = true;
272 continue;
273 }
274 }
275
276 pBox->InsertEntry(completion);
277 }
278
279 if( !bNoSelection && !aCompletions.empty() && !bValidCompletionsFiltered )
280 {
281 // select the first one
282 OUString aTmp( pBox->GetEntry(0) );
283 pBox->SetText( aTmp );
284 pBox->SetSelection( Selection( aText.getLength(), aTmp.getLength() ) );
285 }
286
287 // transfer string lists to listbox and forget them
288 pBox->pImpl->aURLs = aURLs;
289 pBox->pImpl->aCompletions = aCompletions;
290 aURLs.clear();
291 aCompletions.clear();
292
293 // force listbox to resize ( it may be open )
294 pBox->Resize();
295
296 // the box has this control as a member so we have to set that member
297 // to zero before deleting ourself.
298 pBox->pCtx.clear();
299 }
300
301
Insert(const OUString & rCompletion,const OUString & rURL,bool bForce)302 void SvtMatchContext_Impl::Insert( const OUString& rCompletion,
303 const OUString& rURL,
304 bool bForce )
305 {
306 if( !bForce )
307 {
308 // avoid doubles
309 if(find(aCompletions.begin(), aCompletions.end(), rCompletion) != aCompletions.end())
310 return;
311 }
312
313 aCompletions.push_back(rCompletion);
314 aURLs.push_back(rURL);
315 }
316
317
ReadFolder(const OUString & rURL,const OUString & rMatch,bool bSmart)318 void SvtMatchContext_Impl::ReadFolder( const OUString& rURL,
319 const OUString& rMatch,
320 bool bSmart )
321 {
322 // check folder to scan
323 if( !UCBContentHelper::IsFolder( rURL ) )
324 return;
325
326 bool bPureHomePath = false;
327 #ifdef UNX
328 bPureHomePath = aText.startsWith( "~" ) && aText.indexOf( '/' ) == -1;
329 #endif
330
331 bool bExectMatch = bPureHomePath
332 || aText == "."
333 || aText.endsWith("/.")
334 || aText.endsWith("/..");
335
336 // for pure home paths ( ~username ) the '.' at the end of rMatch
337 // means that it points to root catalog
338 // this is done only for file contents since home paths parsing is useful only for them
339 if ( bPureHomePath && rMatch == "file:///." )
340 {
341 // a home that refers to /
342
343 OUString aNewText = aText + "/";
344 Insert( aNewText, rURL, true );
345
346 return;
347 }
348
349 // string to match with
350 INetURLObject aMatchObj( rMatch );
351 OUString aMatchName;
352
353 if ( rURL != aMatchObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
354 {
355 aMatchName = aMatchObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
356
357 // matching is always done case insensitive, but completion will be case sensitive and case preserving
358 aMatchName = aMatchName.toAsciiLowerCase();
359
360 // if the matchstring ends with a slash, we must search for this also
361 if ( rMatch.endsWith("/") )
362 aMatchName += "/";
363 }
364
365 sal_Int32 nMatchLen = aMatchName.getLength();
366
367 INetURLObject aFolderObj( rURL );
368 DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
369
370 try
371 {
372 Content aCnt( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
373 new ::ucbhelper::CommandEnvironment( uno::Reference< XInteractionHandler >(),
374 uno::Reference< XProgressHandler >() ),
375 comphelper::getProcessComponentContext() );
376 uno::Reference< XResultSet > xResultSet;
377 Sequence< OUString > aProps(2);
378 OUString* pProps = aProps.getArray();
379 pProps[0] = "Title";
380 pProps[1] = "IsFolder";
381
382 try
383 {
384 uno::Reference< XDynamicResultSet > xDynResultSet;
385 ResultSetInclude eInclude = INCLUDE_FOLDERS_AND_DOCUMENTS;
386 if ( bOnlyDirectories )
387 eInclude = INCLUDE_FOLDERS_ONLY;
388
389 xDynResultSet = aCnt.createDynamicCursor( aProps, eInclude );
390
391 uno::Reference < XAnyCompareFactory > xCompare;
392 uno::Reference < XSortedDynamicResultSetFactory > xSRSFac =
393 SortedDynamicResultSetFactory::create( ::comphelper::getProcessComponentContext() );
394
395 Sequence< NumberedSortingInfo > aSortInfo( 2 );
396 NumberedSortingInfo* pInfo = aSortInfo.getArray();
397 pInfo[ 0 ].ColumnIndex = 2;
398 pInfo[ 0 ].Ascending = false;
399 pInfo[ 1 ].ColumnIndex = 1;
400 pInfo[ 1 ].Ascending = true;
401
402 uno::Reference< XDynamicResultSet > xDynamicResultSet =
403 xSRSFac->createSortedDynamicResultSet( xDynResultSet, aSortInfo, xCompare );
404
405 if ( xDynamicResultSet.is() )
406 {
407 xResultSet = xDynamicResultSet->getStaticResultSet();
408 }
409 }
410 catch( css::uno::Exception& ) {}
411
412 if ( xResultSet.is() )
413 {
414 uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
415 uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
416
417 try
418 {
419 while ( schedule() && xResultSet->next() )
420 {
421 OUString aURL = xContentAccess->queryContentIdentifierString();
422 OUString aTitle = xRow->getString(1);
423 bool bIsFolder = xRow->getBoolean(2);
424
425 // matching is always done case insensitive, but completion will be case sensitive and case preserving
426 aTitle = aTitle.toAsciiLowerCase();
427
428 if (
429 !nMatchLen ||
430 (bExectMatch && aMatchName == aTitle) ||
431 (!bExectMatch && aTitle.startsWith(aMatchName))
432 )
433 {
434 // all names fit if matchstring is empty
435 INetURLObject aObj( aURL );
436 sal_Unicode aDelimiter = '/';
437 if ( bSmart )
438 // when parsing is done "smart", the delimiter must be "guessed"
439 aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos), &aDelimiter );
440
441 if ( bIsFolder )
442 aObj.setFinalSlash();
443
444 // get the last name of the URL
445 OUString aMatch = aObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
446 OUString aInput( aText );
447 if ( nMatchLen )
448 {
449 if (aText.endsWith(".") || bPureHomePath)
450 {
451 // if a "special folder" URL was typed, don't touch the user input
452 aMatch = aMatch.copy( nMatchLen );
453 }
454 else
455 {
456 // make the user input case preserving
457 DBG_ASSERT( aInput.getLength() >= nMatchLen, "Suspicious Matching!" );
458 aInput = aInput.copy( 0, aInput.getLength() - nMatchLen );
459 }
460 }
461
462 aInput += aMatch;
463
464 // folders should get a final slash automatically
465 if ( bIsFolder )
466 aInput += OUStringChar(aDelimiter);
467
468 Insert( aInput, aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), true );
469 }
470 }
471 }
472 catch( css::uno::Exception& )
473 {
474 }
475 }
476 }
477 catch( css::uno::Exception& )
478 {
479 }
480 }
481
MatchContext_Impl(URLBox * pBoxP,const OUString & rText)482 MatchContext_Impl::MatchContext_Impl(URLBox* pBoxP, const OUString& rText)
483 : Thread( "MatchContext_Impl" )
484 , aLink( LINK( this, MatchContext_Impl, Select_Impl ) )
485 , aText( rText )
486 , pBox( pBoxP )
487 , bOnlyDirectories( pBoxP->bOnlyDirectories )
488 , bNoSelection( pBoxP->bNoSelection )
489 , stopped_(false)
490 , commandId_(0)
491 {
492 aLink.CreateMutex();
493
494 FillPicklist( aPickList );
495 }
496
~MatchContext_Impl()497 MatchContext_Impl::~MatchContext_Impl()
498 {
499 aLink.ClearPendingCall();
500 }
501
FillPicklist(std::vector<OUString> & rPickList)502 void MatchContext_Impl::FillPicklist(std::vector<OUString>& rPickList)
503 {
504 // Read the history of picks
505 Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
506 sal_uInt32 nCount = seqPicklist.getLength();
507
508 for( sal_uInt32 nItem=0; nItem < nCount; nItem++ )
509 {
510 Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ];
511
512 auto pProperty = std::find_if(seqPropertySet.begin(), seqPropertySet.end(),
513 [](const PropertyValue& rProperty) { return rProperty.Name == HISTORY_PROPERTYNAME_TITLE; });
514 if (pProperty != seqPropertySet.end())
515 {
516 OUString sTitle;
517 INetURLObject aURL;
518
519 pProperty->Value >>= sTitle;
520 aURL.SetURL( sTitle );
521 rPickList.insert(rPickList.begin() + nItem, aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset));
522 }
523 }
524 }
525
Stop()526 void MatchContext_Impl::Stop()
527 {
528 css::uno::Reference< css::ucb::XCommandProcessor > proc;
529 sal_Int32 id(0);
530 {
531 osl::MutexGuard g(mutex_);
532 if (!stopped_) {
533 stopped_ = true;
534 proc = processor_;
535 id = commandId_;
536 }
537 }
538 if (proc.is()) {
539 proc->abort(id);
540 }
541 terminate();
542 }
543
execute()544 void MatchContext_Impl::execute( )
545 {
546 doExecute();
547 aLink.Call( this );
548 }
549
550
551 // This method is called via AsynchronLink, so it has the SolarMutex and
552 // calling solar code ( VCL ... ) is safe. It is called when the thread is
553 // terminated ( finished work or stopped ). Cancelling the thread via
554 // Cancellable does not discard the information gained so far, it
555 // inserts all collected completions into the listbox.
556
IMPL_LINK_NOARG(MatchContext_Impl,Select_Impl,void *,void)557 IMPL_LINK_NOARG( MatchContext_Impl, Select_Impl, void*, void )
558 {
559 // avoid recursion through cancel button
560 {
561 osl::MutexGuard g(mutex_);
562 if (stopped_) {
563 // Completion was stopped, no display:
564 return;
565 }
566 }
567
568 // insert all completed strings into the listbox
569 pBox->clear();
570
571 for (auto const& completion : aCompletions)
572 {
573 // convert the file into a URL
574 OUString sURL;
575 osl::FileBase::getFileURLFromSystemPath(completion, sURL);
576 // note: if this doesn't work, we're not interested in: we're checking the
577 // untouched sCompletion then
578
579 if ( !sURL.isEmpty() && !sURL.endsWith("/") )
580 {
581 OUString sUpperURL( sURL.toAsciiUpperCase() );
582
583 if ( ::std::none_of( pBox->pImpl->m_aFilters.begin(),
584 pBox->pImpl->m_aFilters.end(),
585 FilterMatch( sUpperURL ) ) )
586 { // this URL is not allowed
587 continue;
588 }
589 }
590
591 pBox->append_text(completion);
592 }
593
594 pBox->EnableAutocomplete(!bNoSelection);
595
596 // transfer string lists to listbox and forget them
597 pBox->pImpl->aURLs = aURLs;
598 pBox->pImpl->aCompletions = aCompletions;
599 aURLs.clear();
600 aCompletions.clear();
601
602 // the box has this control as a member so we have to set that member
603 // to zero before deleting ourself.
604 pBox->pCtx.clear();
605 }
606
Insert(const OUString & rCompletion,const OUString & rURL,bool bForce)607 void MatchContext_Impl::Insert( const OUString& rCompletion,
608 const OUString& rURL,
609 bool bForce )
610 {
611 if( !bForce )
612 {
613 // avoid doubles
614 if(find(aCompletions.begin(), aCompletions.end(), rCompletion) != aCompletions.end())
615 return;
616 }
617
618 aCompletions.push_back(rCompletion);
619 aURLs.push_back(rURL);
620 }
621
622
ReadFolder(const OUString & rURL,const OUString & rMatch,bool bSmart)623 void MatchContext_Impl::ReadFolder( const OUString& rURL,
624 const OUString& rMatch,
625 bool bSmart )
626 {
627 // check folder to scan
628 if( !UCBContentHelper::IsFolder( rURL ) )
629 return;
630
631 bool bPureHomePath = false;
632 #ifdef UNX
633 bPureHomePath = aText.startsWith( "~" ) && aText.indexOf( '/' ) == -1;
634 #endif
635
636 bool bExectMatch = bPureHomePath
637 || aText == "."
638 || aText.endsWith("/.")
639 || aText.endsWith("/..");
640
641 // for pure home paths ( ~username ) the '.' at the end of rMatch
642 // means that it points to root catalog
643 // this is done only for file contents since home paths parsing is useful only for them
644 if ( bPureHomePath && rMatch == "file:///." )
645 {
646 // a home that refers to /
647
648 OUString aNewText = aText + "/";
649 Insert( aNewText, rURL, true );
650
651 return;
652 }
653
654 // string to match with
655 INetURLObject aMatchObj( rMatch );
656 OUString aMatchName;
657
658 if ( rURL != aMatchObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
659 {
660 aMatchName = aMatchObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
661
662 // matching is always done case insensitive, but completion will be case sensitive and case preserving
663 aMatchName = aMatchName.toAsciiLowerCase();
664
665 // if the matchstring ends with a slash, we must search for this also
666 if ( rMatch.endsWith("/") )
667 aMatchName += "/";
668 }
669
670 sal_Int32 nMatchLen = aMatchName.getLength();
671
672 INetURLObject aFolderObj( rURL );
673 DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
674
675 try
676 {
677 Content aCnt( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
678 new ::ucbhelper::CommandEnvironment( uno::Reference< XInteractionHandler >(),
679 uno::Reference< XProgressHandler >() ),
680 comphelper::getProcessComponentContext() );
681 uno::Reference< XResultSet > xResultSet;
682 Sequence< OUString > aProps(2);
683 OUString* pProps = aProps.getArray();
684 pProps[0] = "Title";
685 pProps[1] = "IsFolder";
686
687 try
688 {
689 ResultSetInclude eInclude = INCLUDE_FOLDERS_AND_DOCUMENTS;
690 if ( bOnlyDirectories )
691 eInclude = INCLUDE_FOLDERS_ONLY;
692 uno::Reference< XDynamicResultSet > xDynResultSet = aCnt.createDynamicCursor( aProps, eInclude );
693
694 uno::Reference < XAnyCompareFactory > xCompare;
695 uno::Reference < XSortedDynamicResultSetFactory > xSRSFac =
696 SortedDynamicResultSetFactory::create( ::comphelper::getProcessComponentContext() );
697
698 Sequence< NumberedSortingInfo > aSortInfo( 2 );
699 NumberedSortingInfo* pInfo = aSortInfo.getArray();
700 pInfo[ 0 ].ColumnIndex = 2;
701 pInfo[ 0 ].Ascending = false;
702 pInfo[ 1 ].ColumnIndex = 1;
703 pInfo[ 1 ].Ascending = true;
704
705 uno::Reference< XDynamicResultSet > xDynamicResultSet =
706 xSRSFac->createSortedDynamicResultSet( xDynResultSet, aSortInfo, xCompare );
707
708 if ( xDynamicResultSet.is() )
709 {
710 xResultSet = xDynamicResultSet->getStaticResultSet();
711 }
712 }
713 catch( css::uno::Exception& ) {}
714
715 if ( xResultSet.is() )
716 {
717 uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
718 uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
719
720 try
721 {
722 while ( schedule() && xResultSet->next() )
723 {
724 OUString aURL = xContentAccess->queryContentIdentifierString();
725 OUString aTitle = xRow->getString(1);
726 bool bIsFolder = xRow->getBoolean(2);
727
728 // matching is always done case insensitive, but completion will be case sensitive and case preserving
729 aTitle = aTitle.toAsciiLowerCase();
730
731 if (
732 !nMatchLen ||
733 (bExectMatch && aMatchName == aTitle) ||
734 (!bExectMatch && aTitle.startsWith(aMatchName))
735 )
736 {
737 // all names fit if matchstring is empty
738 INetURLObject aObj( aURL );
739 sal_Unicode aDelimiter = '/';
740 if ( bSmart )
741 // when parsing is done "smart", the delimiter must be "guessed"
742 aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos), &aDelimiter );
743
744 if ( bIsFolder )
745 aObj.setFinalSlash();
746
747 // get the last name of the URL
748 OUString aMatch = aObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
749 OUString aInput( aText );
750 if ( nMatchLen )
751 {
752 if (aText.endsWith(".") || bPureHomePath)
753 {
754 // if a "special folder" URL was typed, don't touch the user input
755 aMatch = aMatch.copy( nMatchLen );
756 }
757 else
758 {
759 // make the user input case preserving
760 DBG_ASSERT( aInput.getLength() >= nMatchLen, "Suspicious Matching!" );
761 aInput = aInput.copy( 0, aInput.getLength() - nMatchLen );
762 }
763 }
764
765 aInput += aMatch;
766
767 // folders should get a final slash automatically
768 if ( bIsFolder )
769 aInput += OUStringChar(aDelimiter);
770
771 Insert( aInput, aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), true );
772 }
773 }
774 }
775 catch( css::uno::Exception& )
776 {
777 }
778 }
779 }
780 catch( css::uno::Exception& )
781 {
782 }
783 }
784
ParseSmart(const OUString & _aText,const OUString & _aBaseURL)785 OUString SvtURLBox::ParseSmart( const OUString& _aText, const OUString& _aBaseURL )
786 {
787 OUString aMatch;
788 OUString aText = _aText;
789 OUString aBaseURL = _aBaseURL;
790
791 // parse ~ for Unix systems
792 // does nothing for Windows
793 if( !SvtURLBox_Impl::TildeParsing( aText, aBaseURL ) )
794 return OUString();
795
796 if( !aBaseURL.isEmpty() )
797 {
798 INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( aBaseURL );
799
800 // if a base URL is set the string may be parsed relative
801 if( aText.startsWith( "/" ) )
802 {
803 // text starting with slashes means absolute file URLs
804 OUString aTemp = INetURLObject::GetScheme( eBaseProt );
805
806 // file URL must be correctly encoded!
807 OUString aTextURL = INetURLObject::encode( aText, INetURLObject::PART_FPATH,
808 INetURLObject::EncodeMechanism::All );
809 aTemp += aTextURL;
810
811 INetURLObject aTmp( aTemp );
812 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
813 aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE );
814 }
815 else
816 {
817 OUString aSmart( aText );
818 INetURLObject aObj( aBaseURL );
819
820 // HRO: I suppose this hack should only be done for Windows !!!???
821 #ifdef _WIN32
822 // HRO: INetURLObject::smatRel2Abs does not recognize '\\' as a relative path
823 // but in case of "\\\\" INetURLObject is right - this is an absolute path !
824
825 if( aText.startsWith("\\") && (aText.getLength() < 2 || aText[ 1 ] != '\\') )
826 {
827 // cut to first segment
828 OUString aTmp = INetURLObject::GetScheme( eBaseProt ) + "/";
829 aTmp += aObj.getName( 0, true, INetURLObject::DecodeMechanism::WithCharset );
830 aObj.SetURL( aTmp );
831
832 aSmart = aSmart.copy(1);
833 }
834 #endif
835 // base URL must be a directory !
836 aObj.setFinalSlash();
837
838 // take base URL and append current input
839 bool bWasAbsolute = false;
840 #ifdef UNX
841 // encode file URL correctly
842 aSmart = INetURLObject::encode( aSmart, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All );
843 #endif
844 INetURLObject aTmp( aObj.smartRel2Abs( aSmart, bWasAbsolute ) );
845
846 if ( aText.endsWith(".") )
847 // INetURLObject appends a final slash for the directories "." and "..", this is a bug!
848 // Remove it as a workaround
849 aTmp.removeFinalSlash();
850 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
851 aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE );
852 }
853 }
854 else
855 {
856 OUString aTmpMatch;
857 osl::FileBase::getFileURLFromSystemPath( aText, aTmpMatch );
858 aMatch = aTmpMatch;
859 }
860
861 return aMatch;
862 }
863
doExecute()864 void SvtMatchContext_Impl::doExecute()
865 {
866 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
867 {
868 // have we been stopped while we were waiting for the mutex?
869 osl::MutexGuard g(mutex_);
870 if (stopped_) {
871 return;
872 }
873 }
874
875 // Reset match lists
876 aCompletions.clear();
877 aURLs.clear();
878
879 // check for input
880 if ( aText.isEmpty() )
881 return;
882
883 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
884 // no autocompletion for wildcards
885 return;
886
887 OUString aMatch;
888 INetProtocol eProt = INetURLObject::CompareProtocolScheme( aText );
889 INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( aBaseURL );
890 if ( aBaseURL.isEmpty() )
891 eBaseProt = INetURLObject::CompareProtocolScheme( SvtPathOptions().GetWorkPath() );
892 INetProtocol eSmartProt = pBox->GetSmartProtocol();
893
894 // if the user input is a valid URL, go on with it
895 // otherwise it could be parsed smart with a predefined smart protocol
896 // ( or if this is not set with the protocol of a predefined base URL )
897 if( eProt == INetProtocol::NotValid || eProt == eSmartProt || (eSmartProt == INetProtocol::NotValid && eProt == eBaseProt) )
898 {
899 // not stopped yet ?
900 if( schedule() )
901 {
902 if ( eProt == INetProtocol::NotValid )
903 aMatch = SvtURLBox::ParseSmart( aText, aBaseURL );
904 else
905 aMatch = aText;
906 if ( !aMatch.isEmpty() )
907 {
908 INetURLObject aURLObject( aMatch );
909 OUString aMainURL( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
910 // Disable autocompletion for anything but the (local) file
911 // system (for which access is hopefully fast), as the logic of
912 // how MatchContext_Impl is used requires this code to run to
913 // completion before further user input is processed, and even
914 // MatchContext_Impl::Stop does not guarantee a speedy
915 // return:
916 if ( !aMainURL.isEmpty()
917 && aURLObject.GetProtocol() == INetProtocol::File )
918 {
919 // if text input is a directory, it must be part of the match list! Until then it is scanned
920 bool folder = false;
921 if (aURLObject.hasFinalSlash()) {
922 try {
923 css::uno::Reference< css::uno::XComponentContext >
924 ctx(comphelper::getProcessComponentContext());
925 css::uno::Reference<
926 css::ucb::XUniversalContentBroker > ucb(
927 css::ucb::UniversalContentBroker::create(
928 ctx));
929 css::uno::Sequence< css::beans::Property > prop(1);
930 prop[0].Name = "IsFolder";
931 prop[0].Handle = -1;
932 prop[0].Type = cppu::UnoType< bool >::get();
933 css::uno::Any res;
934 css::uno::Reference< css::ucb::XCommandProcessor >
935 proc(
936 ucb->queryContent(
937 ucb->createContentIdentifier(aMainURL)),
938 css::uno::UNO_QUERY_THROW);
939 css::uno::Reference< css::ucb::XCommandProcessor2 >
940 proc2(proc, css::uno::UNO_QUERY);
941 sal_Int32 id = proc->createCommandIdentifier();
942 try {
943 {
944 osl::MutexGuard g(mutex_);
945 processor_ = proc;
946 commandId_ = id;
947 }
948 res = proc->execute(
949 css::ucb::Command(
950 "getPropertyValues", -1,
951 css::uno::makeAny(prop)),
952 id,
953 css::uno::Reference<
954 css::ucb::XCommandEnvironment >());
955 } catch (...) {
956 if (proc2.is()) {
957 try {
958 proc2->releaseCommandIdentifier(id);
959 } catch (css::uno::RuntimeException &) {
960 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
961 }
962 }
963 throw;
964 }
965 if (proc2.is()) {
966 proc2->releaseCommandIdentifier(id);
967 }
968 {
969 osl::MutexGuard g(mutex_);
970 processor_.clear();
971 // At least the neon-based WebDAV UCP does not
972 // properly support aborting commands, so return
973 // anyway now if an abort request had been
974 // ignored and the command execution only
975 // returned "successfully" after some timeout:
976 if (stopped_) {
977 return;
978 }
979 }
980 css::uno::Reference< css::sdbc::XRow > row(
981 res, css::uno::UNO_QUERY_THROW);
982 folder = row->getBoolean(1) && !row->wasNull();
983 } catch (css::uno::Exception &) {
984 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
985 return;
986 }
987 }
988 if ( folder )
989 Insert( aText, aMatch );
990 else
991 // otherwise the parent folder will be taken
992 aURLObject.removeSegment();
993
994 // scan directory and insert all matches
995 ReadFolder( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aMatch, eProt == INetProtocol::NotValid );
996 }
997 }
998 }
999 }
1000
1001 if ( bOnlyDirectories )
1002 // don't scan history picklist if only directories are allowed, picklist contains only files
1003 return;
1004
1005 bool bFull = false;
1006
1007 INetURLObject aCurObj;
1008 OUString aCurString, aCurMainURL;
1009 INetURLObject aObj;
1010 aObj.SetSmartProtocol( eSmartProt == INetProtocol::NotValid ? INetProtocol::Http : eSmartProt );
1011 for( ;; )
1012 {
1013 for(const auto& rPick : aPickList)
1014 {
1015 if (!schedule())
1016 break;
1017
1018 aCurObj.SetURL(rPick);
1019 aCurObj.SetSmartURL( aCurObj.GetURLNoPass());
1020 aCurMainURL = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1021
1022 if( eProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eProt )
1023 continue;
1024
1025 if( eSmartProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eSmartProt )
1026 continue;
1027
1028 switch( aCurObj.GetProtocol() )
1029 {
1030 case INetProtocol::Http:
1031 case INetProtocol::Https:
1032 case INetProtocol::Ftp:
1033 {
1034 if( eProt == INetProtocol::NotValid && !bFull )
1035 {
1036 aObj.SetSmartURL( aText );
1037 if( aObj.GetURLPath().getLength() > 1 )
1038 continue;
1039 }
1040
1041 aCurString = aCurMainURL;
1042 if( eProt == INetProtocol::NotValid )
1043 {
1044 // try if text matches the scheme
1045 OUString aScheme( INetURLObject::GetScheme( aCurObj.GetProtocol() ) );
1046 if ( aScheme.startsWithIgnoreAsciiCase( aText ) && aText.getLength() < aScheme.getLength() )
1047 {
1048 if( bFull )
1049 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1050 else
1051 {
1052 aCurObj.SetMark( "" );
1053 aCurObj.SetParam( "" );
1054 aCurObj.SetURLPath( "" );
1055 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1056 }
1057
1058 Insert( aMatch, aMatch );
1059 }
1060
1061 // now try smart matching
1062 aCurString = aCurString.copy( aScheme.getLength() );
1063 }
1064
1065 if( aCurString.startsWithIgnoreAsciiCase( aText ) )
1066 {
1067 if( bFull )
1068 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1069 else
1070 {
1071 aCurObj.SetMark( "" );
1072 aCurObj.SetParam( "" );
1073 aCurObj.SetURLPath( "" );
1074 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1075 }
1076
1077 OUString aURL( aMatch );
1078 if( eProt == INetProtocol::NotValid )
1079 aMatch = aMatch.copy( INetURLObject::GetScheme( aCurObj.GetProtocol() ).getLength() );
1080
1081 if( aText.getLength() < aMatch.getLength() )
1082 Insert( aMatch, aURL );
1083
1084 continue;
1085 }
1086 break;
1087 }
1088 default:
1089 {
1090 if( bFull )
1091 continue;
1092
1093 if( aCurMainURL.startsWith(aText) )
1094 {
1095 if( aText.getLength() < aCurMainURL.getLength() )
1096 Insert( aCurMainURL, aCurMainURL );
1097
1098 continue;
1099 }
1100 break;
1101 }
1102 }
1103 }
1104
1105 if( !bFull )
1106 bFull = true;
1107 else
1108 break;
1109 }
1110 }
1111
doExecute()1112 void MatchContext_Impl::doExecute()
1113 {
1114 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
1115 {
1116 // have we been stopped while we were waiting for the mutex?
1117 osl::MutexGuard g(mutex_);
1118 if (stopped_) {
1119 return;
1120 }
1121 }
1122
1123 // Reset match lists
1124 aCompletions.clear();
1125 aURLs.clear();
1126
1127 // check for input
1128 if ( aText.isEmpty() )
1129 return;
1130
1131 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
1132 // no autocompletion for wildcards
1133 return;
1134
1135 OUString aMatch;
1136 INetProtocol eProt = INetURLObject::CompareProtocolScheme( aText );
1137 INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( pBox->aBaseURL );
1138 if ( pBox->aBaseURL.isEmpty() )
1139 eBaseProt = INetURLObject::CompareProtocolScheme( SvtPathOptions().GetWorkPath() );
1140 INetProtocol eSmartProt = pBox->GetSmartProtocol();
1141
1142 // if the user input is a valid URL, go on with it
1143 // otherwise it could be parsed smart with a predefined smart protocol
1144 // ( or if this is not set with the protocol of a predefined base URL )
1145 if( eProt == INetProtocol::NotValid || eProt == eSmartProt || (eSmartProt == INetProtocol::NotValid && eProt == eBaseProt) )
1146 {
1147 // not stopped yet ?
1148 if( schedule() )
1149 {
1150 if ( eProt == INetProtocol::NotValid )
1151 aMatch = SvtURLBox::ParseSmart( aText, pBox->aBaseURL );
1152 else
1153 aMatch = aText;
1154 if ( !aMatch.isEmpty() )
1155 {
1156 INetURLObject aURLObject( aMatch );
1157 OUString aMainURL( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1158 // Disable autocompletion for anything but the (local) file
1159 // system (for which access is hopefully fast), as the logic of
1160 // how MatchContext_Impl is used requires this code to run to
1161 // completion before further user input is processed, and even
1162 // MatchContext_Impl::Stop does not guarantee a speedy
1163 // return:
1164 if ( !aMainURL.isEmpty()
1165 && aURLObject.GetProtocol() == INetProtocol::File )
1166 {
1167 // if text input is a directory, it must be part of the match list! Until then it is scanned
1168 bool folder = false;
1169 if (aURLObject.hasFinalSlash()) {
1170 try {
1171 css::uno::Reference< css::uno::XComponentContext >
1172 ctx(comphelper::getProcessComponentContext());
1173 css::uno::Reference<
1174 css::ucb::XUniversalContentBroker > ucb(
1175 css::ucb::UniversalContentBroker::create(
1176 ctx));
1177 css::uno::Sequence< css::beans::Property > prop(1);
1178 prop[0].Name = "IsFolder";
1179 prop[0].Handle = -1;
1180 prop[0].Type = cppu::UnoType< bool >::get();
1181 css::uno::Any res;
1182 css::uno::Reference< css::ucb::XCommandProcessor >
1183 proc(
1184 ucb->queryContent(
1185 ucb->createContentIdentifier(aMainURL)),
1186 css::uno::UNO_QUERY_THROW);
1187 css::uno::Reference< css::ucb::XCommandProcessor2 >
1188 proc2(proc, css::uno::UNO_QUERY);
1189 sal_Int32 id = proc->createCommandIdentifier();
1190 try {
1191 {
1192 osl::MutexGuard g(mutex_);
1193 processor_ = proc;
1194 commandId_ = id;
1195 }
1196 res = proc->execute(
1197 css::ucb::Command(
1198 "getPropertyValues", -1,
1199 css::uno::makeAny(prop)),
1200 id,
1201 css::uno::Reference<
1202 css::ucb::XCommandEnvironment >());
1203 } catch (...) {
1204 if (proc2.is()) {
1205 try {
1206 proc2->releaseCommandIdentifier(id);
1207 } catch (css::uno::RuntimeException &) {
1208 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
1209 }
1210 }
1211 throw;
1212 }
1213 if (proc2.is()) {
1214 proc2->releaseCommandIdentifier(id);
1215 }
1216 {
1217 osl::MutexGuard g(mutex_);
1218 processor_.clear();
1219 // At least the neon-based WebDAV UCP does not
1220 // properly support aborting commands, so return
1221 // anyway now if an abort request had been
1222 // ignored and the command execution only
1223 // returned "successfully" after some timeout:
1224 if (stopped_) {
1225 return;
1226 }
1227 }
1228 css::uno::Reference< css::sdbc::XRow > row(
1229 res, css::uno::UNO_QUERY_THROW);
1230 folder = row->getBoolean(1) && !row->wasNull();
1231 } catch (css::uno::Exception &) {
1232 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
1233 return;
1234 }
1235 }
1236 if (folder)
1237 Insert( aText, aMatch );
1238 else
1239 // otherwise the parent folder will be taken
1240 aURLObject.removeSegment();
1241
1242 // scan directory and insert all matches
1243 ReadFolder( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aMatch, eProt == INetProtocol::NotValid );
1244 }
1245 }
1246 }
1247 }
1248
1249 if ( bOnlyDirectories )
1250 // don't scan history picklist if only directories are allowed, picklist contains only files
1251 return;
1252
1253 bool bFull = false;
1254
1255 INetURLObject aCurObj;
1256 OUString aCurString, aCurMainURL;
1257 INetURLObject aObj;
1258 aObj.SetSmartProtocol( eSmartProt == INetProtocol::NotValid ? INetProtocol::Http : eSmartProt );
1259 for( ;; )
1260 {
1261 for(const auto& rPick : aPickList)
1262 {
1263 if (!schedule())
1264 break;
1265
1266 aCurObj.SetURL(rPick);
1267 aCurObj.SetSmartURL( aCurObj.GetURLNoPass());
1268 aCurMainURL = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1269
1270 if( eProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eProt )
1271 continue;
1272
1273 if( eSmartProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eSmartProt )
1274 continue;
1275
1276 switch( aCurObj.GetProtocol() )
1277 {
1278 case INetProtocol::Http:
1279 case INetProtocol::Https:
1280 case INetProtocol::Ftp:
1281 {
1282 if( eProt == INetProtocol::NotValid && !bFull )
1283 {
1284 aObj.SetSmartURL( aText );
1285 if( aObj.GetURLPath().getLength() > 1 )
1286 continue;
1287 }
1288
1289 aCurString = aCurMainURL;
1290 if( eProt == INetProtocol::NotValid )
1291 {
1292 // try if text matches the scheme
1293 OUString aScheme( INetURLObject::GetScheme( aCurObj.GetProtocol() ) );
1294 if ( aScheme.startsWithIgnoreAsciiCase( aText ) && aText.getLength() < aScheme.getLength() )
1295 {
1296 if( bFull )
1297 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1298 else
1299 {
1300 aCurObj.SetMark( "" );
1301 aCurObj.SetParam( "" );
1302 aCurObj.SetURLPath( "" );
1303 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1304 }
1305
1306 Insert( aMatch, aMatch );
1307 }
1308
1309 // now try smart matching
1310 aCurString = aCurString.copy( aScheme.getLength() );
1311 }
1312
1313 if( aCurString.startsWithIgnoreAsciiCase( aText ) )
1314 {
1315 if( bFull )
1316 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1317 else
1318 {
1319 aCurObj.SetMark( "" );
1320 aCurObj.SetParam( "" );
1321 aCurObj.SetURLPath( "" );
1322 aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1323 }
1324
1325 OUString aURL( aMatch );
1326 if( eProt == INetProtocol::NotValid )
1327 aMatch = aMatch.copy( INetURLObject::GetScheme( aCurObj.GetProtocol() ).getLength() );
1328
1329 if( aText.getLength() < aMatch.getLength() )
1330 Insert( aMatch, aURL );
1331
1332 continue;
1333 }
1334 break;
1335 }
1336 default:
1337 {
1338 if( bFull )
1339 continue;
1340
1341 if( aCurMainURL.startsWith(aText) )
1342 {
1343 if( aText.getLength() < aCurMainURL.getLength() )
1344 Insert( aCurMainURL, aCurMainURL );
1345
1346 continue;
1347 }
1348 break;
1349 }
1350 }
1351 }
1352
1353 if( !bFull )
1354 bFull = true;
1355 else
1356 break;
1357 }
1358 }
1359
TryAutoComplete()1360 void SvtURLBox::TryAutoComplete()
1361 {
1362 if( Application::AnyInput( VclInputFlags::KEYBOARD ) ) return;
1363
1364 OUString aCurText = GetText();
1365 Selection aSelection( GetSelection() );
1366 if( aSelection.Max() != aCurText.getLength() )
1367 return;
1368 sal_uInt16 nLen = static_cast<sal_uInt16>(aSelection.Min());
1369 aCurText = aCurText.copy( 0, nLen );
1370 if( !aCurText.isEmpty() && bIsAutoCompleteEnabled )
1371 {
1372 if ( pCtx.is() )
1373 {
1374 pCtx->Stop();
1375 pCtx->join();
1376 pCtx.clear();
1377 }
1378 pCtx = new SvtMatchContext_Impl( this, aCurText );
1379 pCtx->launch();
1380 }
1381 }
1382
1383
SvtURLBox(vcl::Window * pParent,INetProtocol eSmart,bool bSetDefaultHelpID)1384 SvtURLBox::SvtURLBox( vcl::Window* pParent, INetProtocol eSmart, bool bSetDefaultHelpID )
1385 : ComboBox( pParent , WB_DROPDOWN | WB_AUTOHSCROLL ),
1386 eSmartProtocol( eSmart ),
1387 bAutoCompleteMode( false ),
1388 bOnlyDirectories( false ),
1389 bHistoryDisabled( false ),
1390 bNoSelection( false ),
1391 bIsAutoCompleteEnabled( true )
1392 {
1393 Init(bSetDefaultHelpID);
1394
1395 if ( GetDesktopRectPixel().GetWidth() > 800 )
1396 SetSizePixel( Size( 300, 240 ) );
1397 else
1398 SetSizePixel( Size( 225, 240 ) );
1399 }
1400
1401
SvtURLBox(vcl::Window * pParent,WinBits _nStyle,INetProtocol eSmart,bool bSetDefaultHelpID)1402 SvtURLBox::SvtURLBox( vcl::Window* pParent, WinBits _nStyle, INetProtocol eSmart,
1403 bool bSetDefaultHelpID )
1404 : ComboBox( pParent, _nStyle ),
1405 eSmartProtocol( eSmart ),
1406 bAutoCompleteMode( false ),
1407 bOnlyDirectories( false ),
1408 bHistoryDisabled( false ),
1409 bNoSelection( false ),
1410 bIsAutoCompleteEnabled( true )
1411 {
1412 Init(bSetDefaultHelpID);
1413 }
1414
Init(bool bSetDefaultHelpID)1415 void SvtURLBox::Init(bool bSetDefaultHelpID)
1416 {
1417 pImpl.reset( new SvtURLBox_Impl );
1418
1419 if (bSetDefaultHelpID && GetHelpId().isEmpty())
1420 SetHelpId( ".uno:OpenURL" );
1421 EnableAutocomplete( false );
1422
1423 SetText( OUString() );
1424
1425 GetSubEdit()->SetAutocompleteHdl(LINK(this, SvtURLBox, AutoCompleteHdl_Impl));
1426 UpdatePicklistForSmartProtocol_Impl();
1427
1428 EnableAutoSize(true);
1429 }
1430
~SvtURLBox()1431 SvtURLBox::~SvtURLBox()
1432 {
1433 disposeOnce();
1434 }
1435
dispose()1436 void SvtURLBox::dispose()
1437 {
1438 if( pCtx.is() )
1439 {
1440 pCtx->Stop();
1441 pCtx->join();
1442 }
1443
1444 pImpl.reset();
1445 ComboBox::dispose();
1446 }
1447
UpdatePickList()1448 void SvtURLBox::UpdatePickList( )
1449 {
1450 if( pCtx.is() )
1451 {
1452 pCtx->Stop();
1453 pCtx->join();
1454 pCtx.clear();
1455 }
1456
1457 OUString sText = GetText();
1458 if ( !sText.isEmpty() && bIsAutoCompleteEnabled )
1459 {
1460 pCtx = new SvtMatchContext_Impl( this, sText );
1461 pCtx->launch();
1462 }
1463 }
1464
UpdatePicklistForSmartProtocol_Impl()1465 void SvtURLBox::UpdatePicklistForSmartProtocol_Impl()
1466 {
1467 Clear();
1468 if ( bHistoryDisabled )
1469 return;
1470
1471 // read history pick list
1472 const Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
1473 INetURLObject aCurObj;
1474
1475 for( const Sequence< PropertyValue >& rPropertySet : seqPicklist )
1476 {
1477 auto pProperty = std::find_if(rPropertySet.begin(), rPropertySet.end(),
1478 [](const PropertyValue& rProperty) { return rProperty.Name == HISTORY_PROPERTYNAME_URL; });
1479 if (pProperty != rPropertySet.end())
1480 {
1481 OUString sURL;
1482
1483 pProperty->Value >>= sURL;
1484 aCurObj.SetURL( sURL );
1485
1486 if ( !sURL.isEmpty() && ( eSmartProtocol != INetProtocol::NotValid ) )
1487 {
1488 if( aCurObj.GetProtocol() != eSmartProtocol )
1489 continue;
1490 }
1491
1492 OUString aURL( aCurObj.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ) );
1493
1494 if ( !aURL.isEmpty() )
1495 {
1496 bool bFound = aURL.endsWith("/");
1497 if ( !bFound )
1498 {
1499 OUString aUpperURL = aURL.toAsciiUpperCase();
1500
1501 bFound = ::std::any_of(pImpl->m_aFilters.begin(),
1502 pImpl->m_aFilters.end(),
1503 FilterMatch( aUpperURL ) );
1504 }
1505 if ( bFound )
1506 {
1507 OUString aFile;
1508 if (osl::FileBase::getSystemPathFromFileURL(aURL, aFile) == osl::FileBase::E_None)
1509 InsertEntry(aFile);
1510 else
1511 InsertEntry(aURL);
1512 }
1513 }
1514 }
1515 }
1516 }
1517
1518
ProcessKey(const vcl::KeyCode & rKey)1519 bool SvtURLBox::ProcessKey( const vcl::KeyCode& rKey )
1520 {
1521 // every key input stops the current matching thread
1522 if( pCtx.is() )
1523 {
1524 pCtx->Stop();
1525 pCtx->join();
1526 pCtx.clear();
1527 }
1528
1529 vcl::KeyCode aCode( rKey.GetCode() );
1530 if ( aCode == KEY_RETURN && !GetText().isEmpty() )
1531 {
1532 // wait for completion of matching thread
1533 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
1534
1535 if ( bAutoCompleteMode )
1536 {
1537 // reset picklist
1538 bAutoCompleteMode = false;
1539 Selection aSelection( GetSelection() );
1540 SetSelection( Selection( aSelection.Min(), aSelection.Min() ) );
1541 if ( bOnlyDirectories )
1542 Clear();
1543 else
1544 UpdatePicklistForSmartProtocol_Impl();
1545 Resize();
1546 }
1547
1548 bool bHandled = false;
1549 if ( GetOpenHdl().IsSet() )
1550 {
1551 bHandled = true;
1552 GetOpenHdl().Call(this);
1553 }
1554 else if ( GetSelectHdl().IsSet() )
1555 {
1556 bHandled = true;
1557 GetSelectHdl().Call(*this);
1558 }
1559
1560 ClearModifyFlag();
1561 return bHandled;
1562 }
1563 else if ( aCode == KEY_RETURN && GetText().isEmpty() && GetOpenHdl().IsSet() )
1564 {
1565 // for file dialog
1566 bAutoCompleteMode = false;
1567 GetOpenHdl().Call(this);
1568 return true;
1569 }
1570 else if( aCode == KEY_ESCAPE )
1571 {
1572 Selection aSelection( GetSelection() );
1573 if ( bAutoCompleteMode || aSelection.Min() != aSelection.Max() )
1574 {
1575 SetSelection( Selection( aSelection.Min(), aSelection.Min() ) );
1576 if ( bOnlyDirectories )
1577 Clear();
1578 else
1579 UpdatePicklistForSmartProtocol_Impl();
1580 Resize();
1581 }
1582 else
1583 {
1584 return false;
1585 }
1586
1587 bAutoCompleteMode = false;
1588 return true;
1589 }
1590 else
1591 {
1592 return false;
1593 }
1594 }
1595
1596
PreNotify(NotifyEvent & rNEvt)1597 bool SvtURLBox::PreNotify( NotifyEvent& rNEvt )
1598 {
1599 if( rNEvt.GetWindow() == GetSubEdit() && rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
1600 {
1601
1602 const KeyEvent& rEvent = *rNEvt.GetKeyEvent();
1603 const vcl::KeyCode& rKey = rEvent.GetKeyCode();
1604 vcl::KeyCode aCode( rKey.GetCode() );
1605 if( ProcessKey( rKey ) )
1606 {
1607 return true;
1608 }
1609 else if( ( aCode == KEY_UP || aCode == KEY_DOWN ) && !rKey.IsMod2() )
1610 {
1611 Selection aSelection( GetSelection() );
1612 sal_uInt16 nLen = static_cast<sal_uInt16>(aSelection.Min());
1613 GetSubEdit()->KeyInput( rEvent );
1614 SetSelection( Selection( nLen, GetText().getLength() ) );
1615 return true;
1616 }
1617
1618 if ( MatchesPlaceHolder( GetText() ) )
1619 {
1620 // set the selection so a key stroke will overwrite
1621 // the placeholder rather than edit it
1622 SetSelection( Selection( 0, GetText().getLength() ) );
1623 }
1624 }
1625
1626 return ComboBox::PreNotify( rNEvt );
1627 }
1628
IMPL_LINK_NOARG(SvtURLBox,AutoCompleteHdl_Impl,Edit &,void)1629 IMPL_LINK_NOARG(SvtURLBox, AutoCompleteHdl_Impl, Edit&, void)
1630 {
1631 TryAutoComplete();
1632 }
1633
EventNotify(NotifyEvent & rEvt)1634 bool SvtURLBox::EventNotify( NotifyEvent &rEvt )
1635 {
1636 if ( MouseNotifyEvent::GETFOCUS == rEvt.GetType() )
1637 {
1638 #ifndef UNX
1639 // pb: don't select automatically on unix #93251#
1640 SetSelection( Selection( 0, GetText().getLength() ) );
1641 #endif
1642 }
1643 else if ( MouseNotifyEvent::LOSEFOCUS == rEvt.GetType() )
1644 {
1645 if( GetText().isEmpty() )
1646 ClearModifyFlag();
1647 if ( pCtx.is() )
1648 {
1649 pCtx->Stop();
1650 pCtx->join();
1651 pCtx.clear();
1652 }
1653 }
1654
1655 return ComboBox::EventNotify( rEvt );
1656 }
1657
1658
Select()1659 void SvtURLBox::Select()
1660 {
1661 ComboBox::Select();
1662 ClearModifyFlag();
1663 }
1664
1665
GetURL()1666 OUString SvtURLBox::GetURL()
1667 {
1668 // wait for end of autocompletion
1669 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
1670
1671 OUString aText( GetText() );
1672 if ( MatchesPlaceHolder( aText ) )
1673 return aPlaceHolder;
1674
1675 // try to get the right case preserving URL from the list of URLs
1676 for(std::vector<OUString>::iterator i = pImpl->aCompletions.begin(), j = pImpl->aURLs.begin(); i != pImpl->aCompletions.end() && j != pImpl->aURLs.end(); ++i, ++j)
1677 {
1678 if((*i) == aText)
1679 return *j;
1680 }
1681
1682 #ifdef _WIN32
1683 // erase trailing spaces on Windows since they are invalid on this OS and
1684 // most of the time they are inserted by accident via copy / paste
1685 aText = comphelper::string::stripEnd(aText, ' ');
1686 if ( aText.isEmpty() )
1687 return aText;
1688 // #i9739#
1689 #endif
1690
1691 INetURLObject aObj( aText );
1692 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
1693 {
1694 // no autocompletion for wildcards
1695 INetURLObject aTempObj;
1696 if ( eSmartProtocol != INetProtocol::NotValid )
1697 aTempObj.SetSmartProtocol( eSmartProtocol );
1698 if ( aTempObj.SetSmartURL( aText ) )
1699 return aTempObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1700 else
1701 return aText;
1702 }
1703
1704 if ( aObj.GetProtocol() == INetProtocol::NotValid )
1705 {
1706 OUString aName = ParseSmart( aText, aBaseURL );
1707 aObj.SetURL(aName);
1708 OUString aURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1709 if ( aURL.isEmpty() )
1710 // aText itself is invalid, and even together with aBaseURL, it could not
1711 // made valid -> no chance
1712 return aText;
1713
1714 bool bSlash = aObj.hasFinalSlash();
1715 {
1716 const OUString aPropName("CasePreservingURL");
1717
1718 OUString aFileURL;
1719
1720 Any aAny = UCBContentHelper::GetProperty(aURL, aPropName);
1721 bool success = (aAny >>= aFileURL);
1722 OUString aTitle;
1723 if(success)
1724 aTitle = INetURLObject(aFileURL).getName(
1725 INetURLObject::LAST_SEGMENT,
1726 true,
1727 INetURLObject::DecodeMechanism::WithCharset );
1728 else
1729 success =
1730 UCBContentHelper::GetTitle(aURL,&aTitle);
1731
1732 if( success && aTitle != "/" && aTitle != "." )
1733 {
1734 aObj.setName( aTitle );
1735 if ( bSlash )
1736 aObj.setFinalSlash();
1737 }
1738 }
1739 }
1740
1741 return aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1742 }
1743
DisableHistory()1744 void SvtURLBox::DisableHistory()
1745 {
1746 bHistoryDisabled = true;
1747 UpdatePicklistForSmartProtocol_Impl();
1748 }
1749
1750 /** Parse leading ~ for Unix systems,
1751 does nothing for Windows
1752 */
TildeParsing(OUString & aText,OUString & aBaseURL)1753 bool SvtURLBox_Impl::TildeParsing(
1754 OUString&
1755 #ifdef UNX
1756 aText
1757 #endif
1758 , OUString&
1759 #ifdef UNX
1760 aBaseURL
1761 #endif
1762 )
1763 {
1764 #ifdef UNX
1765 if( aText.startsWith( "~" ) )
1766 {
1767 OUString aParseTilde;
1768 bool bTrailingSlash = true; // use trailing slash
1769
1770 if( aText.getLength() == 1 || aText[ 1 ] == '/' )
1771 {
1772 // covers "~" or "~/..." cases
1773 const char* aHomeLocation = getenv( "HOME" );
1774 if( !aHomeLocation )
1775 aHomeLocation = "";
1776
1777 aParseTilde = OUString::createFromAscii(aHomeLocation);
1778
1779 // in case the whole path is just "~" then there should
1780 // be no trailing slash at the end
1781 if( aText.getLength() == 1 )
1782 bTrailingSlash = false;
1783 }
1784 else
1785 {
1786 // covers "~username" and "~username/..." cases
1787 sal_Int32 nNameEnd = aText.indexOf( '/' );
1788 OUString aUserName = aText.copy( 1, ( nNameEnd != -1 ) ? nNameEnd : ( aText.getLength() - 1 ) );
1789
1790 struct passwd* pPasswd = nullptr;
1791 #ifdef __sun
1792 Sequence< sal_Int8 > sBuf( 1024 );
1793 struct passwd aTmp;
1794 sal_Int32 nRes = getpwnam_r( OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US ).getStr(),
1795 &aTmp,
1796 (char*)sBuf.getArray(),
1797 1024,
1798 &pPasswd );
1799 if( !nRes && pPasswd )
1800 aParseTilde = OUString::createFromAscii(pPasswd->pw_dir);
1801 else
1802 return false; // no such user
1803 #else
1804 pPasswd = getpwnam( OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US ).getStr() );
1805 if( pPasswd )
1806 aParseTilde = OUString::createFromAscii(pPasswd->pw_dir);
1807 else
1808 return false; // no such user
1809 #endif
1810
1811 // in case the path is "~username" then there should
1812 // be no trailing slash at the end
1813 if( nNameEnd == -1 )
1814 bTrailingSlash = false;
1815 }
1816
1817 if( !bTrailingSlash )
1818 {
1819 if( aParseTilde.isEmpty() || aParseTilde == "/" )
1820 {
1821 // "/" path should be converted to "/."
1822 aParseTilde = "/.";
1823 }
1824 else
1825 {
1826 // "blabla/" path should be converted to "blabla"
1827 aParseTilde = comphelper::string::stripEnd(aParseTilde, '/');
1828 }
1829 }
1830 else
1831 {
1832 if( !aParseTilde.endsWith("/") )
1833 aParseTilde += "/";
1834 if( aText.getLength() > 2 )
1835 aParseTilde += aText.copy( 2 );
1836 }
1837
1838 aText = aParseTilde;
1839 aBaseURL.clear(); // tilde provide absolute path
1840 }
1841 #endif
1842
1843 return true;
1844 }
1845
1846 //--
1847
ParseSmart(const OUString & _aText,const OUString & _aBaseURL)1848 OUString URLBox::ParseSmart( const OUString& _aText, const OUString& _aBaseURL )
1849 {
1850 OUString aMatch;
1851 OUString aText = _aText;
1852 OUString aBaseURL = _aBaseURL;
1853
1854 // parse ~ for Unix systems
1855 // does nothing for Windows
1856 if( !SvtURLBox_Impl::TildeParsing( aText, aBaseURL ) )
1857 return OUString();
1858
1859 if( !aBaseURL.isEmpty() )
1860 {
1861 INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( aBaseURL );
1862
1863 // if a base URL is set the string may be parsed relative
1864 if( aText.startsWith( "/" ) )
1865 {
1866 // text starting with slashes means absolute file URLs
1867 OUString aTemp = INetURLObject::GetScheme( eBaseProt );
1868
1869 // file URL must be correctly encoded!
1870 OUString aTextURL = INetURLObject::encode( aText, INetURLObject::PART_FPATH,
1871 INetURLObject::EncodeMechanism::All );
1872 aTemp += aTextURL;
1873
1874 INetURLObject aTmp( aTemp );
1875 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
1876 aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1877 }
1878 else
1879 {
1880 OUString aSmart( aText );
1881 INetURLObject aObj( aBaseURL );
1882
1883 // HRO: I suppose this hack should only be done for Windows !!!???
1884 #ifdef _WIN32
1885 // HRO: INetURLObject::smatRel2Abs does not recognize '\\' as a relative path
1886 // but in case of "\\\\" INetURLObject is right - this is an absolute path !
1887
1888 if( aText.startsWith("\\") && (aText.getLength() < 2 || aText[ 1 ] != '\\') )
1889 {
1890 // cut to first segment
1891 OUString aTmp = INetURLObject::GetScheme( eBaseProt ) + "/";
1892 aTmp += aObj.getName( 0, true, INetURLObject::DecodeMechanism::WithCharset );
1893 aObj.SetURL( aTmp );
1894
1895 aSmart = aSmart.copy(1);
1896 }
1897 #endif
1898 // base URL must be a directory !
1899 aObj.setFinalSlash();
1900
1901 // take base URL and append current input
1902 bool bWasAbsolute = false;
1903 #ifdef UNX
1904 // encode file URL correctly
1905 aSmart = INetURLObject::encode( aSmart, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All );
1906 #endif
1907 INetURLObject aTmp( aObj.smartRel2Abs( aSmart, bWasAbsolute ) );
1908
1909 if ( aText.endsWith(".") )
1910 // INetURLObject appends a final slash for the directories "." and "..", this is a bug!
1911 // Remove it as a workaround
1912 aTmp.removeFinalSlash();
1913 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
1914 aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1915 }
1916 }
1917 else
1918 {
1919 OUString aTmpMatch;
1920 osl::FileBase::getFileURLFromSystemPath( aText, aTmpMatch );
1921 aMatch = aTmpMatch;
1922 }
1923
1924 return aMatch;
1925 }
1926
IMPL_LINK_NOARG(URLBox,TryAutoComplete,Timer *,void)1927 IMPL_LINK_NOARG(URLBox, TryAutoComplete, Timer *, void)
1928 {
1929 OUString aCurText = m_xWidget->get_active_text();
1930 int nStartPos, nEndPos;
1931 m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos);
1932 if (std::max(nStartPos, nEndPos) != aCurText.getLength())
1933 return;
1934
1935 auto nLen = std::min(nStartPos, nEndPos);
1936 aCurText = aCurText.copy( 0, nLen );
1937 if (!aCurText.isEmpty())
1938 {
1939 if (pCtx.is())
1940 {
1941 pCtx->Stop();
1942 pCtx->join();
1943 pCtx.clear();
1944 }
1945 pCtx = new MatchContext_Impl(this, aCurText);
1946 pCtx->launch();
1947 }
1948 else
1949 m_xWidget->clear();
1950 }
1951
URLBox(std::unique_ptr<weld::ComboBox> pWidget)1952 URLBox::URLBox(std::unique_ptr<weld::ComboBox> pWidget)
1953 : eSmartProtocol(INetProtocol::NotValid)
1954 , bOnlyDirectories( false )
1955 , bHistoryDisabled( false )
1956 , bNoSelection( false )
1957 , m_xWidget(std::move(pWidget))
1958 {
1959 //don't grow to fix mega-long urls
1960 Size aSize(m_xWidget->get_preferred_size());
1961 m_xWidget->set_size_request(aSize.Width(), -1);
1962
1963 Init();
1964
1965 m_xWidget->connect_focus_in(LINK(this, URLBox, FocusInHdl));
1966 m_xWidget->connect_focus_out(LINK(this, URLBox, FocusOutHdl));
1967 m_xWidget->connect_changed(LINK(this, URLBox, ChangedHdl));
1968
1969 aChangedIdle.SetInvokeHandler(LINK(this, URLBox, TryAutoComplete));
1970 aChangedIdle.SetDebugName("svtools::URLBox aChangedIdle");
1971 }
1972
Init()1973 void URLBox::Init()
1974 {
1975 pImpl.reset( new SvtURLBox_Impl );
1976
1977 m_xWidget->set_entry_completion(false);
1978
1979 UpdatePicklistForSmartProtocol_Impl();
1980 }
1981
~URLBox()1982 URLBox::~URLBox()
1983 {
1984 if (pCtx.is())
1985 {
1986 pCtx->Stop();
1987 pCtx->join();
1988 }
1989 }
1990
SetSmartProtocol(INetProtocol eProt)1991 void URLBox::SetSmartProtocol(INetProtocol eProt)
1992 {
1993 if ( eSmartProtocol != eProt )
1994 {
1995 eSmartProtocol = eProt;
1996 UpdatePicklistForSmartProtocol_Impl();
1997 }
1998 }
1999
UpdatePicklistForSmartProtocol_Impl()2000 void URLBox::UpdatePicklistForSmartProtocol_Impl()
2001 {
2002 m_xWidget->clear();
2003 if ( bHistoryDisabled )
2004 return;
2005
2006 if (bHistoryDisabled)
2007 return;
2008
2009 // read history pick list
2010 const Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST );
2011 INetURLObject aCurObj;
2012
2013 for( const Sequence< PropertyValue >& rPropertySet : seqPicklist )
2014 {
2015 auto pProperty = std::find_if(rPropertySet.begin(), rPropertySet.end(),
2016 [](const PropertyValue& rProperty) { return rProperty.Name == HISTORY_PROPERTYNAME_URL; });
2017 if (pProperty != rPropertySet.end())
2018 {
2019 OUString sURL;
2020
2021 pProperty->Value >>= sURL;
2022 aCurObj.SetURL( sURL );
2023
2024 if ( !sURL.isEmpty() && ( eSmartProtocol != INetProtocol::NotValid ) )
2025 {
2026 if( aCurObj.GetProtocol() != eSmartProtocol )
2027 continue;
2028 }
2029
2030 OUString aURL( aCurObj.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ) );
2031
2032 if ( !aURL.isEmpty() )
2033 {
2034 bool bFound = aURL.endsWith("/");
2035 if ( !bFound )
2036 {
2037 OUString aUpperURL = aURL.toAsciiUpperCase();
2038
2039 bFound = ::std::any_of(pImpl->m_aFilters.begin(),
2040 pImpl->m_aFilters.end(),
2041 FilterMatch( aUpperURL ) );
2042 }
2043 if ( bFound )
2044 {
2045 OUString aFile;
2046 if (osl::FileBase::getSystemPathFromFileURL(aURL, aFile) == osl::FileBase::E_None)
2047 m_xWidget->append_text(aFile);
2048 else
2049 m_xWidget->append_text(aURL);
2050 }
2051 }
2052 }
2053 }
2054 }
2055
IMPL_LINK_NOARG(URLBox,ChangedHdl,weld::ComboBox &,void)2056 IMPL_LINK_NOARG(URLBox, ChangedHdl, weld::ComboBox&, void)
2057 {
2058 aChangeHdl.Call(*m_xWidget);
2059 aChangedIdle.Start(); //launch this to happen on idle after cursor position will have been set
2060 }
2061
IMPL_LINK_NOARG(URLBox,FocusInHdl,weld::Widget &,void)2062 IMPL_LINK_NOARG(URLBox, FocusInHdl, weld::Widget&, void)
2063 {
2064 #ifndef UNX
2065 // pb: don't select automatically on unix #93251#
2066 m_xWidget->select_entry_region(0, -1);
2067 #endif
2068 aFocusInHdl.Call(*m_xWidget);
2069 }
2070
IMPL_LINK_NOARG(URLBox,FocusOutHdl,weld::Widget &,void)2071 IMPL_LINK_NOARG(URLBox, FocusOutHdl, weld::Widget&, void)
2072 {
2073 if (pCtx.is())
2074 {
2075 pCtx->Stop();
2076 pCtx->join();
2077 pCtx.clear();
2078 }
2079 aFocusOutHdl.Call(*m_xWidget);
2080 }
2081
SetOnlyDirectories(bool bDir)2082 void URLBox::SetOnlyDirectories( bool bDir )
2083 {
2084 bOnlyDirectories = bDir;
2085 if ( bOnlyDirectories )
2086 m_xWidget->clear();
2087 }
2088
SetNoURLSelection(bool bSet)2089 void URLBox::SetNoURLSelection( bool bSet )
2090 {
2091 bNoSelection = bSet;
2092 }
2093
GetURL()2094 OUString URLBox::GetURL()
2095 {
2096 // wait for end of autocompletion
2097 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
2098
2099 OUString aText(m_xWidget->get_active_text());
2100
2101 // try to get the right case preserving URL from the list of URLs
2102 for(std::vector<OUString>::iterator i = pImpl->aCompletions.begin(), j = pImpl->aURLs.begin(); i != pImpl->aCompletions.end() && j != pImpl->aURLs.end(); ++i, ++j)
2103 {
2104 if((*i) == aText)
2105 return *j;
2106 }
2107
2108 #ifdef _WIN32
2109 // erase trailing spaces on Windows since they are invalid on this OS and
2110 // most of the time they are inserted by accident via copy / paste
2111 aText = comphelper::string::stripEnd(aText, ' ');
2112 if ( aText.isEmpty() )
2113 return aText;
2114 // #i9739#
2115 #endif
2116
2117 INetURLObject aObj( aText );
2118 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
2119 {
2120 // no autocompletion for wildcards
2121 INetURLObject aTempObj;
2122 if ( eSmartProtocol != INetProtocol::NotValid )
2123 aTempObj.SetSmartProtocol( eSmartProtocol );
2124 if ( aTempObj.SetSmartURL( aText ) )
2125 return aTempObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2126 else
2127 return aText;
2128 }
2129
2130 if ( aObj.GetProtocol() == INetProtocol::NotValid )
2131 {
2132 OUString aName = ParseSmart( aText, aBaseURL );
2133 aObj.SetURL(aName);
2134 OUString aURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2135 if ( aURL.isEmpty() )
2136 // aText itself is invalid, and even together with aBaseURL, it could not
2137 // made valid -> no chance
2138 return aText;
2139
2140 bool bSlash = aObj.hasFinalSlash();
2141 {
2142 const OUString aPropName("CasePreservingURL");
2143
2144 OUString aFileURL;
2145
2146 Any aAny = UCBContentHelper::GetProperty(aURL, aPropName);
2147 bool success = (aAny >>= aFileURL);
2148 OUString aTitle;
2149 if(success)
2150 aTitle = INetURLObject(aFileURL).getName(
2151 INetURLObject::LAST_SEGMENT,
2152 true,
2153 INetURLObject::DecodeMechanism::WithCharset );
2154 else
2155 success =
2156 UCBContentHelper::GetTitle(aURL,&aTitle);
2157
2158 if( success && aTitle != "/" && aTitle != "." )
2159 {
2160 aObj.setName( aTitle );
2161 if ( bSlash )
2162 aObj.setFinalSlash();
2163 }
2164 }
2165 }
2166
2167 return aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2168 }
2169
SetBaseURL(const OUString & rURL)2170 void URLBox::SetBaseURL( const OUString& rURL )
2171 {
2172 ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() );
2173
2174 // Reset match lists
2175 pImpl->aCompletions.clear();
2176 pImpl->aURLs.clear();
2177
2178 aBaseURL = rURL;
2179 }
2180
DisableHistory()2181 void URLBox::DisableHistory()
2182 {
2183 bHistoryDisabled = true;
2184 UpdatePicklistForSmartProtocol_Impl();
2185 }
2186
SetFilter(const OUString & _sFilter)2187 void URLBox::SetFilter(const OUString& _sFilter)
2188 {
2189 pImpl->m_aFilters.clear();
2190 FilterMatch::createWildCardFilterList(_sFilter,pImpl->m_aFilters);
2191 }
2192
createWildCardFilterList(const OUString & _rFilterList,::std::vector<WildCard> & _rFilters)2193 void FilterMatch::createWildCardFilterList(const OUString& _rFilterList,::std::vector< WildCard >& _rFilters)
2194 {
2195 if( _rFilterList.getLength() )
2196 {
2197 // filter is given
2198 sal_Int32 nIndex = 0;
2199 OUString sToken;
2200 do
2201 {
2202 sToken = _rFilterList.getToken( 0, ';', nIndex );
2203 if ( !sToken.isEmpty() )
2204 {
2205 _rFilters.emplace_back( sToken.toAsciiUpperCase() );
2206 }
2207 }
2208 while ( nIndex >= 0 );
2209 }
2210 else
2211 {
2212 // no filter is given -> match all
2213 _rFilters.emplace_back("*" );
2214 }
2215 }
2216
2217 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2218