]> git.lyx.org Git - lyx.git/blob - src/insets/insetert.C
handle missing layout gracefully when reading and writing lyx files
[lyx.git] / src / insets / insetert.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *          Copyright 1998 The LyX Team.
7  *
8  *======================================================*/
9
10 #include <config.h>
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 #include "insetert.h"
17 #include "gettext.h"
18 #include "lyxfont.h"
19 #include "language.h"
20 #include "buffer.h"
21 #include "BufferView.h"
22 #include "LyXView.h"
23 #include "lyxtext.h"
24 #include "debug.h"
25 #include "lyxtextclasslist.h"
26 #include "lyxrow.h"
27
28 #include "insets/insettext.h"
29
30 #include "frontends/Dialogs.h"
31 #include "frontends/Alert.h"
32
33 #include "support/LOstream.h"
34
35
36 using std::ostream;
37 using std::min;
38 using std::endl;
39
40 using lyx::pos_type;
41
42
43 void InsetERT::init()
44 {
45         setButtonLabel();
46         labelfont = LyXFont(LyXFont::ALL_SANE);
47         labelfont.decSize();
48         labelfont.decSize();
49         labelfont.setColor(LColor::latex);
50         setInsetName("ERT");
51 }
52
53
54 InsetERT::InsetERT(BufferParams const & bp, bool collapsed)
55         : InsetCollapsable(bp, collapsed)
56 {
57         if (collapsed)
58                 status_ = Collapsed;
59         else
60                 status_ = Open;
61         init();
62 }
63
64
65 InsetERT::InsetERT(InsetERT const & in, bool same_id)
66         : InsetCollapsable(in, same_id), status_(in.status_)
67 {
68         init();
69 }
70
71
72 Inset * InsetERT::clone(Buffer const &, bool same_id) const
73 {
74         return new InsetERT(*const_cast<InsetERT *>(this), same_id);
75 }
76
77
78 InsetERT::InsetERT(BufferParams const & bp,
79                    Language const * l, string const & contents, bool collapsed)
80         : InsetCollapsable(bp, collapsed)
81 {
82         if (collapsed)
83                 status_ = Collapsed;
84         else
85                 status_ = Open;
86
87         LyXFont font(LyXFont::ALL_INHERIT, l);
88 #ifdef SET_HARD_FONT
89         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
90         font.setColor(LColor::latex);
91 #endif
92         
93         string::const_iterator cit = contents.begin();
94         string::const_iterator end = contents.end();
95         pos_type pos = 0;
96         for (; cit != end; ++cit) {
97                 inset.paragraph()->insertChar(pos++, *cit, font);
98         }
99         // the init has to be after the initialization of the paragraph
100         // because of the label settings (draw_label for ert insets).
101         init();
102 }
103
104
105 InsetERT::~InsetERT()
106 {
107         hideDialog();
108 }
109
110
111 void InsetERT::read(Buffer const * buf, LyXLex & lex)
112 {
113         bool token_found = false;
114         if (lex.isOK()) {
115                 lex.next();
116                 string const token = lex.getString();
117                 if (token == "status") {
118                         lex.next();
119                         string const tmp_token = lex.getString();
120                         
121                         if (tmp_token == "Inlined") {
122                                 status(0, Inlined);
123                         } else if (tmp_token == "Collapsed") {
124                                 status(0, Collapsed);
125                         } else {
126                                 // leave this as default!
127                                 status(0, Open);
128                         }
129                         
130                         token_found = true;
131                 } else {
132                         lyxerr << "InsetERT::Read: Missing 'status'-tag!"
133                                    << endl;
134                         // take countermeasures
135                         lex.pushToken(token);
136                 }
137         }
138 #if 0
139 #warning this should be really short lived only for compatibility to
140 #warning files written 07/08/2001 so this has to go before 1.2.0! (Jug)
141         if (lex.isOK()) {
142                 lex.next();
143                 string const token = lex.getString();
144                 if (token == "collapsed") {
145                         lex.next();
146                         collapsed_ = lex.getBool();
147                 } else {
148                         // Take countermeasures
149                         lex.pushToken(token);
150                 }
151         }
152 #endif
153         inset.read(buf, lex);
154
155 #ifdef SET_HARD_FONT
156 #ifndef INHERIT_LANG
157         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
158 #else 
159         LyXFont font(LyXFont::ALL_INHERIT);
160 #endif
161         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
162         font.setColor(LColor::latex);
163         Paragraph * par = inset.paragraph();
164         while (par) {
165                 pos_type siz = par->size();
166                 for (pos_type i = 0; i < siz; ++i) {
167                         par->setFont(i, font);
168                 }
169                 par = par->next();
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(textclasslist[buf->params.textclass].defaultLayoutName());
205         Paragraph * par = inset.paragraph();
206         while (par) {
207                 os << "\n\\layout " << layout << "\n";
208                 pos_type siz = par->size();
209                 for (pos_type i = 0; i < siz; ++i) {
210                         Paragraph::value_type c = par->getChar(i);
211                         switch (c) {
212                         case Paragraph::META_INSET:
213                         case Paragraph::META_HFILL:
214                                 lyxerr << "Element is not allowed in insertERT"
215                                        << endl;
216                         case Paragraph::META_NEWLINE:
217                                 os << "\n\\newline \n";
218                                 break;
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  
267 void InsetERT::edit(BufferView * bv, int x, int y, unsigned int button)
268 {
269         if (button == 3)
270                 return;
271
272         if (status_ == Inlined) {
273                 if (!bv->lockInset(this))
274                         return;
275                 inset.edit(bv, x, y, button);
276         } else {
277                 InsetCollapsable::edit(bv, x, y, button);
278         }
279         set_latex_font(bv);
280         updateStatus(bv);
281 }
282
283
284 Inset::EDITABLE InsetERT::editable() const
285 {
286         if (status_ == Collapsed)
287                 return IS_EDITABLE;
288         return HIGHLY_EDITABLE;
289 }
290
291
292 void InsetERT::edit(BufferView * bv, bool front)
293 {
294         InsetCollapsable::edit(bv, front);
295         updateStatus(0);
296         set_latex_font(bv);
297 }
298
299
300
301
302 void InsetERT::insetButtonPress(BufferView * bv,
303                                         int x, int y, int button)
304 {
305         if (status_ == Inlined) {
306                 inset.insetButtonPress(bv, x, y, button);
307         } else {
308                 InsetCollapsable::insetButtonPress(bv, x, y, button);
309         }
310 }
311
312
313 bool InsetERT::insetButtonRelease(BufferView * bv, int x, int y, int button)
314 {
315         if (button == 3) {
316                 showInsetDialog(bv);
317                 return true;
318         }
319  
320         if (status_ != Inlined && (x >= 0)  && (x < button_length) &&
321             (y >= button_top_y) &&  (y <= button_bottom_y)) {
322                 updateStatus(bv, true);
323         } else {
324                 LyXFont font(LyXFont::ALL_SANE);
325                 int yy = ascent(bv, font) + y - inset.ascent(bv, font);
326  
327                 // inlined is special - the text appears above 
328                 // button_bottom_y
329                 if (status_ == Inlined) {
330                         inset.insetButtonRelease(bv, x, yy, button);
331                 } else if (!collapsed_ && (y > button_bottom_y)) {
332                         yy -= (ascent_collapsed() + descent_collapsed());
333                         inset.insetButtonRelease(bv, x, yy, button);
334                 }
335         }
336         return false;
337 }
338
339
340 void InsetERT::insetMotionNotify(BufferView * bv,
341                                          int x, int y, int state)
342 {
343         if (status_ == Inlined) {
344                 inset.insetMotionNotify(bv, x, y, state);
345         } else {
346                 InsetCollapsable::insetMotionNotify(bv, x, y, state);
347         }
348 }
349
350
351 int InsetERT::latex(Buffer const *, ostream & os, bool /*fragile*/,
352                     bool /*free_spc*/) const
353 {
354         Paragraph * par = inset.paragraph();
355         int lines = 0;
356         while (par) {
357                 pos_type siz = par->size();
358                 for (pos_type i = 0; i < siz; ++i) {
359                         Paragraph::value_type c = par->getChar(i);
360                         switch (c) {
361                         case Paragraph::META_NEWLINE:
362                                 os << '\n';
363                                 ++lines;
364                                 break;
365                         default:
366                                 os << c;
367                                 break;
368                         }
369                 }
370                 par = par->next();
371                 if (par) {
372                         os << "\n\n";
373                         lines += 2;
374                 }
375         }
376         
377         return lines;
378 }
379
380
381 int InsetERT::ascii(Buffer const *,
382                     ostream &, int /*linelen*/) const 
383 {
384         return 0;
385 }
386
387
388 int InsetERT::linuxdoc(Buffer const *, ostream & os) const
389 {
390         Paragraph * par = inset.paragraph();
391         int lines = 0;
392         while (par) {
393                 pos_type siz = par->size();
394                 for (pos_type i = 0; i < siz; ++i) {
395                         Paragraph::value_type c = par->getChar(i);
396                         switch (c) {
397                         case Paragraph::META_NEWLINE:
398                                 os << '\n';
399                                 ++lines;
400                                 break;
401                         default:
402                                 os << c;
403                                 break;
404                         }
405                 }
406                 par = par->next();
407                 if (par) {
408                         os << "\n";
409                         lines ++;
410                 }
411         }
412         
413         return lines;
414 }
415
416
417 int InsetERT::docbook(Buffer const *, ostream & os) const
418 {
419         Paragraph * par = inset.paragraph();
420         int lines = 0;
421         while (par) {
422                 pos_type siz = par->size();
423                 for (pos_type i = 0; i < siz; ++i) {
424                         Paragraph::value_type c = par->getChar(i);
425                         switch (c) {
426                         case Paragraph::META_NEWLINE:
427                                 os << '\n';
428                                 ++lines;
429                                 break;
430                         default:
431                                 os << c;
432                                 break;
433                         }
434                 }
435                 par = par->next();
436                 if (par) {
437                         os << "\n";
438                         lines ++;
439                 }
440         }
441         
442         return lines;
443 }
444
445
446 UpdatableInset::RESULT
447 InsetERT::localDispatch(BufferView * bv, kb_action action, string const & arg)
448 {
449         UpdatableInset::RESULT result = DISPATCHED_NOUPDATE;
450
451         if (!inset.paragraph()->size()) {
452                 set_latex_font(bv);
453         }
454
455         switch (action) {
456         case LFUN_LAYOUT:
457                 bv->owner()->setLayout(inset.paragraph()->layout());
458                 break;
459         default:
460                 result = InsetCollapsable::localDispatch(bv, action, arg);
461         }
462         switch (action) {
463         case LFUN_BREAKPARAGRAPH:
464         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
465         case LFUN_BACKSPACE:
466         case LFUN_BACKSPACE_SKIP:
467         case LFUN_DELETE:
468         case LFUN_DELETE_SKIP:
469         case LFUN_DELETE_LINE_FORWARD:
470         case LFUN_CUT:
471                 set_latex_font(bv);
472                 break;
473         
474         default:
475                 break;
476         }
477         return result;
478 }
479
480
481 string const InsetERT::get_new_label() const
482 {
483         string la;
484         pos_type const max_length = 15;
485         pos_type const p_siz = inset.paragraph()->size();
486         pos_type const n = min(max_length, p_siz);
487         int i = 0;
488         int j = 0;
489         for(; i < n && j < p_siz; ++j) {
490                 if (inset.paragraph()->isInset(j))
491                         continue;
492                 la += inset.paragraph()->getChar(j);
493                 ++i;
494         }
495         if (i > 0 && j < p_siz) {
496                 la += "...";
497         }
498         if (la.empty()) {
499                 la = _("ERT");
500         }
501         return la;
502 }
503
504
505 void InsetERT::setButtonLabel() const
506 {
507         if (status_ == Collapsed) {
508                 setLabel(get_new_label());
509         } else {
510                 setLabel(_("ERT"));
511         }
512 }
513
514
515 bool InsetERT::checkInsertChar(LyXFont & /* font */)
516 {
517 #ifdef SET_HARD_FONT
518 #ifndef INHERIT_LANG
519         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
520 #else 
521         LyXFont f(LyXFont::ALL_INHERIT);
522 #endif
523         font = f;
524         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
525         font.setColor(LColor::latex);
526 #endif
527         return true;
528 }
529
530
531 int InsetERT::ascent(BufferView * bv, LyXFont const & font) const
532 {
533         if (!inlined())
534                 return InsetCollapsable::ascent(bv, font);
535
536         return inset.ascent(bv, font);
537 }
538
539
540 int InsetERT::descent(BufferView * bv, LyXFont const & font) const
541 {
542         if (!inlined())
543                 return InsetCollapsable::descent(bv, font);
544
545         return inset.descent(bv, font);
546 }
547
548
549 int InsetERT::width(BufferView * bv, LyXFont const & font) const
550 {
551         if (!inlined())
552                 return InsetCollapsable::width(bv, font);
553
554         return inset.width(bv, font);
555 }
556
557
558 void InsetERT::draw(BufferView * bv, LyXFont const & f, 
559                     int baseline, float & x, bool cleared) const
560 {
561         Painter & pain = bv->painter();
562
563         button_length = width_collapsed();
564         button_top_y = -ascent(bv, f);
565         button_bottom_y = -ascent(bv, f) + ascent_collapsed() +
566                 descent_collapsed();
567
568         if (!isOpen()) {
569                 draw_collapsed(pain, baseline, x);
570                 x += TEXT_TO_INSET_OFFSET;
571                 return;
572         }
573
574         float old_x = x;
575
576         if (!owner())
577                 x += static_cast<float>(scroll());
578
579         if (!cleared && (inset.need_update == InsetText::FULL ||
580                          inset.need_update == InsetText::INIT ||
581                          top_x != int(x) ||
582                          top_baseline != baseline))
583         {
584                 // we don't need anymore to clear here we just have to tell
585                 // the underlying LyXText that it should do the RowClear!
586                 inset.setUpdateStatus(bv, InsetText::FULL);
587                 bv->text->status(bv, LyXText::CHANGED_IN_DRAW);
588                 return;
589         }
590
591         top_x = int(x);
592         topx_set = true;
593         top_baseline = baseline;
594
595         int const bl = baseline - ascent(bv, f) + ascent_collapsed();
596
597         if (inlined()) {
598                 inset.draw(bv, f, baseline, x, cleared);
599         } else {
600                 draw_collapsed(pain, bl, old_x);
601                 inset.draw(bv, f, 
602                                    bl + descent_collapsed() + inset.ascent(bv, f),
603                                    x, cleared);
604         }
605         need_update = NONE;
606 }
607
608
609 void InsetERT::set_latex_font(BufferView * /* bv */)
610 {
611 #ifdef SET_HARD_FONT
612 #ifndef INHERIT_LANG
613         LyXFont font(LyXFont::ALL_INHERIT, latex_language);
614 #else 
615         LyXFont font(LyXFont::ALL_INHERIT);
616 #endif
617
618         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
619         font.setColor(LColor::latex);
620
621         inset.getLyXText(bv)->setFont(bv, font, false);
622 #endif
623 }
624
625
626 // attention this function can be called with bv == 0
627 void InsetERT::status(BufferView * bv, ERTStatus const st) const
628 {
629         if (st != status_) {
630                 status_ = st;
631                 need_update = FULL;
632                 switch (st) {
633                 case Inlined:
634 #warning Another gross hack. (Lgb)
635                         if (bv)
636                                 inset.setUpdateStatus(bv, InsetText::INIT);
637                         break;
638                 case Open:
639                         collapsed_ = false;
640                         setButtonLabel();
641                         break;
642                 case Collapsed:
643                         collapsed_ = true;
644                         setButtonLabel();
645                         if (bv)
646                                 bv->unlockInset(const_cast<InsetERT *>(this));
647                         break;
648                 }
649                 if (bv)
650                         bv->updateInset(const_cast<InsetERT *>(this), false);
651         }
652 }
653
654
655 bool InsetERT::showInsetDialog(BufferView * bv) const
656 {
657         bv->owner()->getDialogs()->showERT(const_cast<InsetERT *>(this));
658         return true;
659 }
660
661
662 void InsetERT::open(BufferView * bv)
663 {
664         if (!collapsed_)
665                 return;
666         status(bv, Open);
667 }
668
669
670 void InsetERT::close(BufferView * bv) const
671 {
672         if (collapsed_)
673                 return;
674         status(bv, Collapsed);
675 }
676
677
678 string const InsetERT::selectNextWordToSpellcheck(BufferView * bv,
679                                                   float &) const
680 {
681         bv->unlockInset(const_cast<InsetERT *>(this));
682         return string();
683 }
684
685
686 void InsetERT::getDrawFont(LyXFont & font) const
687 {
688 #ifndef INHERIT_LANG
689         LyXFont f(LyXFont::ALL_INHERIT, latex_language);
690 #else 
691         LyXFont f(LyXFont::ALL_INHERIT);
692 #endif
693         font = f;
694         font.setFamily(LyXFont::TYPEWRITER_FAMILY);
695         font.setColor(LColor::latex);
696 }
697
698
699 int InsetERT::getMaxWidth(BufferView * bv, UpdatableInset const * in) const
700 {
701         int w = InsetCollapsable::getMaxWidth(bv, in);
702         if (status_ != Inlined || w < 0)
703                 return w;
704         LyXText * text = inset.getLyXText(bv);
705         int rw = text->firstRow()->width();
706         if (!rw)
707                 rw = w;
708         rw += 40;
709         if (!text->firstRow()->next() && rw < w)
710                 return -1;
711         return w;
712 }