1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <txtlists.hxx>
22 #include <comphelper/random.hxx>
23 #include <tools/date.hxx>
24 #include <tools/time.hxx>
26 #include <xmloff/txtimp.hxx>
27 #include <xmloff/xmlimp.hxx>
28 #include <xmloff/xmlnumi.hxx>
30 #include <com/sun/star/style/XStyle.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include "XMLTextListItemContext.hxx"
33 #include "XMLTextListBlockContext.hxx"
34 #include "txtparai.hxx"
37 using namespace ::com::sun::star
;
40 XMLTextListsHelper::XMLTextListsHelper()
41 : mpProcessedLists( 0 ),
42 msLastProcessedListId(),
43 msListStyleOfLastProcessedList(),
44 // Inconsistent behavior regarding lists (#i92811#)
45 mpMapListIdToListStyleDefaultListId( 0 ),
46 mpContinuingLists( 0 ),
51 XMLTextListsHelper::~XMLTextListsHelper()
53 if ( mpProcessedLists
)
55 mpProcessedLists
->clear();
56 delete mpProcessedLists
;
58 // Inconsistent behavior regarding lists (#i92811#)#
59 if ( mpMapListIdToListStyleDefaultListId
)
61 mpMapListIdToListStyleDefaultListId
->clear();
62 delete mpMapListIdToListStyleDefaultListId
;
64 if ( mpContinuingLists
)
66 mpContinuingLists
->clear();
67 delete mpContinuingLists
;
76 void XMLTextListsHelper::PushListContext(
77 XMLTextListBlockContext
*i_pListBlock
)
79 mListStack
.push(::boost::make_tuple(i_pListBlock
,
80 static_cast<XMLTextListItemContext
*>(0),
81 static_cast<XMLNumberedParaContext
*>(0)));
84 void XMLTextListsHelper::PushListContext(
85 XMLNumberedParaContext
*i_pNumberedParagraph
)
87 mListStack
.push(::boost::make_tuple(
88 static_cast<XMLTextListBlockContext
*>(0),
89 static_cast<XMLTextListItemContext
*>(0), i_pNumberedParagraph
));
92 void XMLTextListsHelper::PopListContext()
94 assert(mListStack
.size());
95 if ( !mListStack
.empty())
99 void XMLTextListsHelper::ListContextTop(
100 XMLTextListBlockContext
*& o_pListBlockContext
,
101 XMLTextListItemContext
*& o_pListItemContext
,
102 XMLNumberedParaContext
*& o_pNumberedParagraphContext
)
104 if ( !mListStack
.empty() ) {
105 o_pListBlockContext
=
106 static_cast<XMLTextListBlockContext
*>(&mListStack
.top().get
<0>());
108 static_cast<XMLTextListItemContext
*>(&mListStack
.top().get
<1>());
109 o_pNumberedParagraphContext
=
110 static_cast<XMLNumberedParaContext
*>(&mListStack
.top().get
<2>());
114 void XMLTextListsHelper::SetListItem( XMLTextListItemContext
*i_pListItem
)
116 // may be cleared by ListBlockContext for upper list...
118 assert(mListStack
.size());
119 assert(mListStack
.top().get
<0>() &&
120 "internal error: SetListItem: mListStack has no ListBlock");
121 assert(!mListStack
.top().get
<1>() &&
122 "error: SetListItem: list item already exists");
124 if ( !mListStack
.empty() ) {
125 mListStack
.top().get
<1>() = i_pListItem
;
129 // Handling for parameter <sListStyleDefaultListId> (#i92811#)
130 void XMLTextListsHelper::KeepListAsProcessed( const OUString
& sListId
,
131 const OUString
& sListStyleName
,
132 const OUString
& sContinueListId
,
133 const OUString
& sListStyleDefaultListId
)
135 if ( IsListProcessed( sListId
) )
138 "<XMLTextListsHelper::KeepListAsProcessed(..)> - list id already added" );
142 if ( mpProcessedLists
== 0 )
144 mpProcessedLists
= new tMapForLists();
147 ::std::pair
< OUString
, OUString
>
148 aListData( sListStyleName
, sContinueListId
);
149 (*mpProcessedLists
)[ sListId
] = aListData
;
151 msLastProcessedListId
= sListId
;
152 msListStyleOfLastProcessedList
= sListStyleName
;
154 // Inconsistent behavior regarding lists (#i92811#)
155 if ( !sListStyleDefaultListId
.isEmpty())
157 if ( mpMapListIdToListStyleDefaultListId
== 0 )
159 mpMapListIdToListStyleDefaultListId
= new tMapForLists();
162 if ( mpMapListIdToListStyleDefaultListId
->find( sListStyleName
) ==
163 mpMapListIdToListStyleDefaultListId
->end() )
165 ::std::pair
< OUString
, OUString
>
166 aListIdMapData( sListId
, sListStyleDefaultListId
);
167 (*mpMapListIdToListStyleDefaultListId
)[ sListStyleName
] =
173 bool XMLTextListsHelper::IsListProcessed( const OUString
& sListId
) const
175 if ( mpProcessedLists
== 0 )
180 return mpProcessedLists
->find( sListId
) != mpProcessedLists
->end();
183 OUString
XMLTextListsHelper::GetListStyleOfProcessedList(
184 const OUString
& sListId
) const
186 if ( mpProcessedLists
!= 0 )
188 tMapForLists::const_iterator aIter
= mpProcessedLists
->find( sListId
);
189 if ( aIter
!= mpProcessedLists
->end() )
191 return (*aIter
).second
.first
;
198 OUString
XMLTextListsHelper::GetContinueListIdOfProcessedList(
199 const OUString
& sListId
) const
201 if ( mpProcessedLists
!= 0 )
203 tMapForLists::const_iterator aIter
= mpProcessedLists
->find( sListId
);
204 if ( aIter
!= mpProcessedLists
->end() )
206 return (*aIter
).second
.second
;
215 OUString
XMLTextListsHelper::GenerateNewListId() const
217 static bool bHack
= (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != NULL
);
218 OUString
sTmpStr( "list" );
222 static sal_Int64 nIdCounter
= SAL_CONST_INT64(5000000000);
223 sTmpStr
+= OUString::number(nIdCounter
++);
227 // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
228 sal_Int64 n
= ::tools::Time( ::tools::Time::SYSTEM
).GetTime();
229 n
+= Date( Date::SYSTEM
).GetDate();
230 n
+= comphelper::rng::uniform_int_distribution(0, std::numeric_limits
<int>::max());
231 // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
232 sTmpStr
+= OUString::number( n
);
235 OUString
sNewListId( sTmpStr
);
236 if ( mpProcessedLists
!= 0 )
239 while ( mpProcessedLists
->find( sNewListId
) != mpProcessedLists
->end() )
242 sNewListId
= sTmpStr
;
243 sNewListId
+= OUString::number( nHitCount
);
250 // Provide list id for a certain list block for import (#i92811#)
251 OUString
XMLTextListsHelper::GetListIdForListBlock( XMLTextListBlockContext
& rListBlock
)
253 OUString
sListBlockListId( rListBlock
.GetContinueListId() );
254 if ( sListBlockListId
.isEmpty() )
256 sListBlockListId
= rListBlock
.GetListId();
259 if ( mpMapListIdToListStyleDefaultListId
!= 0 )
261 if ( !sListBlockListId
.isEmpty() )
263 const OUString sListStyleName
=
264 GetListStyleOfProcessedList( sListBlockListId
);
266 tMapForLists::const_iterator aIter
=
267 mpMapListIdToListStyleDefaultListId
->find( sListStyleName
);
268 if ( aIter
!= mpMapListIdToListStyleDefaultListId
->end() )
270 if ( (*aIter
).second
.first
== sListBlockListId
)
272 sListBlockListId
= (*aIter
).second
.second
;
278 return sListBlockListId
;
281 void XMLTextListsHelper::StoreLastContinuingList( const OUString
& sListId
,
282 const OUString
& sContinuingListId
)
284 if ( mpContinuingLists
== 0 )
286 mpContinuingLists
= new tMapForContinuingLists();
289 (*mpContinuingLists
)[ sListId
] = sContinuingListId
;
292 OUString
XMLTextListsHelper::GetLastContinuingListId(
293 const OUString
& sListId
) const
295 if ( mpContinuingLists
!= 0)
297 tMapForContinuingLists::const_iterator aIter
=
298 mpContinuingLists
->find( sListId
);
299 if ( aIter
!= mpContinuingLists
->end() )
301 return (*aIter
).second
;
308 void XMLTextListsHelper::PushListOnStack( const OUString
& sListId
,
309 const OUString
& sListStyleName
)
311 if ( mpListStack
== 0 )
313 mpListStack
= new tStackForLists();
315 ::std::pair
< OUString
, OUString
>
316 aListData( sListId
, sListStyleName
);
317 mpListStack
->push_back( aListData
);
319 void XMLTextListsHelper::PopListFromStack()
321 if ( mpListStack
!= 0 &&
322 mpListStack
->size() > 0 )
324 mpListStack
->pop_back();
328 bool XMLTextListsHelper::EqualsToTopListStyleOnStack( const OUString
& sListId
) const
330 return mpListStack
!= 0 && sListId
== mpListStack
->back().second
;
334 XMLTextListsHelper::GetNumberedParagraphListId(
335 const sal_uInt16 i_Level
,
336 const OUString
& i_StyleName
)
338 if (i_StyleName
.isEmpty()) {
339 SAL_INFO("xmloff.text", "invalid numbered-paragraph: no style-name");
341 if (!i_StyleName
.isEmpty()
342 && (i_Level
< mLastNumberedParagraphs
.size())
343 && (mLastNumberedParagraphs
[i_Level
].first
== i_StyleName
) )
345 assert(!mLastNumberedParagraphs
[i_Level
].second
.isEmpty() &&
346 "internal error: numbered-paragraph style-name but no list-id?");
347 return mLastNumberedParagraphs
[i_Level
].second
;
349 return GenerateNewListId();
354 ClampLevel(uno::Reference
<container::XIndexReplace
> const& i_xNumRules
,
355 sal_Int16
& io_rLevel
)
357 assert(i_xNumRules
.is());
358 if (i_xNumRules
.is()) {
359 const sal_Int32
nLevelCount( i_xNumRules
->getCount() );
360 if ( io_rLevel
>= nLevelCount
) {
361 io_rLevel
= sal::static_int_cast
< sal_Int16
>(nLevelCount
-1);
366 uno::Reference
<container::XIndexReplace
>
367 XMLTextListsHelper::EnsureNumberedParagraph(
368 SvXMLImport
& i_rImport
,
369 const OUString
& i_ListId
,
370 sal_Int16
& io_rLevel
, const OUString
& i_StyleName
)
372 assert(!i_ListId
.isEmpty());
373 assert(io_rLevel
>= 0);
374 NumParaList_t
& rNPList( mNPLists
[i_ListId
] );
375 const OUString none
; // default
376 if ( rNPList
.empty() ) {
377 // create default list style for top level
379 rNPList
.push_back(::std::make_pair(none
,
380 MakeNumRule(i_rImport
, 0, none
, none
, lev
) ));
382 // create num rule first because this might clamp the level...
383 uno::Reference
<container::XIndexReplace
> xNumRules
;
384 if ((0 == io_rLevel
) || rNPList
.empty() || !i_StyleName
.isEmpty()) {
385 // no parent to inherit from, or explicit style given => new numrules!
386 // index of parent: level - 1, but maybe that does not exist
387 const size_t parent( std::min(static_cast<size_t>(io_rLevel
),
388 rNPList
.size()) - 1 );
389 xNumRules
= MakeNumRule(i_rImport
,
390 io_rLevel
> 0 ? rNPList
[parent
].second
: 0,
391 io_rLevel
> 0 ? rNPList
[parent
].first
: none
,
392 i_StyleName
, io_rLevel
);
394 // no style given, but has a parent => reuse parent numrules!
395 ClampLevel(rNPList
.back().second
, io_rLevel
);
397 if (static_cast<sal_uInt16
>(io_rLevel
) + 1U > rNPList
.size()) {
398 // new level: need to enlarge
399 for (size_t i
= rNPList
.size();
400 i
< static_cast<size_t>(io_rLevel
); ++i
)
402 NumParaList_t::value_type
const rule(rNPList
.back());
403 rNPList
.push_back(rule
);
405 NumParaList_t::value_type
const rule(rNPList
.back());
406 rNPList
.push_back(xNumRules
.is()
407 ? ::std::make_pair(i_StyleName
, xNumRules
)
410 // old level: no need to enlarge; possibly shrink
411 if (xNumRules
.is()) {
412 rNPList
[io_rLevel
] = std::make_pair(i_StyleName
, xNumRules
);
414 if (static_cast<sal_uInt16
>(io_rLevel
) + 1U < rNPList
.size()) {
415 rNPList
.erase(rNPList
.begin() + io_rLevel
+ 1, rNPList
.end());
418 // remember the list id
419 if (mLastNumberedParagraphs
.size() <= static_cast<size_t>(io_rLevel
)) {
420 mLastNumberedParagraphs
.resize(io_rLevel
+1);
422 mLastNumberedParagraphs
[io_rLevel
] = std::make_pair(i_StyleName
, i_ListId
);
423 return rNPList
.back().second
;
426 /** extracted from the XMLTextListBlockContext constructor */
427 uno::Reference
<container::XIndexReplace
>
428 XMLTextListsHelper::MakeNumRule(
429 SvXMLImport
& i_rImport
,
430 const uno::Reference
<container::XIndexReplace
>& i_rNumRule
,
431 const OUString
& i_ParentStyleName
,
432 const OUString
& i_StyleName
,
433 sal_Int16
& io_rLevel
,
434 bool* o_pRestartNumbering
,
435 bool* io_pSetDefaults
)
437 static const char s_NumberingRules
[] = "NumberingRules";
438 uno::Reference
<container::XIndexReplace
> xNumRules(i_rNumRule
);
439 if ( !i_StyleName
.isEmpty() && i_StyleName
!= i_ParentStyleName
)
441 const OUString
sDisplayStyleName(
442 i_rImport
.GetStyleDisplayName( XML_STYLE_FAMILY_TEXT_LIST
,
444 const uno::Reference
< container::XNameContainer
>& rNumStyles(
445 i_rImport
.GetTextImport()->GetNumberingStyles() );
446 if( rNumStyles
.is() && rNumStyles
->hasByName( sDisplayStyleName
) )
448 uno::Reference
< style::XStyle
> xStyle
;
449 uno::Any any
= rNumStyles
->getByName( sDisplayStyleName
);
452 uno::Reference
< beans::XPropertySet
> xPropSet( xStyle
,
454 any
= xPropSet
->getPropertyValue(s_NumberingRules
);
459 const SvxXMLListStyleContext
*pListStyle(
460 i_rImport
.GetTextImport()->FindAutoListStyle( i_StyleName
) );
463 xNumRules
= pListStyle
->GetNumRules();
464 if( !xNumRules
.is() )
466 pListStyle
->CreateAndInsertAuto();
467 xNumRules
= pListStyle
->GetNumRules();
473 bool bSetDefaults(io_pSetDefaults
&& *io_pSetDefaults
);
474 if ( !xNumRules
.is() )
476 // If no style name has been specified for this style and for any
477 // parent or if no num rule with the specified name exists,
481 SvxXMLListStyleContext::CreateNumRule( i_rImport
.GetModel() );
482 assert(xNumRules
.is());
483 if ( !xNumRules
.is() )
486 // Because it is a new num rule, numbering must not be restarted.
487 if (o_pRestartNumbering
) *o_pRestartNumbering
= false;
489 if (io_pSetDefaults
) *io_pSetDefaults
= bSetDefaults
;
492 ClampLevel(xNumRules
, io_rLevel
);
496 // Because there is no list style sheet for this style, a default
497 // format must be set for any level of this num rule.
498 SvxXMLListStyleContext::SetDefaultStyle( xNumRules
, io_rLevel
,
505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */