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>
23 #include <tools/debug.hxx>
24 #include <tools/date.hxx>
25 #include <tools/time.hxx>
27 #include <xmloff/txtimp.hxx>
28 #include <xmloff/xmlimp.hxx>
29 #include <xmloff/xmlnumi.hxx>
31 #include <com/sun/star/style/XStyle.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include "XMLTextListItemContext.hxx"
34 #include "XMLTextListBlockContext.hxx"
35 #include "txtparai.hxx"
38 using namespace ::com::sun::star
;
41 XMLTextListsHelper::XMLTextListsHelper()
42 : mpProcessedLists( 0 ),
43 msLastProcessedListId(),
44 msListStyleOfLastProcessedList(),
45 // Inconsistent behavior regarding lists (#i92811#)
46 mpMapListIdToListStyleDefaultListId( 0 ),
47 mpContinuingLists( 0 ),
52 XMLTextListsHelper::~XMLTextListsHelper()
54 if ( mpProcessedLists
)
56 mpProcessedLists
->clear();
57 delete mpProcessedLists
;
59 // Inconsistent behavior regarding lists (#i92811#)#
60 if ( mpMapListIdToListStyleDefaultListId
)
62 mpMapListIdToListStyleDefaultListId
->clear();
63 delete mpMapListIdToListStyleDefaultListId
;
65 if ( mpContinuingLists
)
67 mpContinuingLists
->clear();
68 delete mpContinuingLists
;
77 void XMLTextListsHelper::PushListContext(
78 XMLTextListBlockContext
*i_pListBlock
)
80 // fprintf(stderr, "PushListContext\n");
81 mListStack
.push(::boost::make_tuple(i_pListBlock
,
82 static_cast<XMLTextListItemContext
*>(0),
83 static_cast<XMLNumberedParaContext
*>(0)));
86 void XMLTextListsHelper::PushListContext(
87 XMLNumberedParaContext
*i_pNumberedParagraph
)
89 // fprintf(stderr, "PushListContext(NP)\n");
90 mListStack
.push(::boost::make_tuple(
91 static_cast<XMLTextListBlockContext
*>(0),
92 static_cast<XMLTextListItemContext
*>(0), i_pNumberedParagraph
));
95 void XMLTextListsHelper::PopListContext()
97 OSL_ENSURE(mListStack
.size(),
98 "internal error: PopListContext: mListStack empty");
99 // fprintf(stderr, "PopListContext\n");
100 if ( !mListStack
.empty())
104 void XMLTextListsHelper::ListContextTop(
105 XMLTextListBlockContext
*& o_pListBlockContext
,
106 XMLTextListItemContext
*& o_pListItemContext
,
107 XMLNumberedParaContext
*& o_pNumberedParagraphContext
)
109 if ( !mListStack
.empty() ) {
110 o_pListBlockContext
=
111 static_cast<XMLTextListBlockContext
*>(&mListStack
.top().get
<0>());
113 static_cast<XMLTextListItemContext
*>(&mListStack
.top().get
<1>());
114 o_pNumberedParagraphContext
=
115 static_cast<XMLNumberedParaContext
*>(&mListStack
.top().get
<2>());
119 void XMLTextListsHelper::SetListItem( XMLTextListItemContext
*i_pListItem
)
121 // may be cleared by ListBlockContext for upper list...
123 OSL_ENSURE(mListStack
.size(),
124 "internal error: SetListItem: mListStack empty");
125 OSL_ENSURE(mListStack
.top().get
<0>(),
126 "internal error: SetListItem: mListStack has no ListBlock");
127 OSL_ENSURE(!mListStack
.top().get
<1>(),
128 "error: SetListItem: list item already exists");
130 if ( !mListStack
.empty() ) {
131 mListStack
.top().get
<1>() = i_pListItem
;
135 // Handling for parameter <sListStyleDefaultListId> (#i92811#)
136 void XMLTextListsHelper::KeepListAsProcessed( OUString sListId
,
137 OUString sListStyleName
,
138 OUString sContinueListId
,
139 OUString sListStyleDefaultListId
)
141 if ( IsListProcessed( sListId
) )
144 "<XMLTextListsHelper::KeepListAsProcessed(..)> - list id already added" );
148 if ( mpProcessedLists
== 0 )
150 mpProcessedLists
= new tMapForLists();
153 ::std::pair
< OUString
, OUString
>
154 aListData( sListStyleName
, sContinueListId
);
155 (*mpProcessedLists
)[ sListId
] = aListData
;
157 msLastProcessedListId
= sListId
;
158 msListStyleOfLastProcessedList
= sListStyleName
;
160 // Inconsistent behavior regarding lists (#i92811#)
161 if ( !sListStyleDefaultListId
.isEmpty())
163 if ( mpMapListIdToListStyleDefaultListId
== 0 )
165 mpMapListIdToListStyleDefaultListId
= new tMapForLists();
168 if ( mpMapListIdToListStyleDefaultListId
->find( sListStyleName
) ==
169 mpMapListIdToListStyleDefaultListId
->end() )
171 ::std::pair
< OUString
, OUString
>
172 aListIdMapData( sListId
, sListStyleDefaultListId
);
173 (*mpMapListIdToListStyleDefaultListId
)[ sListStyleName
] =
179 sal_Bool
XMLTextListsHelper::IsListProcessed( const OUString sListId
) const
181 if ( mpProcessedLists
== 0 )
186 return mpProcessedLists
->find( sListId
) != mpProcessedLists
->end();
189 OUString
XMLTextListsHelper::GetListStyleOfProcessedList(
190 const OUString sListId
) const
192 if ( mpProcessedLists
!= 0 )
194 tMapForLists::const_iterator aIter
= mpProcessedLists
->find( sListId
);
195 if ( aIter
!= mpProcessedLists
->end() )
197 return (*aIter
).second
.first
;
204 OUString
XMLTextListsHelper::GetContinueListIdOfProcessedList(
205 const OUString sListId
) const
207 if ( mpProcessedLists
!= 0 )
209 tMapForLists::const_iterator aIter
= mpProcessedLists
->find( sListId
);
210 if ( aIter
!= mpProcessedLists
->end() )
212 return (*aIter
).second
.second
;
219 const OUString
& XMLTextListsHelper::GetLastProcessedListId() const
221 return msLastProcessedListId
;
224 const OUString
& XMLTextListsHelper::GetListStyleOfLastProcessedList() const
226 return msListStyleOfLastProcessedList
;
229 OUString
XMLTextListsHelper::GenerateNewListId() const
231 // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
232 OUString
sTmpStr( "list" );
233 sal_Int64 n
= Time( Time::SYSTEM
).GetTime();
234 n
+= Date( Date::SYSTEM
).GetDate();
236 // Value of xml:id in element <text:list> has to be a valid ID type (#i92478#)
237 sTmpStr
+= OUString::valueOf( n
);
239 OUString
sNewListId( sTmpStr
);
240 if ( mpProcessedLists
!= 0 )
243 while ( mpProcessedLists
->find( sNewListId
) != mpProcessedLists
->end() )
246 sNewListId
= sTmpStr
;
247 sNewListId
+= OUString::valueOf( nHitCount
);
254 // Provide list id for a certain list block for import (#i92811#)
255 OUString
XMLTextListsHelper::GetListIdForListBlock( XMLTextListBlockContext
& rListBlock
)
257 OUString
sListBlockListId( rListBlock
.GetContinueListId() );
258 if ( sListBlockListId
.isEmpty() )
260 sListBlockListId
= rListBlock
.GetListId();
263 if ( mpMapListIdToListStyleDefaultListId
!= 0 )
265 if ( !sListBlockListId
.isEmpty() )
267 const OUString sListStyleName
=
268 GetListStyleOfProcessedList( sListBlockListId
);
270 tMapForLists::const_iterator aIter
=
271 mpMapListIdToListStyleDefaultListId
->find( sListStyleName
);
272 if ( aIter
!= mpMapListIdToListStyleDefaultListId
->end() )
274 if ( (*aIter
).second
.first
== sListBlockListId
)
276 sListBlockListId
= (*aIter
).second
.second
;
282 return sListBlockListId
;
285 void XMLTextListsHelper::StoreLastContinuingList( OUString sListId
,
286 OUString sContinuingListId
)
288 if ( mpContinuingLists
== 0 )
290 mpContinuingLists
= new tMapForContinuingLists();
293 (*mpContinuingLists
)[ sListId
] = sContinuingListId
;
296 OUString
XMLTextListsHelper::GetLastContinuingListId(
297 OUString sListId
) const
299 if ( mpContinuingLists
!= 0)
301 tMapForContinuingLists::const_iterator aIter
=
302 mpContinuingLists
->find( sListId
);
303 if ( aIter
!= mpContinuingLists
->end() )
305 return (*aIter
).second
;
312 void XMLTextListsHelper::PushListOnStack( OUString sListId
,
313 OUString sListStyleName
)
315 if ( mpListStack
== 0 )
317 mpListStack
= new tStackForLists();
319 ::std::pair
< OUString
, OUString
>
320 aListData( sListId
, sListStyleName
);
321 mpListStack
->push_back( aListData
);
323 void XMLTextListsHelper::PopListFromStack()
325 if ( mpListStack
!= 0 &&
326 mpListStack
->size() > 0 )
328 mpListStack
->pop_back();
332 sal_Bool
XMLTextListsHelper::EqualsToTopListStyleOnStack( const OUString sListId
) const
334 return mpListStack
!= 0
335 ? sListId
== mpListStack
->back().second
340 XMLTextListsHelper::GetNumberedParagraphListId(
341 const sal_uInt16 i_Level
,
342 const OUString i_StyleName
)
344 if (i_StyleName
.isEmpty()) {
345 OSL_FAIL("invalid numbered-paragraph: no style-name");
347 if (!i_StyleName
.isEmpty()
348 && (i_Level
< mLastNumberedParagraphs
.size())
349 && (mLastNumberedParagraphs
[i_Level
].first
== i_StyleName
) )
351 OSL_ENSURE(!mLastNumberedParagraphs
[i_Level
].second
.isEmpty(),
352 "internal error: numbered-paragraph style-name but no list-id?");
353 return mLastNumberedParagraphs
[i_Level
].second
;
355 return GenerateNewListId();
360 ClampLevel(uno::Reference
<container::XIndexReplace
> const& i_xNumRules
,
361 sal_Int16
& io_rLevel
)
363 OSL_ENSURE(i_xNumRules
.is(), "internal error: ClampLevel: NumRules null");
364 if (i_xNumRules
.is()) {
365 const sal_Int32
nLevelCount( i_xNumRules
->getCount() );
366 if ( io_rLevel
>= nLevelCount
) {
367 io_rLevel
= sal::static_int_cast
< sal_Int16
>(nLevelCount
-1);
372 uno::Reference
<container::XIndexReplace
>
373 XMLTextListsHelper::EnsureNumberedParagraph(
374 SvXMLImport
& i_rImport
,
375 const OUString i_ListId
,
376 sal_Int16
& io_rLevel
, const OUString i_StyleName
)
378 OSL_ENSURE(!i_ListId
.isEmpty(), "invalid ListId");
379 OSL_ENSURE(io_rLevel
>= 0, "invalid Level");
380 NumParaList_t
& rNPList( mNPLists
[i_ListId
] );
381 const OUString none
; // default
382 if ( rNPList
.empty() ) {
383 // create default list style for top level
385 rNPList
.push_back(::std::make_pair(none
,
386 MakeNumRule(i_rImport
, 0, none
, none
, lev
) ));
388 // create num rule first because this might clamp the level...
389 uno::Reference
<container::XIndexReplace
> xNumRules
;
390 if ((0 == io_rLevel
) || rNPList
.empty() || !i_StyleName
.isEmpty()) {
391 // no parent to inherit from, or explicit style given => new numrules!
392 // index of parent: level - 1, but maybe that does not exist
393 const size_t parent( std::min(static_cast<size_t>(io_rLevel
),
394 rNPList
.size()) - 1 );
395 xNumRules
= MakeNumRule(i_rImport
,
396 io_rLevel
> 0 ? rNPList
[parent
].second
: 0,
397 io_rLevel
> 0 ? rNPList
[parent
].first
: none
,
398 i_StyleName
, io_rLevel
);
400 // no style given, but has a parent => reuse parent numrules!
401 ClampLevel(rNPList
.back().second
, io_rLevel
);
403 if (static_cast<sal_uInt16
>(io_rLevel
) + 1U > rNPList
.size()) {
404 // new level: need to enlarge
405 for (size_t i
= rNPList
.size();
406 i
< static_cast<size_t>(io_rLevel
); ++i
)
408 NumParaList_t::value_type
const rule(rNPList
.back());
409 rNPList
.push_back(rule
);
411 NumParaList_t::value_type
const rule(rNPList
.back());
412 rNPList
.push_back(xNumRules
.is()
413 ? ::std::make_pair(i_StyleName
, xNumRules
)
416 // old level: no need to enlarge; possibly shrink
417 if (xNumRules
.is()) {
418 rNPList
[io_rLevel
] = std::make_pair(i_StyleName
, xNumRules
);
420 if (static_cast<sal_uInt16
>(io_rLevel
) + 1U < rNPList
.size()) {
421 rNPList
.erase(rNPList
.begin() + io_rLevel
+ 1, rNPList
.end());
424 // remember the list id
425 if (mLastNumberedParagraphs
.size() <= static_cast<size_t>(io_rLevel
)) {
426 mLastNumberedParagraphs
.resize(io_rLevel
+1);
428 mLastNumberedParagraphs
[io_rLevel
] = std::make_pair(i_StyleName
, i_ListId
);
429 return rNPList
.back().second
;
432 /** extracted from the XMLTextListBlockContext constructor */
433 uno::Reference
<container::XIndexReplace
>
434 XMLTextListsHelper::MakeNumRule(
435 SvXMLImport
& i_rImport
,
436 const uno::Reference
<container::XIndexReplace
>& i_rNumRule
,
437 const OUString i_ParentStyleName
,
438 const OUString i_StyleName
,
439 sal_Int16
& io_rLevel
,
440 sal_Bool
* o_pRestartNumbering
,
441 sal_Bool
* io_pSetDefaults
)
443 static OUString
s_NumberingRules( "NumberingRules");
444 uno::Reference
<container::XIndexReplace
> xNumRules(i_rNumRule
);
445 if ( !i_StyleName
.isEmpty() && i_StyleName
!= i_ParentStyleName
)
447 const OUString
sDisplayStyleName(
448 i_rImport
.GetStyleDisplayName( XML_STYLE_FAMILY_TEXT_LIST
,
450 const uno::Reference
< container::XNameContainer
>& rNumStyles(
451 i_rImport
.GetTextImport()->GetNumberingStyles() );
452 if( rNumStyles
.is() && rNumStyles
->hasByName( sDisplayStyleName
) )
454 uno::Reference
< style::XStyle
> xStyle
;
455 uno::Any any
= rNumStyles
->getByName( sDisplayStyleName
);
458 uno::Reference
< beans::XPropertySet
> xPropSet( xStyle
,
460 any
= xPropSet
->getPropertyValue(s_NumberingRules
);
465 const SvxXMLListStyleContext
*pListStyle(
466 i_rImport
.GetTextImport()->FindAutoListStyle( i_StyleName
) );
469 xNumRules
= pListStyle
->GetNumRules();
470 if( !xNumRules
.is() )
472 pListStyle
->CreateAndInsertAuto();
473 xNumRules
= pListStyle
->GetNumRules();
479 sal_Bool
bSetDefaults(io_pSetDefaults
? *io_pSetDefaults
: sal_False
);
480 if ( !xNumRules
.is() )
482 // If no style name has been specified for this style and for any
483 // parent or if no num rule with the specified name exists,
487 SvxXMLListStyleContext::CreateNumRule( i_rImport
.GetModel() );
488 DBG_ASSERT( xNumRules
.is(), "got no numbering rule" );
489 if ( !xNumRules
.is() )
492 // Because it is a new num rule, numbering must not be restarted.
493 if (o_pRestartNumbering
) *o_pRestartNumbering
= sal_False
;
494 bSetDefaults
= sal_True
;
495 if (io_pSetDefaults
) *io_pSetDefaults
= bSetDefaults
;
498 ClampLevel(xNumRules
, io_rLevel
);
502 // Because there is no list style sheet for this style, a default
503 // format must be set for any level of this num rule.
504 SvxXMLListStyleContext::SetDefaultStyle( xNumRules
, io_rLevel
,
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */