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