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