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