]> git.lyx.org Git - lyx.git/blob - src/insets/insetert.C
prevent crash when inserting minipage in table cell,
[lyx.git] / src / insets / insetert.C
1 /**
2  * \file insetert.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Lars Gullik Bjønnes
8  *
9  * Full author contact details are available in file CREDITS
10  */
11 #include <config.h>
12
13 #include "insetert.h"
14 #include "insettext.h"
15
16 #include "buffer.h"
17 #include "BufferView.h"
18 #include "debug.h"
19 #include "funcrequest.h"
20 #include "gettext.h"
21 #include "language.h"
22 #include "lyxfont.h"
23 #include "lyxlex.h"
24 #include "lyxrow.h"
25 #include "lyxtext.h"
26 #include "WordLangTuple.h"
27
28 #include "frontends/Alert.h"
29 #include "frontends/Dialogs.h"
30 #include "frontends/LyXView.h"
31
32 #include "support/LOstream.h"
33 #include "support/LAssert.h"
34 #include "support/tostr.h"
35
36 using namespace lyx::support;
37
38 using lyx::pos_type;
39
40 using std::ostream;
41 using std::min;
42 using std::endl;
43 using std::auto_ptr;
44
45
46 void InsetERT::init()
47 {
48         setButtonLabel();
49         labelfont = LyXFont(LyXFont::ALL_SANE);
50         labelfont.decSize();
51         labelfont.decSize();
52         labelfont.setColor(LColor::latex);
53         setInsetName("ERT");
54 }
55
56
57 InsetERT::InsetERT(BufferParams const & bp, bool collapsed)
58         : InsetCollapsable(bp, collapsed)
59 {
60         if (collapsed)
61                 status_ = Collapsed;
62         else
63                 status_ = Open;
64         init();
65 }
66
67
68 InsetERT::InsetERT(InsetERT const & in)
69         : InsetCollapsable(in), status_(in.status_)
70 {
71         init();
72 }
73
74
75 auto_ptr<InsetBase> InsetERT::clone() const
76 {
77         return auto_ptr<InsetBase>(new InsetERT(*this));
78 }
79
80
81 InsetERT::InsetERT(BufferParams const & bp,
82                    Language const * l, string const & contents, bool collapsed)
83         : InsetCollapsable(bp, collapsed)
84 {
85         if (collapsed)
86                 status_ = Collapsed;
87         else
88                 status_ = Open;
89
90         LyXFont font(LyXFont::ALL_INHERIT, l);
91 #ifdef SET_HARD_FONT
92         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
93         font.setColor(LColor::latex);
94 #endif
95
96         string::const_iterator cit = contents.begin();
97         string::const_iterator end = contents.end();
98         pos_type pos = 0;
99         for (; cit != end; ++cit) {
100                 inset.paragraphs.begin()->insertChar(pos++, *cit, font);
101         }
102         // the init has to be after the initialization of the paragraph
103         // because of the label settings (draw_label for ert insets).
104         init();
105 }
106
107
108 InsetERT::~InsetERT()
109 {
110         InsetERTMailer(*this).hideDialog();
111 }
112
113
114 void InsetERT::read(Buffer const * buf, LyXLex & lex)
115 {
116         bool token_found = false;
117         if (lex.isOK()) {
118                 lex.next();
119                 string const token = lex.getString();
120                 if (token == "status") {
121                         lex.next();
122                         string const tmp_token = lex.getString();
123
124                         if (tmp_token == "Inlined") {
125                                 status(0, Inlined);
126                         } else if (tmp_token == "Collapsed") {
127                                 status(0, Collapsed);
128                         } else {
129                                 // leave this as default!
130                                 status(0, Open);
131                         }
132
133                         token_found = true;
134                 } else {
135                         lyxerr << "InsetERT::Read: Missing 'status'-tag!"
136                                    << endl;
137                         // take countermeasures
138                         lex.pushToken(token);
139                 }
140         }
141 #if 0
142 #warning this should be really short lived only for compatibility to
143 #warning files written 07/08/2001 so this has to go before 1.2.0! (Jug)
144         if (lex.isOK()) {
145                 lex.next();
146                 string const token = lex.getString();
147                 if (token == "collapsed") {
148                         lex.next();
149                         collapsed_ = lex.getBool();
150                 } else {
151                         // Take countermeasures
152                         lex.pushToken(token);
153                 }
154         }
155 #endif
156         inset.read(buf, lex);
157
158 #ifdef SET_HARD_FONT
159         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
160         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
161         font.setColor(LColor::latex);
162
163         ParagraphList::iterator pit = inset.paragraphs.begin();
164         ParagraphList::iterator pend = inset.paragraphs.end();
165         for (; pit != pend; ++pit) {
166                 pos_type siz = pit->size();
167                 for (pos_type i = 0; i < siz; ++i) {
168                         pit->setFont(i, font);
169                 }
170         }
171 #endif
172
173         if (!token_found) {
174                 if (collapsed_) {
175                         status(0, Collapsed);
176                 } else {
177                         status(0, Open);
178                 }
179         }
180         setButtonLabel();
181 }
182
183
184 void InsetERT::write(Buffer const * buf, ostream & os) const
185 {
186         string st;
187
188         switch (status_) {
189         case Open:
190                 st = "Open";
191                 break;
192         case Collapsed:
193                 st = "Collapsed";
194                 break;
195         case Inlined:
196                 st = "Inlined";
197                 break;
198         }
199
200         os << getInsetName() << "\n"
201            << "status "<< st << "\n";
202
203         //inset.writeParagraphData(buf, os);
204         string const layout(buf->params.getLyXTextClass().defaultLayoutName());
205         ParagraphList::iterator par = inset.paragraphs.begin();
206         ParagraphList::iterator end = inset.paragraphs.end();
207         for (; par != end; ++par) {
208                 os << "\n\\begin_layout " << layout << "\n";
209                 pos_type siz = par->size();
210                 for (pos_type i = 0; i < siz; ++i) {
211                         Paragraph::value_type c = par->getChar(i);
212                         switch (c) {
213                         case Paragraph::META_INSET:
214                                 if (par->getInset(i)->lyxCode() != InsetOld::NEWLINE_CODE) {
215                                         lyxerr << "Element is not allowed in insertERT"
216                                                << endl;
217                                 } else {
218                                         par->getInset(i)->write(buf, os);
219                                 }
220                                 break;
221
222                         case '\\':
223                                 os << "\n\\backslash \n";
224                                 break;
225                         default:
226                                 os << c;
227                                 break;
228                         }
229                 }
230                 os << "\n\\end_layout\n";
231         }
232 }
233
234
235 string const InsetERT::editMessage() const
236 {
237         return _("Opened ERT Inset");
238 }
239
240
241 bool InsetERT::insertInset(BufferView *, InsetOld *)
242 {
243         return false;
244 }
245
246
247 void InsetERT::setFont(BufferView *, LyXFont const &, bool, bool selectall)
248 {
249 #ifdef WITH_WARNINGS
250 #warning FIXME. More UI stupidity...
251 #endif
252         // if selectall is activated then the fontchange was an outside general
253         // fontchange and this messages is not needed
254         if (!selectall)
255                 Alert::error(_("Cannot change font"),
256                            _("You cannot change font settings inside TeX code."));
257 }
258
259
260 void InsetERT::updateStatus(BufferView * bv, bool swap) const
261 {
262         if (status_ != Inlined) {
263                 if (collapsed_) {
264                         status(bv, swap ? Open : Collapsed);
265                 } else {
266                         status(bv, swap ? Collapsed : Open);
267                 }
268         }
269 }
270
271
272 InsetOld::EDITABLE InsetERT::editable() const
273 {
274         if (status_ == Collapsed)
275                 return IS_EDITABLE;
276         return HIGHLY_EDITABLE;
277 }
278
279
280 void InsetERT::lfunMousePress(FuncRequest const & cmd)
281 {
282         if (status_ == Inlined)
283                 inset.localDispatch(cmd);
284         else
285                 InsetCollapsable::localDispatch(cmd);
286 }
287
288
289 bool InsetERT::lfunMouseRelease(FuncRequest const & cmd)
290 {
291         BufferView * bv = cmd.view();
292
293         if (cmd.button() == mouse_button::button3) {
294                 showInsetDialog(bv);
295                 return true;
296         }
297
298         if (status_ != Inlined && hitButton(cmd)) {
299                 updateStatus(bv, true);
300         } else {
301                 FuncRequest cmd1 = cmd;
302 #warning metrics?
303                 cmd1.y = ascent() + cmd.y - inset.ascent();
304
305                 // inlined is special - the text appears above
306                 // button_dim.y2
307                 if (status_ == Inlined)
308                         inset.localDispatch(cmd1);
309                 else if (!collapsed_ && (cmd.y > button_dim.y2)) {
310                         cmd1.y -= height_collapsed();
311                         inset.localDispatch(cmd1);
312                 }
313         }
314         return false;
315 }
316
317
318 void InsetERT::lfunMouseMotion(FuncRequest const & cmd)
319 {
320         if (status_ == Inlined)
321                 inset.localDispatch(cmd);
322         else
323                 InsetCollapsable::localDispatch(cmd);
324 }
325
326
327 int InsetERT::latex(Buffer const *, ostream & os,
328                     LatexRunParams const &) const
329 {
330         ParagraphList::iterator par = inset.paragraphs.begin();
331         ParagraphList::iterator end = inset.paragraphs.end();
332
333         int lines = 0;
334         while (par != end) {
335                 pos_type siz = par->size();
336                 for (pos_type i = 0; i < siz; ++i) {
337                         // ignore all struck out text
338                         if (isDeletedText(*par, i))
339                                 continue;
340
341                         if (par->isNewline(i)) {
342                                 os << '\n';
343                                 ++lines;
344                         } else {
345                                 os << par->getChar(i);
346                         }
347                 }
348                 ++par;
349                 if (par != end) {
350                         os << "\n";
351                         ++lines;
352                 }
353         }
354
355         return lines;
356 }
357
358
359 int InsetERT::ascii(Buffer const *, ostream &, int /*linelen*/) const
360 {
361         return 0;
362 }
363
364
365 int InsetERT::linuxdoc(Buffer const *, ostream & os) const
366 {
367         ParagraphList::iterator par = inset.paragraphs.begin();
368         ParagraphList::iterator end = inset.paragraphs.end();
369
370         int lines = 0;
371         while (par != end) {
372                 pos_type siz = par->size();
373                 for (pos_type i = 0; i < siz; ++i) {
374                         if (par->isNewline(i)) {
375                                 os << '\n';
376                                 ++lines;
377                         } else {
378                                 os << par->getChar(i);
379                         }
380                 }
381                 ++par;
382                 if (par != end) {
383                         os << "\n";
384                         lines ++;
385                 }
386         }
387
388         return lines;
389 }
390
391
392 int InsetERT::docbook(Buffer const *, ostream & os, bool) const
393 {
394         ParagraphList::iterator par = inset.paragraphs.begin();
395         ParagraphList::iterator end = inset.paragraphs.end();
396
397         int lines = 0;
398         while (par != end) {
399                 pos_type siz = par->size();
400                 for (pos_type i = 0; i < siz; ++i) {
401                         if (par->isNewline(i)) {
402                                 os << '\n';
403                                 ++lines;
404                         } else {
405                                 os << par->getChar(i);
406                         }
407                 }
408                 ++par;
409                 if (par != end) {
410                         os << "\n";
411                         lines ++;
412                 }
413         }
414
415         return lines;
416 }
417
418
419 InsetOld::RESULT InsetERT::localDispatch(FuncRequest const & cmd)
420 {
421         InsetOld::RESULT result = UNDISPATCHED;
422         BufferView * bv = cmd.view();
423
424         if (inset.paragraphs.begin()->empty()) {
425                 set_latex_font(bv);
426         }
427
428         switch (cmd.action) {
429
430         case LFUN_INSET_EDIT:
431                 if (cmd.button() == mouse_button::button3)
432                         break;
433                 if (status_ == Inlined) {
434                         if (!bv->lockInset(this))
435                                 break;
436                         result = inset.localDispatch(cmd);
437                 } else {
438                         result = InsetCollapsable::localDispatch(cmd);
439                 }
440                 set_latex_font(bv);
441                 updateStatus(bv);
442                 break;
443
444         case LFUN_INSET_MODIFY: {
445                 InsetERT::ERTStatus status_;
446                 InsetERTMailer::string2params(cmd.argument, status_);
447
448                 status(bv, status_);
449
450                 /* FIXME: I refuse to believe we have to live
451                  * with ugliness like this ! Note that this
452                  * rebreak *is* needed. Consider a change from
453                  * Open (needfullrow) to Inlined (only the space
454                  * taken by the text).
455                  */
456                 inset.getLyXText(cmd.view())->fullRebreak();
457                 bv->updateInset(this);
458                 result = DISPATCHED;
459         }
460         break;
461
462         case LFUN_MOUSE_PRESS:
463                 lfunMousePress(cmd);
464                 result = DISPATCHED;
465                 break;
466
467         case LFUN_MOUSE_MOTION:
468                 lfunMouseMotion(cmd);
469                 result = DISPATCHED;
470                 break;
471
472         case LFUN_MOUSE_RELEASE:
473                 lfunMouseRelease(cmd);
474                 result = DISPATCHED;
475                 break;
476
477         case LFUN_LAYOUT:
478                 bv->owner()->setLayout(inset.paragraphs.begin()->layout()->name());
479                 result = DISPATCHED_NOUPDATE;
480                 break;
481
482         default:
483                 result = InsetCollapsable::localDispatch(cmd);
484         }
485
486         switch (cmd.action) {
487         case LFUN_BREAKPARAGRAPH:
488         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
489         case LFUN_BACKSPACE:
490         case LFUN_BACKSPACE_SKIP:
491         case LFUN_DELETE:
492         case LFUN_DELETE_SKIP:
493         case LFUN_DELETE_LINE_FORWARD:
494         case LFUN_CUT:
495                 set_latex_font(bv);
496                 break;
497
498         default:
499                 break;
500         }
501         return result;
502 }
503
504
505 string const InsetERT::get_new_label() const
506 {
507         string la;
508         pos_type const max_length = 15;
509         pos_type const p_siz = inset.paragraphs.begin()->size();
510         pos_type const n = min(max_length, p_siz);
511         pos_type i = 0;
512         pos_type j = 0;
513         for(; i < n && j < p_siz; ++j) {
514                 if (inset.paragraphs.begin()->isInset(j))
515                         continue;
516                 la += inset.paragraphs.begin()->getChar(j);
517                 ++i;
518         }
519         if (p_siz > 1 || (i > 0 && j < p_siz)) {
520                 la += "...";
521         }
522         if (la.empty()) {
523                 la = _("ERT");
524         }
525         return la;
526 }
527
528
529 void InsetERT::setButtonLabel() const
530 {
531         if (status_ == Collapsed) {
532                 setLabel(get_new_label());
533         } else {
534                 setLabel(_("ERT"));
535         }
536 }
537
538
539 bool InsetERT::checkInsertChar(LyXFont & /* font */)
540 {
541 #ifdef SET_HARD_FONT
542         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
543         font = f;
544         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
545         font.setColor(LColor::latex);
546 #endif
547         return true;
548 }
549
550
551 void InsetERT::metrics(MetricsInfo & mi, Dimension & dim) const
552 {
553         setButtonLabel();
554         if (inlined())
555                 inset.metrics(mi, dim);
556         else
557                 InsetCollapsable::metrics(mi, dim);
558         dim_ = dim;
559 }
560
561
562 void InsetERT::draw(PainterInfo & pi, int x, int y) const
563 {
564         InsetCollapsable::draw(pi, x, y, inlined());
565 }
566
567
568 void InsetERT::set_latex_font(BufferView * /* bv */)
569 {
570 #ifdef SET_HARD_FONT
571         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
572
573         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
574         font.setColor(LColor::latex);
575
576         inset.getLyXText(bv)->setFont(bv, font, false);
577 #endif
578 }
579
580
581 // attention this function can be called with bv == 0
582 void InsetERT::status(BufferView * bv, ERTStatus const st) const
583 {
584         if (st != status_) {
585                 status_ = st;
586                 switch (st) {
587                 case Inlined:
588                         if (bv)
589                                 inset.setUpdateStatus(InsetText::INIT);
590                         break;
591                 case Open:
592                         collapsed_ = false;
593                         setButtonLabel();
594                         break;
595                 case Collapsed:
596                         collapsed_ = true;
597                         setButtonLabel();
598                         if (bv)
599                                 bv->unlockInset(const_cast<InsetERT *>(this));
600                         break;
601                 }
602                 if (bv) {
603                         bv->updateInset(const_cast<InsetERT *>(this));
604                         bv->buffer()->markDirty();
605                 }
606         }
607 }
608
609
610 bool InsetERT::showInsetDialog(BufferView * bv) const
611 {
612         InsetERTMailer(const_cast<InsetERT &>(*this)).showDialog(bv);
613         return true;
614 }
615
616
617 void InsetERT::open(BufferView * bv)
618 {
619         if (!collapsed_)
620                 return;
621         status(bv, Open);
622 }
623
624
625 void InsetERT::close(BufferView * bv) const
626 {
627         if (status_ == Collapsed || status_ == Inlined)
628                 return;
629
630         status(bv, Collapsed);
631 }
632
633
634 WordLangTuple const
635 InsetERT::selectNextWordToSpellcheck(BufferView * bv, float &) const
636 {
637         bv->unlockInset(const_cast<InsetERT *>(this));
638         return WordLangTuple();
639 }
640
641
642 void InsetERT::getDrawFont(LyXFont & font) const
643 {
644         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
645         font = f;
646         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
647         font.setColor(LColor::latex);
648 }
649
650
651 string const InsetERTMailer::name_("ert");
652
653 InsetERTMailer::InsetERTMailer(InsetERT & inset)
654         : inset_(inset)
655 {}
656
657
658 string const InsetERTMailer::inset2string(Buffer const &) const
659 {
660         return params2string(inset_.status());
661 }
662
663
664 void InsetERTMailer::string2params(string const & in,
665                                    InsetERT::ERTStatus & status)
666 {
667         status = InsetERT::Collapsed;
668
669         string name;
670         string body = split(in, name, ' ');
671
672         if (body.empty())
673                 return;
674
675         status = static_cast<InsetERT::ERTStatus>(strToInt(body));
676 }
677
678
679 string const
680 InsetERTMailer::params2string(InsetERT::ERTStatus status)
681 {
682         return name_ + ' ' + tostr(status);
683 }