]> git.lyx.org Git - features.git/blob - src/insets/insetert.C
0e53c9db9ced9656d26f70f448f22ee509a41c91
[features.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/lstrings.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 void InsetERT::edit(BufferView * bv, int x, int y, mouse_button::state button)
270 {
271         if (button == mouse_button::button3)
272                 return;
273
274         if (status_ == Inlined) {
275                 if (!bv->lockInset(this))
276                         return;
277                 inset.edit(bv, x, y, button);
278         } else {
279                 InsetCollapsable::edit(bv, x, y, button);
280         }
281         set_latex_font(bv);
282         updateStatus(bv);
283 }
284
285
286 Inset::EDITABLE InsetERT::editable() const
287 {
288         if (status_ == Collapsed)
289                 return IS_EDITABLE;
290         return HIGHLY_EDITABLE;
291 }
292
293
294 void InsetERT::edit(BufferView * bv, bool front)
295 {
296         InsetCollapsable::edit(bv, front);
297         updateStatus(0);
298         set_latex_font(bv);
299 }
300
301
302 void InsetERT::lfunMousePress(FuncRequest const & cmd)
303 {
304         if (status_ == Inlined)
305                 inset.localDispatch(cmd);
306         else
307                 InsetCollapsable::localDispatch(cmd);
308 }
309
310
311 bool InsetERT::lfunMouseRelease(FuncRequest const & cmd)
312 {
313         BufferView * bv = cmd.view();
314
315         if (cmd.button() == mouse_button::button3) {
316                 showInsetDialog(bv);
317                 return true;
318         }
319
320         if (status_ != Inlined && (cmd.x >= 0) && (cmd.x < button_length) &&
321             (cmd.y >= button_top_y) && (cmd.y <= button_bottom_y)) {
322                 updateStatus(bv, true);
323         } else {
324                 LyXFont font(LyXFont::ALL_SANE);
325                 FuncRequest cmd1 = cmd;
326                 cmd1.y = ascent(bv, font) + cmd.y - inset.ascent(bv, font);
327
328                 // inlined is special - the text appears above
329                 // button_bottom_y
330                 if (status_ == Inlined)
331                         inset.localDispatch(cmd1);
332                 else if (!collapsed_ && (cmd.y > button_bottom_y)) {
333                         cmd1.y -= ascent_collapsed() + descent_collapsed();
334                         inset.localDispatch(cmd1);
335                 }
336         }
337         return false;
338 }
339
340
341 void InsetERT::lfunMouseMotion(FuncRequest const & cmd)
342 {
343         if (status_ == Inlined)
344                 inset.localDispatch(cmd);
345         else
346                 InsetCollapsable::localDispatch(cmd);
347 }
348
349
350 int InsetERT::latex(Buffer const *, ostream & os, bool /*fragile*/,
351                     bool /*free_spc*/) const
352 {
353         ParagraphList::iterator par = inset.paragraphs.begin();
354         ParagraphList::iterator end = inset.paragraphs.end();
355
356         int lines = 0;
357         while (par != end) {
358                 pos_type siz = par->size();
359                 for (pos_type i = 0; i < siz; ++i) {
360                         // ignore all struck out text
361                         if (isDeletedText(*par, i))
362                                 continue;
363
364                         if (par->isNewline(i)) {
365                                 os << '\n';
366                                 ++lines;
367                         } else {
368                                 os << par->getChar(i);
369                         }
370                 }
371                 ++par;
372                 if (par != end) {
373                         os << "\n\n";
374                         lines += 2;
375                 }
376         }
377
378         return lines;
379 }
380
381
382 int InsetERT::ascii(Buffer const *, ostream &, int /*linelen*/) const
383 {
384         return 0;
385 }
386
387
388 int InsetERT::linuxdoc(Buffer const *, ostream & os) 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 int InsetERT::docbook(Buffer const *, ostream & os, bool) const
416 {
417         ParagraphList::iterator par = inset.paragraphs.begin();
418         ParagraphList::iterator end = inset.paragraphs.end();
419
420         int lines = 0;
421         while (par != end) {
422                 pos_type siz = par->size();
423                 for (pos_type i = 0; i < siz; ++i) {
424                         if (par->isNewline(i)) {
425                                 os << '\n';
426                                 ++lines;
427                         } else {
428                                 os << par->getChar(i);
429                         }
430                 }
431                 ++par;
432                 if (par != end) {
433                         os << "\n";
434                         lines ++;
435                 }
436         }
437
438         return lines;
439 }
440
441
442 Inset::RESULT InsetERT::localDispatch(FuncRequest const & cmd)
443 {
444         Inset::RESULT result = UNDISPATCHED;
445         BufferView * bv = cmd.view();
446
447         if (inset.paragraphs.begin()->empty()) {
448                 set_latex_font(bv);
449         }
450
451         switch (cmd.action) {
452         case LFUN_INSET_MODIFY: {
453                 InsetERT::ERTStatus status_;
454                 InsetERTMailer::string2params(cmd.argument, status_);
455
456                 status(bv, status_);
457
458                 /* FIXME: I refuse to believe we have to live
459                  * with ugliness like this ! Note that this
460                  * rebreak *is* needed. Consider a change from
461                  * Open (needfullrow) to Inlined (only the space
462                  * taken by the text).
463                  */
464                 LyXText * t = inset.getLyXText(cmd.view());
465                 t->need_break_row = t->rows().begin();
466                 t->fullRebreak();
467                 t->setCursorIntern(t->cursor.par(), t->cursor.pos());
468                 inset.update(cmd.view(), true);
469                 bv->updateInset(this);
470                 result = DISPATCHED;
471         }
472         break;
473
474         case LFUN_MOUSE_PRESS:
475                 lfunMousePress(cmd);
476                 result = DISPATCHED;
477                 break;
478
479         case LFUN_MOUSE_MOTION:
480                 lfunMouseMotion(cmd);
481                 result = DISPATCHED;
482                 break;
483
484         case LFUN_MOUSE_RELEASE:
485                 lfunMouseRelease(cmd);
486                 result = DISPATCHED;
487                 break;
488
489         case LFUN_LAYOUT:
490                 bv->owner()->setLayout(inset.paragraphs.begin()->layout()->name());
491                 result = DISPATCHED_NOUPDATE;
492                 break;
493
494         default:
495                 result = InsetCollapsable::localDispatch(cmd);
496         }
497
498         switch (cmd.action) {
499         case LFUN_BREAKPARAGRAPH:
500         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
501         case LFUN_BACKSPACE:
502         case LFUN_BACKSPACE_SKIP:
503         case LFUN_DELETE:
504         case LFUN_DELETE_SKIP:
505         case LFUN_DELETE_LINE_FORWARD:
506         case LFUN_CUT:
507                 set_latex_font(bv);
508                 break;
509
510         default:
511                 break;
512         }
513         return result;
514 }
515
516
517 string const InsetERT::get_new_label() const
518 {
519         string la;
520         pos_type const max_length = 15;
521         pos_type const p_siz = inset.paragraphs.begin()->size();
522         pos_type const n = min(max_length, p_siz);
523         pos_type i = 0;
524         pos_type j = 0;
525         for(; i < n && j < p_siz; ++j) {
526                 if (inset.paragraphs.begin()->isInset(j))
527                         continue;
528                 la += inset.paragraphs.begin()->getChar(j);
529                 ++i;
530         }
531         if (boost::next(inset.paragraphs.begin()) != inset.paragraphs.end() ||
532             (i > 0 && j < p_siz)) {
533                 la += "...";
534         }
535         if (la.empty()) {
536                 la = _("ERT");
537         }
538         return la;
539 }
540
541
542 void InsetERT::setButtonLabel() const
543 {
544         if (status_ == Collapsed) {
545                 setLabel(get_new_label());
546         } else {
547                 setLabel(_("ERT"));
548         }
549 }
550
551
552 bool InsetERT::checkInsertChar(LyXFont & /* font */)
553 {
554 #ifdef SET_HARD_FONT
555         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
556         font = f;
557         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
558         font.setColor(LColor::latex);
559 #endif
560         return true;
561 }
562
563
564 int InsetERT::ascent(BufferView * bv, LyXFont const & font) const
565 {
566         if (!inlined())
567                 return InsetCollapsable::ascent(bv, font);
568
569         return inset.ascent(bv, font);
570 }
571
572
573 int InsetERT::descent(BufferView * bv, LyXFont const & font) const
574 {
575         if (!inlined())
576                 return InsetCollapsable::descent(bv, font);
577
578         return inset.descent(bv, font);
579 }
580
581
582 int InsetERT::width(BufferView * bv, LyXFont const & font) const
583 {
584         if (!inlined())
585                 return InsetCollapsable::width(bv, font);
586
587         return inset.width(bv, font);
588 }
589
590
591 void InsetERT::draw(BufferView * bv, LyXFont const & f,
592                     int baseline, float & x) const
593 {
594         InsetCollapsable::draw(bv, f, baseline, x, inlined());
595 }
596
597
598 void InsetERT::set_latex_font(BufferView * /* bv */)
599 {
600 #ifdef SET_HARD_FONT
601         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
602
603         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
604         font.setColor(LColor::latex);
605
606         inset.getLyXText(bv)->setFont(bv, font, false);
607 #endif
608 }
609
610
611 // attention this function can be called with bv == 0
612 void InsetERT::status(BufferView * bv, ERTStatus const st) const
613 {
614         if (st != status_) {
615                 status_ = st;
616                 switch (st) {
617                 case Inlined:
618                         if (bv)
619                                 inset.setUpdateStatus(bv, InsetText::INIT);
620                         break;
621                 case Open:
622                         collapsed_ = false;
623                         setButtonLabel();
624                         break;
625                 case Collapsed:
626                         collapsed_ = true;
627                         setButtonLabel();
628                         if (bv)
629                                 bv->unlockInset(const_cast<InsetERT *>(this));
630                         break;
631                 }
632                 if (bv) {
633                         bv->updateInset(const_cast<InsetERT *>(this));
634                         bv->buffer()->markDirty();
635                 }
636         }
637 }
638
639
640 bool InsetERT::showInsetDialog(BufferView * bv) const
641 {
642         InsetERTMailer mailer(const_cast<InsetERT &>(*this));
643         mailer.showDialog(bv);
644         return true;
645 }
646
647
648 void InsetERT::open(BufferView * bv)
649 {
650         if (!collapsed_)
651                 return;
652         status(bv, Open);
653 }
654
655
656 void InsetERT::close(BufferView * bv) const
657 {
658         if (status_ == Collapsed || status_ == Inlined)
659                 return;
660
661         status(bv, Collapsed);
662 }
663
664
665 WordLangTuple const
666 InsetERT::selectNextWordToSpellcheck(BufferView * bv, float &) const
667 {
668         bv->unlockInset(const_cast<InsetERT *>(this));
669         return WordLangTuple();
670 }
671
672
673 void InsetERT::getDrawFont(LyXFont & font) const
674 {
675         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
676         font = f;
677         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
678         font.setColor(LColor::latex);
679 }
680
681
682 int InsetERT::getMaxWidth(BufferView * bv, UpdatableInset const * in) const
683 {
684         int w = InsetCollapsable::getMaxWidth(bv, in);
685         if (status_ != Inlined || w < 0)
686                 return w;
687         LyXText * text = inset.getLyXText(bv);
688         int rw = text->rows().begin()->width();
689         if (!rw)
690                 rw = w;
691         rw += 40;
692         if (text->rows().size() == 1 && rw < w)
693                 return -1;
694         return w;
695 }
696
697
698 void InsetERT::update(BufferView * bv, bool reinit)
699 {
700         if (inset.need_update & InsetText::INIT ||
701             inset.need_update & InsetText::FULL) {
702                 setButtonLabel();
703         }
704
705         InsetCollapsable::update(bv, reinit);
706 }
707
708
709 string const InsetERTMailer::name_("ert");
710
711 InsetERTMailer::InsetERTMailer(InsetERT & inset)
712         : inset_(inset)
713 {}
714
715
716 string const InsetERTMailer::inset2string() const
717 {
718         return params2string(inset_.status());
719 }
720
721
722 void InsetERTMailer::string2params(string const & in,
723                                    InsetERT::ERTStatus & status)
724 {
725         status = InsetERT::Collapsed;
726
727         string name;
728         string body = split(in, name, ' ');
729
730         if (body.empty())
731                 return;
732
733         status = static_cast<InsetERT::ERTStatus>(strToInt(body));
734 }
735
736
737 string const
738 InsetERTMailer::params2string(InsetERT::ERTStatus status)
739 {
740         return name_ + ' ' + tostr(status);
741 }