1 /* AbiSource Program Utilities
2 *
3 * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho
4 * <daniel.carvalho@indt.org.br>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301 USA.
20 */
21
22 // Class definition include
23 #include "ODi_ListLevelStyle.h"
24
25 // Internal includes
26 #include "ODi_ListenerStateAction.h"
27 #include "ODi_Style_Style.h"
28 #include "ODi_ListLevelStyleFormats.h"
29
30 // AbiWord includes
31 #include <pd_Document.h>
32 #include <fp_types.h>
33 #include <ut_string_class.h>
34 #include <ut_units.h>
35 #include <ut_locale.h>
36 #include <ut_std_string.h>
37
38 //External includes
39 #include <stdlib.h>
40
41
42 /**
43 * Constructor
44 */
ODi_ListLevelStyle(const char * pStateName,ODi_ElementStack & rElementStack)45 ODi_ListLevelStyle::ODi_ListLevelStyle(const char* pStateName,
46 ODi_ElementStack& rElementStack) :
47 ODi_ListenerState(pStateName, rElementStack),
48 m_pTextStyle(NULL)
49 {
50 }
51
52
53 /**
54 *
55 */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction &)56 void ODi_ListLevelStyle::startElement (const gchar* pName,
57 const gchar** ppAtts,
58 ODi_ListenerStateAction& /*rAction*/)
59 {
60 const gchar* pVal = NULL;
61
62 if (!strcmp("text:list-level-style-bullet", pName) ||
63 !strcmp("text:list-level-style-number", pName) ||
64 !strcmp("text:outline-level-style",pName) ||
65 !strcmp("text:list-level-style-image", pName)) {
66
67 UT_uint32 result = 0;
68
69 pVal = UT_getAttribute ("text:level", ppAtts);
70 if (pVal) {
71 result = sscanf(pVal, "%u", &m_levelNumber);
72 if (result != 1) {
73 m_levelNumber = 1;
74 }
75 m_level = pVal;
76 } else {
77 UT_DEBUGMSG(("ODi_ListLevelStyle::startElement: missing text:level attribute\n"));
78 }
79 bool bHeading = false;
80 if(!strcmp("text:outline-level-style",pName))
81 {
82 bHeading = true;
83 }
84 pVal = UT_getAttribute ("text:style-name", ppAtts);
85 if (pVal)
86 {
87 m_textStyleName = pVal;
88 }
89 else if(bHeading)
90 {
91 std::string sStyleName = "BaseHeading ";
92 sStyleName += m_level;
93 m_textStyleName = sStyleName;
94 xxx_UT_DEBUGMSG(("Outline List level Style name %s \n",sStyleName.utf8_str()));
95 pVal = UT_getAttribute ("style:num-format", ppAtts);
96 if(pVal && *pVal)
97 {
98 if (pVal && !strcmp(pVal, ""))
99 {
100 // We have an empty number format.
101
102 // Empty list label or "invisible" list.
103 m_abiListListDelim = "";
104 }
105
106 }
107 }
108 } else if (!strcmp("style:list-level-properties", pName) ||
109 !strcmp("style:list-level-label-alignment", pName)) {
110
111 pVal = UT_getAttribute ("text:space-before", ppAtts);
112 if (pVal) {
113 m_spaceBefore = pVal;
114 } else {
115 m_spaceBefore = "0cm";
116 }
117
118 pVal = UT_getAttribute ("text:min-label-width", ppAtts);
119 if (pVal) {
120 m_minLabelWidth = pVal;
121 } else {
122 m_minLabelWidth = "0cm";
123 }
124
125 pVal = UT_getAttribute ("text:min-label-distance", ppAtts);
126 if (pVal) {
127 m_minLabelDistance = pVal;
128 }
129
130 pVal = UT_getAttribute ("fo:text-indent", ppAtts);
131 if (pVal) {
132 m_textIndent = pVal;
133 }
134
135 pVal = UT_getAttribute ("fo:margin-left", ppAtts);
136 if (pVal) {
137 m_marginLeft = pVal;
138 }
139 }
140 }
141
142
143 /**
144 *
145 */
endElement(const gchar * pName,ODi_ListenerStateAction & rAction)146 void ODi_ListLevelStyle::endElement (const gchar* pName,
147 ODi_ListenerStateAction& rAction) {
148
149 if (!strcmp("text:list-level-style-bullet", pName) ||
150 !strcmp("text:list-level-style-number", pName) ||
151 !strcmp("text:outline-level-style",pName) ||
152 !strcmp("text:list-level-style-image", pName)) {
153
154 // We're done.
155 xxx_UT_DEBUGMSG(("Finished Level %s \n",m_textStyleName.utf8_str()));
156 rAction.popState();
157 }
158 }
159
isVisible(void) const160 bool ODi_ListLevelStyle::isVisible(void) const
161 {
162 return (m_abiListListDelim.size() > 0);
163 }
164
165 /**
166 *
167 */
setAbiListID(UT_uint32 abiListID)168 void ODi_ListLevelStyle::setAbiListID(UT_uint32 abiListID) {
169 gchar buffer[100];
170
171 sprintf(buffer, "%u", abiListID);
172 m_abiListID.assign(buffer);
173 }
174
175
176 /**
177 * Defines a <l> tag on the AbiWord document corresponding to this
178 * list level style.
179 */
defineAbiList(PD_Document * pDocument)180 void ODi_ListLevelStyle::defineAbiList(PD_Document* pDocument) {
181 const gchar* ppAttr[13];
182
183 ppAttr[0] = "id";
184 ppAttr[1] = m_abiListID.c_str();
185 ppAttr[2] = "parentid";
186 ppAttr[3] = m_abiListParentID.c_str();
187 ppAttr[4] = "type";
188 ppAttr[5] = m_abiListType.c_str();
189 ppAttr[6] = "start-value";
190 ppAttr[7] = m_abiListStartValue.c_str();
191 ppAttr[8] = "list-delim";
192 ppAttr[9] = m_abiListListDelim.c_str();
193 ppAttr[10] = "list-decimal";
194 ppAttr[11] = m_abiListListDecimal.c_str();
195 ppAttr[12] = 0;
196
197 pDocument->appendList(ppAttr);
198 }
199
200
201 /**
202 *
203 */
buildAbiPropsString()204 void ODi_ListLevelStyle::buildAbiPropsString() {
205 m_abiProperties.clear();
206 }
207
208
209 /**
210 * The AbiWord properties of the list depends on some properties already
211 * defined by the AbiWord paragraph style.
212 *
213 * @param rProps Will have the properties string appended.
214 * @param pStyle Pointer to the paragraph style used on this list paragraph.
215 */
getAbiProperties(std::string & rProps,const ODi_Style_Style * pStyle) const216 void ODi_ListLevelStyle::getAbiProperties(std::string& rProps,
217 const ODi_Style_Style* pStyle) const {
218
219 // Adds the fixed portion of the properties.
220 if (!m_abiProperties.empty()) {
221 if (!rProps.empty()) {
222 rProps += "; ";
223 }
224 rProps += m_abiProperties;
225 }
226
227 // Precedence of list styles properties:
228 //
229 // 1. The properties of the style denoted by the paragraph's style:list-style-name, overridden by
230 // 2. The properties of the paragraph's parent style, overridden by
231 // 3. The properties of the paragraph style
232
233 std::string odMarginLeft;
234 std::string odTextIndent;
235
236 // 1. The properties of the style denoted by the paragraph's style:list-style-name
237 if (pStyle != NULL && !pStyle->getListStyleName().empty())
238 {
239 if (!m_marginLeft.empty())
240 odMarginLeft = m_marginLeft;
241 if (!m_textIndent.empty())
242 odTextIndent = m_textIndent;
243 }
244
245 // 2. The properties of the paragraph's parent style
246 if (pStyle != NULL && !strcmp(pStyle->getFamily()->c_str(), "paragraph")) {
247 const ODi_Style_Style* pParentStyle = pStyle->getParent();
248 if (pParentStyle != NULL && !strcmp(pParentStyle->getFamily()->c_str(), "paragraph")) {
249 if (pStyle->getMarginLeft() && !pStyle->getMarginLeft()->empty())
250 odMarginLeft = *(pStyle->getMarginLeft());
251 if (pStyle->getTextIndent() && !pStyle->getTextIndent()->empty())
252 odTextIndent = *(pStyle->getTextIndent());
253 }
254 }
255
256 // 3. The properties of the paragraph style
257 if (pStyle != NULL && !strcmp(pStyle->getFamily()->c_str(), "paragraph")) {
258 if (pStyle->getMarginLeft() && !pStyle->getMarginLeft()->empty())
259 odMarginLeft = *(pStyle->getMarginLeft());
260 if (pStyle->getTextIndent() && !pStyle->getTextIndent()->empty())
261 odTextIndent = *(pStyle->getTextIndent());
262 }
263
264 // default to some 'sane' values if not set
265 if (odMarginLeft.empty())
266 odMarginLeft = "0.0cm";
267 if (odTextIndent.empty())
268 odTextIndent = "0.0cm";
269
270 // From the OpenDocument OASIS standard, v1.0:
271 //
272 // "The text:space-before attribute specifies the space to include before
273 // the number for all paragraphs at this level. If a paragraph has a left
274 // margin that is greater than 0, the actual position of the list label box
275 // is the left margin width plus the start indent value."
276 //
277 // AbiWord's margin-left = OpenDocument paragraph property fo:margin-left +
278 // OpenDocument text:space-before +
279 // OpenDocument text:min-label-witdh
280 //
281 // OpenDocument fo:margin-left + fo:text-indent + text:space-before = AbiWord's margin-left + text-indent.
282 //
283
284 double spaceBefore_cm;
285 double minLabelWidth_cm;
286 double marginLeft_cm;
287 double textIndent_cm;
288
289 gchar buffer[320];
290 UT_LocaleTransactor lt(LC_NUMERIC, "C");
291
292 spaceBefore_cm = UT_convertToDimension(m_spaceBefore.c_str(), DIM_CM);
293 minLabelWidth_cm = UT_convertToDimension(m_minLabelWidth.c_str(), DIM_CM);
294 marginLeft_cm = UT_convertToDimension(odMarginLeft.c_str(), DIM_CM);
295 textIndent_cm = UT_convertToDimension(odTextIndent.c_str(), DIM_CM);
296
297 double abiMarginLeft = marginLeft_cm + spaceBefore_cm + minLabelWidth_cm;
298 sprintf(buffer, "%fcm", abiMarginLeft);
299
300 if (!rProps.empty()) {
301 rProps += "; ";
302 }
303 rProps += "margin-left:";
304 rProps.append(buffer);
305
306 sprintf(buffer, "%fcm", marginLeft_cm + textIndent_cm + spaceBefore_cm - abiMarginLeft);
307 rProps += "; text-indent:";
308 rProps.append(buffer);
309 }
310
311
312 /******************************************************************************/
313
314
315 /**
316 *
317 */
ODi_Bullet_ListLevelStyle(ODi_ElementStack & rElementStack)318 ODi_Bullet_ListLevelStyle::ODi_Bullet_ListLevelStyle(ODi_ElementStack& rElementStack)
319 : ODi_ListLevelStyle("Bullet_ListLevelStyle", rElementStack)
320 {
321 // Dummy values
322 m_abiListStartValue.assign("0");
323 m_abiListListDelim.assign("%L");
324 m_abiListListDecimal.assign("NULL");
325 }
326
327
328 /**
329 *
330 */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction & rAction)331 void ODi_Bullet_ListLevelStyle::startElement(const gchar* pName,
332 const gchar** ppAtts,
333 ODi_ListenerStateAction& rAction) {
334
335 const gchar* pVal = NULL;
336 UT_UCS4String ucs4Str;
337
338 // Let the parent class do the processing common to all list types.
339 ODi_ListLevelStyle::startElement (pName, ppAtts, rAction);
340
341 if (!strcmp("text:list-level-style-bullet", pName)) {
342 pVal = UT_getAttribute ("text:bullet-char", ppAtts);
343
344 if (pVal != NULL) {
345
346 ucs4Str = pVal;
347
348 if (!ucs4Str.empty()) {
349 switch (ucs4Str[0]) {
350 case 8226: // U+2022 BULLET
351 // Bullet List
352 m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
353 break;
354
355 case 8211: // U+2013 EN DASH
356 case 8722: // U+2212 MINUS SIGN
357 // Dashed List
358 m_abiListType = UT_std_string_sprintf("%d", DASHED_LIST);
359 break;
360
361 case 9632: // U+25A0 BLACK SQUARE
362 // Square List
363 m_abiListType = UT_std_string_sprintf("%d", SQUARE_LIST);
364 break;
365
366 case 9650: // U+25B2 BLACK UP-POINTING TRIANGLE
367 // Triangle List
368 m_abiListType = UT_std_string_sprintf("%d", TRIANGLE_LIST);
369 break;
370
371 case 9830: // U+2666 BLACK DIAMOND SUIT
372 // Diamond List
373 m_abiListType = UT_std_string_sprintf("%d", DIAMOND_LIST);
374 break;
375
376 case 10035: // U+2733 EIGHT SPOKED ASTERISK
377 // Star List
378 m_abiListType = UT_std_string_sprintf("%d", STAR_LIST);
379 break;
380
381 case 10003: // U+2713 CHECK MARK
382 // Tick List
383 m_abiListType = UT_std_string_sprintf("%d", TICK_LIST);
384 break;
385
386 case 10066: // U+2752 UPPER RIGHT SHADOWED WHITE SQUARE
387 // Box List
388 m_abiListType = UT_std_string_sprintf("%d", BOX_LIST);
389 break;
390
391 case 9758: // U+261E WHITE RIGHT POINTING INDEX
392 // Hand List
393 m_abiListType = UT_std_string_sprintf("%d", HAND_LIST);
394 break;
395
396 case 9829: // U+2665 BLACK HEART SUIT
397 // Heart List
398 m_abiListType = UT_std_string_sprintf("%d", HEART_LIST);
399 break;
400
401 case 8658: // U+21D2 RIGHTWARDS DOUBLE ARROW
402 // Implies List
403 m_abiListType = UT_std_string_sprintf("%d", IMPLIES_LIST);
404 break;
405
406 default:
407 // Bullet List
408 m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
409 };
410
411 } // if (!ucs4Str.empty())
412 } else /* from if (pVal != NULL) */ {
413 // Bullet List
414 m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
415 }
416
417 } else if (!strcmp("text:list-level-style-image", pName)) {
418 // Force it into a default Bullet List
419 m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
420 }
421 }
422
423
424 /**
425 *
426 */
buildAbiPropsString()427 void ODi_Bullet_ListLevelStyle::buildAbiPropsString() {
428
429 ODi_ListLevelStyle::buildAbiPropsString();
430
431 if (!m_abiProperties.empty()) {
432 m_abiProperties += "; ";
433 }
434
435 m_abiProperties += "list-style:";
436 switch (atoi(m_abiListType.c_str())) {
437 case BULLETED_LIST:
438 m_abiProperties += "Bullet List;";
439 break;
440
441 case DASHED_LIST:
442 m_abiProperties += "Dashed List;";
443 break;
444
445 case SQUARE_LIST:
446 m_abiProperties += "Square List;";
447 break;
448
449 case TRIANGLE_LIST:
450 m_abiProperties += "Triangle List;";
451 break;
452
453 case DIAMOND_LIST:
454 m_abiProperties += "Diamond List;";
455 break;
456
457 case STAR_LIST:
458 m_abiProperties += "Star List;";
459 break;
460
461 case IMPLIES_LIST:
462 m_abiProperties += "Implies List;";
463 break;
464
465 case TICK_LIST:
466 m_abiProperties += "Tick List;";
467 break;
468
469 case BOX_LIST:
470 m_abiProperties += "Box List;";
471 break;
472
473 case HAND_LIST:
474 m_abiProperties += "Hand List;";
475 break;
476
477 case HEART_LIST:
478 m_abiProperties += "Heart List;";
479 break;
480
481 default:
482 UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
483 }
484
485 m_abiProperties += " field-font:NULL";
486 }
487
488
489 /******************************************************************************/
490
491
492 /**
493 * Constructor
494 */
ODi_Numbered_ListLevelStyle(ODi_ElementStack & rElementStack)495 ODi_Numbered_ListLevelStyle::ODi_Numbered_ListLevelStyle(ODi_ElementStack& rElementStack)
496 : ODi_ListLevelStyle("Numbered_ListLevelStyle", rElementStack) {
497
498 // It seens that OpenDocument aways uses a dot "." as level delimiter.
499 m_abiListListDecimal = ".";
500 //
501 // These are default values for OpenDocument lists
502 //
503 m_abiListListDelim += "%L";
504 m_abiListStartValue = "1";
505 m_abiListType = UT_std_string_sprintf( "%d", NUMBERED_LIST);
506 }
507
508
509
510 /**
511 *
512 */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction & rAction)513 void ODi_Numbered_ListLevelStyle::startElement (const gchar* pName,
514 const gchar** ppAtts,
515 ODi_ListenerStateAction& rAction) {
516
517 const gchar* pVal;
518
519 // Let the parent class do the processing common to all list types.
520 ODi_ListLevelStyle::startElement (pName, ppAtts, rAction);
521
522 if (!strcmp("text:list-level-style-number", pName) ||
523 !strcmp("text:outline-level-style", pName)) {
524 std::string prefix, suffix;
525 xxx_UT_DEBUGMSG(("Doing a numbered list type %s \n",pName));
526 pVal = UT_getAttribute ("style:num-format", ppAtts);
527 UT_ASSERT_HARMLESS(pVal);
528 _setAbiListType(pVal);
529
530 if (pVal && !strcmp(pVal, "")) {
531 // We have an empty number format.
532
533 // Empty list label or "invisible" list.
534 m_abiListListDelim = "";
535
536 }
537 else {
538 // We have a number format defined.
539
540 pVal = UT_getAttribute ("style:num-prefix", ppAtts);
541 if(pVal) {
542 prefix = pVal;
543 }
544
545 pVal = UT_getAttribute ("style:num-suffix", ppAtts);
546 if(pVal) {
547 suffix = pVal;
548 }
549
550 m_abiListListDelim = prefix;
551 m_abiListListDelim += "%L";
552 m_abiListListDelim += suffix;
553 }
554
555 pVal = UT_getAttribute ("text:start-value", ppAtts);
556 if(pVal) {
557 m_abiListStartValue = pVal;
558 } else {
559 // AbiWord's default value is 0, but on OpenDocument it's 1.
560 m_abiListStartValue = "1";
561 }
562 }
563 }
564
565
566 /**
567 *
568 */
buildAbiPropsString()569 void ODi_Numbered_ListLevelStyle::buildAbiPropsString() {
570
571 ODi_ListLevelStyle::buildAbiPropsString();
572
573 if (!m_abiProperties.empty()) {
574 m_abiProperties += "; ";
575 }
576
577 m_abiProperties += "field-font: ";
578 if (m_pTextStyle) {
579 m_abiProperties += *(m_pTextStyle->getFontName());
580 } else {
581 m_abiProperties += "NULL";
582 }
583
584 m_abiProperties += "; list-style:";
585 switch (atoi(m_abiListType.c_str())) {
586 case NUMBERED_LIST:
587 m_abiProperties += "Numbered List";
588 break;
589
590 case LOWERCASE_LIST:
591 m_abiProperties += "Lower Case List";
592 break;
593
594 case UPPERCASE_LIST:
595 m_abiProperties += "Upper Case List";
596 break;
597
598 case LOWERROMAN_LIST:
599 m_abiProperties += "Lower Roman List";
600 break;
601
602 case UPPERROMAN_LIST:
603 m_abiProperties += "Upper Roman List";
604 break;
605
606 case ARABICNUMBERED_LIST:
607 m_abiProperties += "Arabic List";
608 break;
609
610 default:
611 UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
612 }
613 }
614
615
616 /**
617 * Maps the value of the OpenDocument attribute style:num-format to the
618 * correspondent AbiWord "type" attribute of the list (<l>) element tag.
619 *
620 * @param pStyleNumFormat The value of the style:num-format attribute.
621 */
_setAbiListType(const gchar * pStyleNumFormat)622 void ODi_Numbered_ListLevelStyle::_setAbiListType(const gchar* pStyleNumFormat) {
623
624 if (!pStyleNumFormat) {
625 // Use an arbitrary list type.
626 m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
627
628 } else if (!strcmp(pStyleNumFormat, "1")) {
629 m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
630
631 } else if (!strcmp(pStyleNumFormat, "a")) {
632 m_abiListType = UT_std_string_sprintf("%d", LOWERCASE_LIST);
633
634 } else if (!strcmp(pStyleNumFormat, "A")) {
635 m_abiListType = UT_std_string_sprintf("%d", UPPERCASE_LIST);
636
637 } else if (!strcmp(pStyleNumFormat, "i")) {
638 m_abiListType = UT_std_string_sprintf("%d", LOWERROMAN_LIST);
639
640 } else if (!strcmp(pStyleNumFormat, "I")) {
641 m_abiListType = UT_std_string_sprintf("%d", UPPERROMAN_LIST);
642
643 } else if (!strcmp(pStyleNumFormat, ODI_LISTLEVELSTYLE_ARABIC)) {
644 m_abiListType = UT_std_string_sprintf("%d", ARABICNUMBERED_LIST);
645
646 } else {
647 // Use an arbitrary list type.
648 m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
649 }
650 }
651