2 * \file paragraph_funcs.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * Full author contact details are available in file CREDITS.
13 #include "paragraph_funcs.h"
15 #include "BufferParams.h"
18 #include "Paragraph.h"
19 #include "ParagraphParameters.h"
28 static bool moveItem(Paragraph
& fromPar
, pos_type fromPos
,
29 Paragraph
& toPar
, pos_type toPos
, BufferParams
const & params
)
31 // Note: moveItem() does not honour change tracking!
32 // Therefore, it should only be used for breaking and merging paragraphs
34 Paragraph::value_type
const tmpChar
= fromPar
.getChar(fromPos
);
35 Font
const tmpFont
= fromPar
.getFontSettings(params
, fromPos
);
36 Change
const tmpChange
= fromPar
.lookupChange(fromPos
);
38 if (tmpChar
== Paragraph::META_INSET
) {
40 if (fromPar
.getInset(fromPos
)) {
41 // the inset is not in the paragraph any more
42 tmpInset
= fromPar
.insetlist
.release(fromPos
);
45 fromPar
.eraseChar(fromPos
, false);
47 if (!toPar
.insetAllowed(tmpInset
->lyxCode())) {
52 toPar
.insertInset(toPos
, tmpInset
, tmpFont
, tmpChange
);
54 fromPar
.eraseChar(fromPos
, false);
55 toPar
.insertChar(toPos
, tmpChar
, tmpFont
, tmpChange
);
62 void breakParagraph(BufferParams
const & bparams
,
63 ParagraphList
& pars
, pit_type par_offset
, pos_type pos
, int flag
)
65 // create a new paragraph, and insert into the list
66 ParagraphList::iterator tmp
=
67 pars
.insert(boost::next(pars
.begin(), par_offset
+ 1),
70 Paragraph
& par
= pars
[par_offset
];
72 // without doing that we get a crash when typing <Return> at the
74 tmp
->layout(bparams
.getTextClass().defaultLayout());
75 // remember to set the inset_owner
76 tmp
->setInsetOwner(par
.inInset());
78 // layout stays the same with latex-environments
80 tmp
->layout(par
.layout());
81 tmp
->setLabelWidthString(par
.params().labelWidthString());
82 tmp
->params().depth(par
.params().depth());
83 } else if (par
.params().depth() > 0) {
84 Paragraph
const & hook
= pars
[outerHook(par_offset
, pars
)];
85 tmp
->layout(hook
.layout());
86 // not sure the line below is useful
87 tmp
->setLabelWidthString(par
.params().labelWidthString());
88 tmp
->params().depth(hook
.params().depth());
91 bool const isempty
= (par
.allowEmpty() && par
.empty());
93 if (!isempty
&& (par
.size() > pos
|| par
.empty() || flag
== 2)) {
94 tmp
->layout(par
.layout());
95 tmp
->params().align(par
.params().align());
96 tmp
->setLabelWidthString(par
.params().labelWidthString());
98 tmp
->params().depth(par
.params().depth());
99 tmp
->params().noindent(par
.params().noindent());
101 // move everything behind the break position
102 // to the new paragraph
104 /* Note: if !keepempty, empty() == true, then we reach
105 * here with size() == 0. So pos_end becomes - 1. This
106 * doesn't cause problems because both loops below
107 * enforce pos <= pos_end and 0 <= pos
109 pos_type pos_end
= par
.size() - 1;
111 for (pos_type i
= pos
, j
= 0; i
<= pos_end
; ++i
) {
112 if (moveItem(par
, pos
, *tmp
, j
, bparams
)) {
118 // Move over the end-of-par change information
119 tmp
->setChange(tmp
->size(), par
.lookupChange(par
.size()));
120 par
.setChange(par
.size(), Change(bparams
.trackChanges
?
121 Change::INSERTED
: Change::UNCHANGED
));
124 // Make sure that we keep the language when
125 // breaking paragraph.
127 Font changed
= tmp
->getFirstFontSettings(bparams
);
128 Font old
= par
.getFontSettings(bparams
, par
.size());
129 changed
.setLanguage(old
.language());
130 tmp
->setFont(0, changed
);
137 par
.params().clear();
138 par
.layout(bparams
.getTextClass().defaultLayout());
141 // layout stays the same with latex-environments
143 par
.layout(tmp
->layout());
144 par
.setLabelWidthString(tmp
->params().labelWidthString());
145 par
.params().depth(tmp
->params().depth());
150 void breakParagraphConservative(BufferParams
const & bparams
,
151 ParagraphList
& pars
, pit_type par_offset
, pos_type pos
)
153 // create a new paragraph
154 Paragraph
& tmp
= *pars
.insert(boost::next(pars
.begin(), par_offset
+ 1),
156 Paragraph
& par
= pars
[par_offset
];
158 tmp
.makeSameLayout(par
);
160 BOOST_ASSERT(pos
<= par
.size());
162 if (pos
< par
.size()) {
163 // move everything behind the break position to the new paragraph
164 pos_type pos_end
= par
.size() - 1;
166 for (pos_type i
= pos
, j
= 0; i
<= pos_end
; ++i
) {
167 if (moveItem(par
, pos
, tmp
, j
, bparams
)) {
171 // Move over the end-of-par change information
172 tmp
.setChange(tmp
.size(), par
.lookupChange(par
.size()));
173 par
.setChange(par
.size(), Change(bparams
.trackChanges
?
174 Change::INSERTED
: Change::UNCHANGED
));
179 void mergeParagraph(BufferParams
const & bparams
,
180 ParagraphList
& pars
, pit_type par_offset
)
182 Paragraph
& next
= pars
[par_offset
+ 1];
183 Paragraph
& par
= pars
[par_offset
];
185 pos_type pos_end
= next
.size() - 1;
186 pos_type pos_insert
= par
.size();
188 // the imaginary end-of-paragraph character (at par.size()) has to be
189 // marked as unmodified. Otherwise, its change is adopted by the first
190 // character of the next paragraph.
191 if (par
.lookupChange(par
.size()).type
!= Change::UNCHANGED
) {
192 LYXERR(Debug::CHANGES
) <<
193 "merging par with inserted/deleted end-of-par character" << endl
;
194 par
.setChange(par
.size(), Change(Change::UNCHANGED
));
197 Change change
= next
.lookupChange(next
.size());
199 // move the content of the second paragraph to the end of the first one
200 for (pos_type i
= 0, j
= pos_insert
; i
<= pos_end
; ++i
) {
201 if (moveItem(next
, 0, par
, j
, bparams
)) {
206 // move the change of the end-of-paragraph character
207 par
.setChange(par
.size(), change
);
209 pars
.erase(boost::next(pars
.begin(), par_offset
+ 1));
213 pit_type
depthHook(pit_type pit
, ParagraphList
const & pars
, depth_type depth
)
215 pit_type newpit
= pit
;
220 while (newpit
!= 0 && pars
[newpit
].getDepth() > depth
)
223 if (pars
[newpit
].getDepth() > depth
)
230 pit_type
outerHook(pit_type par_offset
, ParagraphList
const & pars
)
232 Paragraph
const & par
= pars
[par_offset
];
234 if (par
.getDepth() == 0)
236 return depthHook(par_offset
, pars
, depth_type(par
.getDepth() - 1));
240 bool isFirstInSequence(pit_type par_offset
, ParagraphList
const & pars
)
242 Paragraph
const & par
= pars
[par_offset
];
244 pit_type dhook_offset
= depthHook(par_offset
, pars
, par
.getDepth());
246 if (dhook_offset
== par_offset
)
249 Paragraph
const & dhook
= pars
[dhook_offset
];
251 return dhook
.layout() != par
.layout()
252 || dhook
.getDepth() != par
.getDepth();
256 int getEndLabel(pit_type p
, ParagraphList
const & pars
)
259 depth_type par_depth
= pars
[p
].getDepth();
260 while (pit
!= pit_type(pars
.size())) {
261 Layout_ptr
const & layout
= pars
[pit
].layout();
262 int const endlabeltype
= layout
->endlabeltype
;
264 if (endlabeltype
!= END_LABEL_NO_LABEL
) {
265 if (p
+ 1 == pit_type(pars
.size()))
268 depth_type
const next_depth
=
269 pars
[p
+ 1].getDepth();
270 if (par_depth
> next_depth
||
271 (par_depth
== next_depth
&& layout
!= pars
[p
+ 1].layout()))
277 pit
= outerHook(pit
, pars
);
278 if (pit
!= pit_type(pars
.size()))
279 par_depth
= pars
[pit
].getDepth();
281 return END_LABEL_NO_LABEL
;
285 Font
const outerFont(pit_type par_offset
, ParagraphList
const & pars
)
287 depth_type par_depth
= pars
[par_offset
].getDepth();
288 Font
tmpfont(Font::ALL_INHERIT
);
290 // Resolve against environment font information
291 while (par_offset
!= pit_type(pars
.size())
293 && !tmpfont
.resolved()) {
294 par_offset
= outerHook(par_offset
, pars
);
295 if (par_offset
!= pit_type(pars
.size())) {
296 tmpfont
.realize(pars
[par_offset
].layout()->font
);
297 par_depth
= pars
[par_offset
].getDepth();
305 /// return the number of InsetOptArg in a paragraph
306 int numberOfOptArgs(Paragraph
const & par
)
310 InsetList::const_iterator it
= par
.insetlist
.begin();
311 InsetList::const_iterator end
= par
.insetlist
.end();
312 for (; it
!= end
; ++it
) {
313 if (it
->inset
->lyxCode() == Inset::OPTARG_CODE
)
320 void acceptChanges(ParagraphList
& pars
, BufferParams
const & bparams
)
322 pit_type pars_size
= static_cast<pit_type
>(pars
.size());
324 // first, accept changes within each individual paragraph
325 // (do not consider end-of-par)
326 for (pit_type pit
= 0; pit
< pars_size
; ++pit
) {
327 if (!pars
[pit
].empty()) // prevent assertion failure
328 pars
[pit
].acceptChanges(bparams
, 0, pars
[pit
].size());
331 // next, accept imaginary end-of-par characters
332 for (pit_type pit
= 0; pit
< pars_size
; ++pit
) {
333 pos_type pos
= pars
[pit
].size();
335 if (pars
[pit
].isInserted(pos
)) {
336 pars
[pit
].setChange(pos
, Change(Change::UNCHANGED
));
337 } else if (pars
[pit
].isDeleted(pos
)) {
338 if (pit
== pars_size
- 1) {
339 // we cannot remove a par break at the end of the last
340 // paragraph; instead, we mark it unchanged
341 pars
[pit
].setChange(pos
, Change(Change::UNCHANGED
));
343 mergeParagraph(bparams
, pars
, pit
);