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