]> git.lyx.org Git - lyx.git/blob - src/paragraph_funcs.C
fix reading the author field.
[lyx.git] / src / paragraph_funcs.C
1 /**
2  * \file paragraph_funcs.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "paragraph_funcs.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17
18 #include "debug.h"
19 #include "encoding.h"
20 #include "gettext.h"
21 #include "language.h"
22 #include "lyxtext.h"
23 #include "outputparams.h"
24 #include "paragraph_pimpl.h"
25 #include "pariterator.h"
26 #include "sgml.h"
27 #include "texrow.h"
28 #include "vspace.h"
29
30 #include "support/filetools.h"
31 #include "support/lstrings.h"
32 #include "support/lyxlib.h"
33
34 #include <sstream>
35 #include <vector>
36
37 using lyx::pos_type;
38 using lyx::par_type;
39
40 using lyx::support::ascii_lowercase;
41 using lyx::support::atoi;
42 using lyx::support::bformat;
43 using lyx::support::compare_ascii_no_case;
44 using lyx::support::compare_no_case;
45 using lyx::support::contains;
46 using lyx::support::split;
47 using lyx::support::subst;
48
49 using std::auto_ptr;
50 using std::endl;
51 using std::string;
52 using std::vector;
53 using std::istringstream;
54 using std::ostream;
55 using std::pair;
56
57
58 namespace {
59
60 bool moveItem(Paragraph & from, Paragraph & to,
61         BufferParams const & params, pos_type i, pos_type j)
62 {
63         Paragraph::value_type const tmpchar = from.getChar(i);
64         LyXFont tmpfont = from.getFontSettings(params, i);
65
66         if (tmpchar == Paragraph::META_INSET) {
67                 InsetBase * tmpinset = 0;
68                 if (from.getInset(i)) {
69                         // the inset is not in a paragraph anymore
70                         tmpinset = from.insetlist.release(i);
71                         from.insetlist.erase(i);
72                 }
73
74                 if (!to.insetAllowed(tmpinset->lyxCode())) {
75                         delete tmpinset;
76                         return false;
77                 }
78                 if (tmpinset)
79                         to.insertInset(j, tmpinset, tmpfont);
80         } else {
81                 if (!to.checkInsertChar(tmpfont))
82                         return false;
83                 to.insertChar(j, tmpchar, tmpfont);
84         }
85         return true;
86 }
87
88 }
89
90
91 void breakParagraph(BufferParams const & bparams,
92         ParagraphList & pars, par_type par_offset, pos_type pos, int flag)
93 {
94         // create a new paragraph, and insert into the list
95         ParagraphList::iterator tmp =
96                 pars.insert(pars.begin() + par_offset + 1, Paragraph());
97
98         Paragraph & par = pars[par_offset];
99
100         // without doing that we get a crash when typing <Return> at the
101         // end of a paragraph
102         tmp->layout(bparams.getLyXTextClass().defaultLayout());
103         // remember to set the inset_owner
104         tmp->setInsetOwner(par.inInset());
105
106         if (bparams.tracking_changes)
107                 tmp->trackChanges();
108
109         // this is an idea for a more userfriendly layout handling, I will
110         // see what the users say
111
112         // layout stays the same with latex-environments
113         if (flag) {
114                 tmp->layout(par.layout());
115                 tmp->setLabelWidthString(par.params().labelWidthString());
116         }
117
118         bool const isempty = (par.allowEmpty() && par.empty());
119
120         if (!isempty && (par.size() > pos || par.empty() || flag == 2)) {
121                 tmp->layout(par.layout());
122                 tmp->params().align(par.params().align());
123                 tmp->setLabelWidthString(par.params().labelWidthString());
124
125                 tmp->params().depth(par.params().depth());
126                 tmp->params().noindent(par.params().noindent());
127
128                 // copy everything behind the break-position
129                 // to the new paragraph
130
131                 /* Note: if !keepempty, empty() == true, then we reach
132                  * here with size() == 0. So pos_end becomes - 1. This
133                  * doesn't cause problems because both loops below
134                  * enforce pos <= pos_end and 0 <= pos
135                  */
136                 pos_type pos_end = par.size() - 1;
137
138                 for (pos_type i = pos, j = pos; i <= pos_end; ++i) {
139                         Change::Type change = par.lookupChange(i);
140                         if (moveItem(par, *tmp, bparams, i, j - pos)) {
141                                 tmp->setChange(j - pos, change);
142                                 ++j;
143                         }
144                 }
145
146                 for (pos_type i = pos_end; i >= pos; --i)
147                         par.eraseIntern(i);
148         }
149
150         if (pos) {
151                 // Make sure that we keep the language when
152                 // breaking paragrpah.
153                 if (tmp->empty()) {
154                         LyXFont changed = tmp->getFirstFontSettings();
155                         LyXFont old = par.getFontSettings(bparams, par.size());
156                         changed.setLanguage(old.language());
157                         tmp->setFont(0, changed);
158                 }
159
160                 return;
161         }
162
163         par.params().clear();
164
165         par.layout(bparams.getLyXTextClass().defaultLayout());
166
167         // layout stays the same with latex-environments
168         if (flag) {
169                 par.layout(tmp->layout());
170                 par.setLabelWidthString(tmp->params().labelWidthString());
171                 par.params().depth(tmp->params().depth());
172         }
173
174         // subtle, but needed to get empty pars working right
175         if (bparams.tracking_changes) {
176                 if (!par.size()) {
177                         par.cleanChanges();
178                 } else if (!tmp->size()) {
179                         tmp->cleanChanges();
180                 }
181         }
182 }
183
184
185 void breakParagraphConservative(BufferParams const & bparams,
186         ParagraphList & pars, par_type par_offset, pos_type pos)
187 {
188         // create a new paragraph
189         Paragraph & tmp = *pars.insert(pars.begin() + par_offset + 1, Paragraph());
190         Paragraph & par = pars[par_offset];
191
192         tmp.makeSameLayout(par);
193
194         // When can pos > size()?
195         // I guess pos == size() is possible.
196         if (par.size() > pos) {
197                 // copy everything behind the break-position to the new
198                 // paragraph
199                 pos_type pos_end = par.size() - 1;
200
201                 for (pos_type i = pos, j = pos; i <= pos_end; ++i)
202                         if (moveItem(par, tmp, bparams, i, j - pos))
203                                 ++j;
204
205                 for (pos_type k = pos_end; k >= pos; --k)
206                         par.erase(k);
207         }
208 }
209
210
211 void mergeParagraph(BufferParams const & bparams,
212         ParagraphList & pars, par_type par_offset)
213 {
214         Paragraph & next = pars[par_offset + 1];
215         Paragraph & par = pars[par_offset];
216
217         pos_type pos_end = next.size() - 1;
218         pos_type pos_insert = par.size();
219
220         // ok, now copy the paragraph
221         for (pos_type i = 0, j = 0; i <= pos_end; ++i)
222                 if (moveItem(next, par, bparams, i, pos_insert + j))
223                         ++j;
224
225         pars.erase(pars.begin() + par_offset + 1);
226 }
227
228
229 par_type depthHook(par_type pit,
230         ParagraphList const & pars, Paragraph::depth_type depth)
231 {
232         par_type newpit = pit;
233
234         if (newpit != 0)
235                 --newpit;
236
237         while (newpit != 0 && pars[newpit].getDepth() > depth)
238                 --newpit;
239
240         if (pars[newpit].getDepth() > depth)
241                 return pit;
242
243         return newpit;
244 }
245
246
247 par_type outerHook(par_type par_offset, ParagraphList const & pars)
248 {
249         Paragraph const & par = pars[par_offset];
250
251         if (par.getDepth() == 0)
252                 return pars.size();
253         return depthHook(par_offset, pars, Paragraph::depth_type(par.getDepth() - 1));
254 }
255
256
257 bool isFirstInSequence(par_type par_offset, ParagraphList const & pars)
258 {
259         Paragraph const & par = pars[par_offset];
260
261         par_type dhook_offset = depthHook(par_offset, pars, par.getDepth());
262
263         Paragraph const & dhook = pars[dhook_offset];
264
265         return dhook_offset == par_offset
266                 || dhook.layout() != par.layout()
267                 || dhook.getDepth() != par.getDepth();
268 }
269
270
271 int getEndLabel(par_type p, ParagraphList const & pars)
272 {
273         par_type pit = p;
274         Paragraph::depth_type par_depth = pars[p].getDepth();
275         while (pit != par_type(pars.size())) {
276                 LyXLayout_ptr const & layout = pars[pit].layout();
277                 int const endlabeltype = layout->endlabeltype;
278
279                 if (endlabeltype != END_LABEL_NO_LABEL) {
280                         if (p + 1 == par_type(pars.size()))
281                                 return endlabeltype;
282
283                         Paragraph::depth_type const next_depth =
284                                 pars[p + 1].getDepth();
285                         if (par_depth > next_depth ||
286                             (par_depth == next_depth && layout != pars[p + 1].layout()))
287                                 return endlabeltype;
288                         break;
289                 }
290                 if (par_depth == 0)
291                         break;
292                 pit = outerHook(pit, pars);
293                 if (pit != par_type(pars.size()))
294                         par_depth = pars[pit].getDepth();
295         }
296         return END_LABEL_NO_LABEL;
297 }
298
299
300 LyXFont const outerFont(par_type par_offset, ParagraphList const & pars)
301 {
302         Paragraph::depth_type par_depth = pars[par_offset].getDepth();
303         LyXFont tmpfont(LyXFont::ALL_INHERIT);
304
305         // Resolve against environment font information
306         while (par_offset != par_type(pars.size())
307                && par_depth
308                && !tmpfont.resolved()) {
309                 par_offset = outerHook(par_offset, pars);
310                 if (par_offset != par_type(pars.size())) {
311                         tmpfont.realize(pars[par_offset].layout()->font);
312                         par_depth = pars[par_offset].getDepth();
313                 }
314         }
315
316         return tmpfont;
317 }
318
319
320 par_type outerPar(Buffer const & buf, InsetBase const * inset)
321 {
322         ParIterator pit = const_cast<Buffer &>(buf).par_iterator_begin();
323         ParIterator end = const_cast<Buffer &>(buf).par_iterator_end();
324         for ( ; pit != end; ++pit) {
325                 LyXText * text;
326                 // the second '=' below is intentional
327                 for (int i = 0; (text = inset->getText(i)); ++i)
328                         if (&text->paragraphs() == &pit.plist())
329                                 return pit.outerPar();
330
331                 InsetList::const_iterator ii = pit->insetlist.begin();
332                 InsetList::const_iterator iend = pit->insetlist.end();
333                 for ( ; ii != iend; ++ii)
334                         if (ii->inset == inset)
335                                 return pit.outerPar();
336         }
337         lyxerr << "outerPar: should not happen" << endl;
338         BOOST_ASSERT(false);
339         return buf.paragraphs().size(); // shut up compiler
340 }
341
342
343 /// return the range of pars [beg, end[ owning the range of y [ystart, yend]
344 void getParsInRange(ParagraphList & pars, int ystart, int yend,
345         par_type & beg, par_type & end)
346 {
347         BOOST_ASSERT(!pars.empty());
348         par_type const endpar = pars.size();
349         par_type const begpar = 0;
350
351         for (beg = endpar - 1; beg != begpar && pars[beg].y > ystart; --beg)
352                 ;
353
354         for (end = beg ; end != endpar && pars[end].y <= yend; ++end)
355                 ;
356 }