Bug Summary

File:home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx
Warning:line 769, column 56
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name filtnav.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SVX_DLLIMPLEMENTATION -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/svx/inc -I /home/maarten/src/libreoffice/core/svx/source/inc -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx

/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <memory>
21#include <filtnav.hxx>
22#include <fmexch.hxx>
23#include <helpids.h>
24#include <fmprop.hxx>
25#include <svx/strings.hrc>
26
27#include <com/sun/star/awt/XControl.hpp>
28#include <com/sun/star/form/runtime/XFormController.hpp>
29#include <com/sun/star/util/NumberFormatter.hpp>
30#include <com/sun/star/sdb/SQLContext.hpp>
31
32#include <comphelper/processfactory.hxx>
33#include <comphelper/string.hxx>
34#include <connectivity/dbtools.hxx>
35#include <connectivity/sqlnode.hxx>
36#include <cppuhelper/implbase.hxx>
37#include <i18nlangtag/languagetag.hxx>
38#include <fmshimp.hxx>
39#include <o3tl/safeint.hxx>
40#include <sfx2/objitem.hxx>
41#include <sfx2/request.hxx>
42#include <svx/dialmgr.hxx>
43#include <svx/fmshell.hxx>
44#include <svx/fmtools.hxx>
45#include <svx/svxids.hrc>
46#include <vcl/settings.hxx>
47#include <tools/diagnose_ex.h>
48#include <vcl/commandevent.hxx>
49#include <vcl/event.hxx>
50#include <vcl/svapp.hxx>
51
52#include <bitmaps.hlst>
53
54#include <functional>
55
56using namespace ::svxform;
57using namespace ::connectivity;
58using namespace ::dbtools;
59
60namespace svxform
61{
62 using ::com::sun::star::uno::Reference;
63 using ::com::sun::star::container::XIndexAccess;
64 using ::com::sun::star::uno::UNO_QUERY;
65 using ::com::sun::star::beans::XPropertySet;
66 using ::com::sun::star::form::runtime::XFormController;
67 using ::com::sun::star::form::runtime::XFilterController;
68 using ::com::sun::star::form::runtime::XFilterControllerListener;
69 using ::com::sun::star::form::runtime::FilterEvent;
70 using ::com::sun::star::lang::EventObject;
71 using ::com::sun::star::form::XForm;
72 using ::com::sun::star::container::XChild;
73 using ::com::sun::star::awt::XControl;
74 using ::com::sun::star::sdbc::XConnection;
75 using ::com::sun::star::util::XNumberFormatsSupplier;
76 using ::com::sun::star::util::XNumberFormatter;
77 using ::com::sun::star::util::NumberFormatter;
78 using ::com::sun::star::sdbc::XRowSet;
79 using ::com::sun::star::lang::Locale;
80 using ::com::sun::star::sdb::SQLContext;
81 using ::com::sun::star::uno::XInterface;
82 using ::com::sun::star::uno::UNO_QUERY_THROW;
83 using ::com::sun::star::uno::UNO_SET_THROW;
84 using ::com::sun::star::uno::Exception;
85 using ::com::sun::star::uno::Sequence;
86
87
88OFilterItemExchange::OFilterItemExchange()
89 : m_pFormItem(nullptr)
90{
91}
92
93void OFilterItemExchange::AddSupportedFormats()
94{
95 AddFormat(getFormatId());
96}
97
98SotClipboardFormatId OFilterItemExchange::getFormatId()
99{
100 static SotClipboardFormatId s_nFormat =
101 SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"form.FilterControlExchange\"");
102 DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OFilterExchangeHelper::getFormatId: bad exchange id!")do { if (true && (!(static_cast<SotClipboardFormatId
>(-1) != s_nFormat))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "102" ": "), "%s", "OFilterExchangeHelper::getFormatId: bad exchange id!"
); } } while (false)
;
103 return s_nFormat;
104}
105
106OLocalExchange* OFilterExchangeHelper::createExchange() const
107{
108 return new OFilterItemExchange;
109}
110
111OUString FmFilterData::GetImage() const
112{
113 return OUString();
114}
115
116FmParentData::~FmParentData()
117{
118}
119
120OUString FmFormItem::GetImage() const
121{
122 return RID_SVXBMP_FORM"res/sx10593.png";
123}
124
125FmFilterItem* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex ) const
126{
127 for ( auto & pData : m_aChildren )
128 {
129 FmFilterItem& rCondition = dynamic_cast<FmFilterItem&>(*pData);
130 if ( _nFilterComponentIndex == rCondition.GetComponentIndex() )
131 return &rCondition;
132 }
133 return nullptr;
134}
135
136OUString FmFilterItems::GetImage() const
137{
138 return RID_SVXBMP_FILTER"res/sx10715.png";
139}
140
141FmFilterItem::FmFilterItem( FmFilterItems* pParent,
142 const OUString& aFieldName,
143 const OUString& aText,
144 const sal_Int32 _nComponentIndex )
145 :FmFilterData(pParent, aText)
146 ,m_aFieldName(aFieldName)
147 ,m_nComponentIndex( _nComponentIndex )
148{
149}
150
151OUString FmFilterItem::GetImage() const
152{
153 return RID_SVXBMP_FIELD"res/sx18027.png";
154}
155
156// Hints for communication between model and view
157
158namespace {
159
160class FmFilterHint : public SfxHint
161{
162 FmFilterData* m_pData;
163
164public:
165 explicit FmFilterHint(FmFilterData* pData):m_pData(pData){}
166 FmFilterData* GetData() const { return m_pData; }
167};
168
169class FmFilterInsertedHint : public FmFilterHint
170{
171 size_t m_nPos; // Position relative to the parent of the data
172
173public:
174 FmFilterInsertedHint(FmFilterData* pData, size_t nRelPos)
175 :FmFilterHint(pData)
176 ,m_nPos(nRelPos){}
177
178 size_t GetPos() const { return m_nPos; }
179};
180
181class FmFilterRemovedHint : public FmFilterHint
182{
183public:
184 explicit FmFilterRemovedHint(FmFilterData* pData)
185 :FmFilterHint(pData){}
186};
187
188
189class FmFilterTextChangedHint : public FmFilterHint
190{
191public:
192 explicit FmFilterTextChangedHint(FmFilterData* pData)
193 :FmFilterHint(pData){}
194};
195
196class FilterClearingHint : public SfxHint
197{
198public:
199 FilterClearingHint(){}
200};
201
202class FmFilterCurrentChangedHint : public SfxHint
203{
204public:
205 FmFilterCurrentChangedHint(){}
206};
207
208}
209
210// class FmFilterAdapter, listener at the FilterControls
211class FmFilterAdapter : public ::cppu::WeakImplHelper< XFilterControllerListener >
212{
213 FmFilterModel* m_pModel;
214 Reference< XIndexAccess > m_xControllers;
215
216public:
217 FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers);
218
219// XEventListener
220 virtual void SAL_CALL disposing(const EventObject& Source) override;
221
222// XFilterControllerListener
223 virtual void SAL_CALL predicateExpressionChanged( const FilterEvent& Event ) override;
224 virtual void SAL_CALL disjunctiveTermRemoved( const FilterEvent& Event ) override;
225 virtual void SAL_CALL disjunctiveTermAdded( const FilterEvent& Event ) override;
226
227// helpers
228 /// @throws RuntimeException
229 void dispose();
230
231 void AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd );
232
233 static void setText(sal_Int32 nPos,
234 const FmFilterItem* pFilterItem,
235 const OUString& rText);
236};
237
238
239FmFilterAdapter::FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers)
240 :m_pModel( pModel )
241 ,m_xControllers( xControllers )
242{
243 AddOrRemoveListener( m_xControllers, true );
244}
245
246
247void FmFilterAdapter::dispose()
248{
249 AddOrRemoveListener( m_xControllers, false );
250}
251
252
253void FmFilterAdapter::AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd )
254{
255 for (sal_Int32 i = 0, nLen = _rxControllers->getCount(); i < nLen; ++i)
256 {
257 Reference< XIndexAccess > xElement( _rxControllers->getByIndex(i), UNO_QUERY );
258
259 // step down
260 AddOrRemoveListener( xElement, _bAdd );
261
262 // handle this particular controller
263 Reference< XFilterController > xController( xElement, UNO_QUERY );
264 OSL_ENSURE( xController.is(), "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!" )do { if (true && (!(xController.is()))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "264" ": "), "%s", "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!"
); } } while (false)
;
265 if ( xController.is() )
266 {
267 if ( _bAdd )
268 xController->addFilterControllerListener( this );
269 else
270 xController->removeFilterControllerListener( this );
271 }
272 }
273}
274
275
276void FmFilterAdapter::setText(sal_Int32 nRowPos,
277 const FmFilterItem* pFilterItem,
278 const OUString& rText)
279{
280 FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pFilterItem->GetParent()->GetParent() );
281 assert(pFormItem)(static_cast <bool> (pFormItem) ? void (0) : __assert_fail
("pFormItem", "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
, 281, __extension__ __PRETTY_FUNCTION__))
;
282 try
283 {
284 Reference< XFilterController > xController( pFormItem->GetController(), UNO_QUERY_THROW );
285 xController->setPredicateExpression( pFilterItem->GetComponentIndex(), nRowPos, rText );
286 }
287 catch( const Exception& )
288 {
289 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "289" ": ", "svx" );
;
290 }
291}
292
293
294// XEventListener
295
296void SAL_CALL FmFilterAdapter::disposing(const EventObject& /*e*/)
297{
298}
299
300
301namespace
302{
303 OUString lcl_getLabelName_nothrow( const Reference< XControl >& _rxControl )
304 {
305 OUString sLabelName;
306 try
307 {
308 Reference< XPropertySet > xModel( _rxControl->getModel(), UNO_QUERY_THROW );
309 sLabelName = getLabelName( xModel );
310 }
311 catch( const Exception& )
312 {
313 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "313" ": ", "svx" );
;
314 }
315 return sLabelName;
316 }
317
318 Reference< XPropertySet > lcl_getBoundField_nothrow( const Reference< XControl >& _rxControl )
319 {
320 Reference< XPropertySet > xField;
321 try
322 {
323 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY_THROW );
324 xField.set( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD"BoundField" ), UNO_QUERY_THROW );
325 }
326 catch( const Exception& )
327 {
328 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "328" ": ", "svx" );
;
329 }
330 return xField;
331 }
332}
333
334// XFilterControllerListener
335void FmFilterAdapter::predicateExpressionChanged( const FilterEvent& Event )
336{
337 SolarMutexGuard aGuard;
338
339 if ( !m_pModel )
340 return;
341
342 // the controller which sent the event
343 Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
344 Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
345 Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
346
347 FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
348 OSL_ENSURE( pFormItem, "FmFilterAdapter::predicateExpressionChanged: don't know this form!" )do { if (true && (!(pFormItem))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "348" ": "), "%s", "FmFilterAdapter::predicateExpressionChanged: don't know this form!"
); } } while (false)
;
349 if ( !pFormItem )
350 return;
351
352 const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
353
354 FmFilterData* pData = pFormItem->GetChildren()[nActiveTerm].get();
355 FmFilterItems& rFilter = dynamic_cast<FmFilterItems&>(*pData);
356 FmFilterItem* pFilterItem = rFilter.Find( Event.FilterComponent );
357 if ( pFilterItem )
358 {
359 if ( !Event.PredicateExpression.isEmpty())
360 {
361 pFilterItem->SetText( Event.PredicateExpression );
362 // notify the UI
363 FmFilterTextChangedHint aChangeHint(pFilterItem);
364 m_pModel->Broadcast( aChangeHint );
365 }
366 else
367 {
368 // no text anymore so remove the condition
369 m_pModel->Remove(pFilterItem);
370 }
371 }
372 else
373 {
374 // searching the component by field name
375 OUString aFieldName( lcl_getLabelName_nothrow( xFilterController->getFilterComponent( Event.FilterComponent ) ) );
376
377 std::unique_ptr<FmFilterItem> pNewFilterItem(new FmFilterItem(&rFilter, aFieldName, Event.PredicateExpression, Event.FilterComponent));
378 m_pModel->Insert(rFilter.GetChildren().end(), std::move(pNewFilterItem));
379 }
380
381 // ensure there's one empty term in the filter, just in case the active term was previously empty
382 m_pModel->EnsureEmptyFilterRows( *pFormItem );
383}
384
385
386void SAL_CALL FmFilterAdapter::disjunctiveTermRemoved( const FilterEvent& Event )
387{
388 SolarMutexGuard aGuard;
389
390 Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
391 Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
392 Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
393
394 FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
395 OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!" )do { if (true && (!(pFormItem))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "395" ": "), "%s", "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!"
); } } while (false)
;
396 if ( !pFormItem )
397 return;
398
399 auto& rTermItems = pFormItem->GetChildren();
400 const bool bValidIndex = ( Event.DisjunctiveTerm >= 0 ) && ( o3tl::make_unsigned(Event.DisjunctiveTerm) < rTermItems.size() );
401 OSL_ENSURE( bValidIndex, "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!" )do { if (true && (!(bValidIndex))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "401" ": "), "%s", "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!"
); } } while (false)
;
402 if ( !bValidIndex )
403 return;
404
405 // if the first term was removed, then the to-be first term needs its text updated
406 if ( Event.DisjunctiveTerm == 0 )
407 {
408 rTermItems[1]->SetText( SvxResId(RID_STR_FILTER_FILTER_FORreinterpret_cast<char const *>("RID_STR_FILTER_FILTER_FOR"
"\004" u8"Filter for")
));
409 FmFilterTextChangedHint aChangeHint( rTermItems[1].get() );
410 m_pModel->Broadcast( aChangeHint );
411 }
412
413 // finally remove the entry from the model
414 m_pModel->Remove( rTermItems.begin() + Event.DisjunctiveTerm );
415
416 // ensure there's one empty term in the filter, just in case the currently removed one was the last empty one
417 m_pModel->EnsureEmptyFilterRows( *pFormItem );
418}
419
420
421void SAL_CALL FmFilterAdapter::disjunctiveTermAdded( const FilterEvent& Event )
422{
423 SolarMutexGuard aGuard;
424
425 Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
426 Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
427 Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
428
429 FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
430 OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermAdded: don't know this form!" )do { if (true && (!(pFormItem))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "430" ": "), "%s", "FmFilterAdapter::disjunctiveTermAdded: don't know this form!"
); } } while (false)
;
431 if ( !pFormItem )
432 return;
433
434 const sal_Int32 nInsertPos = Event.DisjunctiveTerm;
435 bool bValidIndex = ( nInsertPos >= 0 ) && ( o3tl::make_unsigned(nInsertPos) <= pFormItem->GetChildren().size() );
436 if ( !bValidIndex )
437 {
438 OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" )do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "438" ": "), "%s", "FmFilterAdapter::disjunctiveTermAdded: invalid index!"
); } } while (false)
;
439 return;
440 }
441
442 auto insertPos = pFormItem->GetChildren().begin() + nInsertPos;
443
444 // "Filter for" for first position, "Or" for the other positions
445 std::unique_ptr<FmFilterItems> pFilterItems(new FmFilterItems(pFormItem, (nInsertPos?SvxResId(RID_STR_FILTER_FILTER_ORreinterpret_cast<char const *>("RID_STR_FILTER_FILTER_OR"
"\004" u8"Or")
):SvxResId(RID_STR_FILTER_FILTER_FORreinterpret_cast<char const *>("RID_STR_FILTER_FILTER_FOR"
"\004" u8"Filter for")
))));
446 m_pModel->Insert( insertPos, std::move(pFilterItems) );
447}
448
449
450FmFilterModel::FmFilterModel()
451 :FmParentData(nullptr, OUString())
452 ,OSQLParserClient(comphelper::getProcessComponentContext())
453 ,m_pCurrentItems(nullptr)
454{
455}
456
457
458FmFilterModel::~FmFilterModel()
459{
460 Clear();
461}
462
463
464void FmFilterModel::Clear()
465{
466 // notify
467 FilterClearingHint aClearedHint;
468 Broadcast( aClearedHint );
469
470 // lose endings
471 if (m_pAdapter.is())
472 {
473 m_pAdapter->dispose();
474 m_pAdapter.clear();
475 }
476
477 m_pCurrentItems = nullptr;
478 m_xController = nullptr;
479 m_xControllers = nullptr;
480
481 m_aChildren.clear();
482}
483
484
485void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
486{
487 if ( xCurrent == m_xController )
488 return;
489
490 if (!xControllers.is())
491 {
492 Clear();
493 return;
494 }
495
496 // there is only a new current controller
497 if ( m_xControllers != xControllers )
498 {
499 Clear();
500
501 m_xControllers = xControllers;
502 Update(m_xControllers, this);
503
504 DBG_ASSERT(xCurrent.is(), "FmFilterModel::Update(...) no current controller")do { if (true && (!(xCurrent.is()))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "504" ": "), "%s", "FmFilterModel::Update(...) no current controller"
); } } while (false)
;
505
506 // Listening for TextChanges
507 m_pAdapter = new FmFilterAdapter(this, xControllers);
508
509 SetCurrentController(xCurrent);
510 EnsureEmptyFilterRows( *this );
511 }
512 else
513 SetCurrentController(xCurrent);
514}
515
516
517void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, FmParentData* pParent)
518{
519 try
520 {
521 sal_Int32 nCount = xControllers->getCount();
522 for ( sal_Int32 i = 0; i < nCount; ++i )
523 {
524 Reference< XFormController > xController( xControllers->getByIndex(i), UNO_QUERY_THROW );
525
526 Reference< XPropertySet > xFormProperties( xController->getModel(), UNO_QUERY_THROW );
527 OUString aName;
528 OSL_VERIFY( xFormProperties->getPropertyValue( FM_PROP_NAME ) >>= aName )do { if (!(xFormProperties->getPropertyValue( "Name" ) >>=
aName)) do { if (true && (!(0))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "528" ": "), "OSL_ASSERT: %s", "0"); } } while (false); }
while (0)
;
529
530 // Insert a new item for the form
531 FmFormItem* pFormItem = new FmFormItem( pParent, xController, aName );
532 Insert( pParent->GetChildren().end(), std::unique_ptr<FmFilterData>(pFormItem) );
533
534 Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
535
536 // insert the existing filters for the form
537 OUString aTitle(SvxResId(RID_STR_FILTER_FILTER_FORreinterpret_cast<char const *>("RID_STR_FILTER_FILTER_FOR"
"\004" u8"Filter for")
));
538
539 const Sequence< Sequence< OUString > > aExpressions = xFilterController->getPredicateExpressions();
540 for ( auto const & conjunctionTerm : aExpressions )
541 {
542 // we always display one row, even if there's no term to be displayed
543 FmFilterItems* pFilterItems = new FmFilterItems( pFormItem, aTitle );
544 Insert( pFormItem->GetChildren().end(), std::unique_ptr<FmFilterData>(pFilterItems) );
545
546 const Sequence< OUString >& rDisjunction( conjunctionTerm );
547 sal_Int32 nComponentIndex = -1;
548 for ( const OUString& rDisjunctiveTerm : rDisjunction )
549 {
550 ++nComponentIndex;
551
552 if ( rDisjunctiveTerm.isEmpty() )
553 // no condition for this particular component in this particular conjunction term
554 continue;
555
556 // determine the display name of the control
557 const Reference< XControl > xFilterControl( xFilterController->getFilterComponent( nComponentIndex ) );
558 const OUString sDisplayName( lcl_getLabelName_nothrow( xFilterControl ) );
559
560 // insert a new entry
561 std::unique_ptr<FmFilterItem> pANDCondition(new FmFilterItem( pFilterItems, sDisplayName, rDisjunctiveTerm, nComponentIndex ));
562 Insert( pFilterItems->GetChildren().end(), std::move(pANDCondition) );
563 }
564
565 // title for the next conditions
566 aTitle = SvxResId( RID_STR_FILTER_FILTER_ORreinterpret_cast<char const *>("RID_STR_FILTER_FILTER_OR"
"\004" u8"Or")
);
567 }
568
569 // now add dependent controllers
570 Update( xController, pFormItem );
571 }
572 }
573 catch( const Exception& )
574 {
575 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "575" ": ", "svx" );
;
576 }
577}
578
579
580FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XFormController > & xController) const
581{
582 for (const auto& rItem : rItems)
583 {
584 FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
585 if (pForm)
586 {
587 if ( xController == pForm->GetController() )
588 return pForm;
589 else
590 {
591 pForm = Find(pForm->GetChildren(), xController);
592 if (pForm)
593 return pForm;
594 }
595 }
596 }
597 return nullptr;
598}
599
600
601FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XForm >& xForm) const
602{
603 for (const auto& rItem : rItems)
604 {
605 FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
606 if (pForm)
607 {
608 if (xForm == pForm->GetController()->getModel())
609 return pForm;
610 else
611 {
612 pForm = Find(pForm->GetChildren(), xForm);
613 if (pForm)
614 return pForm;
615 }
616 }
617 }
618 return nullptr;
619}
620
621void FmFilterModel::SetCurrentController(const Reference< XFormController > & xCurrent)
622{
623 if ( xCurrent == m_xController )
624 return;
625
626 m_xController = xCurrent;
627
628 FmFormItem* pItem = Find( m_aChildren, xCurrent );
629 if ( !pItem )
630 return;
631
632 try
633 {
634 Reference< XFilterController > xFilterController( m_xController, UNO_QUERY_THROW );
635 const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
636 if (nActiveTerm != -1 && pItem->GetChildren().size() > o3tl::make_unsigned(nActiveTerm))
637 {
638 SetCurrentItems( static_cast< FmFilterItems* >( pItem->GetChildren()[ nActiveTerm ].get() ) );
639 }
640 }
641 catch( const Exception& )
642 {
643 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "643" ": ", "svx" );
;
644 }
645}
646
647void FmFilterModel::AppendFilterItems( FmFormItem& _rFormItem )
648{
649 // insert the condition behind the last filter items
650 auto iter = std::find_if(_rFormItem.GetChildren().rbegin(), _rFormItem.GetChildren().rend(),
651 [](const std::unique_ptr<FmFilterData>& rChild) { return dynamic_cast<const FmFilterItems*>(rChild.get()) != nullptr; });
652
653 sal_Int32 nInsertPos = iter.base() - _rFormItem.GetChildren().begin();
654 // delegate this to the FilterController, it will notify us, which will let us update our model
655 try
656 {
657 Reference< XFilterController > xFilterController( _rFormItem.GetFilterController(), UNO_SET_THROW );
658 if ( nInsertPos >= xFilterController->getDisjunctiveTerms() )
659 xFilterController->appendEmptyDisjunctiveTerm();
660 }
661 catch( const Exception& )
662 {
663 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "663" ": ", "svx" );
;
664 }
665}
666
667void FmFilterModel::Insert(const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos, std::unique_ptr<FmFilterData> pData)
668{
669 auto pTemp = pData.get();
670 size_t nPos;
671 ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pData->GetParent()->GetChildren();
672 if (rPos == rItems.end())
673 {
674 nPos = rItems.size();
675 rItems.push_back(std::move(pData));
676 }
677 else
678 {
679 nPos = rPos - rItems.begin();
680 rItems.insert(rPos, std::move(pData));
681 }
682
683 // notify the UI
684 FmFilterInsertedHint aInsertedHint(pTemp, nPos);
685 Broadcast( aInsertedHint );
686}
687
688void FmFilterModel::Remove(FmFilterData* pData)
689{
690 FmParentData* pParent = pData->GetParent();
691 ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pParent->GetChildren();
692
693 // erase the item from the model
694 auto i = ::std::find_if(rItems.begin(), rItems.end(),
695 [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pData; } );
696 DBG_ASSERT(i != rItems.end(), "FmFilterModel::Remove(): unknown Item")do { if (true && (!(i != rItems.end()))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "696" ": "), "%s", "FmFilterModel::Remove(): unknown Item"
); } } while (false)
;
697 // position within the parent
698 sal_Int32 nPos = i - rItems.begin();
699 if (dynamic_cast<const FmFilterItems*>( pData) != nullptr)
700 {
701 FmFormItem* pFormItem = static_cast<FmFormItem*>(pParent);
702
703 try
704 {
705 Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
706
707 bool bEmptyLastTerm = ( ( nPos == 0 ) && xFilterController->getDisjunctiveTerms() == 1 );
708 if ( bEmptyLastTerm )
709 {
710 // remove all children (by setting an empty predicate expression)
711 ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = static_cast<FmFilterItems*>(pData)->GetChildren();
712 while ( !rChildren.empty() )
713 {
714 auto removePos = rChildren.end() - 1;
715 if (FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( removePos->get() ))
716 {
717 FmFilterAdapter::setText( nPos, pFilterItem, OUString() );
718 }
719 Remove( removePos );
720 }
721 }
722 else
723 {
724 xFilterController->removeDisjunctiveTerm( nPos );
725 }
726 }
727 catch( const Exception& )
728 {
729 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "729" ": ", "svx" );
;
730 }
731 }
732 else // FormItems can not be deleted
733 {
734 FmFilterItem& rFilterItem = dynamic_cast<FmFilterItem&>(*pData);
735
736 // if it's the last condition remove the parent
737 if (rItems.size() == 1)
738 Remove(rFilterItem.GetParent());
739 else
740 {
741 // find the position of the father within his father
742 ::std::vector<std::unique_ptr<FmFilterData>>& rParentParentItems = pData->GetParent()->GetParent()->GetChildren();
743 auto j = ::std::find_if(rParentParentItems.begin(), rParentParentItems.end(),
744 [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == rFilterItem.GetParent(); });
745 DBG_ASSERT(j != rParentParentItems.end(), "FmFilterModel::Remove(): unknown Item")do { if (true && (!(j != rParentParentItems.end()))) {
sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "745" ": "), "%s", "FmFilterModel::Remove(): unknown Item"
); } } while (false)
;
746 sal_Int32 nParentPos = j - rParentParentItems.begin();
747
748 // EmptyText removes the filter
749 FmFilterAdapter::setText(nParentPos, &rFilterItem, OUString());
750 Remove( i );
751 }
752 }
753}
754
755void FmFilterModel::Remove( const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos )
756{
757 // remove from parent's child list
758 std::unique_ptr<FmFilterData> pData = std::move(*rPos);
759 pData->GetParent()->GetChildren().erase( rPos );
760
761 // notify the view, this will remove the actual SvTreeListEntry
762 FmFilterRemovedHint aRemoveHint( pData.get() );
763 Broadcast( aRemoveHint );
764}
765
766
767bool FmFilterModel::ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const
768{
769 FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pItem->GetParent()->GetParent() );
35
Called C++ object pointer is null
770 assert(pFormItem)(static_cast <bool> (pFormItem) ? void (0) : __assert_fail
("pFormItem", "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
, 770, __extension__ __PRETTY_FUNCTION__))
;
771 try
772 {
773 Reference< XFormController > xFormController( pFormItem->GetController() );
774 // obtain the connection of the form belonging to the controller
775 Reference< XRowSet > xRowSet( xFormController->getModel(), UNO_QUERY_THROW );
776 Reference< XConnection > xConnection( getConnection( xRowSet ) );
777
778 // obtain a number formatter for this connection
779 // TODO: shouldn't this be cached?
780 Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats( xConnection, true );
781 Reference< XNumberFormatter > xFormatter( NumberFormatter::create( comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
782 xFormatter->attachNumberFormatsSupplier( xFormatSupplier );
783
784 // get the field (database column) which the item is responsible for
785 Reference< XFilterController > xFilterController( xFormController, UNO_QUERY_THROW );
786 Reference< XPropertySet > xField( lcl_getBoundField_nothrow( xFilterController->getFilterComponent( pItem->GetComponentIndex() ) ), UNO_SET_THROW );
787
788 // parse the given text as filter predicate
789 OUString aErr, aTxt( rText );
790 std::unique_ptr< OSQLParseNode > pParseNode = predicateTree( aErr, aTxt, xFormatter, xField );
791 rErrorMsg = aErr;
792 rText = aTxt;
793 if ( pParseNode != nullptr )
794 {
795 OUString aPreparedText;
796 Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
797 pParseNode->parseNodeToPredicateStr(
798 aPreparedText, xConnection, xFormatter, xField, OUString(), aAppLocale, OUString("."), getParseContext() );
799 rText = aPreparedText;
800 return true;
801 }
802 }
803 catch( const Exception& )
804 {
805 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "805" ": ", "svx" );
;
806 }
807
808 return false;
809}
810
811
812void FmFilterModel::Append(FmFilterItems* pItems, std::unique_ptr<FmFilterItem> pFilterItem)
813{
814 Insert(pItems->GetChildren().end(), std::move(pFilterItem));
815}
816
817
818void FmFilterModel::SetTextForItem(FmFilterItem* pItem, const OUString& rText)
819{
820 ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pItem->GetParent()->GetParent()->GetChildren();
821 auto i = ::std::find_if(rItems.begin(), rItems.end(),
822 [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pItem->GetParent(); });
823 sal_Int32 nParentPos = i - rItems.begin();
824
825 FmFilterAdapter::setText(nParentPos, pItem, rText);
826
827 if (rText.isEmpty())
828 Remove(pItem);
829 else
830 {
831 // Change the text
832 pItem->SetText(rText);
833 FmFilterTextChangedHint aChangeHint(pItem);
834 Broadcast( aChangeHint );
835 }
836}
837
838
839void FmFilterModel::SetCurrentItems(FmFilterItems* pCurrent)
840{
841 if (m_pCurrentItems == pCurrent)
842 return;
843
844 // search for the condition
845 if (pCurrent)
846 {
847 FmFormItem* pFormItem = static_cast<FmFormItem*>(pCurrent->GetParent());
848 ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pFormItem->GetChildren();
849 auto i = ::std::find_if(rItems.begin(), rItems.end(),
850 [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pCurrent; });
851
852 if (i != rItems.end())
853 {
854 // determine the filter position
855 sal_Int32 nPos = i - rItems.begin();
856 try
857 {
858 Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
859 xFilterController->setActiveTerm( nPos );
860 }
861 catch( const Exception& )
862 {
863 DBG_UNHANDLED_EXCEPTION("svx")DbgUnhandledException( DbgGetCaughtException(), __func__, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "863" ": ", "svx" );
;
864 }
865
866 if ( m_xController != pFormItem->GetController() )
867 // calls SetCurrentItems again
868 SetCurrentController( pFormItem->GetController() );
869 else
870 m_pCurrentItems = pCurrent;
871 }
872 else
873 m_pCurrentItems = nullptr;
874 }
875 else
876 m_pCurrentItems = nullptr;
877
878
879 // notify the UI
880 FmFilterCurrentChangedHint aHint;
881 Broadcast( aHint );
882}
883
884
885void FmFilterModel::EnsureEmptyFilterRows( FmParentData& _rItem )
886{
887 // checks whether for each form there's one free level for input
888 ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = _rItem.GetChildren();
889 bool bAppendLevel = dynamic_cast<const FmFormItem*>(&_rItem) != nullptr;
890
891 for ( const auto& rpChild : rChildren )
892 {
893 FmFilterItems* pItems = dynamic_cast<FmFilterItems*>( rpChild.get() );
894 if ( pItems && pItems->GetChildren().empty() )
895 {
896 bAppendLevel = false;
897 break;
898 }
899
900 FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( rpChild.get() );
901 if (pFormItem)
902 {
903 EnsureEmptyFilterRows( *pFormItem );
904 continue;
905 }
906 }
907
908 if ( bAppendLevel )
909 {
910 FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( &_rItem );
911 OSL_ENSURE( pFormItem, "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?" )do { if (true && (!(pFormItem))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "911" ": "), "%s", "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?"
); } } while (false)
;
912 if ( pFormItem )
913 AppendFilterItems( *pFormItem );
914 }
915}
916
917const int nxD = 4;
918const int nxDBmp = 12;
919
920IMPL_STATIC_LINK(FmFilterNavigator, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size)Size FmFilterNavigator::LinkStubCustomGetSizeHdl(void * instance
, weld::TreeView::get_size_args data) { return CustomGetSizeHdl
(static_cast<FmFilterNavigator *>(instance), data); } Size
FmFilterNavigator::CustomGetSizeHdl(__attribute__ ((unused))
FmFilterNavigator *, weld::TreeView::get_size_args aPayload)
921{
922 vcl::RenderContext& rRenderContext = aPayload.first;
923 const OUString& rId = aPayload.second;
924
925 Size aSize;
926
927 FmFilterData* pData = reinterpret_cast<FmFilterData*>(rId.toUInt64());
928 OUString sText = pData->GetText();
929
930 if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
931 {
932 rRenderContext.Push(PushFlags::FONT);
933 vcl::Font aFont(rRenderContext.GetFont());
934 aFont.SetWeight(WEIGHT_BOLD);
935 rRenderContext.SetFont(aFont);
936
937 OUString sName = pItem->GetFieldName() + ": ";
938 aSize = Size(rRenderContext.GetTextWidth(sName), rRenderContext.GetTextHeight());
939
940 rRenderContext.Pop();
941
942 aSize.AdjustWidth(rRenderContext.GetTextWidth(sText) + nxD);
943 }
944 else
945 {
946 aSize = Size(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
947 if (dynamic_cast<FmFilterItems*>(pData))
948 aSize.AdjustWidth(nxDBmp);
949 }
950
951 return aSize;
952}
953
954IMPL_STATIC_LINK(FmFilterNavigator, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)void FmFilterNavigator::LinkStubCustomRenderHdl(void * instance
, weld::TreeView::render_args data) { return CustomRenderHdl(
static_cast<FmFilterNavigator *>(instance), data); } void
FmFilterNavigator::CustomRenderHdl(__attribute__ ((unused)) FmFilterNavigator
*, weld::TreeView::render_args aPayload)
955{
956 vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
957 const ::tools::Rectangle& rRect = std::get<1>(aPayload);
958 ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
959 bool bSelected = std::get<2>(aPayload);
960 const OUString& rId = std::get<3>(aPayload);
961
962 rRenderContext.Push(PushFlags::TEXTCOLOR);
963 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
964 if (bSelected)
965 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
966 else
967 rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
968
969 FmFilterData* pData = reinterpret_cast<FmFilterData*>(rId.toUInt64());
970 OUString sText = pData->GetText();
971 Point aPos(aRect.TopLeft());
972
973 if (FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pData))
974 {
975 vcl::Font aFont(rRenderContext.GetFont());
976 aFont.SetWeight(WEIGHT_BOLD);
977
978 rRenderContext.Push(PushFlags::FONT);
979 rRenderContext.SetFont(aFont);
980
981 OUString sName = pFilter->GetFieldName() + ": ";
982 rRenderContext.DrawText(aPos, sName);
983
984 // position for the second text
985 aPos.AdjustX(rRenderContext.GetTextWidth(sName) + nxD);
986 rRenderContext.Pop();
987
988 rRenderContext.DrawText(aPos, sText);
989 }
990 else if (FmFilterItems* pRow = dynamic_cast<FmFilterItems*>(pData))
991 {
992 FmFormItem* pForm = static_cast<FmFormItem*>(pRow->GetParent());
993
994 // current filter is significant painted
995 const bool bIsCurrentFilter = pForm->GetChildren()[ pForm->GetFilterController()->getActiveTerm() ].get() == pRow;
996 if (bIsCurrentFilter)
997 {
998 rRenderContext.Push(PushFlags::LINECOLOR);
999 rRenderContext.SetLineColor(rRenderContext.GetTextColor());
1000
1001 Point aFirst(aPos.X(), aRect.Bottom() - 6);
1002 Point aSecond(aFirst .X() + 2, aFirst.Y() + 3);
1003
1004 rRenderContext.DrawLine(aFirst, aSecond);
1005
1006 aFirst = aSecond;
1007 aFirst.AdjustX(1);
1008 aSecond.AdjustX(6);
1009 aSecond.AdjustY(-5);
1010
1011 rRenderContext.DrawLine(aFirst, aSecond);
1012 rRenderContext.Pop();
1013 }
1014
1015 rRenderContext.DrawText(Point(aPos.X() + nxDBmp, aPos.Y()), sText);
1016 }
1017 else
1018 rRenderContext.DrawText(aPos, sText);
1019
1020 rRenderContext.Pop();
1021}
1022
1023FmFilterNavigatorDropTarget::FmFilterNavigatorDropTarget(FmFilterNavigator& rTreeView)
1024 : DropTargetHelper(rTreeView.get_widget().get_drop_target())
1025 , m_rTreeView(rTreeView)
1026{
1027}
1028
1029sal_Int8 FmFilterNavigatorDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
1030{
1031 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
1032
1033 if (nAccept != DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE)
1034 {
1035 // to enable the autoscroll when we're close to the edges
1036 weld::TreeView& rWidget = m_rTreeView.get_widget();
1037 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
1038 }
1039
1040 return nAccept;
1041}
1042
1043sal_Int8 FmFilterNavigatorDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
1044{
1045 return m_rTreeView.ExecuteDrop(rEvt);
1046}
1047
1048FmFilterNavigator::FmFilterNavigator(vcl::Window* pTopLevel, std::unique_ptr<weld::TreeView> xTreeView)
1049 : m_xTopLevel(pTopLevel)
1050 , m_xTreeView(std::move(xTreeView))
1051 , m_aDropTargetHelper(*this)
1052 , m_aControlExchange()
1053 , m_nAsyncRemoveEvent(nullptr)
1054{
1055 m_xTreeView->set_help_id(HID_FILTER_NAVIGATOR"SVX_HID_FILTER_NAVIGATOR");
1056
1057 m_xTreeView->set_selection_mode(SelectionMode::Multiple);
1058
1059 m_pModel.reset( new FmFilterModel() );
1060 StartListening( *m_pModel );
1061
1062 m_xTreeView->connect_custom_get_size(LINK(this, FmFilterNavigator, CustomGetSizeHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubCustomGetSizeHdl
)
);
1063 m_xTreeView->connect_custom_render(LINK(this, FmFilterNavigator, CustomRenderHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubCustomRenderHdl
)
);
1064 m_xTreeView->set_column_custom_renderer(0, true);
1065
1066 m_xTreeView->connect_changed(LINK(this, FmFilterNavigator, SelectHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubSelectHdl)
);
1067 m_xTreeView->connect_key_press(LINK(this, FmFilterNavigator, KeyInputHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubKeyInputHdl)
);
1068 m_xTreeView->connect_popup_menu(LINK(this, FmFilterNavigator, PopupMenuHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubPopupMenuHdl)
);
1069 m_xTreeView->connect_editing(LINK(this, FmFilterNavigator, EditingEntryHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubEditingEntryHdl
)
,
1070 LINK(this, FmFilterNavigator, EditedEntryHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubEditedEntryHdl)
);
1071 m_xTreeView->connect_drag_begin(LINK(this, FmFilterNavigator, DragBeginHdl)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubDragBeginHdl)
);
1072}
1073
1074FmFilterNavigator::~FmFilterNavigator()
1075{
1076 if (m_nAsyncRemoveEvent)
1077 Application::RemoveUserEvent(m_nAsyncRemoveEvent);
1078 EndListening(*m_pModel);
1079 m_pModel.reset();
1080}
1081
1082void FmFilterNavigator::UpdateContent(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
1083{
1084 if (xCurrent == m_pModel->GetCurrentController())
1085 return;
1086
1087 m_pModel->Update(xControllers, xCurrent);
1088
1089 // expand the filters for the current controller
1090 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(m_pModel->GetCurrentForm());
1091 if (!xEntry || m_xTreeView->get_row_expanded(*xEntry))
1092 return;
1093
1094 m_xTreeView->unselect_all();
1095
1096 m_xTreeView->expand_row(*xEntry);
1097
1098 xEntry = FindEntry(m_pModel->GetCurrentItems());
1099 if (xEntry)
1100 {
1101 if (!m_xTreeView->get_row_expanded(*xEntry))
1102 m_xTreeView->expand_row(*xEntry);
1103 m_xTreeView->select(*xEntry);
1104 SelectHdl(*m_xTreeView);
1105 }
1106}
1107
1108IMPL_LINK(FmFilterNavigator, EditingEntryHdl, const weld::TreeIter&, rIter, bool)bool FmFilterNavigator::LinkStubEditingEntryHdl(void * instance
, const weld::TreeIter& data) { return static_cast<FmFilterNavigator
*>(instance)->EditingEntryHdl(data); } bool FmFilterNavigator
::EditingEntryHdl(const weld::TreeIter& rIter)
1109{
1110 // returns true to allow editing
1111 if (dynamic_cast<const FmFilterItem*>(reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(rIter).toUInt64())))
1112 {
1113 m_xEditingCurrently = m_xTreeView->make_iterator(&rIter);
1114 return true;
1115 }
1116 m_xEditingCurrently.reset();
1117 return false;
1118}
1119
1120IMPL_LINK(FmFilterNavigator, EditedEntryHdl, const IterString&, rIterString, bool)bool FmFilterNavigator::LinkStubEditedEntryHdl(void * instance
, const IterString& data) { return static_cast<FmFilterNavigator
*>(instance)->EditedEntryHdl(data); } bool FmFilterNavigator
::EditedEntryHdl(const IterString& rIterString)
1121{
1122 const weld::TreeIter& rIter = rIterString.first;
1123 const OUString& rNewText = rIterString.second;
1124
1125 assert(m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 &&(static_cast <bool> (m_xEditingCurrently && m_xTreeView
->iter_compare(rIter, *m_xEditingCurrently) == 0 &&
"FmFilterNavigator::EditedEntry: suspicious entry!") ? void (
0) : __assert_fail ("m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 && \"FmFilterNavigator::EditedEntry: suspicious entry!\""
, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
, 1126, __extension__ __PRETTY_FUNCTION__))
1126 "FmFilterNavigator::EditedEntry: suspicious entry!")(static_cast <bool> (m_xEditingCurrently && m_xTreeView
->iter_compare(rIter, *m_xEditingCurrently) == 0 &&
"FmFilterNavigator::EditedEntry: suspicious entry!") ? void (
0) : __assert_fail ("m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 && \"FmFilterNavigator::EditedEntry: suspicious entry!\""
, "/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
, 1126, __extension__ __PRETTY_FUNCTION__))
;
1127 m_xEditingCurrently.reset();
1128
1129 FmFilterData* pData = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(rIter).toUInt64());
1130
1131 DBG_ASSERT(dynamic_cast<const FmFilterItem*>(pData) != nullptr,do { if (true && (!(dynamic_cast<const FmFilterItem
*>(pData) != nullptr))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "1132" ": "), "%s", "FmFilterNavigator::EditedEntry() wrong entry"
); } } while (false)
1132 "FmFilterNavigator::EditedEntry() wrong entry")do { if (true && (!(dynamic_cast<const FmFilterItem
*>(pData) != nullptr))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.tools"), ("/home/maarten/src/libreoffice/core/svx/source/form/filtnav.cxx"
":" "1132" ": "), "%s", "FmFilterNavigator::EditedEntry() wrong entry"
); } } while (false)
;
1133
1134 OUString aText(comphelper::string::strip(rNewText, ' '));
1135 if (aText.isEmpty())
1136 {
1137 // deleting the entry asynchron
1138 m_nAsyncRemoveEvent = Application::PostUserEvent(LINK(this, FmFilterNavigator, OnRemove)::tools::detail::makeLink( ::tools::detail::castTo<FmFilterNavigator
*>(this), &FmFilterNavigator::LinkStubOnRemove)
, pData);
1139 }
1140 else
1141 {
1142 OUString aErrorMsg;
1143
1144 if (m_pModel->ValidateText(static_cast<FmFilterItem*>(pData), aText, aErrorMsg))
1145 {
1146 // this will set the text at the FmFilterItem, as well as update any filter controls
1147 // which are connected to this particular entry
1148 m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pData), aText);
1149 m_xTreeView->set_text(rIter, aText);
1150 }
1151 else
1152 {
1153 // display the error and return sal_False
1154 SQLContext aError;
1155 aError.Message = SvxResId(RID_STR_SYNTAXERRORreinterpret_cast<char const *>("RID_STR_SYNTAXERROR" "\004"
u8"Syntax error in query expression")
);
1156 aError.Details = aErrorMsg;
1157 displayException(aError, m_xTopLevel);
1158
1159 return false;
1160 }
1161 }
1162 return true;
1163}
1164
1165IMPL_LINK( FmFilterNavigator, OnRemove, void*, p, void )void FmFilterNavigator::LinkStubOnRemove(void * instance, void
* data) { return static_cast<FmFilterNavigator *>(instance
)->OnRemove(data); } void FmFilterNavigator::OnRemove(void
* p)
1166{
1167 m_nAsyncRemoveEvent = nullptr;
1168 // now remove the entry
1169 m_pModel->Remove(static_cast<FmFilterData*>(p));
1170}
1171
1172sal_Int8 FmFilterNavigator::AcceptDrop( const AcceptDropEvent& rEvt )
1173{
1174 if (!m_aControlExchange.isDragSource())
1175 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1176
1177 if (!OFilterItemExchange::hasFormat(m_aDropTargetHelper.GetDataFlavorExVector()))
1178 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1179
1180 // do we contain the formitem?
1181 if (!FindEntry(m_aControlExchange->getFormItem()))
1182 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1183
1184 Point aDropPos = rEvt.maPosPixel;
1185 std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
1186 // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
1187 if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
1188 xDropTarget.reset();
1189
1190 if (!xDropTarget)
1191 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1192
1193 FmFilterData* pData = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(*xDropTarget).toUInt64());
1194 FmFormItem* pForm = nullptr;
1195 if (dynamic_cast<const FmFilterItem*>(pData) != nullptr)
1196 {
1197 pForm = dynamic_cast<FmFormItem*>( pData->GetParent()->GetParent() );
1198 if (pForm != m_aControlExchange->getFormItem())
1199 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1200 }
1201 else if (dynamic_cast<const FmFilterItems*>( pData) != nullptr)
1202 {
1203 pForm = dynamic_cast<FmFormItem*>( pData->GetParent() );
1204 if (pForm != m_aControlExchange->getFormItem())
1205 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1206 }
1207 else
1208 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1209
1210 return rEvt.mnAction;
1211}
1212
1213namespace
1214{
1215 FmFilterItems* getTargetItems(const weld::TreeView& rTreeView, const weld::TreeIter& rTarget)
1216 {
1217 FmFilterData* pData = reinterpret_cast<FmFilterData*>(rTreeView.get_id(rTarget).toUInt64());
1218 FmFilterItems* pTargetItems = dynamic_cast<FmFilterItems*>(pData);
1219 if (!pTargetItems)
1220 pTargetItems = dynamic_cast<FmFilterItems*>(pData->GetParent());
1221 return pTargetItems;
1222 }
1223}
1224
1225sal_Int8 FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent& rEvt )
1226{
1227 if (!m_aControlExchange.isDragSource())
1228 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1229
1230 Point aDropPos = rEvt.maPosPixel;
1231 std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
1232 // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
1233 if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
1234 xDropTarget.reset();
1235 if (!xDropTarget)
1236 return DND_ACTION_NONEcss::datatransfer::dnd::DNDConstants::ACTION_NONE;
1237
1238 // search the container where to add the items
1239 FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xDropTarget);
1240 m_xTreeView->unselect_all();
1241 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pTargetItems);
1242 m_xTreeView->select(*xEntry);
1243 m_xTreeView->set_cursor(*xEntry);
1244
1245 insertFilterItem(m_aControlExchange->getDraggedEntries(),pTargetItems,DND_ACTION_COPYcss::datatransfer::dnd::DNDConstants::ACTION_COPY == rEvt.mnAction);
1246
1247 return DND_ACTION_COPYcss::datatransfer::dnd::DNDConstants::ACTION_COPY;
1248}
1249
1250IMPL_LINK_NOARG(FmFilterNavigator, SelectHdl, weld::TreeView&, void)void FmFilterNavigator::LinkStubSelectHdl(void * instance, weld
::TreeView& data) { return static_cast<FmFilterNavigator
*>(instance)->SelectHdl(data); } void FmFilterNavigator
::SelectHdl(__attribute__ ((unused)) weld::TreeView&)
1251{
1252 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
1253 if (!m_xTreeView->get_selected(xIter.get()))
1254 return;
1255
1256 FmFilterData* pData = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(*xIter).toUInt64());
1257
1258 FmFormItem* pFormItem = nullptr;
1259 if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
1260 pFormItem = static_cast<FmFormItem*>(pItem->GetParent()->GetParent());
1261 else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
1262 pFormItem = static_cast<FmFormItem*>(pItems->GetParent()->GetParent());
1263 else
1264 pFormItem = dynamic_cast<FmFormItem*>(pData);
1265
1266 if (pFormItem)
1267 {
1268 // will the controller be exchanged?
1269 if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
1270 m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(pItem->GetParent()));
1271 else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
1272 m_pModel->SetCurrentItems(pItems);
1273 else
1274 m_pModel->SetCurrentController(pFormItem->GetController());
1275 }
1276}
1277
1278void FmFilterNavigator::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1279{
1280 if (const FmFilterInsertedHint* pInsertHint = dynamic_cast<const FmFilterInsertedHint*>(&rHint))
1281 {
1282 Insert(pInsertHint->GetData(), pInsertHint->GetPos());
1283 }
1284 else if( dynamic_cast<const FilterClearingHint*>(&rHint) )
1285 {
1286 m_xTreeView->clear();
1287 }
1288 else if (const FmFilterRemovedHint* pRemoveHint = dynamic_cast<const FmFilterRemovedHint*>(&rHint))
1289 {
1290 Remove(pRemoveHint->GetData());
1291 }
1292 else if (const FmFilterTextChangedHint *pChangeHint = dynamic_cast<const FmFilterTextChangedHint*>(&rHint))
1293 {
1294 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pChangeHint->GetData());
1295 if (xEntry)
1296 m_xTreeView->set_text(*xEntry, pChangeHint->GetData()->GetText());
1297 }
1298 else if( dynamic_cast<const FmFilterCurrentChangedHint*>(&rHint) )
1299 {
1300 m_xTreeView->queue_draw();
1301 }
1302}
1303
1304std::unique_ptr<weld::TreeIter> FmFilterNavigator::FindEntry(const FmFilterData* pItem) const
1305{
1306 if (!pItem)
1307 return nullptr;
1308 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
1309 if (!m_xTreeView->get_iter_first(*xEntry))
1310 return nullptr;
1311 do
1312 {
1313 FmFilterData* pEntryItem = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(*xEntry).toUInt64());
1314 if (pEntryItem == pItem)
1315 return xEntry;
1316 }
1317 while (m_xTreeView->iter_next(*xEntry));
1318
1319 return nullptr;
1320}
1321
1322void FmFilterNavigator::Insert(FmFilterData* pItem, int nPos)
1323{
1324 const FmParentData* pParent = pItem->GetParent() ? pItem->GetParent() : m_pModel.get();
1325
1326 // insert the item
1327 std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry(pParent);
1328
1329 OUString sId(OUString::number(reinterpret_cast<sal_uIntPtr>(pItem)));
1330 std::unique_ptr<weld::TreeIter> xRet(m_xTreeView->make_iterator());
1331 m_xTreeView->insert(xParentEntry.get(), nPos, &pItem->GetText(), &sId,
1332 nullptr, nullptr, false, xRet.get());
1333 m_xTreeView->set_image(*xRet, pItem->GetImage());
1334
1335 if (!xParentEntry)
1336 return;
1337 m_xTreeView->expand_row(*xParentEntry);
1338}
1339
1340void FmFilterNavigator::EndEditing()
1341{
1342 if (m_xEditingCurrently)
1343 {
1344 // end editing
1345 m_xTreeView->end_editing();
1346 m_xEditingCurrently.reset();
1347 }
1348}
1349
1350void FmFilterNavigator::Remove(FmFilterData const * pItem)
1351{
1352 // the entry for the data
1353 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pItem);
1354 if (!xEntry)
1355 return;
1356
1357 if (m_xEditingCurrently && m_xTreeView->iter_compare(*xEntry, *m_xEditingCurrently) == 0)
1358 EndEditing();
1359
1360 m_xTreeView->remove(*xEntry);
1361}
1362
1363FmFormItem* FmFilterNavigator::getSelectedFilterItems(::std::vector<FmFilterItem*>& _rItemList)
1364{
1365 // be sure that the data is only used within only one form!
1366 FmFormItem* pFirstItem = nullptr;
1367
1368 bool bHandled = true;
1369 bool bFoundSomething = false;
1370
1371 m_xTreeView->selected_foreach([this, &bHandled, &bFoundSomething, &pFirstItem, &_rItemList](weld::TreeIter& rEntry) {
1372 FmFilterData* pFilterEntry = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(rEntry).toInt64());
1373 FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pFilterEntry);
1374 if (pFilter)
1375 {
1376 FmFormItem* pForm = dynamic_cast<FmFormItem*>( pFilter->GetParent()->GetParent() );
1377 if (!pForm)
1378 bHandled = false;
1379 else if (!pFirstItem)
1380 pFirstItem = pForm;
1381 else if (pFirstItem != pForm)
1382 bHandled = false;
1383
1384 if (bHandled)
1385 {
1386 _rItemList.push_back(pFilter);
1387 bFoundSomething = true;
1388 }
1389 }
1390 return !bHandled;
1391 });
1392
1393 if ( !bHandled || !bFoundSomething )
1394 pFirstItem = nullptr;
1395 return pFirstItem;
1396}
1397
1398void FmFilterNavigator::insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems,bool _bCopy)
1399{
1400 for (FmFilterItem* pLookupItem : _rFilterList)
1401 {
1402 if ( pLookupItem->GetParent() == _pTargetItems )
1403 continue;
1404
1405 FmFilterItem* pFilterItem = _pTargetItems->Find( pLookupItem->GetComponentIndex() );
1406 OUString aText = pLookupItem->GetText();
1407 if ( !pFilterItem )
1408 {
1409 pFilterItem = new FmFilterItem( _pTargetItems, pLookupItem->GetFieldName(), aText, pLookupItem->GetComponentIndex() );
1410 m_pModel->Append( _pTargetItems, std::unique_ptr<FmFilterItem>(pFilterItem) );
1411 }
1412
1413 if ( !_bCopy )
1414 m_pModel->Remove( pLookupItem );
1415
1416 // now set the text for the new dragged item
1417 m_pModel->SetTextForItem( pFilterItem, aText );
1418 }
1419
1420 m_pModel->EnsureEmptyFilterRows( *_pTargetItems->GetParent() );
1421}
1422
1423IMPL_LINK(FmFilterNavigator, DragBeginHdl, bool&, rUnsetDragIcon, bool)bool FmFilterNavigator::LinkStubDragBeginHdl(void * instance,
bool& data) { return static_cast<FmFilterNavigator *>
(instance)->DragBeginHdl(data); } bool FmFilterNavigator::
DragBeginHdl(bool& rUnsetDragIcon)
1424{
1425 rUnsetDragIcon = false;
1426
1427 // be sure that the data is only used within an only one form!
1428 m_aControlExchange.prepareDrag();
1429
1430 ::std::vector<FmFilterItem*> aItemList;
1431 if (FmFormItem* pFirstItem = getSelectedFilterItems(aItemList))
1432 {
1433 m_aControlExchange->setDraggedEntries(aItemList);
1434 m_aControlExchange->setFormItem(pFirstItem);
1435
1436 OFilterItemExchange& rExchange = *m_aControlExchange;
1437 rtl::Reference<TransferDataContainer> xHelper(&rExchange);
1438 m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVEcss::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE);
1439 rExchange.setDragging(true);
1440
1441 return false;
1442 }
1443 return true;
1444}
1445
1446IMPL_LINK(FmFilterNavigator, PopupMenuHdl, const CommandEvent&, rEvt, bool)bool FmFilterNavigator::LinkStubPopupMenuHdl(void * instance,
const CommandEvent& data) { return static_cast<FmFilterNavigator
*>(instance)->PopupMenuHdl(data); } bool FmFilterNavigator
::PopupMenuHdl(const CommandEvent& rEvt)
1
Calling 'FmFilterNavigator::PopupMenuHdl'
1447{
1448 bool bHandled = false;
1449 switch (rEvt.GetCommand())
2
Control jumps to 'case ContextMenu:' at line 1451
1450 {
1451 case CommandEventId::ContextMenu:
1452 {
1453 // the place where it was clicked
1454 Point aWhere;
1455 std::unique_ptr<weld::TreeIter> xClicked(m_xTreeView->make_iterator());
1456 if (rEvt.IsMouseEvent())
3
Assuming the condition is true
4
Taking true branch
1457 {
1458 aWhere = rEvt.GetMousePosPixel();
1459 if (!m_xTreeView->get_dest_row_at_pos(aWhere, xClicked.get(), false))
5
Assuming the condition is false
6
Taking false branch
1460 break;
1461
1462 if (!m_xTreeView->is_selected(*xClicked))
7
Assuming the condition is false
8
Taking false branch
1463 {
1464 m_xTreeView->unselect_all();
1465 m_xTreeView->select(*xClicked);
1466 m_xTreeView->set_cursor(*xClicked);
1467 }
1468 }
1469 else
1470 {
1471 if (!m_xTreeView->get_cursor(xClicked.get()))
1472 break;
1473 aWhere = m_xTreeView->get_row_area(*xClicked).Center();
1474 }
1475
1476 ::std::vector<FmFilterData*> aSelectList;
1477 m_xTreeView->selected_foreach([this, &aSelectList](weld::TreeIter& rEntry) {
1478 FmFilterData* pFilterEntry = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(rEntry).toInt64());
1479
1480 // don't delete forms
1481 FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
1482 if (!pForm)
1483 aSelectList.push_back(pFilterEntry);
1484
1485 return false;
1486 });
1487
1488 if (aSelectList.size() == 1)
9
Assuming the condition is true
10
Taking true branch
1489 {
1490 // don't delete the only empty row of a form
1491 FmFilterItems* pFilterItems = dynamic_cast<FmFilterItems*>( aSelectList[0] );
1492 if (pFilterItems && pFilterItems->GetChildren().empty()
11
Assuming 'pFilterItems' is null
1493 && pFilterItems->GetParent()->GetChildren().size() == 1)
1494 aSelectList.clear();
1495 }
1496
1497 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/filtermenu.ui"));
1498 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
1499
1500 // every condition could be deleted except the first one if it's the only one
1501 bool bNoDelete = false;
1502 if (aSelectList.empty())
12
Assuming the condition is false
13
Taking false branch
1503 {
1504 bNoDelete = true;
1505 xContextMenu->remove("delete");
1506 }
1507
1508 FmFilterData* pFilterEntry = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(*xClicked).toInt64());
14
'pFilterEntry' initialized here
1509 bool bEdit = dynamic_cast<FmFilterItem*>(pFilterEntry) != nullptr &&
15
Assuming pointer value is null
16
Assuming the condition is false
1510 m_xTreeView->is_selected(*xClicked) && m_xTreeView->count_selected_rows() == 1;
1511
1512 if (bNoDelete
16.1
'bNoDelete' is false
16.1
'bNoDelete' is false
&& !bEdit)
1513 {
1514 // nothing is in the menu, don't bother
1515 return true;
1516 }
1517
1518 if (!bEdit
16.2
'bEdit' is false
16.2
'bEdit' is false
)
17
Taking true branch
1519 {
1520 xContextMenu->remove("edit");
1521 xContextMenu->remove("isnull");
1522 xContextMenu->remove("isnotnull");
1523 }
1524
1525 OString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, ::Size(1, 1)));
1526 if (sIdent == "edit")
18
Calling 'operator==<char const[5]>'
23
Returning from 'operator==<char const[5]>'
24
Taking false branch
1527 {
1528 m_xTreeView->start_editing(*xClicked);
1529 }
1530 else if (sIdent == "isnull")
25
Calling 'operator==<char const[7]>'
31
Returning from 'operator==<char const[7]>'
32
Taking true branch
1531 {
1532 OUString aErrorMsg;
1533 OUString aText = "IS NULL";
1534 m_pModel->ValidateText(static_cast<FmFilterItem*>(pFilterEntry),
33
Passing null pointer value via 1st parameter 'pItem'
34
Calling 'FmFilterModel::ValidateText'
1535 aText, aErrorMsg);
1536 m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pFilterEntry), aText);
1537 }
1538 else if (sIdent == "isnotnull")
1539 {
1540 OUString aErrorMsg;
1541 OUString aText = "IS NOT NULL";
1542
1543 m_pModel->ValidateText(static_cast<FmFilterItem*>(pFilterEntry),
1544 aText, aErrorMsg);
1545 m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pFilterEntry), aText);
1546 }
1547 else if (sIdent == "delete")
1548 {
1549 DeleteSelection();
1550 }
1551 bHandled = true;
1552 }
1553 break;
1554 default: break;
1555 }
1556
1557 return bHandled;
1558}
1559
1560typedef std::vector<std::unique_ptr<weld::TreeIter>> iter_vector;
1561
1562bool FmFilterNavigator::getNextEntry(weld::TreeIter& rEntry)
1563{
1564 bool bEntry = m_xTreeView->iter_next(rEntry);
1565 // we need the next filter entry
1566 if (bEntry)
1567 {
1568 while (!m_xTreeView->iter_has_child(rEntry))
1569 {
1570 std::unique_ptr<weld::TreeIter> xNext = m_xTreeView->make_iterator(&rEntry);
1571 if (!m_xTreeView->iter_next(*xNext))
1572 break;
1573 m_xTreeView->copy_iterator(*xNext, rEntry);
1574 }
1575 }
1576 return bEntry;
1577}
1578
1579bool FmFilterNavigator::getPrevEntry(weld::TreeIter& rEntry)
1580{
1581 bool bEntry = m_xTreeView->iter_previous(rEntry);
1582 // check if the previous entry is a filter, if so get the next prev
1583 if (bEntry && m_xTreeView->iter_has_child(rEntry))
1584 {
1585 bEntry = m_xTreeView->iter_previous(rEntry);
1586 // if the entry is still no leaf return
1587 if (bEntry && m_xTreeView->iter_has_child(rEntry))
1588 bEntry = false;
1589 }
1590 return bEntry;
1591}
1592IMPL_LINK(FmFilterNavigator, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)bool FmFilterNavigator::LinkStubKeyInputHdl(void * instance, const
::KeyEvent& data) { return static_cast<FmFilterNavigator
*>(instance)->KeyInputHdl(data); } bool FmFilterNavigator
::KeyInputHdl(const ::KeyEvent& rKEvt)
1593{
1594 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1595
1596 switch ( rKeyCode.GetCode() )
1597 {
1598 case KEY_UP:
1599 case KEY_DOWN:
1600 {
1601 if ( !rKeyCode.IsMod1() || !rKeyCode.IsMod2() || rKeyCode.IsShift() )
1602 break;
1603
1604 ::std::vector<FmFilterItem*> aItemList;
1605 if ( !getSelectedFilterItems( aItemList ) )
1606 break;
1607
1608
1609 iter_vector aSelected;
1610 m_xTreeView->selected_foreach([this, &aSelected](weld::TreeIter& rEntry){
1611 aSelected.emplace_back(m_xTreeView->make_iterator(&rEntry));
1612 return false;
1613 });
1614
1615 std::unique_ptr<weld::TreeIter> xTarget;
1616 ::std::function<bool(FmFilterNavigator*, weld::TreeIter&)> getter;
1617
1618 if (rKeyCode.GetCode() == KEY_UP)
1619 {
1620 xTarget = m_xTreeView->make_iterator(aSelected.front().get());
1621 getter = ::std::mem_fn(&FmFilterNavigator::getPrevEntry);
1622 }
1623 else
1624 {
1625 xTarget = m_xTreeView->make_iterator(aSelected.back().get());
1626 getter = ::std::mem_fn(&FmFilterNavigator::getNextEntry);
1627 }
1628
1629 bool bTarget = getter(this, *xTarget);
1630 if (!bTarget)
1631 break;
1632
1633 FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
1634 if (!pTargetItems)
1635 break;
1636
1637 ::std::vector<FmFilterItem*>::const_iterator aEnd = aItemList.end();
1638 bool bNextTargetItem = true;
1639 while ( bNextTargetItem )
1640 {
1641 ::std::vector<FmFilterItem*>::const_iterator i = aItemList.begin();
1642 for (; i != aEnd; ++i)
1643 {
1644 if ( (*i)->GetParent() == pTargetItems )
1645 {
1646 bTarget = getter(this, *xTarget);
1647 if (!bTarget)
1648 return true;
1649 pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
1650 break;
1651 }
1652 else
1653 {
1654 FmFilterItem* pFilterItem = pTargetItems->Find( (*i)->GetComponentIndex() );
1655 // we found the text component so jump above
1656 if ( pFilterItem )
1657 {
1658 bTarget = getter(this, *xTarget);
1659 if (!bTarget)
1660 return true;
1661
1662 pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
1663 break;
1664 }
1665 }
1666 }
1667 bNextTargetItem = i != aEnd && pTargetItems;
1668 }
1669
1670 if ( pTargetItems )
1671 {
1672 insertFilterItem( aItemList, pTargetItems, false );
1673 return true;
1674 }
1675 }
1676 break;
1677
1678 case KEY_DELETE:
1679 {
1680 if ( rKeyCode.GetModifier() )
1681 break;
1682
1683 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
1684 if (m_xTreeView->get_iter_first(*xEntry) && !m_xTreeView->is_selected(*xEntry))
1685 DeleteSelection();
1686
1687 return true;
1688 }
1689 }
1690
1691 return false;
1692}
1693
1694void FmFilterNavigator::DeleteSelection()
1695{
1696 // to avoid the deletion of an entry twice (e.g. deletion of a parent and afterward
1697 // the deletion of its child, I have to shrink the selection list
1698 std::vector<FmFilterData*> aEntryList;
1699
1700 m_xTreeView->selected_foreach([this, &aEntryList](weld::TreeIter& rEntry) {
1701 FmFilterData* pFilterEntry = reinterpret_cast<FmFilterData*>(m_xTreeView->get_id(rEntry).toInt64());
1702
1703 if (dynamic_cast<FmFilterItem*>(pFilterEntry))
1704 {
1705 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
1706 if (m_xTreeView->iter_parent(*xParent) && m_xTreeView->is_selected(*xParent))
1707 return false;
1708 }
1709
1710 FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
1711 if (!pForm)
1712 aEntryList.emplace_back(pFilterEntry);
1713
1714 return false;
1715 });
1716
1717 // Remove the selection
1718 m_xTreeView->unselect_all();
1719
1720 for (auto i = aEntryList.rbegin(); i != aEntryList.rend(); ++i)
1721 m_pModel->Remove(*i);
1722}
1723
1724FmFilterNavigatorWin::FmFilterNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr,
1725 vcl::Window* _pParent)
1726 : SfxDockingWindow(_pBindings, _pMgr, _pParent, "FilterNavigator", "svx/ui/filternavigator.ui")
1727 , SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL( 10000 + 752 ), *_pBindings )
1728 , m_xNavigatorTree(new FmFilterNavigator(this, m_xBuilder->weld_tree_view("treeview")))
1729{
1730 SetHelpId( HID_FILTER_NAVIGATOR_WIN"SVX_HID_FILTER_NAVIGATOR_WIN" );
1731
1732 SetText( SvxResId(RID_STR_FILTER_NAVIGATORreinterpret_cast<char const *>("RID_STR_FILTER_NAVIGATOR"
"\004" u8"Filter navigator")
) );
1733 SfxDockingWindow::SetFloatingSize( Size(200,200) );
1734}
1735
1736FmFilterNavigatorWin::~FmFilterNavigatorWin()
1737{
1738 disposeOnce();
1739}
1740
1741void FmFilterNavigatorWin::dispose()
1742{
1743 m_xNavigatorTree.reset();
1744 ::SfxControllerItem::dispose();
1745 SfxDockingWindow::dispose();
1746}
1747
1748void FmFilterNavigatorWin::UpdateContent(FmFormShell const * pFormShell)
1749{
1750 if (!m_xNavigatorTree)
1751 return;
1752
1753 if (!pFormShell)
1754 m_xNavigatorTree->UpdateContent( nullptr, nullptr );
1755 else
1756 {
1757 Reference<XFormController> const xController(pFormShell->GetImpl()->getActiveInternalController_Lock());
1758 Reference< XIndexAccess > xContainer;
1759 if (xController.is())
1760 {
1761 Reference< XChild > xChild = xController;
1762 for (Reference< XInterface > xParent(xChild->getParent());
1763 xParent.is();
1764 xParent = xChild.is() ? xChild->getParent() : Reference< XInterface > ())
1765 {
1766 xContainer.set(xParent, UNO_QUERY);
1767 xChild.set(xParent, UNO_QUERY);
1768 }
1769 }
1770 m_xNavigatorTree->UpdateContent(xContainer, xController);
1771 }
1772}
1773
1774void FmFilterNavigatorWin::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
1775{
1776 if( !pState || SID_FM_FILTER_NAVIGATOR_CONTROL( 10000 + 752 ) != nSID )
1777 return;
1778
1779 if( eState >= SfxItemState::DEFAULT )
1780 {
1781 FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
1782 UpdateContent( pShell );
1783 }
1784 else
1785 UpdateContent( nullptr );
1786}
1787
1788bool FmFilterNavigatorWin::Close()
1789{
1790 if (m_xNavigatorTree)
1791 m_xNavigatorTree->EndEditing();
1792
1793 UpdateContent( nullptr );
1794 return SfxDockingWindow::Close();
1795}
1796
1797void FmFilterNavigatorWin::FillInfo( SfxChildWinInfo& rInfo ) const
1798{
1799 SfxDockingWindow::FillInfo( rInfo );
1800 rInfo.bVisible = false;
1801}
1802
1803Size FmFilterNavigatorWin::CalcDockingSize( SfxChildAlignment eAlign )
1804{
1805 if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
1806 return Size();
1807
1808 return SfxDockingWindow::CalcDockingSize( eAlign );
1809}
1810
1811SfxChildAlignment FmFilterNavigatorWin::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
1812{
1813 switch (eAlign)
1814 {
1815 case SfxChildAlignment::LEFT:
1816 case SfxChildAlignment::RIGHT:
1817 case SfxChildAlignment::NOALIGNMENT:
1818 return eAlign;
1819 default:
1820 break;
1821 }
1822
1823 return eActAlign;
1824}
1825
1826void FmFilterNavigatorWin::GetFocus()
1827{
1828 // oj #97405#
1829 if (m_xNavigatorTree)
1830 m_xNavigatorTree->GrabFocus();
1831 else
1832 SfxDockingWindow::GetFocus();
1833}
1834
1835SFX_IMPL_DOCKINGWINDOW( FmFilterNavigatorWinMgr, SID_FM_FILTER_NAVIGATOR )std::unique_ptr<SfxChildWindow> FmFilterNavigatorWinMgr
::CreateImpl( vcl::Window *pParent, sal_uInt16 nId, SfxBindings
*pBindings, SfxChildWinInfo* pInfo ) { return std::make_unique
<FmFilterNavigatorWinMgr>(pParent, nId, pBindings, pInfo
); } void FmFilterNavigatorWinMgr::RegisterChildWindow (bool bVis
, SfxModule *pMod, SfxChildWindowFlags nFlags) { auto pFact =
std::make_unique<SfxChildWinFactory>( FmFilterNavigatorWinMgr
::CreateImpl, ( 10000 + 732 ), (32767 *2 +1) ); pFact->aInfo
.nFlags |= nFlags; pFact->aInfo.bVisible = bVis; SfxChildWindow
::RegisterChildWindow(pMod, std::move(pFact)); } SfxChildWinInfo
FmFilterNavigatorWinMgr::GetInfo() const { SfxChildWinInfo aInfo
= SfxChildWindow::GetInfo(); static_cast<SfxDockingWindow
*>(GetWindow())->FillInfo( aInfo ); return aInfo; }
1836
1837
1838FmFilterNavigatorWinMgr::FmFilterNavigatorWinMgr( vcl::Window *_pParent, sal_uInt16 _nId,
1839 SfxBindings *_pBindings, SfxChildWinInfo* _pInfo )
1840 :SfxChildWindow( _pParent, _nId )
1841{
1842 SetWindow( VclPtr<FmFilterNavigatorWin>::Create( _pBindings, this, _pParent ) );
1843 static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
1844}
1845
1846
1847}
1848
1849/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/rtl/string.hxx

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#ifndef INCLUDED_RTL_STRING_HXX
21#define INCLUDED_RTL_STRING_HXX
22
23#include "sal/config.h"
24
25#include <cassert>
26#include <cstddef>
27#include <cstdlib>
28#include <limits>
29#include <new>
30#include <ostream>
31#include <utility>
32#include <string.h>
33
34#if defined LIBO_INTERNAL_ONLY1
35#include <string_view>
36#include <type_traits>
37#endif
38
39#include "rtl/textenc.h"
40#include "rtl/string.h"
41#include "rtl/stringutils.hxx"
42
43#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
44#include "config_global.h"
45#include "rtl/stringconcat.hxx"
46#endif
47
48#ifdef RTL_STRING_UNITTEST
49extern bool rtl_string_unittest_const_literal;
50extern bool rtl_string_unittest_const_literal_function;
51#endif
52
53// The unittest uses slightly different code to help check that the proper
54// calls are made. The class is put into a different namespace to make
55// sure the compiler generates a different (if generating also non-inline)
56// copy of the function and does not merge them together. The class
57// is "brought" into the proper rtl namespace by a typedef below.
58#ifdef RTL_STRING_UNITTEST
59#define rtl rtlunittest
60#endif
61
62namespace rtl
63{
64
65/// @cond INTERNAL
66#ifdef RTL_STRING_UNITTEST
67#undef rtl
68// helper macro to make functions appear more readable
69#define RTL_STRING_CONST_FUNCTION rtl_string_unittest_const_literal_function = true;
70#else
71#define RTL_STRING_CONST_FUNCTION
72#endif
73/// @endcond
74
75#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
76/**
77A wrapper dressing a string literal as a static-refcount rtl_String.
78
79This class is not part of public API and is meant to be used only in LibreOffice code.
80@since LibreOffice 4.0
81*/
82template<std::size_t N> class SAL_WARN_UNUSED__attribute__((warn_unused)) OStringLiteral {
83 static_assert(N != 0);
84 static_assert(N - 1 <= std::numeric_limits<sal_Int32>::max(), "literal too long");
85
86public:
87#if HAVE_CPP_CONSTEVAL0
88 consteval
89#else
90 constexpr
91#endif
92 explicit OStringLiteral(char const (&literal)[N]) {
93 assertLayout();
94 assert(literal[N - 1] == '\0')(static_cast <bool> (literal[N - 1] == '\0') ? void (0)
: __assert_fail ("literal[N - 1] == '\\0'", "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 94, __extension__ __PRETTY_FUNCTION__))
;
95 //TODO: Use C++20 constexpr std::copy_n (P0202R3):
96 for (std::size_t i = 0; i != N; ++i) {
97 buffer[i] = literal[i];
98 }
99 }
100
101#if defined __cpp_char8_t
102#if HAVE_CPP_CONSTEVAL0
103 consteval
104#else
105 constexpr
106#endif
107 explicit OStringLiteral(char8_t const (&literal)[N]) {
108 assertLayout();
109 assert(literal[N - 1] == '\0')(static_cast <bool> (literal[N - 1] == '\0') ? void (0)
: __assert_fail ("literal[N - 1] == '\\0'", "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 109, __extension__ __PRETTY_FUNCTION__))
;
110 //TODO: Use C++20 constexpr std::copy_n (P0202R3):
111 for (std::size_t i = 0; i != N; ++i) {
112 buffer[i] = literal[i];
113 }
114 }
115#endif
116
117 constexpr sal_Int32 getLength() const { return length; }
118
119 constexpr char const * getStr() const SAL_RETURNS_NONNULL__attribute__((returns_nonnull)) { return buffer; }
120
121private:
122 static constexpr void assertLayout() {
123 // These static_asserts verifying the layout compatibility with rtl_String cannot be class
124 // member declarations, as offsetof requires a complete type, so defer them to here:
125 static_assert(offsetof(OStringLiteral, refCount)__builtin_offsetof(OStringLiteral, refCount) == offsetof(rtl_String, refCount)__builtin_offsetof(rtl_String, refCount));
126 static_assert(
127 std::is_same_v<decltype(refCount), decltype(rtl_String::refCount)>);
128 static_assert(offsetof(OStringLiteral, length)__builtin_offsetof(OStringLiteral, length) == offsetof(rtl_String, length)__builtin_offsetof(rtl_String, length));
129 static_assert(std::is_same_v<decltype(length), decltype(rtl_String::length)>);
130 static_assert(offsetof(OStringLiteral, buffer)__builtin_offsetof(OStringLiteral, buffer) == offsetof(rtl_String, buffer)__builtin_offsetof(rtl_String, buffer));
131 static_assert(
132 std::is_same_v<
133 std::remove_extent_t<decltype(buffer)>,
134 std::remove_extent_t<decltype(rtl_String::buffer)>>);
135 }
136
137 // Same layout as rtl_String (include/rtl/string.h):
138 oslInterlockedCount refCount = 0x40000000; // SAL_STRING_STATIC_FLAG (sal/rtl/strimp.hxx)
139 sal_Int32 length = N - 1;
140 char buffer[N] = {}; //TODO: drop initialization for C++20 (P1331R2)
141};
142#endif
143
144/* ======================================================================= */
145
146/**
147 This String class provide base functionality for C++ like 8-Bit
148 character array handling. The advantage of this class is, that it
149 handle all the memory management for you - and it do it
150 more efficient. If you assign a string to another string, the
151 data of both strings are shared (without any copy operation or
152 memory allocation) as long as you do not change the string. This class
153 stores also the length of the string, so that many operations are
154 faster as the C-str-functions.
155
156 This class provides only readonly string handling. So you could create
157 a string and you could only query the content from this string.
158 It provides also functionality to change the string, but this results
159 in every case in a new string instance (in the most cases with an
160 memory allocation). You don't have functionality to change the
161 content of the string. If you want to change the string content, then
162 you should use the OStringBuffer class, which provides these
163 functionalities and avoid too much memory allocation.
164
165 The design of this class is similar to the string classes in Java
166 and so more people should have fewer understanding problems when they
167 use this class.
168*/
169
170class SAL_WARN_UNUSED__attribute__((warn_unused)) SAL_DLLPUBLIC_RTTI__attribute__ ((type_visibility("default"))) OString
171{
172public:
173 /// @cond INTERNAL
174 rtl_String * pData;
175 /// @endcond
176
177 /**
178 New string containing no characters.
179 */
180 OString()
181 {
182 pData = NULL__null;
183 rtl_string_new( &pData );
184 }
185
186 /**
187 New string from OString.
188
189 @param str an OString.
190 */
191 OString( const OString & str )
192 {
193 pData = str.pData;
194 rtl_string_acquire( pData );
195 }
196
197#if defined LIBO_INTERNAL_ONLY1
198 /**
199 Move constructor.
200
201 @param str an OString.
202 @since LibreOffice 5.2
203 */
204 OString( OString && str ) noexcept
205 {
206 pData = str.pData;
207 str.pData = nullptr;
208 rtl_string_new( &str.pData );
209 }
210#endif
211
212 /**
213 New string from OString data.
214
215 @param str an OString data.
216 */
217 OString( rtl_String * str )
218 {
219 pData = str;
220 rtl_string_acquire( pData );
221 }
222
223 /** New string from OString data without acquiring it. Takeover of ownership.
224
225 The SAL_NO_ACQUIRE dummy parameter is only there to distinguish this
226 from other constructors.
227
228 @param str an OString data.
229 */
230 OString( rtl_String * str, __sal_NoAcquire )
231 {
232 pData = str;
233 }
234
235 /**
236 New string from a single character.
237
238 @param value a character.
239 */
240 explicit OString( char value )
241 : pData (NULL__null)
242 {
243 rtl_string_newFromStr_WithLength( &pData, &value, 1 );
244 }
245
246 /**
247 New string from a character buffer array.
248
249 Note: The argument type is always either char* or const char*. The template is
250 used only for technical reasons, as is the second argument.
251
252 @param value a NULL-terminated character array.
253 */
254 template< typename T >
255 OString( const T& value, typename libreoffice_internal::CharPtrDetector< T, libreoffice_internal::Dummy >::Type = libreoffice_internal::Dummy() )
256 {
257 pData = NULL__null;
258 rtl_string_newFromStr( &pData, value );
259 }
260
261 template< typename T >
262 OString( T& value, typename libreoffice_internal::NonConstCharArrayDetector< T, libreoffice_internal::Dummy >::Type = libreoffice_internal::Dummy() )
263 {
264 pData = NULL__null;
265 rtl_string_newFromStr( &pData, value );
266 }
267
268 /**
269 New string from a string literal.
270
271 If there are any embedded \0's in the string literal, the result is undefined.
272 Use the overload that explicitly accepts length.
273
274 @since LibreOffice 3.6
275
276 @param literal a string literal
277 */
278 template< typename T >
279 OString( T& literal, typename libreoffice_internal::ConstCharArrayDetector< T, libreoffice_internal::Dummy >::Type = libreoffice_internal::Dummy() )
280 {
281 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 282, __extension__ __PRETTY_FUNCTION__))
282 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 282, __extension__ __PRETTY_FUNCTION__))
;
283 pData = NULL__null;
284 if (libreoffice_internal::ConstCharArrayDetector<T>::length == 0) {
285 rtl_string_new(&pData);
286 } else {
287 rtl_string_newFromLiteral(
288 &pData,
289 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
290 literal),
291 libreoffice_internal::ConstCharArrayDetector<T>::length, 0);
292 }
293#ifdef RTL_STRING_UNITTEST
294 rtl_string_unittest_const_literal = true;
295#endif
296 }
297
298 /**
299 New string from a character buffer array.
300
301 @param value a character array.
302 @param length the number of character which should be copied.
303 The character array length must be greater or
304 equal than this value.
305 */
306 OString( const char * value, sal_Int32 length )
307 {
308 pData = NULL__null;
309 rtl_string_newFromStr_WithLength( &pData, value, length );
310 }
311
312#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
313 /// @cond INTERNAL
314 /**
315 New string from an 8-Bit string literal.
316
317 @since LibreOffice 7.1
318 */
319 template<std::size_t N> OString(OStringLiteral<N> const & literal):
320 pData(const_cast<rtl_String *>(reinterpret_cast<rtl_String const *>(&literal))) {}
321 template<std::size_t N> OString(OStringLiteral<N> &&) = delete;
322 /// @endcond
323#endif
324
325#if defined LIBO_INTERNAL_ONLY1
326 OString(std::string_view sv) {
327 if (sv.size() > sal_uInt32(std::numeric_limits<sal_Int32>::max())) {
328 throw std::bad_alloc();
329 }
330 pData = nullptr;
331 rtl_string_newFromStr_WithLength(&pData, sv.data(), sv.size());
332 }
333#endif
334
335 /**
336 New string from a Unicode character buffer array.
337
338 @param value a Unicode character array.
339 @param length the number of character which should be converted.
340 The Unicode character array length must be
341 greater or equal than this value.
342 @param encoding the text encoding in which the Unicode character
343 sequence should be converted.
344 @param convertFlags flags which controls the conversion.
345 see RTL_UNICODETOTEXT_FLAGS_...
346
347 @exception std::bad_alloc is thrown if an out-of-memory condition occurs
348 */
349 OString( const sal_Unicode * value, sal_Int32 length,
350 rtl_TextEncoding encoding,
351 sal_uInt32 convertFlags = OUSTRING_TO_OSTRING_CVTFLAGS(((sal_uInt32)0x0006) | ((sal_uInt32)0x0060) | ((sal_uInt32)0x0100
) | ((sal_uInt32)0x0400))
)
352 {
353 pData = NULL__null;
354 rtl_uString2String( &pData, value, length, encoding, convertFlags );
355 if (pData == NULL__null) {
356 throw std::bad_alloc();
357 }
358 }
359
360#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
361 /**
362 @overload
363 @internal
364 */
365 template< typename T1, typename T2 >
366 OString( OStringConcat< T1, T2 >&& c )
367 {
368 const sal_Int32 l = c.length();
369 pData = rtl_string_alloc( l );
370 if (l != 0)
371 {
372 char* end = c.addData( pData->buffer );
373 pData->length = l;
374 *end = '\0';
375 }
376 }
377
378 /**
379 @overload
380 @internal
381 */
382 template< typename T >
383 OString( OStringNumber< T >&& n )
384 : OString( n.buf, n.length )
385 {}
386#endif
387
388#ifdef LIBO_INTERNAL_ONLY1
389 OString(std::nullptr_t) = delete;
390#endif
391
392 /**
393 Release the string data.
394 */
395 ~OString()
396 {
397 rtl_string_release( pData );
398 }
399
400 /**
401 Assign a new string.
402
403 @param str an OString.
404 */
405 OString & operator=( const OString & str )
406 {
407 rtl_string_assign( &pData, str.pData );
408 return *this;
409 }
410
411#if defined LIBO_INTERNAL_ONLY1
412 /**
413 Move assign a new string.
414
415 @param str an OString.
416 @since LibreOffice 5.2
417 */
418 OString & operator=( OString && str ) noexcept
419 {
420 rtl_string_release( pData );
421 pData = str.pData;
422 str.pData = nullptr;
423 rtl_string_new( &str.pData );
424 return *this;
425 }
426#endif
427
428 /**
429 @overload
430 This function accepts an ASCII string literal as its argument.
431 @since LibreOffice 3.6
432 */
433 template< typename T >
434 typename libreoffice_internal::ConstCharArrayDetector< T, OString& >::Type operator=( T& literal )
435 {
436 RTL_STRING_CONST_FUNCTION
437 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 438, __extension__ __PRETTY_FUNCTION__))
438 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 438, __extension__ __PRETTY_FUNCTION__))
;
439 if (libreoffice_internal::ConstCharArrayDetector<T>::length == 0) {
440 rtl_string_new(&pData);
441 } else {
442 rtl_string_newFromLiteral(
443 &pData,
444 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
445 literal),
446 libreoffice_internal::ConstCharArrayDetector<T>::length, 0);
447 }
448 return *this;
449 }
450
451 /**
452 Append a string to this string.
453
454 @param str an OString.
455 */
456 OString & operator+=( const OString & str )
457#if defined LIBO_INTERNAL_ONLY1
458 &
459#endif
460 {
461 rtl_string_newConcat( &pData, pData, str.pData );
462 return *this;
463 }
464#if defined LIBO_INTERNAL_ONLY1
465 void operator+=(OString const &) && = delete;
466#endif
467
468#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
469 /**
470 @overload
471 @internal
472 */
473 template< typename T1, typename T2 >
474 OString& operator+=( OStringConcat< T1, T2 >&& c ) & {
475 sal_Int32 l = c.length();
476 if( l == 0 )
477 return *this;
478 l += pData->length;
479 rtl_string_ensureCapacity( &pData, l );
480 char* end = c.addData( pData->buffer + pData->length );
481 *end = '\0';
482 pData->length = l;
483 return *this;
484 }
485 template<typename T1, typename T2> void operator +=(
486 OStringConcat<T1, T2> &&) && = delete;
487
488 /**
489 @overload
490 @internal
491 */
492 template< typename T >
493 OString& operator+=( OStringNumber< T >&& n ) & {
494 sal_Int32 l = n.length;
495 if( l == 0 )
496 return *this;
497 l += pData->length;
498 rtl_string_ensureCapacity( &pData, l );
499 char* end = addDataHelper( pData->buffer + pData->length, n.buf, n.length );
500 *end = '\0';
501 pData->length = l;
502 return *this;
503 }
504 template<typename T> void operator +=(
505 OStringNumber<T> &&) && = delete;
506#endif
507
508 /**
509 Clears the string, i.e, makes a zero-character string
510 @since LibreOffice 4.4
511 */
512 void clear()
513 {
514 rtl_string_new( &pData );
515 }
516
517 /**
518 Returns the length of this string.
519
520 The length is equal to the number of characters in this string.
521
522 @return the length of the sequence of characters represented by this
523 object.
524 */
525 sal_Int32 getLength() const { return pData->length; }
526
527 /**
528 Checks if a string is empty.
529
530 @return true if the string is empty;
531 false, otherwise.
532
533 @since LibreOffice 3.4
534 */
535 bool isEmpty() const
536 {
537 return pData->length == 0;
538 }
539
540 /**
541 Returns a pointer to the characters of this string.
542
543 <p>The returned pointer is guaranteed to point to a null-terminated byte
544 string. But note that this string object may contain embedded null
545 characters, which will thus also be embedded in the returned
546 null-terminated byte string.</p>
547
548 @return a pointer to a null-terminated byte string representing the
549 characters of this string object.
550 */
551 const char * getStr() const SAL_RETURNS_NONNULL__attribute__((returns_nonnull)) { return pData->buffer; }
552
553 /**
554 Access to individual characters.
555
556 @param index must be non-negative and less than length.
557
558 @return the character at the given index.
559
560 @since LibreOffice 3.5
561 */
562 char operator [](sal_Int32 index) const {
563 // silence spurious -Werror=strict-overflow warnings from GCC 4.8.2
564 assert(index >= 0 && static_cast<sal_uInt32>(index) < static_cast<sal_uInt32>(getLength()))(static_cast <bool> (index >= 0 && static_cast
<sal_uInt32>(index) < static_cast<sal_uInt32>(
getLength())) ? void (0) : __assert_fail ("index >= 0 && static_cast<sal_uInt32>(index) < static_cast<sal_uInt32>(getLength())"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 564, __extension__ __PRETTY_FUNCTION__))
;
565 return getStr()[index];
566 }
567
568 /**
569 Compares two strings.
570
571 The comparison is based on the numeric value of each character in
572 the strings and return a value indicating their relationship.
573 This function can't be used for language specific sorting.
574
575 @param str the object to be compared.
576 @return 0 - if both strings are equal
577 < 0 - if this string is less than the string argument
578 > 0 - if this string is greater than the string argument
579 */
580 sal_Int32 compareTo( const OString & str ) const
581 {
582 return rtl_str_compare_WithLength( pData->buffer, pData->length,
583 str.pData->buffer, str.pData->length );
584 }
585
586 /**
587 Compares two strings with an maximum count of characters.
588
589 The comparison is based on the numeric value of each character in
590 the strings and return a value indicating their relationship.
591 This function can't be used for language specific sorting.
592
593 @param rObj the object to be compared.
594 @param maxLength the maximum count of characters to be compared.
595 @return 0 - if both strings are equal
596 < 0 - if this string is less than the string argument
597 > 0 - if this string is greater than the string argument
598 */
599 sal_Int32 compareTo( const OString & rObj, sal_Int32 maxLength ) const
600 {
601 return rtl_str_shortenedCompare_WithLength( pData->buffer, pData->length,
602 rObj.pData->buffer, rObj.pData->length, maxLength );
603 }
604
605 /**
606 Compares two strings in reverse order.
607
608 The comparison is based on the numeric value of each character in
609 the strings and return a value indicating their relationship.
610 This function can't be used for language specific sorting.
611
612 @param str the object to be compared.
613 @return 0 - if both strings are equal
614 < 0 - if this string is less than the string argument
615 > 0 - if this string is greater than the string argument
616 */
617 sal_Int32 reverseCompareTo( const OString & str ) const
618 {
619 return rtl_str_reverseCompare_WithLength( pData->buffer, pData->length,
620 str.pData->buffer, str.pData->length );
621 }
622
623 /**
624 Perform a comparison of two strings.
625
626 The result is true if and only if second string
627 represents the same sequence of characters as the first string.
628 This function can't be used for language specific comparison.
629
630 @param str the object to be compared.
631 @return true if the strings are equal;
632 false, otherwise.
633 */
634 bool equals( const OString & str ) const
635 {
636 if ( pData->length != str.pData->length )
637 return false;
638 if ( pData == str.pData )
639 return true;
640 return rtl_str_reverseCompare_WithLength( pData->buffer, pData->length,
641 str.pData->buffer, str.pData->length ) == 0;
642 }
643
644 /**
645 Perform a comparison of two strings.
646
647 The result is true if and only if second string
648 represents the same sequence of characters as the first string.
649 The ASCII string must be NULL-terminated and must be greater or
650 equal as length.
651 This function can't be used for language specific comparison.
652
653
654 @param value a character array.
655 @param length the length of the character array.
656 @return true if the strings are equal;
657 false, otherwise.
658 */
659 bool equalsL( const char* value, sal_Int32 length ) const
660 {
661 if ( pData->length != length )
662 return false;
663
664 return rtl_str_reverseCompare_WithLength( pData->buffer, pData->length,
665 value, length ) == 0;
666 }
667
668 /**
669 Perform an ASCII lowercase comparison of two strings.
670
671 The result is true if and only if second string
672 represents the same sequence of characters as the first string,
673 ignoring the case.
674 Character values between 65 and 90 (ASCII A-Z) are interpreted as
675 values between 97 and 122 (ASCII a-z).
676 This function can't be used for language specific comparison.
677
678 @param str the object to be compared.
679 @return true if the strings are equal;
680 false, otherwise.
681 */
682 bool equalsIgnoreAsciiCase( const OString & str ) const
683 {
684 if ( pData->length != str.pData->length )
685 return false;
686 if ( pData == str.pData )
687 return true;
688 return rtl_str_compareIgnoreAsciiCase_WithLength( pData->buffer, pData->length,
689 str.pData->buffer, str.pData->length ) == 0;
690 }
691
692 /**
693 Perform an ASCII lowercase comparison of two strings.
694
695 The result is true if and only if second string
696 represents the same sequence of characters as the first string,
697 ignoring the case.
698 Character values between 65 and 90 (ASCII A-Z) are interpreted as
699 values between 97 and 122 (ASCII a-z).
700 Since this method is optimized for performance, the ASCII character
701 values are not converted in any way. The caller has to make sure that
702 all ASCII characters are in the allowed range between 0 and
703 127. The ASCII string must be NULL-terminated.
704 This function can't be used for language specific comparison.
705
706 Note: The argument type is always either char* or const char*, the return type is bool.
707 The template is used only for technical reasons.
708
709 @param asciiStr the 8-Bit ASCII character string to be compared.
710 @return true if the strings are equal;
711 false, otherwise.
712 */
713 template< typename T >
714 typename libreoffice_internal::CharPtrDetector< T, bool >::Type equalsIgnoreAsciiCase( const T& asciiStr ) const
715 {
716 return rtl_str_compareIgnoreAsciiCase( pData->buffer, asciiStr ) == 0;
717 }
718
719 template< typename T >
720 typename libreoffice_internal::NonConstCharArrayDetector< T, bool >::Type equalsIgnoreAsciiCase( T& asciiStr ) const
721 {
722 return rtl_str_compareIgnoreAsciiCase( pData->buffer, asciiStr ) == 0;
723 }
724
725 /**
726 @overload
727 This function accepts an ASCII string literal as its argument.
728 @since LibreOffice 3.6
729 */
730 template< typename T >
731 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type equalsIgnoreAsciiCase( T& literal ) const
732 {
733 RTL_STRING_CONST_FUNCTION
734 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 735, __extension__ __PRETTY_FUNCTION__))
735 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 735, __extension__ __PRETTY_FUNCTION__))
;
736 return
737 (pData->length
738 == libreoffice_internal::ConstCharArrayDetector<T>::length)
739 && (rtl_str_compareIgnoreAsciiCase_WithLength(
740 pData->buffer, pData->length,
741 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
742 literal),
743 libreoffice_internal::ConstCharArrayDetector<T>::length)
744 == 0);
745 }
746
747 /**
748 Perform an ASCII lowercase comparison of two strings.
749
750 The result is true if and only if second string
751 represents the same sequence of characters as the first string,
752 ignoring the case.
753 Character values between 65 and 90 (ASCII A-Z) are interpreted as
754 values between 97 and 122 (ASCII a-z).
755 Since this method is optimized for performance, the ASCII character
756 values are not converted in any way. The caller has to make sure that
757 all ASCII characters are in the allowed range between 0 and
758 127. The ASCII string must be greater or equal in length as asciiStrLength.
759 This function can't be used for language specific comparison.
760
761 @param asciiStr the 8-Bit ASCII character string to be compared.
762 @param asciiStrLength the length of the ascii string
763 @return true if the strings are equal;
764 false, otherwise.
765 */
766 bool equalsIgnoreAsciiCaseL( const char * asciiStr, sal_Int32 asciiStrLength ) const
767 {
768 if ( pData->length != asciiStrLength )
769 return false;
770
771 return rtl_str_compareIgnoreAsciiCase_WithLength( pData->buffer, pData->length,
772 asciiStr, asciiStrLength ) == 0;
773 }
774
775 /**
776 Match against a substring appearing in this string.
777
778 The result is true if and only if the second string appears as a substring
779 of this string, at the given position.
780 This function can't be used for language specific comparison.
781
782 @param str the object (substring) to be compared.
783 @param fromIndex the index to start the comparison from.
784 The index must be greater or equal than 0
785 and less or equal as the string length.
786 @return true if str match with the characters in the string
787 at the given position;
788 false, otherwise.
789 */
790 bool match( const OString & str, sal_Int32 fromIndex = 0 ) const
791 {
792 return rtl_str_shortenedCompare_WithLength( pData->buffer+fromIndex, pData->length-fromIndex,
793 str.pData->buffer, str.pData->length, str.pData->length ) == 0;
794 }
795
796 /**
797 @overload
798 This function accepts an ASCII string literal as its argument.
799 @since LibreOffice 3.6
800 */
801 template< typename T >
802 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type match( T& literal, sal_Int32 fromIndex = 0 ) const
803 {
804 RTL_STRING_CONST_FUNCTION
805 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 806, __extension__ __PRETTY_FUNCTION__))
806 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 806, __extension__ __PRETTY_FUNCTION__))
;
807 return
808 rtl_str_shortenedCompare_WithLength(
809 pData->buffer + fromIndex, pData->length - fromIndex,
810 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
811 literal),
812 libreoffice_internal::ConstCharArrayDetector<T>::length,
813 libreoffice_internal::ConstCharArrayDetector<T>::length)
814 == 0;
815 }
816
817 /**
818 Match against a substring appearing in this string.
819
820 @param str the substring to be compared; must not be null and must point
821 to memory of at least strLength bytes
822
823 @param strLength the length of the substring; must be non-negative
824
825 @param fromIndex the index into this string to start the comparison at;
826 must be non-negative and not greater than this string's length
827
828 @return true if and only if the given str is contained as a substring of
829 this string at the given fromIndex
830
831 @since LibreOffice 3.6
832 */
833 bool matchL(
834 char const * str, sal_Int32 strLength, sal_Int32 fromIndex = 0)
835 const
836 {
837 return rtl_str_shortenedCompare_WithLength(
838 pData->buffer + fromIndex, pData->length - fromIndex,
839 str, strLength, strLength) == 0;
840 }
841
842 // This overload is left undefined, to detect calls of matchL that
843 // erroneously use RTL_CONSTASCII_USTRINGPARAM instead of
844 // RTL_CONSTASCII_STRINGPARAM (but would lead to ambiguities on 32 bit
845 // platforms):
846#if SAL_TYPES_SIZEOFLONG8 == 8
847 void matchL(char const *, sal_Int32, rtl_TextEncoding) const;
848#endif
849
850 /**
851 Match against a substring appearing in this string, ignoring the case of
852 ASCII letters.
853
854 The result is true if and only if the second string appears as a substring
855 of this string, at the given position.
856 Character values between 65 and 90 (ASCII A-Z) are interpreted as
857 values between 97 and 122 (ASCII a-z).
858 This function can't be used for language specific comparison.
859
860 @param str the object (substring) to be compared.
861 @param fromIndex the index to start the comparison from.
862 The index must be greater or equal than 0
863 and less or equal as the string length.
864 @return true if str match with the characters in the string
865 at the given position;
866 false, otherwise.
867 */
868 bool matchIgnoreAsciiCase( const OString & str, sal_Int32 fromIndex = 0 ) const
869 {
870 return rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( pData->buffer+fromIndex, pData->length-fromIndex,
871 str.pData->buffer, str.pData->length,
872 str.pData->length ) == 0;
873 }
874
875 /**
876 @overload
877 This function accepts an ASCII string literal as its argument.
878 @since LibreOffice 3.6
879 */
880 template< typename T >
881 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type matchIgnoreAsciiCase( T& literal, sal_Int32 fromIndex = 0 ) const
882 {
883 RTL_STRING_CONST_FUNCTION
884 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 885, __extension__ __PRETTY_FUNCTION__))
885 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 885, __extension__ __PRETTY_FUNCTION__))
;
886 return
887 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
888 pData->buffer+fromIndex, pData->length-fromIndex,
889 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
890 literal),
891 libreoffice_internal::ConstCharArrayDetector<T>::length,
892 libreoffice_internal::ConstCharArrayDetector<T>::length)
893 == 0;
894 }
895
896 /**
897 Check whether this string starts with a given substring.
898
899 @param str the substring to be compared
900
901 @param rest if non-null, and this function returns true, then assign a
902 copy of the remainder of this string to *rest. Available since
903 LibreOffice 4.2
904
905 @return true if and only if the given str appears as a substring at the
906 start of this string
907
908 @since LibreOffice 4.0
909 */
910 bool startsWith(OString const & str, OString * rest = NULL__null) const {
911 bool b = match(str);
912 if (b && rest != NULL__null) {
913 *rest = copy(str.getLength());
914 }
915 return b;
916 }
917
918 /**
919 @overload
920 This function accepts an ASCII string literal as its argument.
921 @since LibreOffice 4.0
922 */
923 template< typename T >
924 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type startsWith(
925 T & literal, OString * rest = NULL__null) const
926 {
927 RTL_STRING_CONST_FUNCTION
928 bool b = match(literal, 0);
929 if (b && rest != NULL__null) {
930 *rest = copy(
931 libreoffice_internal::ConstCharArrayDetector<T>::length);
932 }
933 return b;
934 }
935
936 /**
937 Check whether this string starts with a given string, ignoring the case of
938 ASCII letters.
939
940 Character values between 65 and 90 (ASCII A-Z) are interpreted as
941 values between 97 and 122 (ASCII a-z).
942 This function can't be used for language specific comparison.
943
944 @param str the substring to be compared
945
946 @param rest if non-null, and this function returns true, then assign a
947 copy of the remainder of this string to *rest.
948
949 @return true if and only if the given str appears as a substring at the
950 start of this string, ignoring the case of ASCII letters ("A"--"Z" and
951 "a"--"z")
952
953 @since LibreOffice 5.1
954 */
955 bool startsWithIgnoreAsciiCase(OString const & str, OString * rest = NULL__null)
956 const
957 {
958 bool b = matchIgnoreAsciiCase(str);
959 if (b && rest != NULL__null) {
960 *rest = copy(str.getLength());
961 }
962 return b;
963 }
964
965 /**
966 @overload
967 This function accepts an ASCII string literal as its argument.
968 @since LibreOffice 5.1
969 */
970 template< typename T >
971 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type
972 startsWithIgnoreAsciiCase(T & literal, OString * rest = NULL__null) const
973 {
974 RTL_STRING_CONST_FUNCTION
975 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 976, __extension__ __PRETTY_FUNCTION__))
976 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 976, __extension__ __PRETTY_FUNCTION__))
;
977 bool b = matchIgnoreAsciiCase(literal);
978 if (b && rest != NULL__null) {
979 *rest = copy(
980 libreoffice_internal::ConstCharArrayDetector<T>::length);
981 }
982 return b;
983 }
984
985 /**
986 Check whether this string ends with a given substring.
987
988 @param str the substring to be compared
989
990 @param rest if non-null, and this function returns true, then assign a
991 copy of the remainder of this string to *rest. Available since
992 LibreOffice 4.2
993
994 @return true if and only if the given str appears as a substring at the
995 end of this string
996
997 @since LibreOffice 3.6
998 */
999 bool endsWith(OString const & str, OString * rest = NULL__null) const {
1000 bool b = str.getLength() <= getLength()
1001 && match(str, getLength() - str.getLength());
1002 if (b && rest != NULL__null) {
1003 *rest = copy(0, getLength() - str.getLength());
1004 }
1005 return b;
1006 }
1007
1008 /**
1009 @overload
1010 This function accepts an ASCII string literal as its argument.
1011 @since LibreOffice 3.6
1012 */
1013 template< typename T >
1014 typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type endsWith(
1015 T & literal, OString * rest = NULL__null) const
1016 {
1017 RTL_STRING_CONST_FUNCTION
1018 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1019, __extension__ __PRETTY_FUNCTION__))
1019 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1019, __extension__ __PRETTY_FUNCTION__))
;
1020 bool b
1021 = (libreoffice_internal::ConstCharArrayDetector<T>::length
1022 <= sal_uInt32(getLength()))
1023 && match(
1024 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
1025 literal),
1026 (getLength()
1027 - libreoffice_internal::ConstCharArrayDetector<T>::length));
1028 if (b && rest != NULL__null) {
1029 *rest = copy(
1030 0,
1031 (getLength()
1032 - libreoffice_internal::ConstCharArrayDetector<T>::length));
1033 }
1034 return b;
1035 }
1036
1037 /**
1038 Check whether this string ends with a given substring.
1039
1040 @param str the substring to be compared; must not be null and must point
1041 to memory of at least strLength bytes
1042
1043 @param strLength the length of the substring; must be non-negative
1044
1045 @return true if and only if the given str appears as a substring at the
1046 end of this string
1047
1048 @since LibreOffice 3.6
1049 */
1050 bool endsWithL(char const * str, sal_Int32 strLength) const {
1051 return strLength <= getLength()
1052 && matchL(str, strLength, getLength() - strLength);
1053 }
1054
1055 friend bool operator == ( const OString& rStr1, const OString& rStr2 )
1056 { return rStr1.equals(rStr2); }
1057 friend bool operator != ( const OString& rStr1, const OString& rStr2 )
1058 { return !(operator == ( rStr1, rStr2 )); }
1059 friend bool operator < ( const OString& rStr1, const OString& rStr2 )
1060 { return rStr1.compareTo( rStr2 ) < 0; }
1061 friend bool operator > ( const OString& rStr1, const OString& rStr2 )
1062 { return rStr1.compareTo( rStr2 ) > 0; }
1063 friend bool operator <= ( const OString& rStr1, const OString& rStr2 )
1064 { return rStr1.compareTo( rStr2 ) <= 0; }
1065 friend bool operator >= ( const OString& rStr1, const OString& rStr2 )
1066 { return rStr1.compareTo( rStr2 ) >= 0; }
1067
1068 template< typename T >
1069 friend typename libreoffice_internal::CharPtrDetector< T, bool >::Type operator==( const OString& rStr1, const T& value )
1070 {
1071 return rStr1.compareTo( value ) == 0;
1072 }
1073
1074 template< typename T >
1075 friend typename libreoffice_internal::NonConstCharArrayDetector< T, bool >::Type operator==( const OString& rStr1, T& value )
1076 {
1077 return rStr1.compareTo( value ) == 0;
1078 }
1079
1080 template< typename T >
1081 friend typename libreoffice_internal::CharPtrDetector< T, bool >::Type operator==( const T& value, const OString& rStr2 )
1082 {
1083 return rStr2.compareTo( value ) == 0;
1084 }
1085
1086 template< typename T >
1087 friend typename libreoffice_internal::NonConstCharArrayDetector< T, bool >::Type operator==( T& value, const OString& rStr2 )
1088 {
1089 return rStr2.compareTo( value ) == 0;
1090 }
1091
1092 /**
1093 @overload
1094 This function accepts an ASCII string literal as its argument.
1095 @since LibreOffice 3.6
1096 */
1097 template< typename T >
1098 friend typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type operator==( const OString& rStr, T& literal )
1099 {
1100 RTL_STRING_CONST_FUNCTION
1101 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1102, __extension__ __PRETTY_FUNCTION__))
19
Assuming the condition is true
20
'?' condition is true
26
Assuming the condition is true
27
'?' condition is true
1102 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1102, __extension__ __PRETTY_FUNCTION__))
;
1103 return
22
Returning zero, which participates in a condition later
30
Returning the value 1, which participates in a condition later
1104 (rStr.getLength()
21
Assuming the condition is false
28
Assuming the condition is true
1105 == libreoffice_internal::ConstCharArrayDetector<T>::length)
1106 && (rtl_str_compare_WithLength(
29
Assuming the condition is true
1107 rStr.pData->buffer, rStr.pData->length,
1108 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
1109 literal),
1110 libreoffice_internal::ConstCharArrayDetector<T>::length)
1111 == 0);
1112 }
1113
1114 /**
1115 @overload
1116 This function accepts an ASCII string literal as its argument.
1117 @since LibreOffice 3.6
1118 */
1119 template< typename T >
1120 friend typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type operator==( T& literal, const OString& rStr )
1121 {
1122 RTL_STRING_CONST_FUNCTION
1123 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1124, __extension__ __PRETTY_FUNCTION__))
1124 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1124, __extension__ __PRETTY_FUNCTION__))
;
1125 return
1126 (rStr.getLength()
1127 == libreoffice_internal::ConstCharArrayDetector<T>::length)
1128 && (rtl_str_compare_WithLength(
1129 rStr.pData->buffer, rStr.pData->length,
1130 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(
1131 literal),
1132 libreoffice_internal::ConstCharArrayDetector<T>::length)
1133 == 0);
1134 }
1135
1136 template< typename T >
1137 friend typename libreoffice_internal::CharPtrDetector< T, bool >::Type operator!=( const OString& rStr1, const T& value )
1138 {
1139 return !(operator == ( rStr1, value ));
1140 }
1141
1142 template< typename T >
1143 friend typename libreoffice_internal::NonConstCharArrayDetector< T, bool >::Type operator!=( const OString& rStr1, T& value )
1144 {
1145 return !(operator == ( rStr1, value ));
1146 }
1147
1148 template< typename T >
1149 friend typename libreoffice_internal::CharPtrDetector< T, bool >::Type operator!=( const T& value, const OString& rStr2 )
1150 {
1151 return !(operator == ( value, rStr2 ));
1152 }
1153
1154 template< typename T >
1155 friend typename libreoffice_internal::NonConstCharArrayDetector< T, bool >::Type operator!=( T& value, const OString& rStr2 )
1156 {
1157 return !(operator == ( value, rStr2 ));
1158 }
1159
1160 /**
1161 @overload
1162 This function accepts an ASCII string literal as its argument.
1163 @since LibreOffice 3.6
1164 */
1165 template< typename T >
1166 friend typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type operator!=( const OString& rStr, T& literal )
1167 {
1168 return !( rStr == literal );
1169 }
1170
1171 /**
1172 @overload
1173 This function accepts an ASCII string literal as its argument.
1174 @since LibreOffice 3.6
1175 */
1176 template< typename T >
1177 friend typename libreoffice_internal::ConstCharArrayDetector< T, bool >::Type operator!=( T& literal, const OString& rStr )
1178 {
1179 return !( literal == rStr );
1180 }
1181
1182 /**
1183 Returns a hashcode for this string.
1184
1185 @return a hash code value for this object.
1186
1187 @see rtl::OStringHash for convenient use of std::unordered_map
1188 */
1189 sal_Int32 hashCode() const
1190 {
1191 return rtl_str_hashCode_WithLength( pData->buffer, pData->length );
1192 }
1193
1194 /**
1195 Returns the index within this string of the first occurrence of the
1196 specified character, starting the search at the specified index.
1197
1198 @param ch character to be located.
1199 @param fromIndex the index to start the search from.
1200 The index must be greater or equal than 0
1201 and less or equal as the string length.
1202 @return the index of the first occurrence of the character in the
1203 character sequence represented by this string that is
1204 greater than or equal to fromIndex, or
1205 -1 if the character does not occur.
1206 */
1207 sal_Int32 indexOf( char ch, sal_Int32 fromIndex = 0 ) const
1208 {
1209 sal_Int32 ret = rtl_str_indexOfChar_WithLength( pData->buffer+fromIndex, pData->length-fromIndex, ch );
1210 return (ret < 0 ? ret : ret+fromIndex);
1211 }
1212
1213 /**
1214 Returns the index within this string of the last occurrence of the
1215 specified character, searching backward starting at the end.
1216
1217 @param ch character to be located.
1218 @return the index of the last occurrence of the character in the
1219 character sequence represented by this string, or
1220 -1 if the character does not occur.
1221 */
1222 sal_Int32 lastIndexOf( char ch ) const
1223 {
1224 return rtl_str_lastIndexOfChar_WithLength( pData->buffer, pData->length, ch );
1225 }
1226
1227 /**
1228 Returns the index within this string of the last occurrence of the
1229 specified character, searching backward starting before the specified
1230 index.
1231
1232 @param ch character to be located.
1233 @param fromIndex the index before which to start the search.
1234 @return the index of the last occurrence of the character in the
1235 character sequence represented by this string that
1236 is less than fromIndex, or -1
1237 if the character does not occur before that point.
1238 */
1239 sal_Int32 lastIndexOf( char ch, sal_Int32 fromIndex ) const
1240 {
1241 return rtl_str_lastIndexOfChar_WithLength( pData->buffer, fromIndex, ch );
1242 }
1243
1244 /**
1245 Returns the index within this string of the first occurrence of the
1246 specified substring, starting at the specified index.
1247
1248 If str doesn't include any character, always -1 is
1249 returned. This is also the case, if both strings are empty.
1250
1251 @param str the substring to search for.
1252 @param fromIndex the index to start the search from.
1253 @return If the string argument occurs one or more times as a substring
1254 within this string at the starting index, then the index
1255 of the first character of the first such substring is
1256 returned. If it does not occur as a substring starting
1257 at fromIndex or beyond, -1 is returned.
1258 */
1259 sal_Int32 indexOf( const OString & str, sal_Int32 fromIndex = 0 ) const
1260 {
1261 sal_Int32 ret = rtl_str_indexOfStr_WithLength( pData->buffer+fromIndex, pData->length-fromIndex,
1262 str.pData->buffer, str.pData->length );
1263 return (ret < 0 ? ret : ret+fromIndex);
1264 }
1265
1266 /**
1267 @overload
1268 This function accepts an ASCII string literal as its argument.
1269 @since LibreOffice 3.6
1270 */
1271 template< typename T >
1272 typename libreoffice_internal::ConstCharArrayDetector< T, sal_Int32 >::Type indexOf( T& literal, sal_Int32 fromIndex = 0 ) const
1273 {
1274 RTL_STRING_CONST_FUNCTION
1275 assert((static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1276, __extension__ __PRETTY_FUNCTION__))
1276 libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal))(static_cast <bool> (libreoffice_internal::ConstCharArrayDetector
<T>::isValid(literal)) ? void (0) : __assert_fail ("libreoffice_internal::ConstCharArrayDetector<T>::isValid(literal)"
, "/home/maarten/src/libreoffice/core/include/rtl/string.hxx"
, 1276, __extension__ __PRETTY_FUNCTION__))
;
1277 sal_Int32 n = rtl_str_indexOfStr_WithLength(
1278 pData->buffer + fromIndex, pData->length - fromIndex,
1279 libreoffice_internal::ConstCharArrayDetector<T>::toPointer(literal),
1280 libreoffice_internal::ConstCharArrayDetector<T>::length);
1281 return n < 0 ? n : n + fromIndex;
1282 }
1283
1284 /**
1285 Returns the index within this string of the first occurrence of the
1286 specified substring, starting at the specified index.
1287
1288 If str doesn't include any character, always -1 is
1289 returned. This is also the case, if both strings are empty.
1290
1291 @param str the substring to search for.
1292 @param len the length of the substring.
1293 @param fromIndex the index to start the search from.
1294 @return If the string argument occurs one or more times as a substring
1295 within this string at the starting index, then the index
1296 of the first character of the first such substring is
1297 returned. If it does not occur as a substring starting
1298 at fromIndex or beyond, -1 is returned.
1299
1300 @since LibreOffice 3.6
1301 */
1302 sal_Int32 indexOfL(char const * str, sal_Int32 len, sal_Int32 fromIndex = 0)
1303 const
1304 {
1305 sal_Int32 n = rtl_str_indexOfStr_WithLength(
1306 pData->buffer + fromIndex, pData->length - fromIndex, str, len);
1307 return n < 0 ? n : n + fromIndex;
1308 }
1309
1310 // This overload is left undefined, to detect calls of indexOfL that
1311 // erroneously use RTL_CONSTASCII_USTRINGPARAM instead of
1312 // RTL_CONSTASCII_STRINGPARAM (but would lead to ambiguities on 32 bit
1313 // platforms):
1314#if SAL_TYPES_SIZEOFLONG8 == 8
1315 void indexOfL(char const *, sal_Int32, rtl_TextEncoding) const;
1316#endif
1317
1318 /**
1319 Returns the index within this string of the last occurrence of
1320 the specified substring, searching backward starting at the end.
1321
1322 The returned index indicates the starting index of the substring
1323 in this string.
1324 If str doesn't include any character, always -1 is
1325 returned. This is also the case, if both strings are empty.
1326
1327 @param str the substring to search for.
1328 @return If the string argument occurs one or more times as a substring
1329 within this string, then the index of the first character of
1330 the last such substring is returned. If it does not occur as
1331 a substring, -1 is returned.
1332 */
1333 sal_Int32 lastIndexOf( const OString & str ) const
1334 {
1335 return rtl_str_lastIndexOfStr_WithLength( pData->buffer, pData->length,
1336 str.pData->buffer, str.pData->length );
1337 }
1338
1339 /**
1340 Returns the index within this string of the last occurrence of
1341 the specified substring, searching backward starting before the specified
1342 index.
1343
1344 The returned index indicates the starting index of the substring
1345 in this string.
1346 If str doesn't include any character, always -1 is
1347 returned. This is also the case, if both strings are empty.
1348
1349 @param str the substring to search for.
1350 @param fromIndex the index before which to start the search.
1351 @return If the string argument occurs one or more times as a substring
1352 within this string before the starting index, then the index
1353 of the first character of the last such substring is
1354 returned. Otherwise, -1 is returned.
1355 */
1356 sal_Int32 lastIndexOf( const OString & str, sal_Int32 fromIndex ) const
1357 {
1358 return rtl_str_lastIndexOfStr_WithLength( pData->buffer, fromIndex,
1359 str.pData->buffer, str.pData->length );
1360 }
1361
1362 /**
1363 Returns a new string that is a substring of this string.
1364
1365 The substring begins at the specified beginIndex. If
1366 beginIndex is negative or be greater than the length of
1367 this string, behaviour is undefined.
1368
1369 @param beginIndex the beginning index, inclusive.
1370 @return the specified substring.
1371 */
1372 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString copy( sal_Int32 beginIndex ) const
1373 {
1374 return copy(beginIndex, getLength() - beginIndex);
1375 }
1376
1377 /**
1378 Returns a new string that is a substring of this string.
1379
1380 The substring begins at the specified beginIndex and contains count
1381 characters. If either beginIndex or count are negative,
1382 or beginIndex + count are greater than the length of this string
1383 then behaviour is undefined.
1384
1385 @param beginIndex the beginning index, inclusive.
1386 @param count the number of characters.
1387 @return the specified substring.
1388 */
1389 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString copy( sal_Int32 beginIndex, sal_Int32 count ) const
1390 {
1391 rtl_String *pNew = NULL__null;
1392 rtl_string_newFromSubString( &pNew, pData, beginIndex, count );
1393 return OString( pNew, SAL_NO_ACQUIRE );
1394 }
1395
1396 /**
1397 Concatenates the specified string to the end of this string.
1398
1399 @param str the string that is concatenated to the end
1400 of this string.
1401 @return a string that represents the concatenation of this string
1402 followed by the string argument.
1403 */
1404 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString concat( const OString & str ) const
1405 {
1406 rtl_String* pNew = NULL__null;
1407 rtl_string_newConcat( &pNew, pData, str.pData );
1408 return OString( pNew, SAL_NO_ACQUIRE );
1409 }
1410
1411#ifndef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
1412 friend OString operator+( const OString & str1, const OString & str2 )
1413 {
1414 return str1.concat( str2 );
1415 }
1416#endif
1417
1418 /**
1419 Returns a new string resulting from replacing n = count characters
1420 from position index in this string with newStr.
1421
1422 @param index the replacing index in str.
1423 The index must be greater or equal as 0 and
1424 less or equal as the length of the string.
1425 @param count the count of characters that will replaced
1426 The count must be greater or equal as 0 and
1427 less or equal as the length of the string minus index.
1428 @param newStr the new substring.
1429 @return the new string.
1430 */
1431 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString replaceAt( sal_Int32 index, sal_Int32 count, const OString& newStr ) const
1432 {
1433 rtl_String* pNew = NULL__null;
1434 rtl_string_newReplaceStrAt( &pNew, pData, index, count, newStr.pData );
1435 return OString( pNew, SAL_NO_ACQUIRE );
1436 }
1437
1438 /**
1439 Returns a new string resulting from replacing all occurrences of
1440 oldChar in this string with newChar.
1441
1442 If the character oldChar does not occur in the character sequence
1443 represented by this object, then the string is assigned with
1444 str.
1445
1446 @param oldChar the old character.
1447 @param newChar the new character.
1448 @return a string derived from this string by replacing every
1449 occurrence of oldChar with newChar.
1450 */
1451 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString replace( char oldChar, char newChar ) const
1452 {
1453 rtl_String* pNew = NULL__null;
1454 rtl_string_newReplace( &pNew, pData, oldChar, newChar );
1455 return OString( pNew, SAL_NO_ACQUIRE );
1456 }
1457
1458 /**
1459 Returns a new string resulting from replacing the first occurrence of a
1460 given substring with another substring.
1461
1462 @param from the substring to be replaced
1463
1464 @param to the replacing substring
1465
1466 @param[in,out] index pointer to a start index; if the pointer is
1467 non-null: upon entry to the function, its value is the index into the this
1468 string at which to start searching for the \p from substring, the value
1469 must be non-negative and not greater than this string's length; upon exit
1470 from the function its value is the index into this string at which the
1471 replacement took place or -1 if no replacement took place; if the pointer
1472 is null, searching always starts at index 0
1473
1474 @since LibreOffice 3.6
1475 */
1476 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString replaceFirst(
1477 OString const & from, OString const & to, sal_Int32 * index = NULL__null) const
1478 {
1479 rtl_String * s = NULL__null;
1480 sal_Int32 i = 0;
1481 rtl_string_newReplaceFirst(
1482 &s, pData, from.pData->buffer, from.pData->length,
1483 to.pData->buffer, to.pData->length, index == NULL__null ? &i : index);
1484 return OString(s, SAL_NO_ACQUIRE);
1485 }
1486
1487 /**
1488 Returns a new string resulting from replacing all occurrences of a given
1489 substring with another substring.
1490
1491 Replacing subsequent occurrences picks up only after a given replacement.
1492 That is, replacing from "xa" to "xx" in "xaa" results in "xxa", not "xxx".
1493
1494 @param from the substring to be replaced
1495
1496 @param to the replacing substring
1497
1498 @since LibreOffice 3.6
1499 */
1500 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString replaceAll(OString const & from, OString const & to) const {
1501 rtl_String * s = NULL__null;
1502 rtl_string_newReplaceAll(
1503 &s, pData, from.pData->buffer, from.pData->length,
1504 to.pData->buffer, to.pData->length);
1505 return OString(s, SAL_NO_ACQUIRE);
1506 }
1507
1508 /**
1509 Converts from this string all ASCII uppercase characters (65-90)
1510 to ASCII lowercase characters (97-122).
1511
1512 This function can't be used for language specific conversion.
1513 If the string doesn't contain characters which must be converted,
1514 then the new string is assigned with str.
1515
1516 @return the string, converted to ASCII lowercase.
1517 */
1518 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString toAsciiLowerCase() const
1519 {
1520 rtl_String* pNew = NULL__null;
1521 rtl_string_newToAsciiLowerCase( &pNew, pData );
1522 return OString( pNew, SAL_NO_ACQUIRE );
1523 }
1524
1525 /**
1526 Converts from this string all ASCII lowercase characters (97-122)
1527 to ASCII uppercase characters (65-90).
1528
1529 This function can't be used for language specific conversion.
1530 If the string doesn't contain characters which must be converted,
1531 then the new string is assigned with str.
1532
1533 @return the string, converted to ASCII uppercase.
1534 */
1535 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString toAsciiUpperCase() const
1536 {
1537 rtl_String* pNew = NULL__null;
1538 rtl_string_newToAsciiUpperCase( &pNew, pData );
1539 return OString( pNew, SAL_NO_ACQUIRE );
1540 }
1541
1542 /**
1543 Returns a new string resulting from removing white space from both ends
1544 of the string.
1545
1546 All characters that have codes less than or equal to
1547 32 (the space character) are considered to be white space.
1548 If the string doesn't contain white spaces at both ends,
1549 then the new string is assigned with str.
1550
1551 @return the string, with white space removed from the front and end.
1552 */
1553 SAL_WARN_UNUSED_RESULT[[nodiscard]] OString trim() const
1554 {
1555 rtl_String* pNew = NULL__null;
1556 rtl_string_newTrim( &pNew, pData );
1557 return OString( pNew, SAL_NO_ACQUIRE );
1558 }
1559
1560 /**
1561 Returns a token in the string.
1562
1563 Example:
1564 sal_Int32 nIndex = 0;
1565 do
1566 {
1567 ...
1568 OString aToken = aStr.getToken( 0, ';', nIndex );
1569 ...
1570 }
1571 while ( nIndex >= 0 );
1572
1573 @param token the number of the token to return.
1574 @param cTok the character which separate the tokens.
1575 @param index the position at which the token is searched in the
1576 string.
1577 The index must not be greater than the length of the
1578 string.
1579 This param is set to the position of the
1580 next token or to -1, if it is the last token.
1581 @return the token; if either token or index is negative, an empty token
1582 is returned (and index is set to -1)
1583 */
1584 OString getToken( sal_Int32 token, char cTok, sal_Int32& index ) const
1585 {
1586 rtl_String * pNew = NULL__null;
1587 index = rtl_string_getToken( &pNew, pData, token, cTok, index );
1588 return OString( pNew, SAL_NO_ACQUIRE );
1589 }
1590
1591 /**
1592 Returns a token from the string.
1593
1594 The same as getToken(sal_Int32, char, sal_Int32 &), but always passing
1595 in 0 as the start index in the third argument.
1596
1597 @param count the number of the token to return, starting with 0
1598 @param separator the character which separates the tokens
1599
1600 @return the given token, or an empty string
1601
1602 @since LibreOffice 3.6
1603 */
1604 OString getToken(sal_Int32 count, char separator) const {
1605 sal_Int32 n = 0;
1606 return getToken(count, separator, n);
1607 }
1608
1609 /**
1610 Returns the Boolean value from this string.
1611
1612 This function can't be used for language specific conversion.
1613
1614 @return true, if the string is 1 or "True" in any ASCII case.
1615 false in any other case.
1616 */
1617 bool toBoolean() const
1618 {
1619 return rtl_str_toBoolean( pData->buffer );
1620 }
1621
1622 /**
1623 Returns the first character from this string.
1624
1625 @return the first character from this string or 0, if this string
1626 is empty.
1627 */
1628 char toChar() const
1629 {
1630 return pData->buffer[0];
1631 }
1632
1633 /**
1634 Returns the int32 value from this string.
1635
1636 This function can't be used for language specific conversion.
1637
1638 @param radix the radix (between 2 and 36)
1639 @return the int32 represented from this string.
1640 0 if this string represents no number or one of too large
1641 magnitude.
1642 */
1643 sal_Int32 toInt32( sal_Int16 radix = 10 ) const
1644 {
1645 return rtl_str_toInt32( pData->buffer, radix );
1646 }
1647
1648 /**
1649 Returns the uint32 value from this string.
1650
1651 This function can't be used for language specific conversion.
1652
1653 @param radix the radix (between 2 and 36)
1654 @return the uint32 represented from this string.
1655 0 if this string represents no number or one of too large
1656 magnitude.
1657
1658 @since LibreOffice 4.2
1659 */
1660 sal_uInt32 toUInt32( sal_Int16 radix = 10 ) const
1661 {
1662 return rtl_str_toUInt32( pData->buffer, radix );
1663 }
1664
1665 /**
1666 Returns the int64 value from this string.
1667
1668 This function can't be used for language specific conversion.
1669
1670 @param radix the radix (between 2 and 36)
1671 @return the int64 represented from this string.
1672 0 if this string represents no number or one of too large
1673 magnitude.
1674 */
1675 sal_Int64 toInt64( sal_Int16 radix = 10 ) const
1676 {
1677 return rtl_str_toInt64( pData->buffer, radix );
1678 }
1679
1680 /**
1681 Returns the uint64 value from this string.
1682
1683 This function can't be used for language specific conversion.
1684
1685 @param radix the radix (between 2 and 36)
1686 @return the uint64 represented from this string.
1687 0 if this string represents no number or one of too large
1688 magnitude.
1689
1690 @since LibreOffice 4.1
1691 */
1692 sal_uInt64 toUInt64( sal_Int16 radix = 10 ) const
1693 {
1694 return rtl_str_toUInt64( pData->buffer, radix );
1695 }
1696
1697 /**
1698 Returns the float value from this string.
1699
1700 This function can't be used for language specific conversion.
1701
1702 @return the float represented from this string.
1703 0.0 if this string represents no number.
1704 */
1705 float toFloat() const
1706 {
1707 return rtl_str_toFloat( pData->buffer );
1708 }
1709
1710 /**
1711 Returns the double value from this string.
1712
1713 This function can't be used for language specific conversion.
1714
1715 @return the double represented from this string.
1716 0.0 if this string represents no number.
1717 */
1718 double toDouble() const
1719 {
1720 return rtl_str_toDouble( pData->buffer );
1721 }
1722
1723#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
1724
1725 static OStringNumber< int > number( int i, sal_Int16 radix = 10 )
1726 {
1727 return OStringNumber< int >( i, radix );
1728 }
1729 static OStringNumber< long long > number( long long ll, sal_Int16 radix = 10 )
1730 {
1731 return OStringNumber< long long >( ll, radix );
1732 }
1733 static OStringNumber< unsigned long long > number( unsigned long long ll, sal_Int16 radix = 10 )
1734 {
1735 return OStringNumber< unsigned long long >( ll, radix );
1736 }
1737 static OStringNumber< unsigned long long > number( unsigned int i, sal_Int16 radix = 10 )
1738 {
1739 return number( static_cast< unsigned long long >( i ), radix );
1740 }
1741 static OStringNumber< long long > number( long i, sal_Int16 radix = 10)
1742 {
1743 return number( static_cast< long long >( i ), radix );
1744 }
1745 static OStringNumber< unsigned long long > number( unsigned long i, sal_Int16 radix = 10 )
1746 {
1747 return number( static_cast< unsigned long long >( i ), radix );
1748 }
1749 static OStringNumber< float > number( float f )
1750 {
1751 return OStringNumber< float >( f );
1752 }
1753 static OStringNumber< double > number( double d )
1754 {
1755 return OStringNumber< double >( d );
1756 }
1757#else
1758 /**
1759 Returns the string representation of the integer argument.
1760
1761 This function can't be used for language specific conversion.
1762
1763 @param i an integer value
1764 @param radix the radix (between 2 and 36)
1765 @return a string with the string representation of the argument.
1766 @since LibreOffice 4.1
1767 */
1768 static OString number( int i, sal_Int16 radix = 10 )
1769 {
1770 char aBuf[RTL_STR_MAX_VALUEOFINT3233];
1771 return OString(aBuf, rtl_str_valueOfInt32(aBuf, i, radix));
1772 }
1773 /// @overload
1774 /// @since LibreOffice 4.1
1775 static OString number( unsigned int i, sal_Int16 radix = 10 )
1776 {
1777 return number( static_cast< unsigned long long >( i ), radix );
1778 }
1779 /// @overload
1780 /// @since LibreOffice 4.1
1781 static OString number( long i, sal_Int16 radix = 10 )
1782 {
1783 return number( static_cast< long long >( i ), radix );
1784 }
1785 /// @overload
1786 /// @since LibreOffice 4.1
1787 static OString number( unsigned long i, sal_Int16 radix = 10 )
1788 {
1789 return number( static_cast< unsigned long long >( i ), radix );
1790 }
1791 /// @overload
1792 /// @since LibreOffice 4.1
1793 static OString number( long long ll, sal_Int16 radix = 10 )
1794 {
1795 char aBuf[RTL_STR_MAX_VALUEOFINT6465];
1796 return OString(aBuf, rtl_str_valueOfInt64(aBuf, ll, radix));
1797 }
1798 /// @overload
1799 /// @since LibreOffice 4.1
1800 static OString number( unsigned long long ll, sal_Int16 radix = 10 )
1801 {
1802 char aBuf[RTL_STR_MAX_VALUEOFUINT6465];
1803 return OString(aBuf, rtl_str_valueOfUInt64(aBuf, ll, radix));
1804 }
1805
1806 /**
1807 Returns the string representation of the float argument.
1808
1809 This function can't be used for language specific conversion.
1810
1811 @param f a float.
1812 @return a string with the decimal representation of the argument.
1813 @since LibreOffice 4.1
1814 */
1815 static OString number( float f )
1816 {
1817 char aBuf[RTL_STR_MAX_VALUEOFFLOAT15];
1818 return OString(aBuf, rtl_str_valueOfFloat(aBuf, f));
1819 }
1820
1821 /**
1822 Returns the string representation of the double argument.
1823
1824 This function can't be used for language specific conversion.
1825
1826 @param d a double.
1827 @return a string with the decimal representation of the argument.
1828 @since LibreOffice 4.1
1829 */
1830 static OString number( double d )
1831 {
1832 char aBuf[RTL_STR_MAX_VALUEOFDOUBLE25];
1833 return OString(aBuf, rtl_str_valueOfDouble(aBuf, d));
1834 }
1835#endif
1836
1837 /**
1838 Returns the string representation of the sal_Bool argument.
1839
1840 If the sal_Bool is true, the string "true" is returned.
1841 If the sal_Bool is false, the string "false" is returned.
1842 This function can't be used for language specific conversion.
1843
1844 @param b a sal_Bool.
1845 @return a string with the string representation of the argument.
1846 @deprecated use boolean()
1847 */
1848 SAL_DEPRECATED("use boolean()")__attribute__((deprecated("use boolean()"))) static OString valueOf( sal_Bool b )
1849 {
1850 return boolean(b);
1851 }
1852
1853 /**
1854 Returns the string representation of the boolean argument.
1855
1856 If the argument is true, the string "true" is returned.
1857 If the argument is false, the string "false" is returned.
1858 This function can't be used for language specific conversion.
1859
1860 @param b a bool.
1861 @return a string with the string representation of the argument.
1862 @since LibreOffice 4.1
1863 */
1864 static OString boolean( bool b )
1865 {
1866 char aBuf[RTL_STR_MAX_VALUEOFBOOLEAN6];
1867 return OString(aBuf, rtl_str_valueOfBoolean(aBuf, b));
1868 }
1869
1870 /**
1871 Returns the string representation of the char argument.
1872
1873 @param c a character.
1874 @return a string with the string representation of the argument.
1875 @deprecated use operator, function or constructor taking char or sal_Unicode argument
1876 */
1877 SAL_DEPRECATED("convert to OString or use directly")__attribute__((deprecated("convert to OString or use directly"
)))
static OString valueOf( char c )
1878 {
1879 return OString( &c, 1 );
1880 }
1881
1882 /**
1883 Returns the string representation of the int argument.
1884
1885 This function can't be used for language specific conversion.
1886
1887 @param i a int32.
1888 @param radix the radix (between 2 and 36)
1889 @return a string with the string representation of the argument.
1890 @deprecated use number()
1891 */
1892 SAL_DEPRECATED("use number()")__attribute__((deprecated("use number()"))) static OString valueOf( sal_Int32 i, sal_Int16 radix = 10 )
1893 {
1894 return number( i, radix );
1895 }
1896
1897 /**
1898 Returns the string representation of the long argument.
1899
1900 This function can't be used for language specific conversion.
1901
1902 @param ll a int64.
1903 @param radix the radix (between 2 and 36)
1904 @return a string with the string representation of the argument.
1905 @deprecated use number()
1906 */
1907 SAL_DEPRECATED("use number()")__attribute__((deprecated("use number()"))) static OString valueOf( sal_Int64 ll, sal_Int16 radix = 10 )
1908 {
1909 return number( ll, radix );
1910 }
1911
1912 /**
1913 Returns the string representation of the float argument.
1914
1915 This function can't be used for language specific conversion.
1916
1917 @param f a float.
1918 @return a string with the string representation of the argument.
1919 @deprecated use number()
1920 */
1921 SAL_DEPRECATED("use number()")__attribute__((deprecated("use number()"))) static OString valueOf( float f )
1922 {
1923 return number(f);
1924 }
1925
1926 /**
1927 Returns the string representation of the double argument.
1928
1929 This function can't be used for language specific conversion.
1930
1931 @param d a double.
1932 @return a string with the string representation of the argument.
1933 @deprecated use number()
1934 */
1935 SAL_DEPRECATED("use number()")__attribute__((deprecated("use number()"))) static OString valueOf( double d )
1936 {
1937 return number(d);
1938 }
1939
1940#if defined LIBO_INTERNAL_ONLY1
1941 operator std::string_view() const { return {getStr(), sal_uInt32(getLength())}; }
1942#endif
1943
1944#if defined LIBO_INTERNAL_ONLY1
1945 // A wrapper for the first expression in an
1946 //
1947 // OString::Concat(e1) + e2 + ...
1948 //
1949 // concatenation chain, when neither of the first two e1, e2 is one of our rtl string-related
1950 // classes (so something like
1951 //
1952 // OString s = "a" + (b ? std::string_view("c") : std::string_view("dd"));
1953 //
1954 // would not compile):
1955 template<typename T> [[nodiscard]] static
1956 typename std::enable_if_t<
1957 ToStringHelper<T>::allowOStringConcat, OStringConcat<OStringConcatMarker, T>>
1958 Concat(T const & value) { return OStringConcat<OStringConcatMarker, T>({}, value); }
1959
1960 // This overload is needed so that an argument of type 'char const[N]' ends up as
1961 // 'OStringConcat<rtl::OStringConcatMarker, char const[N]>' rather than as
1962 // 'OStringConcat<rtl::OStringConcatMarker, char[N]>':
1963 template<typename T, std::size_t N> [[nodiscard]] static
1964 typename std::enable_if_t<
1965 ToStringHelper<T[N]>::allowOStringConcat, OStringConcat<OStringConcatMarker, T[N]>>
1966 Concat(T (& value)[N]) { return OStringConcat<OStringConcatMarker, T[N]>({}, value); }
1967#endif
1968};
1969
1970/* ======================================================================= */
1971
1972#ifdef LIBO_INTERNAL_ONLY1 // "RTL_FAST_STRING"
1973
1974/**
1975 @internal
1976*/
1977template<>
1978struct ToStringHelper< OString >
1979 {
1980 static std::size_t length( const OString& s ) { return s.getLength(); }
1981 static char* addData( char* buffer, const OString& s ) { return addDataHelper( buffer, s.getStr(), s.getLength()); }
1982 static const bool allowOStringConcat = true;
1983 static const bool allowOUStringConcat = false;
1984 };
1985
1986/**
1987 @internal
1988*/
1989template<std::size_t N>
1990struct ToStringHelper< OStringLiteral<N> >
1991 {
1992 static constexpr std::size_t length( const OStringLiteral<N>& str ) { return str.getLength(); }
1993 static char* addData( char* buffer, const OStringLiteral<N>& str ) { return addDataHelper( buffer, str.getStr(), str.getLength() ); }
1994 static const bool allowOStringConcat = true;
1995 static const bool allowOUStringConcat = false;
1996 };
1997
1998/**
1999 @internal
2000*/
2001template< typename charT, typename traits, typename T1, typename T2 >
2002inline std::basic_ostream<charT, traits> & operator <<(
2003 std::basic_ostream<charT, traits> & stream, OStringConcat< T1, T2 >&& concat)
2004{
2005 return stream << OString( std::move(concat) );
2006}
2007#endif
2008
2009
2010/** A helper to use OStrings with hash maps.
2011
2012 Instances of this class are unary function objects that can be used as
2013 hash function arguments to std::unordered_map and similar constructs.
2014 */
2015struct OStringHash
2016{
2017 /** Compute a hash code for a string.
2018
2019 @param rString
2020 a string.
2021
2022 @return
2023 a hash code for the string. This hash code should not be stored
2024 persistently, as its computation may change in later revisions.
2025 */
2026 size_t operator()( const OString& rString ) const
2027 { return static_cast<size_t>(rString.hashCode()); }
2028};
2029
2030/** Equality functor for classic c-strings (i.e., null-terminated char* strings). */
2031struct CStringEqual
2032{
2033 bool operator()( const char* p1, const char* p2) const
2034 { return rtl_str_compare(p1, p2) == 0; }
2035};
2036
2037/** Hashing functor for classic c-strings (i.e., null-terminated char* strings). */
2038struct CStringHash
2039{
2040 size_t operator()(const char* p) const
2041 { return rtl_str_hashCode(p); }
2042};
2043
2044/* ======================================================================= */
2045
2046/**
2047 Support for rtl::OString in std::ostream (and thus in
2048 CPPUNIT_ASSERT or SAL_INFO macros, for example).
2049
2050 @since LibreOffice 4.0
2051 */
2052template< typename charT, typename traits > std::basic_ostream<charT, traits> &
2053operator <<(
2054 std::basic_ostream<charT, traits> & stream, OString const & rString)
2055{
2056 return stream << rString.getStr();
2057 // best effort; potentially loses data due to embedded null characters
2058}
2059
2060} /* Namespace */
2061
2062#ifdef RTL_STRING_UNITTEST
2063namespace rtl
2064{
2065typedef rtlunittest::OString OString;
2066}
2067#undef RTL_STRING_CONST_FUNCTION
2068#endif
2069
2070#if defined LIBO_INTERNAL_ONLY1 && !defined RTL_STRING_UNITTEST
2071using ::rtl::OString;
2072using ::rtl::OStringChar;
2073using ::rtl::OStringHash;
2074using ::rtl::OStringLiteral;
2075#endif
2076
2077/// @cond INTERNAL
2078/**
2079 Make OString hashable by default for use in STL containers.
2080
2081 @since LibreOffice 6.0
2082*/
2083#if defined LIBO_INTERNAL_ONLY1
2084namespace std {
2085
2086template<>
2087struct hash<::rtl::OString>
2088{
2089 std::size_t operator()(::rtl::OString const & s) const
2090 { return std::size_t(s.hashCode()); }
2091};
2092
2093}
2094
2095#endif
2096/// @endcond
2097
2098#endif // INCLUDED_RTL_STRING_HXX
2099
2100/* vim:set shiftwidth=4 softtabstop=4 expandtab: */