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