LibreOffice Module svtools (master) 1
inettbc.cxx
Go to the documentation of this file.
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>
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/sdbc/XResultSet.hpp>
30#include <com/sun/star/sdbc/XRow.hpp>
31#include <com/sun/star/task/XInteractionHandler.hpp>
32#include <com/sun/star/ucb/NumberedSortingInfo.hpp>
33#include <com/sun/star/ucb/UniversalContentBroker.hpp>
34#include <com/sun/star/ucb/XAnyCompareFactory.hpp>
35#include <com/sun/star/ucb/XCommandProcessor2.hpp>
36#include <com/sun/star/ucb/XProgressHandler.hpp>
37#include <com/sun/star/ucb/XContentAccess.hpp>
38#include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp>
40#include <comphelper/string.hxx>
41#include <salhelper/thread.hxx>
42#include <tools/debug.hxx>
43#include <o3tl/string_view.hxx>
44#include <osl/file.hxx>
45#include <osl/mutex.hxx>
49#include <ucbhelper/content.hxx>
51#include <svtools/asynclink.hxx>
52#include <svtools/urlfilter.hxx>
53
54#include <mutex>
55#include <utility>
56#include <vector>
57#include <algorithm>
58
59using namespace ::ucbhelper;
60using namespace ::utl;
61using namespace ::com::sun::star;
62using namespace ::com::sun::star::beans;
63using namespace ::com::sun::star::lang;
64using namespace ::com::sun::star::sdbc;
65using namespace ::com::sun::star::task;
66using namespace ::com::sun::star::ucb;
67using namespace ::com::sun::star::uno;
68
70{
71public:
72 std::vector<OUString> aURLs;
73 std::vector<OUString> aCompletions;
74 std::vector<WildCard> m_aFilters;
75
76 static bool TildeParsing( OUString& aText, OUString& aBaseUrl );
77
79 {
80 FilterMatch::createWildCardFilterList(u"",m_aFilters);
81 }
82};
83
85{
86 std::vector<OUString> aPickList;
87 std::vector<OUString> aCompletions;
88 std::vector<OUString> aURLs;
89 svtools::AsynchronLink aLink;
90 OUString aText;
94
95 std::mutex mutex_;
97 css::uno::Reference< css::ucb::XCommandProcessor > processor_;
98 sal_Int32 commandId_;
99
100 DECL_LINK( Select_Impl, void*, void );
101
102 virtual ~SvtMatchContext_Impl() override;
103 virtual void execute() override;
104 void doExecute();
105 void Insert( const OUString& rCompletion, const OUString& rURL, bool bForce = false);
106 void ReadFolder( const OUString& rURL, const OUString& rMatch, bool bSmart );
107 static void FillPicklist(std::vector<OUString>& rPickList);
108
109public:
110 SvtMatchContext_Impl( SvtURLBox* pBoxP, OUString aText );
111 void Stop();
112};
113
114
115namespace
116{
117 ::osl::Mutex& theSvtMatchContextMutex()
118 {
119 static ::osl::Mutex SINGLETON;
120 return SINGLETON;
121 }
122}
123
125 : Thread( "MatchContext_Impl" )
126 , aLink( LINK( this, SvtMatchContext_Impl, Select_Impl ) )
127 , aText(std::move( _aText ))
128 , pBox( pBoxP )
129 , bOnlyDirectories( pBoxP->bOnlyDirectories )
130 , bNoSelection( pBoxP->bNoSelection )
131 , stopped_(false)
132 , commandId_(0)
133{
135}
136
138{
139 aLink.ClearPendingCall();
140}
141
142void SvtMatchContext_Impl::FillPicklist(std::vector<OUString>& rPickList)
143{
144 // Read the history of picks
145 std::vector< SvtHistoryOptions::HistoryItem > seqPicklist = SvtHistoryOptions::GetList( EHistoryType::PickList );
146 sal_uInt32 nCount = seqPicklist.size();
147
148 for( sal_uInt32 nItem=0; nItem < nCount; nItem++ )
149 {
151 aURL.SetURL( seqPicklist[nItem].sTitle );
152 rPickList.insert(rPickList.begin() + nItem, aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset));
153 }
154}
155
157{
158 css::uno::Reference< css::ucb::XCommandProcessor > proc;
159 sal_Int32 id(0);
160 {
161 std::scoped_lock g(mutex_);
162 if (!stopped_) {
163 stopped_ = true;
164 proc = processor_;
165 id = commandId_;
166 }
167 }
168 if (proc.is()) {
169 proc->abort(id);
170 }
171 terminate();
172}
173
175{
176 doExecute();
177 aLink.Call( this );
178}
179
180
181// This method is called via AsynchronLink, so it has the SolarMutex and
182// calling solar code ( VCL ... ) is safe. It is called when the thread is
183// terminated ( finished work or stopped ). Cancelling the thread via
184// Cancellable does not discard the information gained so far, it
185// inserts all collected completions into the listbox.
186
187IMPL_LINK_NOARG( SvtMatchContext_Impl, Select_Impl, void*, void )
188{
189 // avoid recursion through cancel button
190 {
191 std::scoped_lock g(mutex_);
192 if (stopped_) {
193 // Completion was stopped, no display:
194 return;
195 }
196 }
197
198 // insert all completed strings into the listbox
199 pBox->clear();
200
201 for (auto const& completion : aCompletions)
202 {
203 // convert the file into a URL
204 OUString sURL;
205 osl::FileBase::getFileURLFromSystemPath(completion, sURL);
206 // note: if this doesn't work, we're not interested in: we're checking the
207 // untouched sCompletion then
208
209 if ( !sURL.isEmpty() && !sURL.endsWith("/") )
210 {
211 OUString sUpperURL( sURL.toAsciiUpperCase() );
212
213 if ( ::std::none_of( pBox->pImpl->m_aFilters.begin(),
214 pBox->pImpl->m_aFilters.end(),
215 FilterMatch( sUpperURL ) ) )
216 { // this URL is not allowed
217 continue;
218 }
219 }
220
221 pBox->append_text(completion);
222 }
223
224 pBox->EnableAutocomplete(!bNoSelection);
225
226 // transfer string lists to listbox and forget them
227 pBox->pImpl->aURLs = aURLs;
228 pBox->pImpl->aCompletions = aCompletions;
229 aURLs.clear();
230 aCompletions.clear();
231
232 // the box has this control as a member so we have to set that member
233 // to zero before deleting ourself.
234 pBox->pCtx.clear();
235}
236
237void SvtMatchContext_Impl::Insert( const OUString& rCompletion,
238 const OUString& rURL,
239 bool bForce )
240{
241 if( !bForce )
242 {
243 // avoid doubles
244 if(find(aCompletions.begin(), aCompletions.end(), rCompletion) != aCompletions.end())
245 return;
246 }
247
248 aCompletions.push_back(rCompletion);
249 aURLs.push_back(rURL);
250}
251
252
253void SvtMatchContext_Impl::ReadFolder( const OUString& rURL,
254 const OUString& rMatch,
255 bool bSmart )
256{
257 // check folder to scan
258 if( !UCBContentHelper::IsFolder( rURL ) )
259 return;
260
261 bool bPureHomePath = false;
262#ifdef UNX
263 bPureHomePath = aText.startsWith( "~" ) && aText.indexOf( '/' ) == -1;
264#endif
265
266 bool bExectMatch = bPureHomePath
267 || aText == "."
268 || aText.endsWith("/.")
269 || aText.endsWith("/..");
270
271 // for pure home paths ( ~username ) the '.' at the end of rMatch
272 // means that it points to root catalog
273 // this is done only for file contents since home paths parsing is useful only for them
274 if ( bPureHomePath && rMatch == "file:///." )
275 {
276 // a home that refers to /
277
278 OUString aNewText = aText + "/";
279 Insert( aNewText, rURL, true );
280
281 return;
282 }
283
284 // string to match with
285 INetURLObject aMatchObj( rMatch );
286 OUString aMatchName;
287
288 if ( rURL != aMatchObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
289 {
291
292 // matching is always done case insensitive, but completion will be case sensitive and case preserving
293 aMatchName = aMatchName.toAsciiLowerCase();
294
295 // if the matchstring ends with a slash, we must search for this also
296 if ( rMatch.endsWith("/") )
297 aMatchName += "/";
298 }
299
300 sal_Int32 nMatchLen = aMatchName.getLength();
301
302 INetURLObject aFolderObj( rURL );
303 DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" );
304
305 try
306 {
308 new ::ucbhelper::CommandEnvironment( uno::Reference< XInteractionHandler >(),
309 uno::Reference< XProgressHandler >() ),
311 uno::Reference< XResultSet > xResultSet;
312
313 try
314 {
316 if ( bOnlyDirectories )
317 eInclude = INCLUDE_FOLDERS_ONLY;
318 uno::Reference< XDynamicResultSet > xDynResultSet = aCnt.createDynamicCursor( { "Title", "IsFolder" }, eInclude );
319
320 uno::Reference < XAnyCompareFactory > xCompare;
321 uno::Reference < XSortedDynamicResultSetFactory > xSRSFac =
322 SortedDynamicResultSetFactory::create( ::comphelper::getProcessComponentContext() );
323
324 uno::Reference< XDynamicResultSet > xDynamicResultSet =
325 xSRSFac->createSortedDynamicResultSet( xDynResultSet, { { 2, false }, { 1, true } }, xCompare );
326
327 if ( xDynamicResultSet.is() )
328 {
329 xResultSet = xDynamicResultSet->getStaticResultSet();
330 }
331 }
332 catch( css::uno::Exception& ) {}
333
334 if ( xResultSet.is() )
335 {
336 uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
337 uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
338
339 try
340 {
341 while ( schedule() && xResultSet->next() )
342 {
343 OUString aURL = xContentAccess->queryContentIdentifierString();
344 OUString aTitle = xRow->getString(1);
345 bool bIsFolder = xRow->getBoolean(2);
346
347 // matching is always done case insensitive, but completion will be case sensitive and case preserving
348 aTitle = aTitle.toAsciiLowerCase();
349
350 if (
351 !nMatchLen ||
352 (bExectMatch && aMatchName == aTitle) ||
353 (!bExectMatch && aTitle.startsWith(aMatchName))
354 )
355 {
356 // all names fit if matchstring is empty
357 INetURLObject aObj( aURL );
358 sal_Unicode aDelimiter = '/';
359 if ( bSmart )
360 // when parsing is done "smart", the delimiter must be "guessed"
361 aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos), &aDelimiter );
362
363 if ( bIsFolder )
364 aObj.setFinalSlash();
365
366 // get the last name of the URL
368 OUString aInput( aText );
369 if ( nMatchLen )
370 {
371 if (aText.endsWith(".") || bPureHomePath)
372 {
373 // if a "special folder" URL was typed, don't touch the user input
374 aMatch = aMatch.copy( nMatchLen );
375 }
376 else
377 {
378 // make the user input case preserving
379 DBG_ASSERT( aInput.getLength() >= nMatchLen, "Suspicious Matching!" );
380 aInput = aInput.copy( 0, aInput.getLength() - nMatchLen );
381 }
382 }
383
384 aInput += aMatch;
385
386 // folders should get a final slash automatically
387 if ( bIsFolder )
388 aInput += OUStringChar(aDelimiter);
389
391 }
392 }
393 }
394 catch( css::uno::Exception& )
395 {
396 }
397 }
398 }
399 catch( css::uno::Exception& )
400 {
401 }
402}
403
405{
406 ::osl::MutexGuard aGuard( theSvtMatchContextMutex() );
407 {
408 // have we been stopped while we were waiting for the mutex?
409 std::scoped_lock g(mutex_);
410 if (stopped_) {
411 return;
412 }
413 }
414
415 // Reset match lists
416 aCompletions.clear();
417 aURLs.clear();
418
419 // check for input
420 if ( aText.isEmpty() )
421 return;
422
423 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
424 // no autocompletion for wildcards
425 return;
426
427 OUString aMatch;
430 if ( pBox->aBaseURL.isEmpty() )
431 eBaseProt = INetURLObject::CompareProtocolScheme( SvtPathOptions().GetWorkPath() );
432 INetProtocol eSmartProt = pBox->GetSmartProtocol();
433
434 // if the user input is a valid URL, go on with it
435 // otherwise it could be parsed smart with a predefined smart protocol
436 // ( or if this is not set with the protocol of a predefined base URL )
437 if( eProt == INetProtocol::NotValid || eProt == eSmartProt || (eSmartProt == INetProtocol::NotValid && eProt == eBaseProt) )
438 {
439 // not stopped yet ?
440 if( schedule() )
441 {
442 if ( eProt == INetProtocol::NotValid )
444 else
445 aMatch = aText;
446 if ( !aMatch.isEmpty() )
447 {
448 INetURLObject aURLObject( aMatch );
449 OUString aMainURL( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
450 // Disable autocompletion for anything but the (local) file
451 // system (for which access is hopefully fast), as the logic of
452 // how SvtMatchContext_Impl is used requires this code to run to
453 // completion before further user input is processed, and even
454 // SvtMatchContext_Impl::Stop does not guarantee a speedy
455 // return:
456 if ( !aMainURL.isEmpty()
457 && aURLObject.GetProtocol() == INetProtocol::File )
458 {
459 // if text input is a directory, it must be part of the match list! Until then it is scanned
460 bool folder = false;
461 if (aURLObject.hasFinalSlash()) {
462 try {
463 css::uno::Reference< css::uno::XComponentContext >
465 css::uno::Reference<
466 css::ucb::XUniversalContentBroker > ucb(
467 css::ucb::UniversalContentBroker::create(
468 ctx));
469 css::uno::Sequence< css::beans::Property > prop{
470 { /* Name */ "IsFolder",
471 /* Handle */ -1,
472 /* Type */ cppu::UnoType< bool >::get(),
473 /* Attributes */ {} }
474 };
475 css::uno::Any res;
476 css::uno::Reference< css::ucb::XCommandProcessor >
477 proc(
478 ucb->queryContent(
479 ucb->createContentIdentifier(aMainURL)),
480 css::uno::UNO_QUERY_THROW);
481 css::uno::Reference< css::ucb::XCommandProcessor2 >
482 proc2(proc, css::uno::UNO_QUERY);
483 sal_Int32 id = proc->createCommandIdentifier();
484 try {
485 {
486 std::scoped_lock g(mutex_);
487 processor_ = proc;
488 commandId_ = id;
489 }
490 res = proc->execute(
491 css::ucb::Command(
492 "getPropertyValues", -1,
493 css::uno::Any(prop)),
494 id,
495 css::uno::Reference<
496 css::ucb::XCommandEnvironment >());
497 } catch (...) {
498 if (proc2.is()) {
499 try {
500 proc2->releaseCommandIdentifier(id);
501 } catch (css::uno::RuntimeException &) {
502 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
503 }
504 }
505 throw;
506 }
507 if (proc2.is()) {
508 proc2->releaseCommandIdentifier(id);
509 }
510 {
511 std::scoped_lock g(mutex_);
512 processor_.clear();
513 // At least the neon-based WebDAV UCP does not
514 // properly support aborting commands, so return
515 // anyway now if an abort request had been
516 // ignored and the command execution only
517 // returned "successfully" after some timeout:
518 if (stopped_) {
519 return;
520 }
521 }
522 css::uno::Reference< css::sdbc::XRow > row(
523 res, css::uno::UNO_QUERY_THROW);
524 folder = row->getBoolean(1) && !row->wasNull();
525 } catch (css::uno::Exception &) {
526 TOOLS_WARN_EXCEPTION("svtools.control", "ignoring");
527 return;
528 }
529 }
530 if (folder)
531 Insert( aText, aMatch );
532 else
533 // otherwise the parent folder will be taken
534 aURLObject.removeSegment();
535
536 // scan directory and insert all matches
537 ReadFolder( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aMatch, eProt == INetProtocol::NotValid );
538 }
539 }
540 }
541 }
542
543 if ( bOnlyDirectories )
544 // don't scan history picklist if only directories are allowed, picklist contains only files
545 return;
546
547 bool bFull = false;
548
549 INetURLObject aCurObj;
550 OUString aCurString, aCurMainURL;
551 INetURLObject aObj;
552 aObj.SetSmartProtocol( eSmartProt == INetProtocol::NotValid ? INetProtocol::Http : eSmartProt );
553 for( ;; )
554 {
555 for(const auto& rPick : aPickList)
556 {
557 if (!schedule())
558 break;
559
560 aCurObj.SetURL(rPick);
561 aCurObj.SetSmartURL( aCurObj.GetURLNoPass());
562 aCurMainURL = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
563
564 if( eProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eProt )
565 continue;
566
567 if( eSmartProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eSmartProt )
568 continue;
569
570 switch( aCurObj.GetProtocol() )
571 {
572 case INetProtocol::Http:
573 case INetProtocol::Https:
574 case INetProtocol::Ftp:
575 {
576 if( eProt == INetProtocol::NotValid && !bFull )
577 {
578 aObj.SetSmartURL( aText );
579 if( aObj.GetURLPath().getLength() > 1 )
580 continue;
581 }
582
583 aCurString = aCurMainURL;
584 if( eProt == INetProtocol::NotValid )
585 {
586 // try if text matches the scheme
587 OUString aScheme( INetURLObject::GetScheme( aCurObj.GetProtocol() ) );
588 if ( aScheme.startsWithIgnoreAsciiCase( aText ) && aText.getLength() < aScheme.getLength() )
589 {
590 if( bFull )
592 else
593 {
594 aCurObj.SetMark( u"" );
595 aCurObj.SetParam( u"" );
596 aCurObj.SetURLPath( u"" );
598 }
599
600 Insert( aMatch, aMatch );
601 }
602
603 // now try smart matching
604 aCurString = aCurString.copy( aScheme.getLength() );
605 }
606
607 if( aCurString.startsWithIgnoreAsciiCase( aText ) )
608 {
609 if( bFull )
611 else
612 {
613 aCurObj.SetMark( u"" );
614 aCurObj.SetParam( u"" );
615 aCurObj.SetURLPath( u"" );
617 }
618
619 OUString aURL( aMatch );
620 if( eProt == INetProtocol::NotValid )
621 aMatch = aMatch.copy( INetURLObject::GetScheme( aCurObj.GetProtocol() ).getLength() );
622
623 if( aText.getLength() < aMatch.getLength() )
624 Insert( aMatch, aURL );
625
626 continue;
627 }
628 break;
629 }
630 default:
631 {
632 if( bFull )
633 continue;
634
635 if( aCurMainURL.startsWith(aText) )
636 {
637 if( aText.getLength() < aCurMainURL.getLength() )
638 Insert( aCurMainURL, aCurMainURL );
639
640 continue;
641 }
642 break;
643 }
644 }
645 }
646
647 if( !bFull )
648 bFull = true;
649 else
650 break;
651 }
652}
653
658 OUString&
659#ifdef UNX
660 aText
661#endif
662 , OUString&
663#ifdef UNX
664 aBaseURL
665#endif
666)
667{
668#ifdef UNX
669 if( aText.startsWith( "~" ) )
670 {
671 OUString aParseTilde;
672 bool bTrailingSlash = true; // use trailing slash
673
674 if( aText.getLength() == 1 || aText[ 1 ] == '/' )
675 {
676 // covers "~" or "~/..." cases
677 const char* aHomeLocation = getenv( "HOME" );
678 if( !aHomeLocation )
679 aHomeLocation = "";
680
681 aParseTilde = OUString::createFromAscii(aHomeLocation);
682
683 // in case the whole path is just "~" then there should
684 // be no trailing slash at the end
685 if( aText.getLength() == 1 )
686 bTrailingSlash = false;
687 }
688 else
689 {
690 // covers "~username" and "~username/..." cases
691 sal_Int32 nNameEnd = aText.indexOf( '/' );
692 OUString aUserName = aText.copy( 1, ( nNameEnd != -1 ) ? nNameEnd : ( aText.getLength() - 1 ) );
693
694 struct passwd* pPasswd = nullptr;
695#ifdef __sun
696 Sequence< sal_Int8 > sBuf( 1024 );
697 struct passwd aTmp;
698 sal_Int32 nRes = getpwnam_r( OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US ).getStr(),
699 &aTmp,
700 (char*)sBuf.getArray(),
701 1024,
702 &pPasswd );
703 if( !nRes && pPasswd )
704 aParseTilde = OUString::createFromAscii(pPasswd->pw_dir);
705 else
706 return false; // no such user
707#else
708 pPasswd = getpwnam( OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US ).getStr() );
709 if( pPasswd )
710 aParseTilde = OUString::createFromAscii(pPasswd->pw_dir);
711 else
712 return false; // no such user
713#endif
714
715 // in case the path is "~username" then there should
716 // be no trailing slash at the end
717 if( nNameEnd == -1 )
718 bTrailingSlash = false;
719 }
720
721 if( !bTrailingSlash )
722 {
723 if( aParseTilde.isEmpty() || aParseTilde == "/" )
724 {
725 // "/" path should be converted to "/."
726 aParseTilde = "/.";
727 }
728 else
729 {
730 // "blabla/" path should be converted to "blabla"
731 aParseTilde = comphelper::string::stripEnd(aParseTilde, '/');
732 }
733 }
734 else
735 {
736 if( !aParseTilde.endsWith("/") )
737 aParseTilde += "/";
738 if( aText.getLength() > 2 )
739 aParseTilde += aText.subView( 2 );
740 }
741
742 aText = aParseTilde;
743 aBaseURL.clear(); // tilde provide absolute path
744 }
745#endif
746
747 return true;
748}
749
750//--
751
752OUString SvtURLBox::ParseSmart( const OUString& _aText, const OUString& _aBaseURL )
753{
754 OUString aMatch;
755 OUString aText = _aText;
756 OUString aBaseURL = _aBaseURL;
757
758 // parse ~ for Unix systems
759 // does nothing for Windows
761 return OUString();
762
763 if( !aBaseURL.isEmpty() )
764 {
766
767 // if a base URL is set the string may be parsed relative
768 if( aText.startsWith( "/" ) )
769 {
770 // text starting with slashes means absolute file URLs
771 OUString aTemp = INetURLObject::GetScheme( eBaseProt );
772
773 // file URL must be correctly encoded!
774 OUString aTextURL = INetURLObject::encode( aText, INetURLObject::PART_FPATH,
776 aTemp += aTextURL;
777
778 INetURLObject aTmp( aTemp );
779 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
781 }
782 else
783 {
784 OUString aSmart( aText );
785 INetURLObject aObj( aBaseURL );
786
787 // HRO: I suppose this hack should only be done for Windows !!!???
788#ifdef _WIN32
789 // HRO: INetURLObject::smatRel2Abs does not recognize '\\' as a relative path
790 // but in case of "\\\\" INetURLObject is right - this is an absolute path !
791
792 if( aText.startsWith("\\") && (aText.getLength() < 2 || aText[ 1 ] != '\\') )
793 {
794 // cut to first segment
795 OUString aTmp = INetURLObject::GetScheme( eBaseProt ) + "/";
797 aObj.SetURL( aTmp );
798
799 aSmart = aSmart.copy(1);
800 }
801#endif
802 // base URL must be a directory !
803 aObj.setFinalSlash();
804
805 // take base URL and append current input
806 bool bWasAbsolute = false;
807#ifdef UNX
808 // encode file URL correctly
810#endif
811 INetURLObject aTmp( aObj.smartRel2Abs( aSmart, bWasAbsolute ) );
812
813 if ( aText.endsWith(".") )
814 // INetURLObject appends a final slash for the directories "." and "..", this is a bug!
815 // Remove it as a workaround
816 aTmp.removeFinalSlash();
817 if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid )
819 }
820 }
821 else
822 {
823 OUString aTmpMatch;
824 osl::FileBase::getFileURLFromSystemPath( aText, aTmpMatch );
825 aMatch = aTmpMatch;
826 }
827
828 return aMatch;
829}
830
831IMPL_LINK_NOARG(SvtURLBox, TryAutoComplete, Timer *, void)
832{
833 OUString aCurText = m_xWidget->get_active_text();
834 int nStartPos, nEndPos;
835 m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos);
836 if (std::max(nStartPos, nEndPos) != aCurText.getLength())
837 return;
838
839 auto nLen = std::min(nStartPos, nEndPos);
840 aCurText = aCurText.copy( 0, nLen );
841 if (!aCurText.isEmpty())
842 {
843 if (pCtx.is())
844 {
845 pCtx->Stop();
846 pCtx->join();
847 pCtx.clear();
848 }
849 pCtx = new SvtMatchContext_Impl(this, aCurText);
850 pCtx->launch();
851 }
852 else
853 m_xWidget->clear();
854}
855
856SvtURLBox::SvtURLBox(std::unique_ptr<weld::ComboBox> pWidget)
857 : aChangedIdle("svtools::URLBox aChangedIdle")
858 , eSmartProtocol(INetProtocol::NotValid)
859 , bOnlyDirectories( false )
860 , bHistoryDisabled( false )
861 , bNoSelection( false )
862 , m_xWidget(std::move(pWidget))
863{
864 //don't grow to fix mega-long urls
865 Size aSize(m_xWidget->get_preferred_size());
866 m_xWidget->set_size_request(aSize.Width(), -1);
867
868 Init();
869
870 m_xWidget->connect_focus_in(LINK(this, SvtURLBox, FocusInHdl));
871 m_xWidget->connect_focus_out(LINK(this, SvtURLBox, FocusOutHdl));
872 m_xWidget->connect_changed(LINK(this, SvtURLBox, ChangedHdl));
873
874 aChangedIdle.SetInvokeHandler(LINK(this, SvtURLBox, TryAutoComplete));
875}
876
878{
879 pImpl.reset( new SvtURLBox_Impl );
880
881 m_xWidget->set_entry_completion(false);
882
884}
885
887{
888 if (pCtx.is())
889 {
890 pCtx->Stop();
891 pCtx->join();
892 }
893}
894
896{
897 if ( eSmartProtocol != eProt )
898 {
899 eSmartProtocol = eProt;
901 }
902}
903
905{
906 m_xWidget->clear();
907 if ( bHistoryDisabled )
908 return;
909
911 return;
912
913 // read history pick list
914 const std::vector< SvtHistoryOptions::HistoryItem > seqPicklist = SvtHistoryOptions::GetList( EHistoryType::PickList );
915 INetURLObject aCurObj;
916
917 for( const SvtHistoryOptions::HistoryItem& rPropertySet : seqPicklist )
918 {
919 aCurObj.SetURL( rPropertySet.sURL );
920
921 if ( !rPropertySet.sURL.isEmpty() && ( eSmartProtocol != INetProtocol::NotValid ) )
922 {
923 if( aCurObj.GetProtocol() != eSmartProtocol )
924 continue;
925 }
926
928
929 if ( !aURL.isEmpty() )
930 {
931 bool bFound = aURL.endsWith("/");
932 if ( !bFound )
933 {
934 OUString aUpperURL = aURL.toAsciiUpperCase();
935
936 bFound = ::std::any_of(pImpl->m_aFilters.begin(),
937 pImpl->m_aFilters.end(),
938 FilterMatch( aUpperURL ) );
939 }
940 if ( bFound )
941 {
942 OUString aFile;
943 if (osl::FileBase::getSystemPathFromFileURL(aURL, aFile) == osl::FileBase::E_None)
944 m_xWidget->append_text(aFile);
945 else
946 m_xWidget->append_text(aURL);
947 }
948 }
949 }
950}
951
953{
954 aChangeHdl.Call(*m_xWidget);
955 aChangedIdle.Start(); //launch this to happen on idle after cursor position will have been set
956}
957
959{
960#ifndef UNX
961 // pb: don't select automatically on unix #93251#
962 m_xWidget->select_entry_region(0, -1);
963#endif
964 aFocusInHdl.Call(*m_xWidget);
965}
966
968{
969 if (pCtx.is())
970 {
971 pCtx->Stop();
972 pCtx->join();
973 pCtx.clear();
974 }
975 aFocusOutHdl.Call(*m_xWidget);
976}
977
979{
980 bOnlyDirectories = bDir;
981 if ( bOnlyDirectories )
982 m_xWidget->clear();
983}
984
986{
987 bNoSelection = bSet;
988}
989
991{
992 // wait for end of autocompletion
993 ::osl::MutexGuard aGuard( theSvtMatchContextMutex() );
994
995 OUString aText(m_xWidget->get_active_text());
996 if (MatchesPlaceHolder(aText))
997 return aPlaceHolder;
998
999 // try to get the right case preserving URL from the list of URLs
1000 for(std::vector<OUString>::iterator i = pImpl->aCompletions.begin(), j = pImpl->aURLs.begin(); i != pImpl->aCompletions.end() && j != pImpl->aURLs.end(); ++i, ++j)
1001 {
1002 if((*i) == aText)
1003 return *j;
1004 }
1005
1006#ifdef _WIN32
1007 // erase trailing spaces on Windows since they are invalid on this OS and
1008 // most of the time they are inserted by accident via copy / paste
1009 aText = comphelper::string::stripEnd(aText, ' ');
1010 if ( aText.isEmpty() )
1011 return aText;
1012 // #i9739#
1013#endif
1014
1015 INetURLObject aObj( aText );
1016 if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 )
1017 {
1018 // no autocompletion for wildcards
1019 INetURLObject aTempObj;
1020 if ( eSmartProtocol != INetProtocol::NotValid )
1021 aTempObj.SetSmartProtocol( eSmartProtocol );
1022 if ( aTempObj.SetSmartURL( aText ) )
1024 else
1025 return aText;
1026 }
1027
1028 if ( aObj.GetProtocol() == INetProtocol::NotValid )
1029 {
1030 OUString aName = ParseSmart( aText, aBaseURL );
1031 aObj.SetURL(aName);
1033 if ( aURL.isEmpty() )
1034 // aText itself is invalid, and even together with aBaseURL, it could not
1035 // made valid -> no chance
1036 return aText;
1037
1038 bool bSlash = aObj.hasFinalSlash();
1039 {
1040 OUString aFileURL;
1041
1042 Any aAny = UCBContentHelper::GetProperty(aURL, "CasePreservingURL");
1043 bool success = (aAny >>= aFileURL);
1044 OUString aTitle;
1045 if(success)
1046 aTitle = INetURLObject(aFileURL).getName(
1048 true,
1050 else
1051 success =
1052 UCBContentHelper::GetTitle(aURL,&aTitle);
1053
1054 if( success && aTitle != "/" && aTitle != "." )
1055 {
1056 aObj.setName( aTitle );
1057 if ( bSlash )
1058 aObj.setFinalSlash();
1059 }
1060 }
1061 }
1062
1064}
1065
1066void SvtURLBox::SetBaseURL( const OUString& rURL )
1067{
1068 ::osl::MutexGuard aGuard( theSvtMatchContextMutex() );
1069
1070 // Reset match lists
1071 pImpl->aCompletions.clear();
1072 pImpl->aURLs.clear();
1073
1074 aBaseURL = rURL;
1075}
1076
1078{
1079 bHistoryDisabled = true;
1081}
1082
1083void SvtURLBox::SetFilter(std::u16string_view _sFilter)
1084{
1085 pImpl->m_aFilters.clear();
1086 FilterMatch::createWildCardFilterList(_sFilter,pImpl->m_aFilters);
1087}
1088
1089void FilterMatch::createWildCardFilterList(std::u16string_view _rFilterList,::std::vector< WildCard >& _rFilters)
1090{
1091 if( !_rFilterList.empty() )
1092 {
1093 // filter is given
1094 sal_Int32 nIndex = 0;
1095 OUString sToken;
1096 do
1097 {
1098 sToken = o3tl::getToken(_rFilterList, 0, ';', nIndex );
1099 if ( !sToken.isEmpty() )
1100 {
1101 _rFilters.emplace_back( sToken.toAsciiUpperCase() );
1102 }
1103 }
1104 while ( nIndex >= 0 );
1105 }
1106 else
1107 {
1108 // no filter is given -> match all
1109 _rFilters.emplace_back(u"*" );
1110 }
1111}
1112
1113/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::unique_ptr< weld::Image > m_xWidget
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
void SetSmartProtocol(INetProtocol eTheSmartScheme)
static OUString GetScheme(INetProtocol eTheScheme)
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool removeFinalSlash()
INetURLObject smartRel2Abs(OUString const &rTheRelURIRef, bool &rWasAbsolute, bool bIgnoreFragment=false, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, bool bRelativeNonURIs=false, FSysStyle eStyle=FSysStyle::Detect) const
bool HasError() const
bool removeSegment(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true)
bool setFinalSlash()
bool SetSmartURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
static INetProtocol CompareProtocolScheme(std::u16string_view aTheAbsURIRef)
OUString GetURLPath(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool setName(std::u16string_view rTheName, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
bool hasFinalSlash() const
bool SetURLPath(std::u16string_view rThePath, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
INetProtocol GetProtocol() const
bool SetMark(std::u16string_view rTheFragment, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
bool SetParam(std::u16string_view rTheQuery, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
OUString getFSysPath(FSysStyle eStyle, sal_Unicode *pDelimiter=nullptr) const
OUString GetURLNoPass(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool SetURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
static OUString encode(std::u16string_view rText, Part ePart, EncodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
constexpr tools::Long Width() const
DECL_LINK(Select_Impl, void *, void)
static void FillPicklist(std::vector< OUString > &rPickList)
Definition: inettbc.cxx:142
sal_Int32 commandId_
Definition: inettbc.cxx:98
void ReadFolder(const OUString &rURL, const OUString &rMatch, bool bSmart)
Definition: inettbc.cxx:253
std::vector< OUString > aURLs
Definition: inettbc.cxx:88
virtual void execute() override
Definition: inettbc.cxx:174
std::vector< OUString > aCompletions
Definition: inettbc.cxx:87
std::vector< OUString > aPickList
Definition: inettbc.cxx:86
virtual ~SvtMatchContext_Impl() override
Definition: inettbc.cxx:137
SvtMatchContext_Impl(SvtURLBox *pBoxP, OUString aText)
Definition: inettbc.cxx:124
svtools::AsynchronLink aLink
Definition: inettbc.cxx:89
void Insert(const OUString &rCompletion, const OUString &rURL, bool bForce=false)
Definition: inettbc.cxx:237
css::uno::Reference< css::ucb::XCommandProcessor > processor_
Definition: inettbc.cxx:97
std::mutex mutex_
Definition: inettbc.cxx:95
SvtURLBox * pBox
Definition: inettbc.cxx:91
std::vector< WildCard > m_aFilters
Definition: inettbc.cxx:74
std::vector< OUString > aCompletions
Definition: inettbc.cxx:73
static bool TildeParsing(OUString &aText, OUString &aBaseUrl)
Parse leading ~ for Unix systems, does nothing for Windows.
Definition: inettbc.cxx:657
std::vector< OUString > aURLs
Definition: inettbc.cxx:72
bool bNoSelection
Definition: inettbc.hxx:46
Idle aChangedIdle
Definition: inettbc.hxx:38
OUString GetURL()
Definition: inettbc.cxx:990
SVT_DLLPRIVATE void UpdatePicklistForSmartProtocol_Impl()
Definition: inettbc.cxx:904
SvtURLBox(std::unique_ptr< weld::ComboBox > xWidget)
Definition: inettbc.cxx:856
bool bOnlyDirectories
Definition: inettbc.hxx:44
rtl::Reference< SvtMatchContext_Impl > pCtx
Definition: inettbc.hxx:41
bool MatchesPlaceHolder(std::u16string_view sToMatch) const
Definition: inettbc.hxx:99
void SetOnlyDirectories(bool bDir)
Definition: inettbc.cxx:978
std::unique_ptr< SvtURLBox_Impl > pImpl
Definition: inettbc.hxx:42
std::unique_ptr< weld::ComboBox > m_xWidget
Definition: inettbc.hxx:52
INetProtocol GetSmartProtocol() const
Definition: inettbc.hxx:89
~SvtURLBox()
Definition: inettbc.cxx:886
void SetSmartProtocol(INetProtocol eProt)
Definition: inettbc.cxx:895
SVT_DLLPRIVATE void Init()
Definition: inettbc.cxx:877
void SetBaseURL(const OUString &rURL)
Definition: inettbc.cxx:1066
void DisableHistory()
Definition: inettbc.cxx:1077
bool bHistoryDisabled
Definition: inettbc.hxx:45
INetProtocol eSmartProtocol
Definition: inettbc.hxx:43
OUString aBaseURL
Definition: inettbc.hxx:39
OUString aPlaceHolder
Definition: inettbc.hxx:40
static OUString ParseSmart(const OUString &aText, const OUString &aBaseURL)
Definition: inettbc.cxx:752
void SetNoURLSelection(bool bSet)
Definition: inettbc.cxx:985
void SetFilter(std::u16string_view _sFilter)
Definition: inettbc.cxx:1083
void SetInvokeHandler(const Link< Timer *, void > &rLink)
css::uno::Type const & get()
std::mutex mutex_
int nCount
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
float u
IMPL_LINK_NOARG(SvtMatchContext_Impl, Select_Impl, void *, void)
Definition: inettbc.cxx:187
sal_Int32 nIndex
OUString aName
std::vector< HistoryItem > GetList(EHistoryType eHistory)
OString stripEnd(const OString &rIn, char c)
Reference< XComponentContext > getProcessComponentContext()
OSQLColumns::const_iterator find(const OSQLColumns::const_iterator &first, const OSQLColumns::const_iterator &last, std::u16string_view _rVal, const ::comphelper::UStringMixEqual &_rCase)
int i
ctx
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
ResultSetInclude
INCLUDE_FOLDERS_AND_DOCUMENTS
INCLUDE_FOLDERS_ONLY
sal_uInt16 sal_Unicode
INetProtocol
FSysStyle