]> git.lyx.org Git - lyx.git/blob - src/insets/insetert.C
4940913e0f810a215d1b6ca8c9739236a3bff1c5
[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)
67         : InsetCollapsable(in), status_(in.status_)
68 {
69         init();
70 }
71
72
73 InsetBase * InsetERT::clone() const
74 {
75         return new InsetERT(*this);
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";
350                         ++lines;
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                 inset.getLyXText(cmd.view())->fullRebreak();
456                 inset.update(cmd.view(), true);
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         if (inlined())
554                 inset.metrics(mi, dim);
555         else
556                 InsetCollapsable::metrics(mi, dim);
557 }
558
559
560 void InsetERT::draw(PainterInfo & pi, int x, int y) const
561 {
562         InsetCollapsable::draw(pi, x, y, inlined());
563 }
564
565
566 void InsetERT::set_latex_font(BufferView * /* bv */)
567 {
568 #ifdef SET_HARD_FONT
569         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
570
571         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
572         font.setColor(LColor::latex);
573
574         inset.getLyXText(bv)->setFont(bv, font, false);
575 #endif
576 }
577
578
579 // attention this function can be called with bv == 0
580 void InsetERT::status(BufferView * bv, ERTStatus const st) const
581 {
582         if (st != status_) {
583                 status_ = st;
584                 switch (st) {
585                 case Inlined:
586                         if (bv)
587                                 inset.setUpdateStatus(bv, InsetText::INIT);
588                         break;
589                 case Open:
590                         collapsed_ = false;
591                         setButtonLabel();
592                         break;
593                 case Collapsed:
594                         collapsed_ = true;
595                         setButtonLabel();
596                         if (bv)
597                                 bv->unlockInset(const_cast<InsetERT *>(this));
598                         break;
599                 }
600                 if (bv) {
601                         bv->updateInset(const_cast<InsetERT *>(this));
602                         bv->buffer()->markDirty();
603                 }
604         }
605 }
606
607
608 bool InsetERT::showInsetDialog(BufferView * bv) const
609 {
610         InsetERTMailer mailer(const_cast<InsetERT &>(*this));
611         mailer.showDialog(bv);
612         return true;
613 }
614
615
616 void InsetERT::open(BufferView * bv)
617 {
618         if (!collapsed_)
619                 return;
620         status(bv, Open);
621 }
622
623
624 void InsetERT::close(BufferView * bv) const
625 {
626         if (status_ == Collapsed || status_ == Inlined)
627                 return;
628
629         status(bv, Collapsed);
630 }
631
632
633 WordLangTuple const
634 InsetERT::selectNextWordToSpellcheck(BufferView * bv, float &) const
635 {
636         bv->unlockInset(const_cast<InsetERT *>(this));
637         return WordLangTuple();
638 }
639
640
641 void InsetERT::getDrawFont(LyXFont & font) const
642 {
643         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
644         font = f;
645         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
646         font.setColor(LColor::latex);
647 }
648
649
650 int InsetERT::getMaxWidth(BufferView * bv, UpdatableInset const * in) const
651 {
652         int w = InsetCollapsable::getMaxWidth(bv, in);
653         if (status_ != Inlined || w < 0)
654                 return w;
655         LyXText * text = inset.getLyXText(bv);
656         int rw = text->rows().begin()->width();
657         if (!rw)
658                 rw = w;
659         rw += 40;
660         if (text->rows().size() == 1 && rw < w)
661                 return -1;
662         return w;
663 }
664
665
666 void InsetERT::update(BufferView * bv, bool reinit)
667 {
668         if (inset.need_update & InsetText::INIT ||
669             inset.need_update & InsetText::FULL) {
670                 setButtonLabel();
671         }
672
673         InsetCollapsable::update(bv, reinit);
674 }
675
676
677 string const InsetERTMailer::name_("ert");
678
679 InsetERTMailer::InsetERTMailer(InsetERT & inset)
680         : inset_(inset)
681 {}
682
683
684 string const InsetERTMailer::inset2string() const
685 {
686         return params2string(inset_.status());
687 }
688
689
690 void InsetERTMailer::string2params(string const & in,
691                                    InsetERT::ERTStatus & status)
692 {
693         status = InsetERT::Collapsed;
694
695         string name;
696         string body = split(in, name, ' ');
697
698         if (body.empty())
699                 return;
700
701         status = static_cast<InsetERT::ERTStatus>(strToInt(body));
702 }
703
704
705 string const
706 InsetERTMailer::params2string(InsetERT::ERTStatus status)
707 {
708         return name_ + ' ' + tostr(status);
709 }