]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCollapsable.cpp
This should be the last of the commits refactoring the InsetLayout code.
[lyx.git] / src / insets / InsetCollapsable.cpp
1 /**
2  * \file InsetCollapsable.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Jürgen Vigna
8  * \author Lars Gullik Bjønnes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "InsetCollapsable.h"
16
17 #include "Buffer.h"
18 #include "BufferParams.h"
19 #include "BufferView.h"
20 #include "Cursor.h"
21 #include "Dimension.h"
22 #include "DispatchResult.h"
23 #include "FloatList.h"
24 #include "FuncStatus.h"
25 #include "InsetLayout.h"
26 #include "Language.h"
27 #include "LaTeXFeatures.h"
28 #include "Lexer.h"
29 #include "FuncRequest.h"
30 #include "MetricsInfo.h"
31 #include "ParagraphParameters.h"
32 #include "TextClass.h"
33
34 #include "frontends/FontMetrics.h"
35 #include "frontends/Painter.h"
36
37 #include "support/debug.h"
38 #include "support/docstream.h"
39 #include "support/gettext.h"
40
41 using namespace std;
42
43 namespace lyx {
44
45
46 InsetCollapsable::CollapseStatus InsetCollapsable::status() const
47 {
48         return autoOpen_ ? Open : status_;
49 }
50
51
52 InsetCollapsable::Geometry InsetCollapsable::geometry() const
53 {
54         switch (decoration()) {
55         case Deco_Classic:
56                 if (status() == Open) {
57                         if (openinlined_)
58                                 return LeftButton;
59                         else
60                                 return TopButton;
61                 } else
62                         return ButtonOnly;
63
64         case Deco_Minimalistic:
65                 return status() == Open ? NoButton : ButtonOnly ;
66
67         case Deco_Conglomerate:
68                 return status() == Open ? SubLabel : Corners ;
69
70         case Deco_Default:
71                 break; // this shouldn't happen
72         }
73
74         // dummy return value to shut down a warning,
75         // this is dead code.
76         return NoButton;
77 }
78
79
80 InsetCollapsable::InsetCollapsable(BufferParams const & bp,
81                 CollapseStatus status, TextClassPtr tc)
82         : InsetText(bp), status_(status),
83           openinlined_(false), autoOpen_(false), mouse_hover_(false)
84 {
85         setLayout(tc);
86         setAutoBreakRows(true);
87         setDrawFrame(true);
88         setFrameColor(Color_collapsableframe);
89         paragraphs().back().layout(bp.getTextClass().emptyLayout());
90 }
91
92
93 InsetCollapsable::InsetCollapsable(InsetCollapsable const & rhs)
94         : InsetText(rhs),
95                 textClass_(rhs.textClass_),
96                 layout_(rhs.layout_),
97                 labelstring_(rhs.labelstring_),
98                 button_dim(rhs.button_dim),
99                 status_(rhs.status_),
100                 openinlined_(rhs.openinlined_),
101                 autoOpen_(rhs.autoOpen_),
102                 // the sole purpose of this copy constructor
103                 mouse_hover_(false)
104 {
105 }
106
107
108 docstring InsetCollapsable::toolTip(BufferView const & bv, int x, int y) const
109 {
110         Dimension dim = dimensionCollapsed();
111         if (geometry() == NoButton)
112                 return layout_->labelstring();
113         else if (x > xo(bv) + dim.wid || y > yo(bv) + dim.des)
114                 return docstring();
115
116         switch (status_) {
117         case Open:
118                 return _("Left-click to collapse the inset");
119         case Collapsed:
120                 return _("Left-click to open the inset");
121         }
122         return docstring();
123 }
124
125
126 void InsetCollapsable::setLayout(BufferParams const & bp)
127 {
128         setLayout(bp.getTextClassPtr());
129 }
130
131
132 void InsetCollapsable::setLayout(TextClassPtr tc)
133 {
134         textClass_ = tc;
135         if ( tc.get() != 0 ) {
136                 layout_ = &tc->insetlayout(name());
137                 labelstring_ = layout_->labelstring();
138         } else {
139                 layout_ = &TextClass::emptyInsetLayout();
140                 labelstring_ = _("UNDEFINED");
141         }
142
143         setButtonLabel();
144 }
145
146
147 void InsetCollapsable::write(Buffer const & buf, ostream & os) const
148 {
149         os << "status ";
150         switch (status_) {
151         case Open:
152                 os << "open";
153                 break;
154         case Collapsed:
155                 os << "collapsed";
156                 break;
157         }
158         os << "\n";
159         text_.write(buf, os);
160 }
161
162
163 void InsetCollapsable::read(Buffer const & buf, Lexer & lex)
164 {
165         bool token_found = false;
166         if (lex.isOK()) {
167                 lex.next();
168                 string const token = lex.getString();
169                 if (token == "status") {
170                         lex.next();
171                         string const tmp_token = lex.getString();
172
173                         if (tmp_token == "collapsed") {
174                                 status_ = Collapsed;
175                                 token_found = true;
176                         } else if (tmp_token == "open") {
177                                 status_ = Open;
178                                 token_found = true;
179                         } else {
180                                 lyxerr << "InsetCollapsable::read: Missing status!"
181                                        << endl;
182                                 // Take countermeasures
183                                 lex.pushToken(token);
184                         }
185                 } else {
186                         LYXERR0("InsetCollapsable::read: Missing 'status'-tag!");
187                         // take countermeasures
188                         lex.pushToken(token);
189                 }
190         }
191         //this must be set before we enter InsetText::read()
192         setLayout(buf.params());
193
194         InsetText::read(buf, lex);
195
196         if (!token_found)
197                 status_ = isOpen() ? Open : Collapsed;
198
199         // Force default font, if so requested
200         // This avoids paragraphs in buffer language that would have a
201         // foreign language after a document language change, and it ensures
202         // that all new text in ERT and similar gets the "latex" language,
203         // since new text inherits the language from the last position of the
204         // existing text.  As a side effect this makes us also robust against
205         // bugs in LyX that might lead to font changes in ERT in .lyx files.
206         resetParagraphsFont();
207 }
208
209
210 Dimension InsetCollapsable::dimensionCollapsed() const
211 {
212         BOOST_ASSERT(layout_);
213         Dimension dim;
214         theFontMetrics(layout_->labelfont()).buttonText(
215                 labelstring_, dim.wid, dim.asc, dim.des);
216         return dim;
217 }
218
219
220 void InsetCollapsable::metrics(MetricsInfo & mi, Dimension & dim) const
221 {
222         BOOST_ASSERT(layout_);
223
224         autoOpen_ = mi.base.bv->cursor().isInside(this);
225
226         FontInfo tmpfont = mi.base.font;
227         mi.base.font = layout_->font();
228         mi.base.font.realize(tmpfont);
229
230         switch (geometry()) {
231         case NoButton:
232                 InsetText::metrics(mi, dim);
233                 break;
234         case Corners:
235                 InsetText::metrics(mi, dim);
236                 dim.des -= 3;
237                 dim.asc -= 1;
238                 break;
239         case SubLabel: {
240                 InsetText::metrics(mi, dim);
241                 // consider width of the inset label
242                 FontInfo font(layout_->labelfont());
243                 font.realize(sane_font);
244                 font.decSize();
245                 font.decSize();
246                 int w = 0;
247                 int a = 0;
248                 int d = 0;
249                 theFontMetrics(font).rectText(labelstring_, w, a, d);
250                 dim.des += a + d;
251                 break;
252                 }
253         case TopButton:
254         case LeftButton:
255         case ButtonOnly:
256                 dim = dimensionCollapsed();
257                 if (geometry() == TopButton
258                  || geometry() == LeftButton) {
259                         Dimension textdim;
260                         InsetText::metrics(mi, textdim);
261                         openinlined_ = (textdim.wid + dim.wid) < mi.base.textwidth;
262                         if (openinlined_) {
263                                 // Correct for button width.
264                                 dim.wid += textdim.wid;
265                                 dim.des = max(dim.des - textdim.asc + dim.asc, textdim.des);
266                                 dim.asc = textdim.asc;
267                         } else {
268                                 dim.des += textdim.height() + TEXT_TO_INSET_OFFSET;
269                                 dim.wid = max(dim.wid, textdim.wid);
270                         }
271                 }
272                 break;
273         }
274
275         mi.base.font = tmpfont;
276 }
277
278
279 bool InsetCollapsable::setMouseHover(bool mouse_hover)
280 {
281         mouse_hover_ = mouse_hover;
282         return true;
283 }
284
285
286 void InsetCollapsable::draw(PainterInfo & pi, int x, int y) const
287 {
288         BOOST_ASSERT(layout_);
289
290         autoOpen_ = pi.base.bv->cursor().isInside(this);
291         ColorCode const old_color = pi.background_color;
292         pi.background_color = backgroundColor();
293
294         FontInfo tmpfont = pi.base.font;
295         pi.base.font = layout_->font();
296         pi.base.font.realize(tmpfont);
297
298         // Draw button first -- top, left or only
299         Dimension dimc = dimensionCollapsed();
300
301         if (geometry() == TopButton ||
302             geometry() == LeftButton ||
303             geometry() == ButtonOnly) {
304                 button_dim.x1 = x + 0;
305                 button_dim.x2 = x + dimc.width();
306                 button_dim.y1 = y - dimc.asc;
307                 button_dim.y2 = y + dimc.des;
308
309                 pi.pain.buttonText(x, y, labelstring_, layout_->labelfont(),
310                         mouse_hover_);
311         } else {
312                 button_dim.x1 = 0;
313                 button_dim.y1 = 0;
314                 button_dim.x2 = 0;
315                 button_dim.y2 = 0;
316         }
317
318         Dimension const textdim = InsetText::dimension(*pi.base.bv);
319         int const baseline = y;
320         int textx, texty;
321         switch (geometry()) {
322         case LeftButton:
323                 textx = x + dimc.width();
324                 texty = baseline;
325                 InsetText::draw(pi, textx, texty);
326                 break;
327         case TopButton:
328                 textx = x;
329                 texty = baseline + dimc.des + textdim.asc;
330                 InsetText::draw(pi, textx, texty);
331                 break;
332         case ButtonOnly:
333                 break;
334         case NoButton:
335                 textx = x;
336                 texty = baseline;
337                 InsetText::draw(pi, textx, texty);
338                 break;
339         case SubLabel:
340         case Corners:
341                 textx = x;
342                 texty = baseline;
343                 const_cast<InsetCollapsable *>(this)->setDrawFrame(false);
344                 InsetText::draw(pi, textx, texty);
345                 const_cast<InsetCollapsable *>(this)->setDrawFrame(true);
346
347                 int desc = textdim.descent();
348                 if (geometry() == Corners)
349                         desc -= 3;
350
351                 const int xx1 = x + TEXT_TO_INSET_OFFSET - 1;
352                 const int xx2 = x + textdim.wid - TEXT_TO_INSET_OFFSET + 1;
353                 pi.pain.line(xx1, y + desc - 4, 
354                              xx1, y + desc, 
355                         layout_->labelfont().color());
356                 if (status_ == Open)
357                         pi.pain.line(xx1, y + desc, 
358                                 xx2, y + desc,
359                                 layout_->labelfont().color());
360                 else {
361                         // Make status_ value visible:
362                         pi.pain.line(xx1, y + desc,
363                                 xx1 + 4, y + desc,
364                                 layout_->labelfont().color());
365                         pi.pain.line(xx2 - 4, y + desc,
366                                 xx2, y + desc,
367                                 layout_->labelfont().color());
368                 }
369                 pi.pain.line(x + textdim.wid - 3, y + desc, x + textdim.wid - 3, y + desc - 4,
370                         layout_->labelfont().color());
371
372                 // the label below the text. Can be toggled.
373                 if (geometry() == SubLabel) {
374                         FontInfo font(layout_->labelfont());
375                         font.realize(sane_font);
376                         font.decSize();
377                         font.decSize();
378                         int w = 0;
379                         int a = 0;
380                         int d = 0;
381                         theFontMetrics(font).rectText(labelstring_, w, a, d);
382                         int const ww = max(textdim.wid, w);
383                         pi.pain.rectText(x + (ww - w) / 2, y + desc + a,
384                                 labelstring_, font, Color_none, Color_none);
385                         desc += d;
386                 }
387
388                 // a visual cue when the cursor is inside the inset
389                 Cursor & cur = pi.base.bv->cursor();
390                 if (cur.isInside(this)) {
391                         y -= textdim.asc;
392                         y += 3;
393                         pi.pain.line(xx1, y + 4, xx1, y, layout_->labelfont().color());
394                         pi.pain.line(xx1 + 4, y, xx1, y, layout_->labelfont().color());
395                         pi.pain.line(xx2, y + 4, xx2, y,
396                                 layout_->labelfont().color());
397                         pi.pain.line(xx2 - 4, y, xx2, y,
398                                 layout_->labelfont().color());
399                 }
400                 break;
401         }
402         pi.background_color = old_color;
403
404         pi.base.font = tmpfont;
405 }
406
407
408 void InsetCollapsable::cursorPos(BufferView const & bv,
409                 CursorSlice const & sl, bool boundary, int & x, int & y) const
410 {
411         if (geometry() == ButtonOnly)
412                 status_ = Open;
413         BOOST_ASSERT(geometry() != ButtonOnly);
414
415         InsetText::cursorPos(bv, sl, boundary, x, y);
416         Dimension const textdim = InsetText::dimension(bv);
417
418         switch (geometry()) {
419         case LeftButton:
420                 x += dimensionCollapsed().wid;
421                 break;
422         case TopButton: {
423                 y += dimensionCollapsed().des + textdim.asc;
424                 break;
425         }
426         case NoButton:
427         case SubLabel:
428         case Corners:
429                 // Do nothing
430                 break;
431         case ButtonOnly:
432                 // Cannot get here
433                 break;
434         }
435 }
436
437
438 Inset::EDITABLE InsetCollapsable::editable() const
439 {
440         return geometry() != ButtonOnly? HIGHLY_EDITABLE : IS_EDITABLE;
441 }
442
443
444 bool InsetCollapsable::descendable() const
445 {
446         return geometry() != ButtonOnly;
447 }
448
449
450 bool InsetCollapsable::hitButton(FuncRequest const & cmd) const
451 {
452         return button_dim.contains(cmd.x, cmd.y);
453 }
454
455
456 docstring const InsetCollapsable::getNewLabel(docstring const & l) const
457 {
458         docstring label;
459         pos_type const max_length = 15;
460         pos_type const p_siz = paragraphs().begin()->size();
461         pos_type const n = min(max_length, p_siz);
462         pos_type i = 0;
463         pos_type j = 0;
464         for (; i < n && j < p_siz; ++j) {
465                 if (paragraphs().begin()->isInset(j))
466                         continue;
467                 label += paragraphs().begin()->getChar(j);
468                 ++i;
469         }
470         if (paragraphs().size() > 1 || (i > 0 && j < p_siz)) {
471                 label += "...";
472         }
473         return label.empty() ? l : label;
474 }
475
476
477 void InsetCollapsable::edit(Cursor & cur, bool front, EntryDirection entry_from)
478 {
479         //lyxerr << "InsetCollapsable: edit left/right" << endl;
480         cur.push(*this);
481         InsetText::edit(cur, front, entry_from);
482 }
483
484
485 Inset * InsetCollapsable::editXY(Cursor & cur, int x, int y)
486 {
487         //lyxerr << "InsetCollapsable: edit xy" << endl;
488         if (geometry() == ButtonOnly
489          || (button_dim.contains(x, y) 
490           && geometry() != NoButton))
491                 return this;
492         cur.push(*this);
493         return InsetText::editXY(cur, x, y);
494 }
495
496
497 void InsetCollapsable::doDispatch(Cursor & cur, FuncRequest & cmd)
498 {
499         //lyxerr << "InsetCollapsable::doDispatch (begin): cmd: " << cmd
500         //      << " cur: " << cur << " bvcur: " << cur.bv().cursor() << endl;
501
502         switch (cmd.action) {
503         case LFUN_MOUSE_PRESS:
504                 if (cmd.button() == mouse_button::button1 
505                  && hitButton(cmd) 
506                  && geometry() != NoButton) {
507                         // reset selection if necessary (see bug 3060)
508                         if (cur.selection())
509                                 cur.bv().cursor().clearSelection();
510                         else
511                                 cur.noUpdate();
512                         cur.dispatched();
513                         break;
514                 }
515                 if (geometry() == NoButton)
516                         InsetText::doDispatch(cur, cmd);
517                 else if (geometry() != ButtonOnly 
518                      && !hitButton(cmd))
519                         InsetText::doDispatch(cur, cmd);
520                 else
521                         cur.undispatched();
522                 break;
523
524         case LFUN_MOUSE_MOTION:
525         case LFUN_MOUSE_DOUBLE:
526         case LFUN_MOUSE_TRIPLE:
527                 if (geometry() == NoButton)
528                         InsetText::doDispatch(cur, cmd);
529                 else if (geometry() != ButtonOnly
530                      && !hitButton(cmd))
531                         InsetText::doDispatch(cur, cmd);
532                 else
533                         cur.undispatched();
534                 break;
535
536         case LFUN_MOUSE_RELEASE:
537                 if (cmd.button() == mouse_button::button3) {
538                         // There is no button to right click:
539                         if (decoration() == Deco_Minimalistic ||
540                             geometry() == Corners ||
541                             geometry() == SubLabel ||
542                             geometry() == NoButton
543                            )  {
544                                 if (status_ == Open)
545                                         setStatus(cur, Collapsed);
546                                 else
547                                         setStatus(cur, Open);
548                                 break;
549                         } else {
550                                 // Open the Inset 
551                                 // configuration dialog
552                                 showInsetDialog(&cur.bv());
553                                 break;
554                         }
555                 }
556
557                 if (geometry() == NoButton) {
558                         // The mouse click has to be within the inset!
559                         InsetText::doDispatch(cur, cmd);
560                         break;
561                 }
562
563                 if (cmd.button() == mouse_button::button1 && hitButton(cmd)) {
564                         // if we are selecting, we do not want to
565                         // toggle the inset.
566                         if (cur.selection())
567                                 break;
568                         // Left button is clicked, the user asks to
569                         // toggle the inset visual state.
570                         cur.dispatched();
571                         cur.updateFlags(Update::Force | Update::FitCursor);
572                         if (geometry() == ButtonOnly) {
573                                 setStatus(cur, Open);
574                                 edit(cur, true);
575                         }
576                         else {
577                                 setStatus(cur, Collapsed);
578                         }
579                         cur.bv().cursor() = cur;
580                         break;
581                 }
582
583                 // The mouse click is within the opened inset.
584                 if (geometry() == TopButton
585                  || geometry() == LeftButton)
586                         InsetText::doDispatch(cur, cmd);
587                 break;
588
589         case LFUN_INSET_TOGGLE:
590                 if (cmd.argument() == "open")
591                         setStatus(cur, Open);
592                 else if (cmd.argument() == "close")
593                         setStatus(cur, Collapsed);
594                 else if (cmd.argument() == "toggle" || cmd.argument().empty())
595                         if (status_ == Open) {
596                                 setStatus(cur, Collapsed);
597                                 if (geometry() == ButtonOnly)
598                                         cur.top().forwardPos();
599                         } else
600                                 setStatus(cur, Open);
601                 else // if assign or anything else
602                         cur.undispatched();
603                 cur.dispatched();
604                 break;
605
606         case LFUN_PASTE:
607         case LFUN_CLIPBOARD_PASTE:
608         case LFUN_PRIMARY_SELECTION_PASTE: {
609                 InsetText::doDispatch(cur, cmd);
610                 // Since we can only store plain text, we must reset all
611                 // attributes.
612                 // FIXME: Change only the pasted paragraphs
613
614                 resetParagraphsFont();
615                 break;
616         }
617
618         default:
619                 if (layout_ && layout_->isForceLtr()) {
620                         // Force any new text to latex_language
621                         // FIXME: This should only be necessary in constructor, but
622                         // new paragraphs that are created by pressing enter at the
623                         // start of an existing paragraph get the buffer language
624                         // and not latex_language, so we take this brute force
625                         // approach.
626                         cur.current_font.setLanguage(latex_language);
627                         cur.real_current_font.setLanguage(latex_language);
628                 }
629                 InsetText::doDispatch(cur, cmd);
630                 break;
631         }
632 }
633
634
635 bool InsetCollapsable::allowMultiPar() const
636 {
637         return layout_->isMultiPar();
638 }
639
640
641 void InsetCollapsable::resetParagraphsFont()
642 {
643         Font font;
644         font.fontInfo() = inherit_font;
645         if (layout_->isForceLtr())
646                 font.setLanguage(latex_language);
647         if (layout_->isPassThru()) {
648                 ParagraphList::iterator par = paragraphs().begin();
649                 ParagraphList::iterator const end = paragraphs().end();
650                 while (par != end) {
651                         par->resetFonts(font);
652                         par->params().clear();
653                         ++par;
654                 }
655         }
656 }
657
658
659 bool InsetCollapsable::getStatus(Cursor & cur, FuncRequest const & cmd,
660                 FuncStatus & flag) const
661 {
662         switch (cmd.action) {
663         // suppress these
664         case LFUN_ACCENT_ACUTE:
665         case LFUN_ACCENT_BREVE:
666         case LFUN_ACCENT_CARON:
667         case LFUN_ACCENT_CEDILLA:
668         case LFUN_ACCENT_CIRCLE:
669         case LFUN_ACCENT_CIRCUMFLEX:
670         case LFUN_ACCENT_DOT:
671         case LFUN_ACCENT_GRAVE:
672         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
673         case LFUN_ACCENT_MACRON:
674         case LFUN_ACCENT_OGONEK:
675         case LFUN_ACCENT_SPECIAL_CARON:
676         case LFUN_ACCENT_TIE:
677         case LFUN_ACCENT_TILDE:
678         case LFUN_ACCENT_UMLAUT:
679         case LFUN_ACCENT_UNDERBAR:
680         case LFUN_ACCENT_UNDERDOT:
681         case LFUN_APPENDIX:
682         case LFUN_BIBITEM_INSERT:
683         case LFUN_BOX_INSERT:
684         case LFUN_BRANCH_INSERT:
685         case LFUN_NEW_LINE:
686         case LFUN_CAPTION_INSERT:
687         case LFUN_CLEARPAGE_INSERT:
688         case LFUN_CLEARDOUBLEPAGE_INSERT:
689         case LFUN_DEPTH_DECREMENT:
690         case LFUN_DEPTH_INCREMENT:
691         case LFUN_ENVIRONMENT_INSERT:
692         case LFUN_ERT_INSERT:
693         case LFUN_FILE_INSERT:
694         case LFUN_FLEX_INSERT:
695         case LFUN_FLOAT_INSERT:
696         case LFUN_FLOAT_LIST:
697         case LFUN_FLOAT_WIDE_INSERT:
698         case LFUN_FONT_BOLD:
699         case LFUN_FONT_TYPEWRITER:
700         case LFUN_FONT_DEFAULT:
701         case LFUN_FONT_EMPH:
702         case LFUN_FONT_FREE_APPLY:
703         case LFUN_FONT_FREE_UPDATE:
704         case LFUN_FONT_NOUN:
705         case LFUN_FONT_ROMAN:
706         case LFUN_FONT_SANS:
707         case LFUN_FONT_FRAK:
708         case LFUN_FONT_ITAL:
709         case LFUN_FONT_SIZE:
710         case LFUN_FONT_STATE:
711         case LFUN_FONT_UNDERLINE:
712         case LFUN_FOOTNOTE_INSERT:
713         case LFUN_HFILL_INSERT:
714         case LFUN_HYPERLINK_INSERT:
715         case LFUN_INDEX_INSERT:
716         case LFUN_INDEX_PRINT:
717         case LFUN_INSET_INSERT:
718         case LFUN_LABEL_GOTO:
719         case LFUN_LABEL_INSERT:
720         case LFUN_LINE_INSERT:
721         case LFUN_NEWPAGE_INSERT:
722         case LFUN_PAGEBREAK_INSERT:
723         case LFUN_LAYOUT:
724         case LFUN_LAYOUT_PARAGRAPH:
725         case LFUN_LAYOUT_TABULAR:
726         case LFUN_MARGINALNOTE_INSERT:
727         case LFUN_MATH_DISPLAY:
728         case LFUN_MATH_INSERT:
729         case LFUN_MATH_MATRIX:
730         case LFUN_MATH_MODE:
731         case LFUN_MENU_OPEN:
732         case LFUN_NOACTION:
733         case LFUN_NOMENCL_INSERT:
734         case LFUN_NOMENCL_PRINT:
735         case LFUN_NOTE_INSERT:
736         case LFUN_NOTE_NEXT:
737         case LFUN_OPTIONAL_INSERT:
738         case LFUN_PARAGRAPH_PARAMS:
739         case LFUN_PARAGRAPH_PARAMS_APPLY:
740         case LFUN_PARAGRAPH_SPACING:
741         case LFUN_PARAGRAPH_UPDATE:
742         case LFUN_REFERENCE_NEXT:
743         case LFUN_SERVER_GOTO_FILE_ROW:
744         case LFUN_SERVER_NOTIFY:
745         case LFUN_SERVER_SET_XY:
746         case LFUN_SPACE_INSERT:
747         case LFUN_SPECIALCHAR_INSERT:
748         case LFUN_TABULAR_INSERT:
749         case LFUN_TOC_INSERT:
750         case LFUN_WRAP_INSERT:
751         if (layout_->isPassThru()) {
752                 flag.enabled(false);
753                 return true;
754         } else
755                 return InsetText::getStatus(cur, cmd, flag);
756
757         case LFUN_INSET_TOGGLE:
758                 if (cmd.argument() == "open" || cmd.argument() == "close" ||
759                     cmd.argument() == "toggle")
760                         flag.enabled(true);
761                 else
762                         flag.enabled(false);
763                 return true;
764
765         case LFUN_LANGUAGE:
766                 flag.enabled(!layout_->isForceLtr());
767                 return InsetText::getStatus(cur, cmd, flag);
768
769         case LFUN_BREAK_PARAGRAPH:
770         case LFUN_BREAK_PARAGRAPH_SKIP:
771                 flag.enabled(layout_->isMultiPar());
772                 return true;
773
774         default:
775                 return InsetText::getStatus(cur, cmd, flag);
776         }
777 }
778
779
780 void InsetCollapsable::setLabel(docstring const & l)
781 {
782         labelstring_ = l;
783 }
784
785
786 void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
787 {
788         status_ = status;
789         setButtonLabel();
790         if (status_ == Collapsed)
791                 cur.leaveInset(*this);
792 }
793
794
795 docstring InsetCollapsable::floatName(string const & type, BufferParams const & bp) const
796 {
797         FloatList const & floats = bp.getTextClass().floats();
798         FloatList::const_iterator it = floats[type];
799         // FIXME UNICODE
800         return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());
801 }
802
803
804 InsetDecoration InsetCollapsable::decoration() const
805 {
806         if (!layout_)
807                 return Deco_Classic;
808         InsetDecoration const dec = layout_->decoration();
809         switch (dec) {
810         case Deco_Classic:
811         case Deco_Minimalistic:
812         case Deco_Conglomerate:
813                 return dec;
814         case Deco_Default:
815                 break;
816         }
817         if (lyxCode() == FLEX_CODE)
818                 // FIXME: Is this really necessary?
819                 return Deco_Conglomerate;
820         return Deco_Classic;
821 }
822
823
824 bool InsetCollapsable::isMacroScope(Buffer const &) const
825 {
826         // layout_ == 0 leads to no latex output, so ignore 
827         // the macros outside
828         if (!layout_)
829                 return true;
830
831         // see InsetCollapsable::latex(...) below. In these case
832         // an environment is opened there
833         if (!layout_->latexname().empty())
834                 return true;
835
836         return false;
837 }
838
839
840 int InsetCollapsable::latex(Buffer const & buf, odocstream & os,
841                           OutputParams const & runparams) const
842 {
843         // FIXME: What should we do layout_ is 0?
844         // 1) assert
845         // 2) through an error
846         if (!layout_)
847                 return 0;
848
849         // This implements the standard way of handling the LaTeX output of
850         // a collapsable inset, either a command or an environment. Standard 
851         // collapsable insets should not redefine this, non-standard ones may
852         // call this.
853         if (!layout_->latexname().empty()) {
854                 if (layout_->latextype() == "command") {
855                         // FIXME UNICODE
856                         if (runparams.moving_arg)
857                                 os << "\\protect";
858                         os << '\\' << from_utf8(layout_->latexname());
859                         if (!layout_->latexparam().empty())
860                                 os << from_utf8(layout_->latexparam());
861                         os << '{';
862                 } else if (layout_->latextype() == "environment") {
863                         os << "%\n\\begin{" << from_utf8(layout_->latexname()) << "}\n";
864                         if (!layout_->latexparam().empty())
865                                 os << from_utf8(layout_->latexparam());
866                 }
867         }
868         OutputParams rp = runparams;
869         if (layout_->isPassThru())
870                 rp.verbatim = true;
871         if (layout_->isNeedProtect())
872                 rp.moving_arg = true;
873         int i = InsetText::latex(buf, os, rp);
874         if (!layout_->latexname().empty()) {
875                 if (layout_->latextype() == "command") {
876                         os << "}";
877                 } else if (layout_->latextype() == "environment") {
878                         os << "\n\\end{" << from_utf8(layout_->latexname()) << "}\n";
879                         i += 4;
880                 }
881         }
882         return i;
883 }
884
885
886 void InsetCollapsable::validate(LaTeXFeatures & features) const
887 {
888         if (!layout_)
889                 return;
890
891         // Force inclusion of preamble snippet in layout file
892         features.require(to_utf8(layout_->name()));
893         InsetText::validate(features);
894 }
895
896
897 bool InsetCollapsable::undefined() const
898 {
899         docstring const & n = getLayout().name();
900         return n.empty() || n == TextClass::emptyInsetLayout().name();
901 }
902
903
904 } // namespace lyx