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