3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "InsetText.h"
15 #include "insets/InsetOptArg.h"
17 #include "buffer_funcs.h"
19 #include "BufferParams.h"
20 #include "BufferView.h"
21 #include "CompletionList.h"
22 #include "CoordCache.h"
24 #include "CutAndPaste.h"
25 #include "DispatchResult.h"
26 #include "ErrorList.h"
27 #include "FuncRequest.h"
28 #include "FuncStatus.h"
29 #include "InsetCaption.h"
30 #include "InsetList.h"
35 #include "MetricsInfo.h"
36 #include "output_docbook.h"
37 #include "output_latex.h"
38 #include "output_xhtml.h"
39 #include "OutputParams.h"
40 #include "output_plaintext.h"
41 #include "paragraph_funcs.h"
42 #include "Paragraph.h"
43 #include "ParagraphParameters.h"
44 #include "ParIterator.h"
48 #include "TextClass.h"
50 #include "TextMetrics.h"
51 #include "TocBackend.h"
53 #include "frontends/alert.h"
54 #include "frontends/Painter.h"
56 #include "support/debug.h"
57 #include "support/gettext.h"
58 #include "support/lstrings.h"
60 #include <boost/bind.hpp>
61 #include "support/lassert.h"
64 using namespace lyx::support
;
71 using graphics::PreviewLoader
;
74 /////////////////////////////////////////////////////////////////////
76 InsetText::InsetText(Buffer
const & buf
, UsePlain type
)
77 : drawFrame_(false), frame_color_(Color_insetframe
)
79 setBuffer(const_cast<Buffer
&>(buf
));
84 InsetText::InsetText(InsetText
const & in
)
87 text_
.autoBreakRows_
= in
.text_
.autoBreakRows_
;
88 drawFrame_
= in
.drawFrame_
;
89 frame_color_
= in
.frame_color_
;
90 text_
.paragraphs() = in
.text_
.paragraphs();
95 void InsetText::setBuffer(Buffer
& buf
)
97 ParagraphList::iterator end
= paragraphs().end();
98 for (ParagraphList::iterator it
= paragraphs().begin(); it
!= end
; ++it
)
100 Inset::setBuffer(buf
);
104 void InsetText::initParagraphs(UsePlain type
)
106 LASSERT(paragraphs().empty(), /**/);
107 paragraphs().push_back(Paragraph());
108 Paragraph
& ourpar
= paragraphs().back();
109 ourpar
.setInsetOwner(this);
110 DocumentClass
const & dc
= buffer_
->params().documentClass();
111 if (type
== DefaultLayout
)
112 ourpar
.setDefaultLayout(dc
);
114 ourpar
.setPlainLayout(dc
);
118 void InsetText::setParagraphOwner()
120 for_each(paragraphs().begin(), paragraphs().end(),
121 bind(&Paragraph::setInsetOwner
, _1
, this));
125 void InsetText::clear()
127 ParagraphList
& pars
= paragraphs();
128 LASSERT(!pars
.empty(), /**/);
130 // This is a gross hack...
131 Layout
const & old_layout
= pars
.begin()->layout();
134 pars
.push_back(Paragraph());
135 pars
.begin()->setInsetOwner(this);
136 pars
.begin()->setLayout(old_layout
);
140 Dimension
const InsetText::dimension(BufferView
const & bv
) const
142 TextMetrics
const & tm
= bv
.textMetrics(&text_
);
143 Dimension dim
= tm
.dimension();
144 dim
.wid
+= 2 * TEXT_TO_INSET_OFFSET
;
145 dim
.des
+= TEXT_TO_INSET_OFFSET
;
146 dim
.asc
+= TEXT_TO_INSET_OFFSET
;
151 void InsetText::write(ostream
& os
) const
154 text_
.write(buffer(), os
);
158 void InsetText::read(Lexer
& lex
)
162 // delete the initial paragraph
163 Paragraph oldpar
= *paragraphs().begin();
164 paragraphs().clear();
166 lex
.setContext("InsetText::read");
167 bool res
= text_
.read(buffer(), lex
, errorList
, this);
170 lex
.printError("Missing \\end_inset at this point. ");
173 // ensure we have at least one paragraph.
174 if (paragraphs().empty())
175 paragraphs().push_back(oldpar
);
179 void InsetText::metrics(MetricsInfo
& mi
, Dimension
& dim
) const
181 TextMetrics
& tm
= mi
.base
.bv
->textMetrics(&text_
);
183 //lyxerr << "InsetText::metrics: width: " << mi.base.textwidth << endl;
185 // Hand font through to contained lyxtext:
186 tm
.font_
.fontInfo() = mi
.base
.font
;
187 mi
.base
.textwidth
-= 2 * TEXT_TO_INSET_OFFSET
;
189 // This can happen when a layout has a left and right margin,
190 // and the view is made very narrow. We can't do better than
191 // to draw it partly out of view (bug 5890).
192 if (mi
.base
.textwidth
< 1)
193 mi
.base
.textwidth
= 1;
196 tm
.metrics(mi
, dim
, mi
.base
.textwidth
);
199 mi
.base
.textwidth
+= 2 * TEXT_TO_INSET_OFFSET
;
200 dim
.asc
+= TEXT_TO_INSET_OFFSET
;
201 dim
.des
+= TEXT_TO_INSET_OFFSET
;
202 dim
.wid
+= 2 * TEXT_TO_INSET_OFFSET
;
206 void InsetText::draw(PainterInfo
& pi
, int x
, int y
) const
208 TextMetrics
& tm
= pi
.base
.bv
->textMetrics(&text_
);
210 if (drawFrame_
|| pi
.full_repaint
) {
211 int const w
= tm
.width() + TEXT_TO_INSET_OFFSET
;
212 int const yframe
= y
- TEXT_TO_INSET_OFFSET
- tm
.ascent();
213 int const h
= tm
.height() + 2 * TEXT_TO_INSET_OFFSET
;
214 int const xframe
= x
+ TEXT_TO_INSET_OFFSET
/ 2;
216 pi
.pain
.fillRectangle(xframe
, yframe
, w
, h
,
217 pi
.backgroundColor(this));
220 pi
.pain
.rectangle(xframe
, yframe
, w
, h
, frameColor());
222 ColorCode
const old_color
= pi
.background_color
;
223 pi
.background_color
= pi
.backgroundColor(this, false);
225 tm
.draw(pi
, x
+ TEXT_TO_INSET_OFFSET
, y
);
227 pi
.background_color
= old_color
;
231 docstring
InsetText::editMessage() const
233 return _("Opened Text Inset");
237 void InsetText::edit(Cursor
& cur
, bool front
, EntryDirection entry_from
)
239 pit_type
const pit
= front
? 0 : paragraphs().size() - 1;
240 pos_type pos
= front
? 0 : paragraphs().back().size();
242 // if visual information is not to be ignored, move to extreme right/left
243 if (entry_from
!= ENTRY_DIRECTION_IGNORE
) {
244 Cursor temp_cur
= cur
;
245 temp_cur
.pit() = pit
;
246 temp_cur
.pos() = pos
;
247 temp_cur
.posVisToRowExtremity(entry_from
== ENTRY_DIRECTION_LEFT
);
248 pos
= temp_cur
.pos();
251 text_
.setCursor(cur
.top(), pit
, pos
);
252 cur
.clearSelection();
257 Inset
* InsetText::editXY(Cursor
& cur
, int x
, int y
)
259 return cur
.bv().textMetrics(&text_
).editXY(cur
, x
, y
);
263 void InsetText::doDispatch(Cursor
& cur
, FuncRequest
& cmd
)
265 LYXERR(Debug::ACTION
, "InsetText::doDispatch()"
266 << " [ cmd.action = " << cmd
.action
<< ']');
268 // Dispatch only to text_ if the cursor is inside
269 // the text_. It is not for context menus (bug 5797).
270 if (cur
.text() == &text_
)
271 text_
.dispatch(cur
, cmd
);
275 if (!cur
.result().dispatched())
276 Inset::doDispatch(cur
, cmd
);
280 bool InsetText::getStatus(Cursor
& cur
, FuncRequest
const & cmd
,
281 FuncStatus
& status
) const
283 switch (cmd
.action
) {
285 status
.setEnabled(!forcePlainLayout());
288 case LFUN_LAYOUT_PARAGRAPH
:
289 case LFUN_PARAGRAPH_PARAMS
:
290 case LFUN_PARAGRAPH_PARAMS_APPLY
:
291 case LFUN_PARAGRAPH_SPACING
:
292 case LFUN_PARAGRAPH_UPDATE
:
293 status
.setEnabled(allowParagraphCustomization());
296 // Dispatch only to text_ if the cursor is inside
297 // the text_. It is not for context menus (bug 5797).
299 if (cur
.text() == &text_
)
300 ret
= text_
.getStatus(cur
, cmd
, status
);
303 ret
= Inset::getStatus(cur
, cmd
, status
);
309 void InsetText::setChange(Change
const & change
)
311 ParagraphList::iterator pit
= paragraphs().begin();
312 ParagraphList::iterator end
= paragraphs().end();
313 for (; pit
!= end
; ++pit
) {
314 pit
->setChange(change
);
319 void InsetText::acceptChanges(BufferParams
const & bparams
)
321 text_
.acceptChanges(bparams
);
325 void InsetText::rejectChanges(BufferParams
const & bparams
)
327 text_
.rejectChanges(bparams
);
331 int InsetText::latex(odocstream
& os
, OutputParams
const & runparams
) const
334 latexParagraphs(buffer(), text_
, os
, texrow
, runparams
);
335 return texrow
.rows();
339 int InsetText::plaintext(odocstream
& os
, OutputParams
const & runparams
) const
341 ParagraphList::const_iterator beg
= paragraphs().begin();
342 ParagraphList::const_iterator end
= paragraphs().end();
343 ParagraphList::const_iterator it
= beg
;
344 bool ref_printed
= false;
346 for (; it
!= end
; ++it
) {
349 if (runparams
.linelen
> 0)
352 odocstringstream oss
;
353 writePlaintextParagraph(buffer(), *it
, oss
, runparams
, ref_printed
);
354 docstring
const str
= oss
.str();
356 // FIXME: len is not computed fully correctly; in principle,
357 // we have to count the characters after the last '\n'
365 int InsetText::docbook(odocstream
& os
, OutputParams
const & runparams
) const
367 docbookParagraphs(paragraphs(), buffer(), os
, runparams
);
372 docstring
InsetText::xhtml(odocstream
& os
, OutputParams
const & runparams
) const
374 xhtmlParagraphs(paragraphs(), buffer(), os
, runparams
);
379 void InsetText::validate(LaTeXFeatures
& features
) const
381 for_each(paragraphs().begin(), paragraphs().end(),
382 bind(&Paragraph::validate
, _1
, ref(features
)));
386 void InsetText::cursorPos(BufferView
const & bv
,
387 CursorSlice
const & sl
, bool boundary
, int & x
, int & y
) const
389 x
= bv
.textMetrics(&text_
).cursorX(sl
, boundary
) + TEXT_TO_INSET_OFFSET
;
390 y
= bv
.textMetrics(&text_
).cursorY(sl
, boundary
);
394 bool InsetText::showInsetDialog(BufferView
*) const
400 void InsetText::setText(docstring
const & data
, Font
const & font
, bool trackChanges
)
403 Paragraph
& first
= paragraphs().front();
404 for (unsigned int i
= 0; i
< data
.length(); ++i
)
405 first
.insertChar(i
, data
[i
], font
, trackChanges
);
409 void InsetText::setAutoBreakRows(bool flag
)
411 if (flag
== text_
.autoBreakRows_
)
414 text_
.autoBreakRows_
= flag
;
418 // remove previously existing newlines
419 ParagraphList::iterator it
= paragraphs().begin();
420 ParagraphList::iterator end
= paragraphs().end();
421 for (; it
!= end
; ++it
)
422 for (int i
= 0; i
< it
->size(); ++i
)
423 if (it
->isNewline(i
))
424 // do not track the change, because the user
425 // is not allowed to revert/reject it
426 it
->eraseChar(i
, false);
430 void InsetText::setDrawFrame(bool flag
)
436 ColorCode
InsetText::frameColor() const
442 void InsetText::setFrameColor(ColorCode col
)
448 void InsetText::appendParagraphs(ParagraphList
& plist
)
450 // There is little we can do here to keep track of changes.
451 // As of 2006/10/20, appendParagraphs is used exclusively by
452 // LyXTabular::setMultiColumn. In this context, the paragraph break
453 // is lost irreversibly and the appended text doesn't really change
455 ParagraphList
& pl
= paragraphs();
457 ParagraphList::iterator pit
= plist
.begin();
458 ParagraphList::iterator ins
= pl
.insert(pl
.end(), *pit
);
460 mergeParagraph(buffer().params(), pl
,
461 distance(pl
.begin(), ins
) - 1);
463 for_each(pit
, plist
.end(),
464 bind(&ParagraphList::push_back
, ref(pl
), _1
));
468 void InsetText::addPreview(PreviewLoader
& loader
) const
470 ParagraphList::const_iterator pit
= paragraphs().begin();
471 ParagraphList::const_iterator pend
= paragraphs().end();
473 for (; pit
!= pend
; ++pit
) {
474 InsetList::const_iterator it
= pit
->insetList().begin();
475 InsetList::const_iterator end
= pit
->insetList().end();
476 for (; it
!= end
; ++it
)
477 it
->inset
->addPreview(loader
);
482 ParagraphList
const & InsetText::paragraphs() const
484 return text_
.paragraphs();
488 ParagraphList
& InsetText::paragraphs()
490 return text_
.paragraphs();
494 void InsetText::updateLabels(ParIterator
const & it
)
496 ParIterator it2
= it
;
498 LASSERT(&it2
.inset() == this && it2
.pit() == 0, return);
499 if (producesOutput())
500 buffer().updateLabels(it2
);
502 DocumentClass
const & tclass
= buffer().masterBuffer()->params().documentClass();
503 Counters
const savecnt
= tclass
.counters();
504 buffer().updateLabels(it2
);
505 tclass
.counters() = savecnt
;
510 void InsetText::addToToc(DocIterator
const & cdit
)
512 DocIterator dit
= cdit
;
513 dit
.push_back(CursorSlice(*this));
514 Toc
& toc
= buffer().tocBackend().toc("tableofcontents");
516 BufferParams
const & bufparams
= buffer_
->params();
517 const int min_toclevel
= bufparams
.documentClass().min_toclevel();
519 // For each paragraph, traverse its insets and let them add
521 ParagraphList
& pars
= paragraphs();
522 pit_type pend
= paragraphs().size();
523 for (pit_type pit
= 0; pit
!= pend
; ++pit
) {
524 Paragraph
const & par
= pars
[pit
];
526 // the string that goes to the toc (could be the optarg)
528 InsetList::const_iterator it
= par
.insetList().begin();
529 InsetList::const_iterator end
= par
.insetList().end();
530 for (; it
!= end
; ++it
) {
531 Inset
& inset
= *it
->inset
;
533 //lyxerr << (void*)&inset << " code: " << inset.lyxCode() << std::endl;
535 switch (inset
.lyxCode()) {
537 if (!tocstring
.empty())
540 Paragraph
const & insetpar
=
541 *static_cast<InsetOptArg
&>(inset
).paragraphs().begin();
542 if (!par
.labelString().empty())
543 tocstring
= par
.labelString() + ' ';
544 tocstring
+= insetpar
.asString(AS_STR_INSETS
);
551 // now the toc entry for the paragraph
552 int const toclevel
= par
.layout().toclevel
;
553 if (toclevel
!= Layout::NOT_IN_TOC
&& toclevel
>= min_toclevel
) {
555 // insert this into the table of contents
556 if (tocstring
.empty())
557 tocstring
= par
.asString(AS_STR_LABEL
| AS_STR_INSETS
);
558 toc
.push_back(TocItem(dit
, toclevel
- min_toclevel
, tocstring
));
561 // And now the list of changes.
562 par
.addChangesToToc(dit
, buffer());
567 bool InsetText::notifyCursorLeaves(Cursor
const & old
, Cursor
& cur
)
569 if (buffer().isClean())
570 return Inset::notifyCursorLeaves(old
, cur
);
572 // find text inset in old cursor
573 Cursor insetCur
= old
;
574 int scriptSlice
= insetCur
.find(this);
575 LASSERT(scriptSlice
!= -1, /**/);
576 insetCur
.cutOff(scriptSlice
);
577 LASSERT(&insetCur
.inset() == this, /**/);
579 // update the old paragraph's words
580 insetCur
.paragraph().updateWords();
582 return Inset::notifyCursorLeaves(old
, cur
);
586 bool InsetText::completionSupported(Cursor
const & cur
) const
588 //LASSERT(&cur.bv().cursor().inset() != this, return false);
589 return text_
.completionSupported(cur
);
593 bool InsetText::inlineCompletionSupported(Cursor
const & cur
) const
595 return completionSupported(cur
);
599 bool InsetText::automaticInlineCompletion() const
601 return lyxrc
.completion_inline_text
;
605 bool InsetText::automaticPopupCompletion() const
607 return lyxrc
.completion_popup_text
;
611 bool InsetText::showCompletionCursor() const
613 return lyxrc
.completion_cursor_text
;
617 CompletionList
const * InsetText::createCompletionList(Cursor
const & cur
) const
619 return completionSupported(cur
) ? text_
.createCompletionList(cur
) : 0;
623 docstring
InsetText::completionPrefix(Cursor
const & cur
) const
625 if (!completionSupported(cur
))
627 return text_
.completionPrefix(cur
);
631 bool InsetText::insertCompletion(Cursor
& cur
, docstring
const & s
,
634 if (!completionSupported(cur
))
637 return text_
.insertCompletion(cur
, s
, finished
);
641 void InsetText::completionPosAndDim(Cursor
const & cur
, int & x
, int & y
,
642 Dimension
& dim
) const
644 TextMetrics
const & tm
= cur
.bv().textMetrics(&text_
);
645 tm
.completionPosAndDim(cur
, x
, y
, dim
);
649 docstring
InsetText::contextMenu(BufferView
const &, int, int) const
651 return from_ascii("context-edit");
655 InsetCaption
const * InsetText::getCaptionInset() const
657 ParagraphList::const_iterator pit
= paragraphs().begin();
658 for (; pit
!= paragraphs().end(); ++pit
) {
659 InsetList::const_iterator it
= pit
->insetList().begin();
660 for (; it
!= pit
->insetList().end(); ++it
) {
661 Inset
& inset
= *it
->inset
;
662 if (inset
.lyxCode() == CAPTION_CODE
) {
663 InsetCaption
const * ins
=
664 static_cast<InsetCaption
const *>(it
->inset
);
673 docstring
InsetText::getCaptionText(OutputParams
const & runparams
) const
675 InsetCaption
const * ins
= getCaptionInset();
679 odocstringstream ods
;
680 ins
->getCaptionAsPlaintext(ods
, runparams
);
685 docstring
InsetText::getCaptionHTML(OutputParams
const & runparams
) const
687 InsetCaption
const * ins
= getCaptionInset();
691 odocstringstream ods
;
692 docstring def
= ins
->getCaptionAsHTML(ods
, runparams
);