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