1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <sys/types.h>
25 #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/beans/PropertyValue.hpp>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/sdbc/XResultSet.hpp>
32 #include <com/sun/star/sdbc/XRow.hpp>
33 #include <com/sun/star/task/XInteractionHandler.hpp>
34 #include <com/sun/star/ucb/NumberedSortingInfo.hpp>
35 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
36 #include <com/sun/star/ucb/XAnyCompareFactory.hpp>
37 #include <com/sun/star/ucb/XCommandProcessor2.hpp>
38 #include <com/sun/star/ucb/XProgressHandler.hpp>
39 #include <com/sun/star/ucb/XContentAccess.hpp>
40 #include <com/sun/star/ucb/SortedDynamicResultSetFactory.hpp>
41 #include <comphelper/processfactory.hxx>
42 #include <comphelper/string.hxx>
43 #include <rtl/instance.hxx>
44 #include <salhelper/thread.hxx>
45 #include <osl/mutex.hxx>
46 #include <vcl/builder.hxx>
47 #include <vcl/svapp.hxx>
48 #include <vcl/toolbox.hxx>
49 #include <unotools/historyoptions.hxx>
50 #include <svl/eitem.hxx>
51 #include <svl/stritem.hxx>
52 #include <svl/itemset.hxx>
53 #include "svl/urihelper.hxx"
54 #include <unotools/pathoptions.hxx>
55 #include <ucbhelper/commandenvironment.hxx>
56 #include <ucbhelper/content.hxx>
57 #include <unotools/localfilehelper.hxx>
58 #include <unotools/ucbhelper.hxx>
60 #include <svtools/asynclink.hxx>
61 #include <svl/urlfilter.hxx>
66 using namespace ::rtl
;
67 using namespace ::ucbhelper
;
68 using namespace ::utl
;
69 using namespace ::com::sun::star
;
70 using namespace ::com::sun::star::beans
;
71 using namespace ::com::sun::star::lang
;
72 using namespace ::com::sun::star::sdbc
;
73 using namespace ::com::sun::star::task
;
74 using namespace ::com::sun::star::ucb
;
75 using namespace ::com::sun::star::uno
;
80 std::vector
<OUString
> aURLs
;
81 std::vector
<OUString
> aCompletions
;
82 std::vector
<WildCard
> m_aFilters
;
84 static sal_Bool
TildeParsing( OUString
& aText
, OUString
& aBaseUrl
);
86 inline SvtURLBox_Impl( )
88 FilterMatch::createWildCardFilterList(OUString(),m_aFilters
);
92 class SvtMatchContext_Impl
: public salhelper::Thread
94 static ::osl::Mutex
* pDirMutex
;
96 std::vector
<OUString
> aPickList
;
97 std::vector
<OUString
> aCompletions
;
98 std::vector
<OUString
> aURLs
;
99 svtools::AsynchronLink aLink
;
103 sal_Bool bOnlyDirectories
;
104 sal_Bool bNoSelection
;
108 css::uno::Reference
< css::ucb::XCommandProcessor
> processor_
;
109 sal_Int32 commandId_
;
111 DECL_STATIC_LINK( SvtMatchContext_Impl
, Select_Impl
, void* );
113 virtual ~SvtMatchContext_Impl();
114 virtual void execute();
116 void Insert( const OUString
& rCompletion
, const OUString
& rURL
, sal_Bool bForce
= sal_False
);
117 void ReadFolder( const OUString
& rURL
, const OUString
& rMatch
, sal_Bool bSmart
);
118 void FillPicklist(std::vector
<OUString
>& rPickList
);
121 SvtMatchContext_Impl( SvtURLBox
* pBoxP
, const OUString
& rText
);
127 struct theSvtMatchContextMutex
128 : public rtl::Static
< ::osl::Mutex
, theSvtMatchContextMutex
> {};
131 SvtMatchContext_Impl::SvtMatchContext_Impl(
132 SvtURLBox
* pBoxP
, const OUString
& rText
)
133 : Thread( "SvtMatchContext_Impl" )
134 , aLink( STATIC_LINK( this, SvtMatchContext_Impl
, Select_Impl
) )
135 , aBaseURL( pBoxP
->aBaseURL
)
138 , bOnlyDirectories( pBoxP
->bOnlyDirectories
)
139 , bNoSelection( pBoxP
->bNoSelection
)
145 FillPicklist( aPickList
);
148 SvtMatchContext_Impl::~SvtMatchContext_Impl()
150 aLink
.ClearPendingCall();
153 void SvtMatchContext_Impl::FillPicklist(std::vector
<OUString
>& rPickList
)
155 // Einlesung der Historypickliste
156 Sequence
< Sequence
< PropertyValue
> > seqPicklist
= SvtHistoryOptions().GetList( eHISTORY
);
157 sal_uInt32 nCount
= seqPicklist
.getLength();
159 for( sal_uInt32 nItem
=0; nItem
< nCount
; nItem
++ )
161 Sequence
< PropertyValue
> seqPropertySet
= seqPicklist
[ nItem
];
166 sal_uInt32 nPropertyCount
= seqPropertySet
.getLength();
168 for( sal_uInt32 nProperty
=0; nProperty
< nPropertyCount
; nProperty
++ )
170 if( seqPropertySet
[nProperty
].Name
== HISTORY_PROPERTYNAME_TITLE
)
172 seqPropertySet
[nProperty
].Value
>>= sTitle
;
173 aURL
.SetURL( sTitle
);
174 rPickList
.insert(rPickList
.begin() + nItem
, aURL
.GetMainURL(INetURLObject::DECODE_WITH_CHARSET
));
181 void SvtMatchContext_Impl::Stop()
183 css::uno::Reference
< css::ucb::XCommandProcessor
> proc
;
186 osl::MutexGuard
g(mutex_
);
199 void SvtMatchContext_Impl::execute( )
205 //-------------------------------------------------------------------------
206 // This method is called via AsynchronLink, so it has the SolarMutex and
207 // calling solar code ( VCL ... ) is safe. It is called when the thread is
208 // terminated ( finished work or stopped ). Cancelling the thread via
209 // Cancellable does not not discard the information gained so far, it
210 // inserts all collected completions into the listbox.
212 IMPL_STATIC_LINK( SvtMatchContext_Impl
, Select_Impl
, void*, )
214 // avoid recursion through cancel button
216 osl::MutexGuard
g(pThis
->mutex_
);
217 if (pThis
->stopped_
) {
218 // Completion was stopped, no display:
223 SvtURLBox
* pBox
= pThis
->pBox
;
224 pBox
->bAutoCompleteMode
= sal_True
;
226 // did we filter completions which otherwise would have been valid?
227 // (to be filled below)
228 bool bValidCompletionsFiltered
= false;
230 // insert all completed strings into the listbox
233 for(std::vector
<OUString
>::iterator i
= pThis
->aCompletions
.begin(); i
!= pThis
->aCompletions
.end(); ++i
)
235 OUString
sCompletion(*i
);
237 // convert the file into an URL
238 OUString
sURL( sCompletion
);
239 ::utl::LocalFileHelper::ConvertPhysicalNameToURL( sCompletion
, sURL
);
240 // note: if this doesn't work, we're not interested in: we're checking the
241 // untouched sCompletion then
243 if ( !sURL
.isEmpty() && !sURL
.endsWith("/") )
245 OUString
sUpperURL( sURL
.toAsciiUpperCase() );
247 ::std::vector
< WildCard
>::const_iterator aMatchingFilter
=
249 pBox
->pImp
->m_aFilters
.begin(),
250 pBox
->pImp
->m_aFilters
.end(),
251 FilterMatch( sUpperURL
)
253 if ( aMatchingFilter
== pBox
->pImp
->m_aFilters
.end() )
255 { // this URL is not allowed
256 bValidCompletionsFiltered
= true;
261 pBox
->InsertEntry( sCompletion
);
264 if( !pThis
->bNoSelection
&& !pThis
->aCompletions
.empty() && !bValidCompletionsFiltered
)
266 // select the first one
267 OUString
aTmp( pBox
->GetEntry(0) );
268 pBox
->SetText( aTmp
);
269 pBox
->SetSelection( Selection( pThis
->aText
.getLength(), aTmp
.getLength() ) );
272 // transfer string lists to listbox and forget them
273 pBox
->pImp
->aURLs
= pThis
->aURLs
;
274 pBox
->pImp
->aCompletions
= pThis
->aCompletions
;
275 pThis
->aURLs
.clear();
276 pThis
->aCompletions
.clear();
278 // force listbox to resize ( it may be open )
281 // the box has this control as a member so we have to set that member
282 // to zero before deleting ourself.
288 //-------------------------------------------------------------------------
289 void SvtMatchContext_Impl::Insert( const OUString
& rCompletion
,
290 const OUString
& rURL
,
296 if(find(aCompletions
.begin(), aCompletions
.end(), OUString(rCompletion
)) != aCompletions
.end())
300 aCompletions
.push_back(rCompletion
);
301 aURLs
.push_back(rURL
);
304 //-------------------------------------------------------------------------
305 void SvtMatchContext_Impl::ReadFolder( const OUString
& rURL
,
306 const OUString
& rMatch
,
309 // check folder to scan
310 if( !UCBContentHelper::IsFolder( rURL
) )
313 sal_Bool bPureHomePath
= sal_False
;
315 bPureHomePath
= aText
.startsWith( "~" ) && aText
.indexOf( '/' ) == -1;
318 sal_Bool bExectMatch
= bPureHomePath
320 || aText
.endsWith("/.")
321 || aText
.endsWith("/..");
323 // for pure home paths ( ~username ) the '.' at the end of rMatch
324 // means that it poits to root catalog
325 // this is done only for file contents since home paths parsing is useful only for them
326 if ( bPureHomePath
&& rMatch
== "file:///." )
328 // a home that refers to /
330 OUString
aNewText( aText
);
332 Insert( aNewText
, rURL
, sal_True
);
337 // string to match with
338 INetURLObject
aMatchObj( rMatch
);
341 if ( rURL
!= aMatchObj
.GetMainURL( INetURLObject::NO_DECODE
) )
343 aMatchName
= aMatchObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DECODE_WITH_CHARSET
);
345 // matching is always done case insensitive, but completion will be case sensitive and case preserving
346 aMatchName
= aMatchName
.toAsciiLowerCase();
348 // if the matchstring ends with a slash, we must search for this also
349 if ( rMatch
.endsWith("/") )
353 sal_Int32 nMatchLen
= aMatchName
.getLength();
355 INetURLObject
aFolderObj( rURL
);
356 DBG_ASSERT( aFolderObj
.GetProtocol() != INET_PROT_NOT_VALID
, "Invalid URL!" );
360 Content
aCnt( aFolderObj
.GetMainURL( INetURLObject::NO_DECODE
),
361 new ::ucbhelper::CommandEnvironment( uno::Reference
< XInteractionHandler
>(),
362 uno::Reference
< XProgressHandler
>() ),
363 comphelper::getProcessComponentContext() );
364 uno::Reference
< XResultSet
> xResultSet
;
365 Sequence
< OUString
> aProps(2);
366 OUString
* pProps
= aProps
.getArray();
368 pProps
[1] = "IsFolder";
372 uno::Reference
< XDynamicResultSet
> xDynResultSet
;
373 ResultSetInclude eInclude
= INCLUDE_FOLDERS_AND_DOCUMENTS
;
374 if ( bOnlyDirectories
)
375 eInclude
= INCLUDE_FOLDERS_ONLY
;
377 xDynResultSet
= aCnt
.createDynamicCursor( aProps
, eInclude
);
379 uno::Reference
< XAnyCompareFactory
> xCompare
;
380 uno::Reference
< XSortedDynamicResultSetFactory
> xSRSFac
=
381 SortedDynamicResultSetFactory::create( ::comphelper::getProcessComponentContext() );
383 Sequence
< NumberedSortingInfo
> aSortInfo( 2 );
384 NumberedSortingInfo
* pInfo
= aSortInfo
.getArray();
385 pInfo
[ 0 ].ColumnIndex
= 2;
386 pInfo
[ 0 ].Ascending
= sal_False
;
387 pInfo
[ 1 ].ColumnIndex
= 1;
388 pInfo
[ 1 ].Ascending
= sal_True
;
390 uno::Reference
< XDynamicResultSet
> xDynamicResultSet
;
392 xSRSFac
->createSortedDynamicResultSet( xDynResultSet
, aSortInfo
, xCompare
);
394 if ( xDynamicResultSet
.is() )
396 xResultSet
= xDynamicResultSet
->getStaticResultSet();
399 catch( ::com::sun::star::uno::Exception
& ) {}
401 if ( xResultSet
.is() )
403 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
404 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
408 while ( schedule() && xResultSet
->next() )
410 OUString aURL
= xContentAccess
->queryContentIdentifierString();
411 OUString aTitle
= xRow
->getString(1);
412 sal_Bool bIsFolder
= xRow
->getBoolean(2);
414 // matching is always done case insensitive, but completion will be case sensitive and case preserving
415 aTitle
= aTitle
.toAsciiLowerCase();
419 (bExectMatch
&& aMatchName
== aTitle
) ||
420 (!bExectMatch
&& aMatchName
.compareTo(aTitle
, nMatchLen
) == 0)
423 // all names fit if matchstring is empty
424 INetURLObject
aObj( aURL
);
425 sal_Unicode aDelimiter
= '/';
427 // when parsing is done "smart", the delimiter must be "guessed"
428 aObj
.getFSysPath( (INetURLObject::FSysStyle
)(INetURLObject::FSYS_DETECT
& ~INetURLObject::FSYS_VOS
), &aDelimiter
);
431 aObj
.setFinalSlash();
433 // get the last name of the URL
434 OUString aMatch
= aObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DECODE_WITH_CHARSET
);
435 OUString
aInput( aText
);
438 if (aText
.endsWith(".") || bPureHomePath
)
440 // if a "special folder" URL was typed, don't touch the user input
441 aMatch
= aMatch
.copy( nMatchLen
);
445 // make the user input case preserving
446 DBG_ASSERT( aInput
.getLength() >= nMatchLen
, "Suspicious Matching!" );
447 aInput
= aInput
.copy( 0, aInput
.getLength() - nMatchLen
);
453 // folders should get a final slash automatically
455 aInput
+= OUString(aDelimiter
);
457 Insert( aInput
, aObj
.GetMainURL( INetURLObject::NO_DECODE
), sal_True
);
461 catch( ::com::sun::star::uno::Exception
& )
466 catch( ::com::sun::star::uno::Exception
& )
471 //-------------------------------------------------------------------------
472 OUString
SvtURLBox::ParseSmart( OUString aText
, OUString aBaseURL
, const OUString
& aWorkDir
)
476 // parse ~ for Unix systems
477 // does nothing for Windows
478 if( !SvtURLBox_Impl::TildeParsing( aText
, aBaseURL
) )
481 if( !aBaseURL
.isEmpty() )
483 INetProtocol eBaseProt
= INetURLObject::CompareProtocolScheme( aBaseURL
);
485 // if a base URL is set the string may be parsed relative
486 if( aText
.startsWith( "/" ) )
488 // text starting with slashes means absolute file URLs
489 OUString aTemp
= INetURLObject::GetScheme( eBaseProt
);
491 // file URL must be correctly encoded!
492 OUString aTextURL
= INetURLObject::encode( aText
, INetURLObject::PART_FPATH
,
493 '%', INetURLObject::ENCODE_ALL
);
496 INetURLObject
aTmp( aTemp
);
497 if ( !aTmp
.HasError() && aTmp
.GetProtocol() != INET_PROT_NOT_VALID
)
498 aMatch
= aTmp
.GetMainURL( INetURLObject::NO_DECODE
);
502 OUString
aSmart( aText
);
503 INetURLObject
aObj( aBaseURL
);
505 // HRO: I suppose this hack should only be done for Windows !!!???
507 // HRO: INetURLObject::smatRel2Abs does not recognize '\\' as a relative path
508 // but in case of "\\\\" INetURLObject is right - this is an absolute path !
510 if( aText
.startsWith("\\") && (aText
.getLength() < 2 || aText
[ 1 ] != '\\') )
512 // cut to first segment
513 OUString aTmp
= INetURLObject::GetScheme( eBaseProt
);
515 aTmp
+= aObj
.getName( 0, true, INetURLObject::DECODE_WITH_CHARSET
);
518 aSmart
= aSmart
.copy(1);
521 // base URL must be a directory !
522 aObj
.setFinalSlash();
524 // take base URL and append current input
525 bool bWasAbsolute
= sal_False
;
527 // don't support FSYS_MAC under Unix, because here ':' is a valid character for a filename
528 INetURLObject::FSysStyle eStyle
= static_cast< INetURLObject::FSysStyle
>( INetURLObject::FSYS_VOS
| INetURLObject::FSYS_UNX
| INetURLObject::FSYS_DOS
);
529 // encode file URL correctly
530 aSmart
= INetURLObject::encode( aSmart
, INetURLObject::PART_FPATH
, '%', INetURLObject::ENCODE_ALL
);
531 INetURLObject
aTmp( aObj
.smartRel2Abs(
532 aSmart
, bWasAbsolute
, false, INetURLObject::WAS_ENCODED
, RTL_TEXTENCODING_UTF8
, false, eStyle
) );
534 INetURLObject
aTmp( aObj
.smartRel2Abs( aSmart
, bWasAbsolute
) );
537 if ( aText
.endsWith(".") )
538 // INetURLObject appends a final slash for the directories "." and "..", this is a bug!
539 // Remove it as a workaround
540 aTmp
.removeFinalSlash();
541 if ( !aTmp
.HasError() && aTmp
.GetProtocol() != INET_PROT_NOT_VALID
)
542 aMatch
= aTmp
.GetMainURL( INetURLObject::NO_DECODE
);
548 ::utl::LocalFileHelper::ConvertSystemPathToURL( aText
, aWorkDir
, aTmpMatch
);
555 //-------------------------------------------------------------------------
556 void SvtMatchContext_Impl::doExecute()
558 ::osl::MutexGuard
aGuard( theSvtMatchContextMutex::get() );
560 // have we been stopped while we were waiting for the mutex?
561 osl::MutexGuard
g(mutex_
);
568 aCompletions
.clear();
572 sal_uInt16 nTextLen
= aText
.getLength();
576 if( aText
.indexOf( '*' ) != -1 || aText
.indexOf( '?' ) != -1 )
577 // no autocompletion for wildcards
581 OUString
aWorkDir( SvtPathOptions().GetWorkPath() );
582 INetProtocol eProt
= INetURLObject::CompareProtocolScheme( aText
);
583 INetProtocol eBaseProt
= INetURLObject::CompareProtocolScheme( aBaseURL
);
584 if ( aBaseURL
.isEmpty() )
585 eBaseProt
= INetURLObject::CompareProtocolScheme( aWorkDir
);
586 INetProtocol eSmartProt
= pBox
->GetSmartProtocol();
588 // if the user input is a valid URL, go on with it
589 // otherwise it could be parsed smart with a predefined smart protocol
590 // ( or if this is not set with the protocol of a predefined base URL )
591 if( eProt
== INET_PROT_NOT_VALID
|| eProt
== eSmartProt
|| (eSmartProt
== INET_PROT_NOT_VALID
&& eProt
== eBaseProt
) )
596 if ( eProt
== INET_PROT_NOT_VALID
)
597 aMatch
= SvtURLBox::ParseSmart( aText
, aBaseURL
, aWorkDir
);
600 if ( !aMatch
.isEmpty() )
602 INetURLObject
aURLObject( aMatch
);
603 OUString
aMainURL( aURLObject
.GetMainURL( INetURLObject::NO_DECODE
) );
604 // Disable autocompletion for anything but the (local) file
605 // system (for which access is hopefully fast), as the logic of
606 // how SvtMatchContext_Impl is used requires this code to run to
607 // completion before further user input is processed, and even
608 // SvtMatchContext_Impl::Stop does not guarantee a speedy
610 if ( !aMainURL
.isEmpty()
611 && aURLObject
.GetProtocol() == INET_PROT_FILE
)
613 // if text input is a directory, it must be part of the match list! Until then it is scanned
615 if (aURLObject
.hasFinalSlash()) {
617 css::uno::Reference
< css::uno::XComponentContext
>
618 ctx(comphelper::getProcessComponentContext());
620 css::ucb::XUniversalContentBroker
> ucb(
621 css::ucb::UniversalContentBroker::create(
623 css::uno::Sequence
< css::beans::Property
> prop(1);
624 prop
[0].Name
= "IsFolder";
626 prop
[0].Type
= cppu::UnoType
< bool >::get();
628 css::uno::Reference
< css::ucb::XCommandProcessor
>
631 ucb
->createContentIdentifier(aMainURL
)),
632 css::uno::UNO_QUERY_THROW
);
633 css::uno::Reference
< css::ucb::XCommandProcessor2
>
634 proc2(proc
, css::uno::UNO_QUERY
);
635 sal_Int32 id
= proc
->createCommandIdentifier();
638 osl::MutexGuard
g(mutex_
);
644 "getPropertyValues", -1,
645 css::uno::makeAny(prop
)),
648 css::ucb::XCommandEnvironment
>());
652 proc2
->releaseCommandIdentifier(id
);
653 } catch (css::uno::RuntimeException
& e
) {
656 "ignoring UNO RuntimeException "
663 proc2
->releaseCommandIdentifier(id
);
666 osl::MutexGuard
g(mutex_
);
668 // At least the neon-based WebDAV UCP does not
669 // properly support aborting commands, so return
670 // anyway now if an abort request had been
671 // ignored and the command execution only
672 // returned "successfully" after some timeout:
677 css::uno::Reference
< css::sdbc::XRow
> row(
678 res
, css::uno::UNO_QUERY_THROW
);
679 folder
= row
->getBoolean(1) && !row
->wasNull();
680 } catch (css::uno::Exception
& e
) {
683 "ignoring UNO Exception " << typeid(*&e
).name()
684 << ": " << e
.Message
);
689 Insert( aText
, aMatch
);
691 // otherwise the parent folder will be taken
692 aURLObject
.removeSegment();
694 // scan directory and insert all matches
695 ReadFolder( aURLObject
.GetMainURL( INetURLObject::NO_DECODE
), aMatch
, eProt
== INET_PROT_NOT_VALID
);
701 if ( bOnlyDirectories
)
702 // don't scan history picklist if only directories are allowed, picklist contains only files
705 sal_Bool bFull
= sal_False
;
707 INetURLObject aCurObj
;
708 OUString aEmpty
, aCurString
, aCurMainURL
;
710 aObj
.SetSmartProtocol( eSmartProt
== INET_PROT_NOT_VALID
? INET_PROT_HTTP
: eSmartProt
);
713 for(std::vector
<OUString
>::iterator i
= aPickList
.begin(); schedule() && i
!= aPickList
.end(); ++i
)
716 aCurObj
.SetSmartURL( aCurObj
.GetURLNoPass());
717 aCurMainURL
= aCurObj
.GetMainURL( INetURLObject::NO_DECODE
);
719 if( eProt
!= INET_PROT_NOT_VALID
&& aCurObj
.GetProtocol() != eProt
)
722 if( eSmartProt
!= INET_PROT_NOT_VALID
&& aCurObj
.GetProtocol() != eSmartProt
)
725 switch( aCurObj
.GetProtocol() )
728 case INET_PROT_HTTPS
:
731 if( eProt
== INET_PROT_NOT_VALID
&& !bFull
)
733 aObj
.SetSmartURL( aText
);
734 if( aObj
.GetURLPath().getLength() > 1 )
738 aCurString
= aCurMainURL
;
739 if( eProt
== INET_PROT_NOT_VALID
)
741 // try if text matches the scheme
742 OUString
aScheme( INetURLObject::GetScheme( aCurObj
.GetProtocol() ) );
743 if ( aScheme
.startsWithIgnoreAsciiCase( aText
) && aText
.getLength() < aScheme
.getLength() )
746 aMatch
= aCurObj
.GetMainURL( INetURLObject::NO_DECODE
);
749 aCurObj
.SetMark( aEmpty
);
750 aCurObj
.SetParam( aEmpty
);
751 aCurObj
.SetURLPath( aEmpty
);
752 aMatch
= aCurObj
.GetMainURL( INetURLObject::NO_DECODE
);
755 Insert( aMatch
, aMatch
);
758 // now try smart matching
759 aCurString
= aCurString
.copy( aScheme
.getLength() );
762 if( aCurString
.startsWithIgnoreAsciiCase( aText
) )
765 aMatch
= aCurObj
.GetMainURL( INetURLObject::NO_DECODE
);
768 aCurObj
.SetMark( aEmpty
);
769 aCurObj
.SetParam( aEmpty
);
770 aCurObj
.SetURLPath( aEmpty
);
771 aMatch
= aCurObj
.GetMainURL( INetURLObject::NO_DECODE
);
774 OUString
aURL( aMatch
);
775 if( eProt
== INET_PROT_NOT_VALID
)
776 aMatch
= aMatch
.copy( INetURLObject::GetScheme( aCurObj
.GetProtocol() ).getLength() );
778 if( aText
.getLength() < aMatch
.getLength() )
779 Insert( aMatch
, aURL
);
790 if( aCurMainURL
.startsWith(aText
) )
792 if( aText
.getLength() < aCurMainURL
.getLength() )
793 Insert( aCurMainURL
, aCurMainURL
);
811 void SvtURLBox::TryAutoComplete()
813 if( Application::AnyInput( VCL_INPUT_KEYBOARD
) ) return;
815 OUString aCurText
= GetText();
816 Selection
aSelection( GetSelection() );
817 if( aSelection
.Max() != aCurText
.getLength() )
819 sal_uInt16 nLen
= (sal_uInt16
)aSelection
.Min();
820 aCurText
= aCurText
.copy( 0, nLen
);
821 if( !aCurText
.isEmpty() && bIsAutoCompleteEnabled
)
829 pCtx
= new SvtMatchContext_Impl( this, aCurText
);
834 //-------------------------------------------------------------------------
835 SvtURLBox::SvtURLBox( Window
* pParent
, INetProtocol eSmart
, bool bSetDefaultHelpID
)
836 : ComboBox( pParent
, WB_DROPDOWN
| WB_AUTOSIZE
| WB_AUTOHSCROLL
),
837 eSmartProtocol( eSmart
),
838 bAutoCompleteMode( sal_False
),
839 bOnlyDirectories( sal_False
),
840 bCtrlClick( sal_False
),
841 bHistoryDisabled( sal_False
),
842 bNoSelection( sal_False
),
843 bIsAutoCompleteEnabled( sal_True
)
845 Init(bSetDefaultHelpID
);
847 if ( GetDesktopRectPixel().GetWidth() > 800 )
848 SetSizePixel( Size( 300, 240 ) );
850 SetSizePixel( Size( 225, 240 ) );
853 //-------------------------------------------------------------------------
854 SvtURLBox::SvtURLBox( Window
* pParent
, WinBits _nStyle
, INetProtocol eSmart
,
855 bool bSetDefaultHelpID
)
856 : ComboBox( pParent
, _nStyle
),
857 eSmartProtocol( eSmart
),
858 bAutoCompleteMode( sal_False
),
859 bOnlyDirectories( sal_False
),
860 bCtrlClick( sal_False
),
861 bHistoryDisabled( sal_False
),
862 bNoSelection( sal_False
),
863 bIsAutoCompleteEnabled( sal_True
)
865 Init(bSetDefaultHelpID
);
868 extern "C" SAL_DLLPUBLIC_EXPORT Window
* SAL_CALL
makeSvtURLBox(Window
*pParent
, VclBuilder::stringmap
&)
870 WinBits nWinBits
= WB_LEFT
|WB_VCENTER
|WB_3DLOOK
|WB_TABSTOP
|
871 WB_DROPDOWN
|WB_AUTOSIZE
|WB_AUTOHSCROLL
;
872 SvtURLBox
* pListBox
= new SvtURLBox(pParent
, nWinBits
, INET_PROT_NOT_VALID
, false);
873 pListBox
->EnableAutoSize(true);
877 //-------------------------------------------------------------------------
878 SvtURLBox::SvtURLBox( Window
* pParent
, const ResId
& _rResId
, INetProtocol eSmart
,
879 bool bSetDefaultHelpID
)
880 : ComboBox( pParent
, _rResId
),
881 eSmartProtocol( eSmart
),
882 bAutoCompleteMode( sal_False
),
883 bOnlyDirectories( sal_False
),
884 bCtrlClick( sal_False
),
885 bHistoryDisabled( sal_False
),
886 bNoSelection( sal_False
),
887 bIsAutoCompleteEnabled( sal_True
)
889 Init(bSetDefaultHelpID
);
892 void SvtURLBox::Init(bool bSetDefaultHelpID
)
894 pImp
= new SvtURLBox_Impl();
896 if (bSetDefaultHelpID
&& GetHelpId().isEmpty())
897 SetHelpId( ".uno:OpenURL" );
898 EnableAutocomplete( sal_False
);
900 SetText( OUString() );
902 GetSubEdit()->SetAutocompleteHdl( LINK( this, SvtURLBox
, AutoCompleteHdl_Impl
) );
903 UpdatePicklistForSmartProtocol_Impl();
905 EnableAutoSize(GetStyle() & WB_AUTOSIZE
);
908 SvtURLBox::~SvtURLBox()
919 void SvtURLBox::UpdatePickList( )
928 OUString sText
= GetText();
929 if ( !sText
.isEmpty() && bIsAutoCompleteEnabled
)
931 pCtx
= new SvtMatchContext_Impl( this, sText
);
936 void SvtURLBox::SetSmartProtocol( INetProtocol eProt
)
938 if ( eSmartProtocol
!= eProt
)
940 eSmartProtocol
= eProt
;
941 UpdatePicklistForSmartProtocol_Impl();
945 void SvtURLBox::UpdatePicklistForSmartProtocol_Impl()
948 if ( !bHistoryDisabled
)
950 // read history pick list
951 Sequence
< Sequence
< PropertyValue
> > seqPicklist
= SvtHistoryOptions().GetList( eHISTORY
);
952 sal_uInt32 nCount
= seqPicklist
.getLength();
953 INetURLObject aCurObj
;
955 for( sal_uInt32 nItem
=0; nItem
< nCount
; nItem
++ )
957 Sequence
< PropertyValue
> seqPropertySet
= seqPicklist
[ nItem
];
961 sal_uInt32 nPropertyCount
= seqPropertySet
.getLength();
963 for( sal_uInt32 nProperty
=0; nProperty
< nPropertyCount
; nProperty
++ )
965 if( seqPropertySet
[nProperty
].Name
== HISTORY_PROPERTYNAME_URL
)
967 seqPropertySet
[nProperty
].Value
>>= sURL
;
968 aCurObj
.SetURL( sURL
);
970 if ( !sURL
.isEmpty() && ( eSmartProtocol
!= INET_PROT_NOT_VALID
) )
972 if( aCurObj
.GetProtocol() != eSmartProtocol
)
976 OUString
aURL( aCurObj
.GetMainURL( INetURLObject::DECODE_WITH_CHARSET
) );
978 if ( !aURL
.isEmpty() )
980 sal_Bool bFound
= aURL
.endsWith("/");
983 OUString
aUpperURL( aURL
);
984 aUpperURL
= aUpperURL
.toAsciiUpperCase();
988 pImp
->m_aFilters
.begin(),
989 pImp
->m_aFilters
.end(),
990 FilterMatch( aUpperURL
) )
991 != pImp
->m_aFilters
.end());
996 if (::utl::LocalFileHelper::ConvertURLToSystemPath(aURL
,aFile
))
1009 //-------------------------------------------------------------------------
1010 sal_Bool
SvtURLBox::ProcessKey( const KeyCode
& rKey
)
1012 // every key input stops the current matching thread
1020 KeyCode
aCode( rKey
.GetCode() );
1021 if ( aCode
== KEY_RETURN
&& !GetText().isEmpty() )
1023 // wait for completion of matching thread
1024 ::osl::MutexGuard
aGuard( theSvtMatchContextMutex::get() );
1026 if ( bAutoCompleteMode
)
1029 bAutoCompleteMode
= sal_False
;
1030 Selection
aSelection( GetSelection() );
1031 SetSelection( Selection( aSelection
.Min(), aSelection
.Min() ) );
1032 if ( bOnlyDirectories
)
1035 UpdatePicklistForSmartProtocol_Impl();
1039 bCtrlClick
= rKey
.IsMod1();
1040 sal_Bool bHandled
= sal_False
;
1041 if ( GetOpenHdl().IsSet() )
1043 bHandled
= sal_True
;
1044 GetOpenHdl().Call(this);
1046 else if ( GetSelectHdl().IsSet() )
1048 bHandled
= sal_True
;
1049 GetSelectHdl().Call(this);
1052 bCtrlClick
= sal_False
;
1057 else if ( aCode
== KEY_RETURN
&& GetText().isEmpty() && GetOpenHdl().IsSet() )
1060 bAutoCompleteMode
= sal_False
;
1061 GetOpenHdl().Call(this);
1064 else if( aCode
== KEY_ESCAPE
)
1066 Selection
aSelection( GetSelection() );
1067 if ( bAutoCompleteMode
|| aSelection
.Min() != aSelection
.Max() )
1069 SetSelection( Selection( aSelection
.Min(), aSelection
.Min() ) );
1070 if ( bOnlyDirectories
)
1073 UpdatePicklistForSmartProtocol_Impl();
1081 bAutoCompleteMode
= sal_False
;
1090 //-------------------------------------------------------------------------
1091 void SvtURLBox::Modify()
1096 //-------------------------------------------------------------------------
1097 long SvtURLBox::PreNotify( NotifyEvent
& rNEvt
)
1099 if( rNEvt
.GetWindow() == GetSubEdit() && rNEvt
.GetType() == EVENT_KEYINPUT
)
1102 const KeyEvent
& rEvent
= *rNEvt
.GetKeyEvent();
1103 const KeyCode
& rKey
= rEvent
.GetKeyCode();
1104 KeyCode
aCode( rKey
.GetCode() );
1105 if( ProcessKey( rKey
) )
1109 else if( ( aCode
== KEY_UP
|| aCode
== KEY_DOWN
) && !rKey
.IsMod2() )
1111 Selection
aSelection( GetSelection() );
1112 sal_uInt16 nLen
= (sal_uInt16
)aSelection
.Min();
1113 GetSubEdit()->KeyInput( rEvent
);
1114 SetSelection( Selection( nLen
, GetText().getLength() ) );
1118 if ( MatchesPlaceHolder( GetText() ) )
1120 // set the selection so a key stroke will overwrite
1121 // the placeholder rather than edit it
1122 SetSelection( Selection( 0, GetText().getLength() ) );
1126 return ComboBox::PreNotify( rNEvt
);
1129 //-------------------------------------------------------------------------
1130 IMPL_LINK_NOARG(SvtURLBox
, AutoCompleteHdl_Impl
)
1132 if ( GetSubEdit()->GetAutocompleteAction() == AUTOCOMPLETE_KEYINPUT
)
1141 //-------------------------------------------------------------------------
1142 long SvtURLBox::Notify( NotifyEvent
&rEvt
)
1144 if ( EVENT_GETFOCUS
== rEvt
.GetType() )
1147 // pb: don't select automatically on unix #93251#
1148 SetSelection( Selection( 0, GetText().getLength() ) );
1151 else if ( EVENT_LOSEFOCUS
== rEvt
.GetType() )
1153 if( GetText().isEmpty() )
1163 return ComboBox::Notify( rEvt
);
1166 //-------------------------------------------------------------------------
1167 void SvtURLBox::Select()
1173 //-------------------------------------------------------------------------
1174 void SvtURLBox::SetOnlyDirectories( sal_Bool bDir
)
1176 bOnlyDirectories
= bDir
;
1177 if ( bOnlyDirectories
)
1181 //-------------------------------------------------------------------------
1182 void SvtURLBox::SetNoURLSelection( sal_Bool bSet
)
1184 bNoSelection
= bSet
;
1187 //-------------------------------------------------------------------------
1188 OUString
SvtURLBox::GetURL()
1190 // wait for end of autocompletion
1191 ::osl::MutexGuard
aGuard( theSvtMatchContextMutex::get() );
1193 OUString
aText( GetText() );
1194 if ( MatchesPlaceHolder( aText
) )
1195 return aPlaceHolder
;
1197 // try to get the right case preserving URL from the list of URLs
1198 for(std::vector
<OUString
>::iterator i
= pImp
->aCompletions
.begin(), j
= pImp
->aURLs
.begin(); i
!= pImp
->aCompletions
.end() && j
!= pImp
->aURLs
.end(); ++i
, ++j
)
1200 if((*i
).equals(aText
))
1205 // erase trailing spaces on Windows since thay are invalid on this OS and
1206 // most of the time they are inserted by accident via copy / paste
1207 aText
= comphelper::string::stripEnd(aText
, ' ');
1208 if ( aText
.isEmpty() )
1213 INetURLObject
aObj( aText
);
1214 if( aText
.indexOf( '*' ) != -1 || aText
.indexOf( '?' ) != -1 )
1216 // no autocompletion for wildcards
1217 INetURLObject aTempObj
;
1218 if ( eSmartProtocol
!= INET_PROT_NOT_VALID
)
1219 aTempObj
.SetSmartProtocol( eSmartProtocol
);
1220 if ( aTempObj
.SetSmartURL( aText
) )
1221 return aTempObj
.GetMainURL( INetURLObject::NO_DECODE
);
1226 if ( aObj
.GetProtocol() == INET_PROT_NOT_VALID
)
1228 OUString aName
= ParseSmart( aText
, aBaseURL
, SvtPathOptions().GetWorkPath() );
1230 OUString
aURL( aObj
.GetMainURL( INetURLObject::NO_DECODE
) );
1231 if ( aURL
.isEmpty() )
1232 // aText itself is invalid, and even together with aBaseURL, it could not
1233 // made valid -> no chance
1236 bool bSlash
= aObj
.hasFinalSlash();
1238 const OUString
aPropName("CasePreservingURL");
1242 Any aAny
= UCBContentHelper::GetProperty(aURL
, aPropName
);
1243 sal_Bool success
= (aAny
>>= aFileURL
);
1246 aTitle
= INetURLObject(aFileURL
).getName(
1247 INetURLObject::LAST_SEGMENT
,
1249 INetURLObject::DECODE_WITH_CHARSET
);
1252 UCBContentHelper::GetTitle(aURL
,&aTitle
);
1254 if( success
&& aTitle
!= "/" && aTitle
!= "." )
1256 aObj
.SetName( aTitle
);
1258 aObj
.setFinalSlash();
1263 return aObj
.GetMainURL( INetURLObject::NO_DECODE
);
1266 void SvtURLBox::DisableHistory()
1268 bHistoryDisabled
= sal_True
;
1269 UpdatePicklistForSmartProtocol_Impl();
1272 void SvtURLBox::SetBaseURL( const OUString
& rURL
)
1274 ::osl::MutexGuard
aGuard( theSvtMatchContextMutex::get() );
1276 // Reset match lists
1277 pImp
->aCompletions
.clear();
1278 pImp
->aURLs
.clear();
1283 /** Parse leading ~ for Unix systems,
1284 does nothing for Windows
1286 sal_Bool
SvtURLBox_Impl::TildeParsing(
1298 if( aText
.startsWith( "~" ) )
1300 OUString aParseTilde
;
1301 sal_Bool bTrailingSlash
= sal_True
; // use trailing slash
1303 if( aText
.getLength() == 1 || aText
[ 1 ] == '/' )
1305 // covers "~" or "~/..." cases
1306 const char* aHomeLocation
= getenv( "HOME" );
1307 if( !aHomeLocation
)
1310 aParseTilde
= OUString::createFromAscii(aHomeLocation
);
1312 // in case the whole path is just "~" then there should
1313 // be no trailing slash at the end
1314 if( aText
.getLength() == 1 )
1315 bTrailingSlash
= sal_False
;
1319 // covers "~username" and "~username/..." cases
1320 sal_Int32 nNameEnd
= aText
.indexOf( '/' );
1321 OUString aUserName
= aText
.copy( 1, ( nNameEnd
!= -1 ) ? nNameEnd
: ( aText
.getLength() - 1 ) );
1323 struct passwd
* pPasswd
= NULL
;
1325 Sequence
< sal_Int8
> sBuf( 1024 );
1327 sal_Int32 nRes
= getpwnam_r( OUStringToOString( aUserName
, RTL_TEXTENCODING_ASCII_US
).getStr(),
1329 (char*)sBuf
.getArray(),
1332 if( !nRes
&& pPasswd
)
1333 aParseTilde
= OUString::createFromAscii(pPasswd
->pw_dir
);
1335 return sal_False
; // no such user
1337 pPasswd
= getpwnam( OUStringToOString( aUserName
, RTL_TEXTENCODING_ASCII_US
).getStr() );
1339 aParseTilde
= OUString::createFromAscii(pPasswd
->pw_dir
);
1341 return sal_False
; // no such user
1344 // in case the path is "~username" then there should
1345 // be no trailing slash at the end
1346 if( nNameEnd
== -1 )
1347 bTrailingSlash
= sal_False
;
1350 if( !bTrailingSlash
)
1352 if( aParseTilde
.isEmpty() || aParseTilde
== "/" )
1354 // "/" path should be converted to "/."
1359 // "blabla/" path should be converted to "blabla"
1360 aParseTilde
= comphelper::string::stripEnd(aParseTilde
, '/');
1365 if( !aParseTilde
.endsWith("/") )
1367 if( aText
.getLength() > 2 )
1368 aParseTilde
+= aText
.copy( 2 );
1371 aText
= aParseTilde
;
1372 aBaseURL
= ""; // tilde provide absolute path
1379 void SvtURLBox::SetFilter(const OUString
& _sFilter
)
1381 pImp
->m_aFilters
.clear();
1382 FilterMatch::createWildCardFilterList(_sFilter
,pImp
->m_aFilters
);
1385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */