1<?php
2
3require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
4
5/**
6 * ODTList:
7 * Class containing static code for handling lists.
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author LarsDW223
11 */
12class ODTList
13{
14    /**
15     * Opens a list.
16     * The list style specifies if the list is an ordered or unordered list.
17     *
18     * @param bool $continue Continue numbering?
19     * @param string $styleName Name of style to use for the list
20     */
21    static public function listOpen(ODTInternalParams $params, $continue=false, $styleName, $element=NULL, $attributes=NULL) {
22        $params->document->paragraphClose();
23
24        $properties = array();
25        ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
26
27        $list = new ODTElementList($styleName, $continue);
28        $params->document->state->enter($list);
29        $list->setHTMLElement ($element);
30
31        $params->content .= $list->getOpeningTag();
32    }
33
34    /**
35     * Close a list
36     */
37    static public function listClose(ODTInternalParams $params) {
38        $table = $params->document->state->getCurrentTable();
39        if (isset($table) && $table->getListInterrupted()) {
40            // Do not do anything as long as list is interrupted
41            return;
42        }
43
44        if ($params->document->state->getInListItem()) {
45            // If we are still inside a list item then close it first,
46            // to prevent an error or broken document.
47            $params->document->listItemClose();
48        }
49
50        // Eventually modify last list paragraph first
51        self::replaceLastListParagraph($params);
52
53        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
54        $list = $params->document->state->getCurrentList();
55        $params->content .= $list->getClosingTag();
56
57        $position = $list->getListLastParagraphPosition();
58        $params->document->state->leave();
59
60        // If we are still in a list save the last paragraph position
61        // in the current list (needed for nested lists!).
62        $list = $params->document->state->getCurrentList();
63        if (isset($list)) {
64            $list->setListLastParagraphPosition($position);
65        }
66    }
67
68    /**
69     * Open a list item
70     *
71     * @param int $level The nesting level
72     */
73    static public function listItemOpen(ODTInternalParams $params, $level, $element=NULL, $attributes=NULL) {
74        if (!isset($params->document->state)) {
75            // ??? Can't be...
76            return;
77        }
78        if (!isset($element)) {
79            $element = 'li';
80        }
81
82        // Set marker that list interruption has stopped!!!
83        $table = $params->document->state->getCurrentTable();
84        if (isset($table)) {
85            $table->setListInterrupted(false);
86        }
87
88        $properties = array();
89        ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
90
91        // Attention:
92        // we save the list level here but it might be wrong.
93        // Someone can start a list with level 2 without having created
94        // a list with level 1 before.
95        // When the correct list level is needed better use
96        // $params->document->state->countClass('list'), see table_open().
97        $list_item = new ODTElementListItem($level);
98        $params->document->state->enter($list_item);
99        $list_item->setHTMLElement ($element);
100
101        $params->content .= $list_item->getOpeningTag();
102    }
103
104    /**
105     * Close a list item
106     */
107    static public function listItemClose(ODTInternalParams $params) {
108        $table = $params->document->state->getCurrentTable();
109        if (isset($table) && $table->getListInterrupted()) {
110            // Do not do anything as long as list is interrupted
111            return;
112        }
113
114        if ($params->document->state->getInListContent()) {
115            // If we are still inside list content then close it first,
116            // to prevent an error or broken document.
117            $params->document->listContentClose();
118        }
119
120        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
121        $params->document->closeCurrentElement();
122    }
123
124    /**
125     * Open a list header
126     *
127     * @param int $level The nesting level
128     */
129    static public function listHeaderOpen(ODTInternalParams $params, $level, $element=NULL, $attributes=NULL) {
130        if ( !isset($params->document->state) ) {
131            // ??? Can't be...
132            return;
133        }
134        if (!isset($element)) {
135            $element = 'li';
136        }
137
138        // Set marker that list interruption has stopped!!!
139        $table = $params->document->state->getCurrentTable();
140        if (isset($table)) {
141            $table->setListInterrupted(false);
142        }
143
144        $properties = array();
145        ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
146
147        // Attention:
148        // we save the list level here but it might be wrong.
149        // Someone can start a list with level 2 without having created
150        // a list with level 1 before.
151        // When the correct list level is needed better use
152        // $params->document->state->countClass('list'), see table_open().
153        $list_header = new ODTElementListHeader($level);
154        $params->document->state->enter($list_header);
155        $list_header->setHTMLElement ($element);
156
157        $params->content .= $list_header->getOpeningTag();
158    }
159
160    /**
161     * Close a list header
162     */
163    static public function listHeaderClose(ODTInternalParams $params) {
164        $table = $params->document->state->getCurrentTable();
165        if (isset($table) && $table->getListInterrupted()) {
166            // Do not do anything as long as list is interrupted
167            return;
168        }
169        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
170        $params->document->closeCurrentElement();
171    }
172
173    /**
174     * Open list content/a paragraph in a list item
175     */
176    static public function listContentOpen(ODTInternalParams $params, $element=NULL, $attributes=NULL) {
177        // The default style for list content is body but it should always be
178        // overwritten. It's just assigned here to guarantee some style name is
179        // always set in case of an error also.
180        $styleName = $params->document->getStyleName('body');
181        $list = $params->document->state->getCurrentList();
182        if (isset($list)) {
183            $listStyleName = $list->getStyleName();
184            if ($listStyleName == $params->document->getStyleName('list')) {
185                $styleName = $params->document->getStyleName('list content');
186            }
187            if ($listStyleName == $params->document->getStyleName('numbering')) {
188                $styleName = $params->document->getStyleName('numbering content');
189            }
190        }
191
192        $params->document->paragraphOpen($styleName);
193    }
194
195    /**
196     * Close list content/a paragraph in a list item
197     */
198    static public function listContentClose(ODTInternalParams $params) {
199        $table = $params->document->state->getCurrentTable();
200        if (isset($table) && $table->getListInterrupted()) {
201            // Do not do anything as long as list is interrupted
202            return;
203        }
204        $params->document->paragraphClose();
205    }
206
207    /**
208     * The function replaces the last paragraph of a list
209     * with a style having the properties of 'List_Last_Paragraph'.
210     *
211     * The function does NOT change the last paragraph of nested lists.
212     */
213    static protected function replaceLastListParagraph(ODTInternalParams $params) {
214        $list = $params->document->state->getCurrentList();
215        if (isset($list)) {
216            // We are in a list.
217            $list_count = $params->document->state->countClass('list');
218            $position = $list->getListLastParagraphPosition();
219
220            if ($list_count != 1 || $position == -1) {
221                // Do nothing if this is a nested list or the position was not saved
222                return;
223            }
224
225            $last_p_style = NULL;
226            if (preg_match('/<text:p text:style-name="[^"]*">/', $params->content, $matches, 0, $position) === 1) {
227                $last_p_style = substr($matches [0], strlen('<text:p text:style-name='));
228                $last_p_style = trim($last_p_style, '">');
229            } else {
230                // Nothing found???
231                return;
232            }
233
234            // Create a style for putting a bottom margin for this last paragraph of the list
235            // (if not done yet, the name must be unique!)
236
237            // If we have a standard list content paragraph style then we use the
238            // corresponding always existing first and last default styles
239            if ($last_p_style == $params->document->getStyleName('list content')) {
240                $style_name = $params->document->getStyleName('list last');
241            } else if ($last_p_style == $params->document->getStyleName('numbering content')) {
242                $style_name = $params->document->getStyleName('numbering last');
243            } else {
244                $style_name = 'LastListParagraph_'.$last_p_style;
245                if (!$params->document->styleExists($style_name)) {
246                    // ...no, create style as copy of style 'list first' or 'numbering first'
247                    if ($list->getStyleName() == $params->document->getStyleName('list')) {
248                        $style_last = $params->document->getStyleByAlias('list first');
249                    } else {
250                        $style_last = $params->document->getStyleByAlias('numbering first');
251                    }
252                    if (isset($style_last)) {
253                        $style_body = $params->document->getStyle($last_p_style);
254                        $style_display_name = 'Last '.$style_body->getProperty('style-display-name');
255                        $style_obj = clone $style_last;
256
257                        if (isset($style_obj)) {
258                            $style_obj->setProperty('style-name', $style_name);
259                            $style_obj->setProperty('style-parent', $last_p_style);
260                            $style_obj->setProperty('style-display-name', $style_display_name);
261                            $top = $style_last->getProperty('margin-top');
262                            if (!isset($top)) {
263                                $style_obj->setProperty('margin-top', $style_body->getProperty('margin-top'));
264                            }
265                            $params->document->addStyle($style_obj);
266                        }
267                    }
268                }
269            }
270
271            // Finally replace style name of last paragraph.
272            $params->content = substr_replace ($params->content,
273                '<text:p text:style-name="'.$style_name.'">',
274                $position, strlen($matches[0]));
275        }
276    }
277}
278