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