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