1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "precompiled_xmloff.hxx"
25
26 #include <txtlists.hxx>
27
28 #include <tools/debug.hxx>
29 #include <tools/date.hxx>
30 #include <tools/time.hxx>
31
32 #include <xmloff/txtimp.hxx>
33 #include <xmloff/xmlimp.hxx>
34 #include <xmloff/xmlnumi.hxx>
35
36 #include <com/sun/star/style/XStyle.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include "XMLTextListItemContext.hxx"
39 #include "XMLTextListBlockContext.hxx"
40 #include "txtparai.hxx"
41
42
43 using namespace ::com::sun::star;
44
45
XMLTextListsHelper()46 XMLTextListsHelper::XMLTextListsHelper()
47 : mpProcessedLists( 0 ),
48 msLastProcessedListId(),
49 msListStyleOfLastProcessedList(),
50 // --> OD 2008-08-15 #i92811#
51 mpMapListIdToListStyleDefaultListId( 0 ),
52 // <--
53 mpContinuingLists( 0 ),
54 mpListStack( 0 )
55 {
56 }
57
~XMLTextListsHelper()58 XMLTextListsHelper::~XMLTextListsHelper()
59 {
60 if ( mpProcessedLists )
61 {
62 mpProcessedLists->clear();
63 delete mpProcessedLists;
64 }
65 // --> OD 2008-08-15 #i92811#
66 if ( mpMapListIdToListStyleDefaultListId )
67 {
68 mpMapListIdToListStyleDefaultListId->clear();
69 delete mpMapListIdToListStyleDefaultListId;
70 }
71 // <--
72 if ( mpContinuingLists )
73 {
74 mpContinuingLists->clear();
75 delete mpContinuingLists;
76 }
77 if ( mpListStack )
78 {
79 mpListStack->clear();
80 delete mpListStack;
81 }
82 }
83
PushListContext(XMLTextListBlockContext * i_pListBlock)84 void XMLTextListsHelper::PushListContext(
85 XMLTextListBlockContext *i_pListBlock)
86 {
87 // fprintf(stderr, "PushListContext\n");
88 mListStack.push(::boost::make_tuple(i_pListBlock,
89 static_cast<XMLTextListItemContext*>(0),
90 static_cast<XMLNumberedParaContext*>(0)));
91 }
92
PushListContext(XMLNumberedParaContext * i_pNumberedParagraph)93 void XMLTextListsHelper::PushListContext(
94 XMLNumberedParaContext *i_pNumberedParagraph)
95 {
96 // fprintf(stderr, "PushListContext(NP)\n");
97 mListStack.push(::boost::make_tuple(
98 static_cast<XMLTextListBlockContext*>(0),
99 static_cast<XMLTextListItemContext*>(0), i_pNumberedParagraph));
100 }
101
PopListContext()102 void XMLTextListsHelper::PopListContext()
103 {
104 OSL_ENSURE(mListStack.size(),
105 "internal error: PopListContext: mListStack empty");
106 // fprintf(stderr, "PopListContext\n");
107 if ( !mListStack.empty())
108 mListStack.pop();
109 }
110
ListContextTop(XMLTextListBlockContext * & o_pListBlockContext,XMLTextListItemContext * & o_pListItemContext,XMLNumberedParaContext * & o_pNumberedParagraphContext)111 void XMLTextListsHelper::ListContextTop(
112 XMLTextListBlockContext*& o_pListBlockContext,
113 XMLTextListItemContext*& o_pListItemContext,
114 XMLNumberedParaContext*& o_pNumberedParagraphContext )
115 {
116 if ( !mListStack.empty() ) {
117 o_pListBlockContext =
118 static_cast<XMLTextListBlockContext*>(&mListStack.top().get<0>());
119 o_pListItemContext =
120 static_cast<XMLTextListItemContext *>(&mListStack.top().get<1>());
121 o_pNumberedParagraphContext =
122 static_cast<XMLNumberedParaContext *>(&mListStack.top().get<2>());
123 }
124 }
125
SetListItem(XMLTextListItemContext * i_pListItem)126 void XMLTextListsHelper::SetListItem( XMLTextListItemContext *i_pListItem )
127 {
128 // may be cleared by ListBlockContext for upper list...
129 if (i_pListItem) {
130 OSL_ENSURE(mListStack.size(),
131 "internal error: SetListItem: mListStack empty");
132 OSL_ENSURE(mListStack.top().get<0>(),
133 "internal error: SetListItem: mListStack has no ListBlock");
134 OSL_ENSURE(!mListStack.top().get<1>(),
135 "error: SetListItem: list item already exists");
136 }
137 if ( !mListStack.empty() ) {
138 mListStack.top().get<1>() = i_pListItem;
139 }
140 }
141
142 // --> OD 2008-08-15 #i92811# - handling for parameter <sListStyleDefaultListId>
KeepListAsProcessed(::rtl::OUString sListId,::rtl::OUString sListStyleName,::rtl::OUString sContinueListId,::rtl::OUString sListStyleDefaultListId)143 void XMLTextListsHelper::KeepListAsProcessed( ::rtl::OUString sListId,
144 ::rtl::OUString sListStyleName,
145 ::rtl::OUString sContinueListId,
146 ::rtl::OUString sListStyleDefaultListId )
147 {
148 if ( IsListProcessed( sListId ) )
149 {
150 DBG_ASSERT( false,
151 "<XMLTextListsHelper::KeepListAsProcessed(..)> - list id already added" );
152 return;
153 }
154
155 if ( mpProcessedLists == 0 )
156 {
157 mpProcessedLists = new tMapForLists();
158 }
159
160 ::std::pair< ::rtl::OUString, ::rtl::OUString >
161 aListData( sListStyleName, sContinueListId );
162 (*mpProcessedLists)[ sListId ] = aListData;
163
164 msLastProcessedListId = sListId;
165 msListStyleOfLastProcessedList = sListStyleName;
166
167 // --> OD 2008-08-15 #i92811#
168 if ( sListStyleDefaultListId.getLength() != 0 )
169 {
170 if ( mpMapListIdToListStyleDefaultListId == 0 )
171 {
172 mpMapListIdToListStyleDefaultListId = new tMapForLists();
173 }
174
175 if ( mpMapListIdToListStyleDefaultListId->find( sListStyleName ) ==
176 mpMapListIdToListStyleDefaultListId->end() )
177 {
178 ::std::pair< ::rtl::OUString, ::rtl::OUString >
179 aListIdMapData( sListId, sListStyleDefaultListId );
180 (*mpMapListIdToListStyleDefaultListId)[ sListStyleName ] =
181 aListIdMapData;
182 }
183 }
184 // <--
185 }
186
IsListProcessed(const::rtl::OUString sListId) const187 sal_Bool XMLTextListsHelper::IsListProcessed( const ::rtl::OUString sListId ) const
188 {
189 if ( mpProcessedLists == 0 )
190 {
191 return sal_False;
192 }
193
194 return mpProcessedLists->find( sListId ) != mpProcessedLists->end();
195 }
196
GetListStyleOfProcessedList(const::rtl::OUString sListId) const197 ::rtl::OUString XMLTextListsHelper::GetListStyleOfProcessedList(
198 const ::rtl::OUString sListId ) const
199 {
200 if ( mpProcessedLists != 0 )
201 {
202 tMapForLists::const_iterator aIter = mpProcessedLists->find( sListId );
203 if ( aIter != mpProcessedLists->end() )
204 {
205 return (*aIter).second.first;
206 }
207 }
208
209 return ::rtl::OUString();
210 }
211
GetContinueListIdOfProcessedList(const::rtl::OUString sListId) const212 ::rtl::OUString XMLTextListsHelper::GetContinueListIdOfProcessedList(
213 const ::rtl::OUString sListId ) const
214 {
215 if ( mpProcessedLists != 0 )
216 {
217 tMapForLists::const_iterator aIter = mpProcessedLists->find( sListId );
218 if ( aIter != mpProcessedLists->end() )
219 {
220 return (*aIter).second.second;
221 }
222 }
223
224 return ::rtl::OUString();
225 }
226
GetLastProcessedListId() const227 const ::rtl::OUString& XMLTextListsHelper::GetLastProcessedListId() const
228 {
229 return msLastProcessedListId;
230 }
231
GetListStyleOfLastProcessedList() const232 const ::rtl::OUString& XMLTextListsHelper::GetListStyleOfLastProcessedList() const
233 {
234 return msListStyleOfLastProcessedList;
235 }
236
GenerateNewListId() const237 ::rtl::OUString XMLTextListsHelper::GenerateNewListId() const
238 {
239 // --> OD 2008-08-06 #i92478#
240 ::rtl::OUString sTmpStr( ::rtl::OUString::createFromAscii( "list" ) );
241 // <--
242 sal_Int64 n = Time().GetTime();
243 n += Date().GetDate();
244 n += rand();
245 // --> OD 2008-08-06 #i92478#
246 sTmpStr += ::rtl::OUString::valueOf( n );
247 // <--
248
249 long nHitCount = 0;
250 ::rtl::OUString sNewListId( sTmpStr );
251 if ( mpProcessedLists != 0 )
252 {
253 while ( mpProcessedLists->find( sNewListId ) != mpProcessedLists->end() )
254 {
255 ++nHitCount;
256 sNewListId = sTmpStr;
257 sNewListId += ::rtl::OUString::valueOf( nHitCount );
258 }
259 }
260
261 return sNewListId;
262 }
263
264 // --> OD 2008-08-15 #i92811#
265 // provide list id for a certain list block for import
GetListIdForListBlock(XMLTextListBlockContext & rListBlock)266 ::rtl::OUString XMLTextListsHelper::GetListIdForListBlock( XMLTextListBlockContext& rListBlock )
267 {
268 ::rtl::OUString sListBlockListId( rListBlock.GetContinueListId() );
269 if ( sListBlockListId.getLength() == 0 )
270 {
271 sListBlockListId = rListBlock.GetListId();
272 }
273
274 if ( mpMapListIdToListStyleDefaultListId != 0 )
275 {
276 if ( sListBlockListId.getLength() != 0 )
277 {
278 const ::rtl::OUString sListStyleName =
279 GetListStyleOfProcessedList( sListBlockListId );
280
281 tMapForLists::const_iterator aIter =
282 mpMapListIdToListStyleDefaultListId->find( sListStyleName );
283 if ( aIter != mpMapListIdToListStyleDefaultListId->end() )
284 {
285 if ( (*aIter).second.first == sListBlockListId )
286 {
287 sListBlockListId = (*aIter).second.second;
288 }
289 }
290 }
291 }
292
293 return sListBlockListId;
294 }
295 // <--
296
StoreLastContinuingList(::rtl::OUString sListId,::rtl::OUString sContinuingListId)297 void XMLTextListsHelper::StoreLastContinuingList( ::rtl::OUString sListId,
298 ::rtl::OUString sContinuingListId )
299 {
300 if ( mpContinuingLists == 0 )
301 {
302 mpContinuingLists = new tMapForContinuingLists();
303 }
304
305 (*mpContinuingLists)[ sListId ] = sContinuingListId;
306 }
307
GetLastContinuingListId(::rtl::OUString sListId) const308 ::rtl::OUString XMLTextListsHelper::GetLastContinuingListId(
309 ::rtl::OUString sListId ) const
310 {
311 if ( mpContinuingLists != 0)
312 {
313 tMapForContinuingLists::const_iterator aIter =
314 mpContinuingLists->find( sListId );
315 if ( aIter != mpContinuingLists->end() )
316 {
317 return (*aIter).second;
318 }
319 }
320
321 return sListId;
322 }
323
PushListOnStack(::rtl::OUString sListId,::rtl::OUString sListStyleName)324 void XMLTextListsHelper::PushListOnStack( ::rtl::OUString sListId,
325 ::rtl::OUString sListStyleName )
326 {
327 if ( mpListStack == 0 )
328 {
329 mpListStack = new tStackForLists();
330 }
331 ::std::pair< ::rtl::OUString, ::rtl::OUString >
332 aListData( sListId, sListStyleName );
333 mpListStack->push_back( aListData );
334 }
PopListFromStack()335 void XMLTextListsHelper::PopListFromStack()
336 {
337 if ( mpListStack != 0 &&
338 mpListStack->size() > 0 )
339 {
340 mpListStack->pop_back();
341 }
342 }
343
EqualsToTopListStyleOnStack(const::rtl::OUString sListId) const344 sal_Bool XMLTextListsHelper::EqualsToTopListStyleOnStack( const ::rtl::OUString sListId ) const
345 {
346 return mpListStack != 0
347 ? sListId == mpListStack->back().second
348 : sal_False;
349 }
350
351 ::rtl::OUString
GetNumberedParagraphListId(const sal_uInt16 i_Level,const::rtl::OUString i_StyleName)352 XMLTextListsHelper::GetNumberedParagraphListId(
353 const sal_uInt16 i_Level,
354 const ::rtl::OUString i_StyleName)
355 {
356 if (!i_StyleName.getLength()) {
357 OSL_ENSURE(false, "invalid numbered-paragraph: no style-name");
358 }
359 if (i_StyleName.getLength()
360 && (i_Level < mLastNumberedParagraphs.size())
361 && (mLastNumberedParagraphs[i_Level].first == i_StyleName) )
362 {
363 OSL_ENSURE(mLastNumberedParagraphs[i_Level].second.getLength(),
364 "internal error: numbered-paragraph style-name but no list-id?");
365 return mLastNumberedParagraphs[i_Level].second;
366 } else {
367 return GenerateNewListId();
368 }
369 }
370
371 static void
ClampLevel(uno::Reference<container::XIndexReplace> const & i_xNumRules,sal_Int16 & io_rLevel)372 ClampLevel(uno::Reference<container::XIndexReplace> const& i_xNumRules,
373 sal_Int16 & io_rLevel)
374 {
375 OSL_ENSURE(i_xNumRules.is(), "internal error: ClampLevel: NumRules null");
376 if (i_xNumRules.is()) {
377 const sal_Int32 nLevelCount( i_xNumRules->getCount() );
378 if ( io_rLevel >= nLevelCount ) {
379 io_rLevel = sal::static_int_cast< sal_Int16 >(nLevelCount-1);
380 }
381 }
382 }
383
384 uno::Reference<container::XIndexReplace>
EnsureNumberedParagraph(SvXMLImport & i_rImport,const::rtl::OUString i_ListId,sal_Int16 & io_rLevel,const::rtl::OUString i_StyleName)385 XMLTextListsHelper::EnsureNumberedParagraph(
386 SvXMLImport & i_rImport,
387 const ::rtl::OUString i_ListId,
388 sal_Int16 & io_rLevel, const ::rtl::OUString i_StyleName)
389 {
390 OSL_ENSURE(i_ListId.getLength(), "inavlid ListId");
391 OSL_ENSURE(io_rLevel >= 0, "inavlid Level");
392 NumParaList_t & rNPList( mNPLists[i_ListId] );
393 const ::rtl::OUString none; // default
394 if ( rNPList.empty() && (0 != io_rLevel)) {
395 // create default list style for top level
396 sal_Int16 lev(0);
397 rNPList.push_back(::std::make_pair(none,
398 MakeNumRule(i_rImport, 0, none, none, lev) ));
399 }
400 // create num rule first because this might clamp the level...
401 uno::Reference<container::XIndexReplace> xNumRules;
402 if ((0 == io_rLevel) || rNPList.empty() || i_StyleName.getLength()) {
403 // no parent to inherit from, or explicit style given => new numrules!
404 // index of parent: level - 1, but maybe that does not exist
405 const size_t parent( std::min(static_cast<size_t>(io_rLevel),
406 rNPList.size()) - 1 );
407 xNumRules = MakeNumRule(i_rImport,
408 io_rLevel > 0 ? rNPList[parent].second : 0,
409 io_rLevel > 0 ? rNPList[parent].first : none,
410 i_StyleName, io_rLevel);
411 } else {
412 // no style given, but has a parent => reuse parent numrules!
413 ClampLevel(rNPList.back().second, io_rLevel);
414 }
415 if (static_cast<sal_uInt16>(io_rLevel) + 1U > rNPList.size()) {
416 // new level: need to enlarge
417 for (size_t i = rNPList.size();
418 i < static_cast<size_t>(io_rLevel); ++i) {
419 rNPList.push_back(rNPList.back());
420 }
421 rNPList.push_back(xNumRules.is()
422 ? ::std::make_pair(i_StyleName, xNumRules)
423 : rNPList.back());
424 } else {
425 // old level: no need to enlarge; possibly shrink
426 if (xNumRules.is()) {
427 rNPList[io_rLevel] = std::make_pair(i_StyleName, xNumRules);
428 }
429 if (static_cast<sal_uInt16>(io_rLevel) + 1U < rNPList.size()) {
430 rNPList.erase(rNPList.begin() + io_rLevel + 1, rNPList.end());
431 }
432 }
433 // remember the list id
434 if (mLastNumberedParagraphs.size() <= static_cast<size_t>(io_rLevel)) {
435 mLastNumberedParagraphs.resize(io_rLevel+1);
436 }
437 mLastNumberedParagraphs[io_rLevel] = std::make_pair(i_StyleName, i_ListId);
438 return rNPList.back().second;
439 }
440
441 /** extracted from the XMLTextListBlockContext constructor */
442 uno::Reference<container::XIndexReplace>
MakeNumRule(SvXMLImport & i_rImport,const uno::Reference<container::XIndexReplace> & i_rNumRule,const::rtl::OUString i_ParentStyleName,const::rtl::OUString i_StyleName,sal_Int16 & io_rLevel,sal_Bool * o_pRestartNumbering,sal_Bool * io_pSetDefaults)443 XMLTextListsHelper::MakeNumRule(
444 SvXMLImport & i_rImport,
445 const uno::Reference<container::XIndexReplace>& i_rNumRule,
446 const ::rtl::OUString i_ParentStyleName,
447 const ::rtl::OUString i_StyleName,
448 sal_Int16 & io_rLevel,
449 sal_Bool* o_pRestartNumbering,
450 sal_Bool* io_pSetDefaults)
451 {
452 static ::rtl::OUString s_NumberingRules(
453 RTL_CONSTASCII_USTRINGPARAM("NumberingRules"));
454 uno::Reference<container::XIndexReplace> xNumRules(i_rNumRule);
455 if ( i_StyleName.getLength() &&
456 i_StyleName != i_ParentStyleName )
457 {
458 const ::rtl::OUString sDisplayStyleName(
459 i_rImport.GetStyleDisplayName( XML_STYLE_FAMILY_TEXT_LIST,
460 i_StyleName) );
461 const uno::Reference < container::XNameContainer >& rNumStyles(
462 i_rImport.GetTextImport()->GetNumberingStyles() );
463 if( rNumStyles.is() && rNumStyles->hasByName( sDisplayStyleName ) )
464 {
465 uno::Reference < style::XStyle > xStyle;
466 uno::Any any = rNumStyles->getByName( sDisplayStyleName );
467 any >>= xStyle;
468
469 // --> OD 2008-05-07 #refactorlists# - no longer needed
470 // // If the style has not been used, the restart numbering has
471 // // to be set never.
472 // if ( mbRestartNumbering && !xStyle->isInUse() )
473 // {
474 // mbRestartNumbering = sal_False;
475 // }
476 // <--
477
478 uno::Reference< beans::XPropertySet > xPropSet( xStyle,
479 uno::UNO_QUERY );
480 any = xPropSet->getPropertyValue(s_NumberingRules);
481 any >>= xNumRules;
482 }
483 else
484 {
485 const SvxXMLListStyleContext *pListStyle(
486 i_rImport.GetTextImport()->FindAutoListStyle( i_StyleName ) );
487 if( pListStyle )
488 {
489 xNumRules = pListStyle->GetNumRules();
490 // --> OD 2008-05-07 #refactorlists# - no longer needed
491 // sal_Bool bUsed = mxNumRules.is();
492 // <--
493 if( !xNumRules.is() )
494 {
495 pListStyle->CreateAndInsertAuto();
496 xNumRules = pListStyle->GetNumRules();
497 }
498 // --> OD 2008-05-07 #refactorlists# - no longer needed
499 // if( mbRestartNumbering && !bUsed )
500 // mbRestartNumbering = sal_False;
501 // <--
502 }
503 }
504 }
505
506 sal_Bool bSetDefaults(io_pSetDefaults ? *io_pSetDefaults : sal_False);
507 if ( !xNumRules.is() )
508 {
509 // If no style name has been specified for this style and for any
510 // parent or if no num rule with the specified name exists,
511 // create a new one.
512
513 xNumRules =
514 SvxXMLListStyleContext::CreateNumRule( i_rImport.GetModel() );
515 DBG_ASSERT( xNumRules.is(), "got no numbering rule" );
516 if ( !xNumRules.is() )
517 return xNumRules;
518
519 // Because it is a new num rule, numbering must not be restarted.
520 if (o_pRestartNumbering) *o_pRestartNumbering = sal_False;
521 bSetDefaults = sal_True;
522 if (io_pSetDefaults) *io_pSetDefaults = bSetDefaults;
523 }
524
525 ClampLevel(xNumRules, io_rLevel);
526
527 if ( bSetDefaults )
528 {
529 // Because there is no list style sheet for this style, a default
530 // format must be set for any level of this num rule.
531 SvxXMLListStyleContext::SetDefaultStyle( xNumRules, io_rLevel,
532 sal_False );
533 }
534
535 return xNumRules;
536 }
537
538