]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCollapsable.cpp
Andre's s/getTextClass/textClass/ cleanup.
[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 InsetLayout::Classic:
56                 if (status() == Open) {
57                         if (openinlined_)
58                                 return LeftButton;
59                         else
60                                 return TopButton;
61                 } else
62                         return ButtonOnly;
63
64         case InsetLayout::Minimalistic:
65                 return status() == Open ? NoButton : ButtonOnly ;
66
67         case InsetLayout::Conglomerate:
68                 return status() == Open ? SubLabel : Corners ;
69
70         case InsetLayout::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().setLayout(bp.textClass().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.textClassPtr());
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, 
370                         y + desc - 4, 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(
478                 Cursor & cur, bool front, EntryDirection entry_from)
479 {
480         //lyxerr << "InsetCollapsable: edit left/right" << endl;
481         cur.push(*this);
482         InsetText::edit(cur, front, entry_from);
483 }
484
485
486 Inset * InsetCollapsable::editXY(Cursor & cur, int x, int y)
487 {
488         //lyxerr << "InsetCollapsable: edit xy" << endl;
489         if (geometry() == ButtonOnly
490          || (button_dim.contains(x, y) 
491           && geometry() != NoButton))
492                 return this;
493         cur.push(*this);
494         return InsetText::editXY(cur, x, y);
495 }
496
497
498 void InsetCollapsable::doDispatch(Cursor & cur, FuncRequest & cmd)
499 {
500         //lyxerr << "InsetCollapsable::doDispatch (begin): cmd: " << cmd
501         //      << " cur: " << cur << " bvcur: " << cur.bv().cursor() << endl;
502
503         switch (cmd.action) {
504         case LFUN_MOUSE_PRESS:
505                 if (cmd.button() == mouse_button::button1 
506                  && hitButton(cmd) 
507                  && geometry() != NoButton) {
508                         // reset selection if necessary (see bug 3060)
509                         if (cur.selection())
510                                 cur.bv().cursor().clearSelection();
511                         else
512                                 cur.noUpdate();
513                         cur.dispatched();
514                         break;
515                 }
516                 if (geometry() == NoButton)
517                         InsetText::doDispatch(cur, cmd);
518                 else if (geometry() != ButtonOnly 
519                      && !hitButton(cmd))
520                         InsetText::doDispatch(cur, cmd);
521                 else
522                         cur.undispatched();
523                 break;
524
525         case LFUN_MOUSE_MOTION:
526         case LFUN_MOUSE_DOUBLE:
527         case LFUN_MOUSE_TRIPLE:
528                 if (geometry() == NoButton)
529                         InsetText::doDispatch(cur, cmd);
530                 else if (geometry() != ButtonOnly
531                      && !hitButton(cmd))
532                         InsetText::doDispatch(cur, cmd);
533                 else
534                         cur.undispatched();
535                 break;
536
537         case LFUN_MOUSE_RELEASE:
538                 if (cmd.button() == mouse_button::button3) {
539                         // There is no button to right click:
540                         if (decoration() == InsetLayout::Minimalistic ||
541                             geometry() == Corners ||
542                             geometry() == SubLabel ||
543                             geometry() == NoButton
544                            )  {
545                                 if (status_ == Open)
546                                         setStatus(cur, Collapsed);
547                                 else
548                                         setStatus(cur, Open);
549                                 break;
550                         } else {
551                                 // Open the Inset 
552                                 // configuration dialog
553                                 showInsetDialog(&cur.bv());
554                                 break;
555                         }
556                 }
557
558                 if (geometry() == NoButton) {
559                         // The mouse click has to be within the inset!
560                         InsetText::doDispatch(cur, cmd);
561                         break;
562                 }
563
564                 if (cmd.button() == mouse_button::button1 && hitButton(cmd)) {
565                         // if we are selecting, we do not want to
566                         // toggle the inset.
567                         if (cur.selection())
568                                 break;
569                         // Left button is clicked, the user asks to
570                         // toggle the inset visual state.
571                         cur.dispatched();
572                         cur.updateFlags(Update::Force | Update::FitCursor);
573                         if (geometry() == ButtonOnly) {
574                                 setStatus(cur, Open);
575                                 edit(cur, true);
576                         }
577                         else {
578                                 setStatus(cur, Collapsed);
579                         }
580                         cur.bv().cursor() = cur;
581                         break;
582                 }
583
584                 // The mouse click is within the opened inset.
585                 if (geometry() == TopButton
586                  || geometry() == LeftButton)
587                         InsetText::doDispatch(cur, cmd);
588                 break;
589
590         case LFUN_INSET_TOGGLE:
591                 if (cmd.argument() == "open")
592                         setStatus(cur, Open);
593                 else if (cmd.argument() == "close")
594                         setStatus(cur, Collapsed);
595                 else if (cmd.argument() == "toggle" || cmd.argument().empty())
596                         if (status_ == Open) {
597                                 setStatus(cur, Collapsed);
598                                 if (geometry() == ButtonOnly)
599                                         cur.top().forwardPos();
600                         } else
601                                 setStatus(cur, Open);
602                 else // if assign or anything else
603                         cur.undispatched();
604                 cur.dispatched();
605                 break;
606
607         case LFUN_PASTE:
608         case LFUN_CLIPBOARD_PASTE:
609         case LFUN_PRIMARY_SELECTION_PASTE: {
610                 InsetText::doDispatch(cur, cmd);
611                 // Since we can only store plain text, we must reset all
612                 // attributes.
613                 // FIXME: Change only the pasted paragraphs
614
615                 resetParagraphsFont();
616                 break;
617         }
618
619         default:
620                 if (layout_ && layout_->isForceLtr()) {
621                         // Force any new text to latex_language
622                         // FIXME: This should only be necessary in constructor, but
623                         // new paragraphs that are created by pressing enter at the
624                         // start of an existing paragraph get the buffer language
625                         // and not latex_language, so we take this brute force
626                         // approach.
627                         cur.current_font.setLanguage(latex_language);
628                         cur.real_current_font.setLanguage(latex_language);
629                 }
630                 InsetText::doDispatch(cur, cmd);
631                 break;
632         }
633 }
634
635
636 bool InsetCollapsable::allowMultiPar() const
637 {
638         return layout_->isMultiPar();
639 }
640
641
642 void InsetCollapsable::resetParagraphsFont()
643 {
644         Font font;
645         font.fontInfo() = inherit_font;
646         if (layout_->isForceLtr())
647                 font.setLanguage(latex_language);
648         if (layout_->isPassThru()) {
649                 ParagraphList::iterator par = paragraphs().begin();
650                 ParagraphList::iterator const end = paragraphs().end();
651                 while (par != end) {
652                         par->resetFonts(font);
653                         par->params().clear();
654                         ++par;
655                 }
656         }
657 }
658
659
660 bool InsetCollapsable::getStatus(Cursor & cur, FuncRequest const & cmd,
661                 FuncStatus & flag) const
662 {
663         switch (cmd.action) {
664         // suppress these
665         case LFUN_ACCENT_ACUTE:
666         case LFUN_ACCENT_BREVE:
667         case LFUN_ACCENT_CARON:
668         case LFUN_ACCENT_CEDILLA:
669         case LFUN_ACCENT_CIRCLE:
670         case LFUN_ACCENT_CIRCUMFLEX:
671         case LFUN_ACCENT_DOT:
672         case LFUN_ACCENT_GRAVE:
673         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
674         case LFUN_ACCENT_MACRON:
675         case LFUN_ACCENT_OGONEK:
676         case LFUN_ACCENT_SPECIAL_CARON:
677         case LFUN_ACCENT_TIE:
678         case LFUN_ACCENT_TILDE:
679         case LFUN_ACCENT_UMLAUT:
680         case LFUN_ACCENT_UNDERBAR:
681         case LFUN_ACCENT_UNDERDOT:
682         case LFUN_APPENDIX:
683         case LFUN_BIBITEM_INSERT:
684         case LFUN_BOX_INSERT:
685         case LFUN_BRANCH_INSERT:
686         case LFUN_NEW_LINE:
687         case LFUN_CAPTION_INSERT:
688         case LFUN_CLEARPAGE_INSERT:
689         case LFUN_CLEARDOUBLEPAGE_INSERT:
690         case LFUN_DEPTH_DECREMENT:
691         case LFUN_DEPTH_INCREMENT:
692         case LFUN_ENVIRONMENT_INSERT:
693         case LFUN_ERT_INSERT:
694         case LFUN_FILE_INSERT:
695         case LFUN_FLEX_INSERT:
696         case LFUN_FLOAT_INSERT:
697         case LFUN_FLOAT_LIST:
698         case LFUN_FLOAT_WIDE_INSERT:
699         case LFUN_FONT_BOLD:
700         case LFUN_FONT_TYPEWRITER:
701         case LFUN_FONT_DEFAULT:
702         case LFUN_FONT_EMPH:
703         case LFUN_FONT_FREE_APPLY:
704         case LFUN_FONT_FREE_UPDATE:
705         case LFUN_FONT_NOUN:
706         case LFUN_FONT_ROMAN:
707         case LFUN_FONT_SANS:
708         case LFUN_FONT_FRAK:
709         case LFUN_FONT_ITAL:
710         case LFUN_FONT_SIZE:
711         case LFUN_FONT_STATE:
712         case LFUN_FONT_UNDERLINE:
713         case LFUN_FOOTNOTE_INSERT:
714         case LFUN_HFILL_INSERT:
715         case LFUN_HYPERLINK_INSERT:
716         case LFUN_INDEX_INSERT:
717         case LFUN_INDEX_PRINT:
718         case LFUN_INSET_INSERT:
719         case LFUN_LABEL_GOTO:
720         case LFUN_LABEL_INSERT:
721         case LFUN_LINE_INSERT:
722         case LFUN_NEWPAGE_INSERT:
723         case LFUN_PAGEBREAK_INSERT:
724         case LFUN_LAYOUT:
725         case LFUN_LAYOUT_PARAGRAPH:
726         case LFUN_LAYOUT_TABULAR:
727         case LFUN_MARGINALNOTE_INSERT:
728         case LFUN_MATH_DISPLAY:
729         case LFUN_MATH_INSERT:
730         case LFUN_MATH_MATRIX:
731         case LFUN_MATH_MODE:
732         case LFUN_MENU_OPEN:
733         case LFUN_NOACTION:
734         case LFUN_NOMENCL_INSERT:
735         case LFUN_NOMENCL_PRINT:
736         case LFUN_NOTE_INSERT:
737         case LFUN_NOTE_NEXT:
738         case LFUN_OPTIONAL_INSERT:
739         case LFUN_PARAGRAPH_PARAMS:
740         case LFUN_PARAGRAPH_PARAMS_APPLY:
741         case LFUN_PARAGRAPH_SPACING:
742         case LFUN_PARAGRAPH_UPDATE:
743         case LFUN_REFERENCE_NEXT:
744         case LFUN_SERVER_GOTO_FILE_ROW:
745         case LFUN_SERVER_NOTIFY:
746         case LFUN_SERVER_SET_XY:
747         case LFUN_SPACE_INSERT:
748         case LFUN_SPECIALCHAR_INSERT:
749         case LFUN_TABULAR_INSERT:
750         case LFUN_TOC_INSERT:
751         case LFUN_WRAP_INSERT:
752         if (layout_->isPassThru()) {
753                 flag.enabled(false);
754                 return true;
755         } else
756                 return InsetText::getStatus(cur, cmd, flag);
757
758         case LFUN_INSET_TOGGLE:
759                 if (cmd.argument() == "open" || cmd.argument() == "close" ||
760                     cmd.argument() == "toggle")
761                         flag.enabled(true);
762                 else
763                         flag.enabled(false);
764                 return true;
765
766         case LFUN_LANGUAGE:
767                 flag.enabled(!layout_->isForceLtr());
768                 return InsetText::getStatus(cur, cmd, flag);
769
770         case LFUN_BREAK_PARAGRAPH:
771         case LFUN_BREAK_PARAGRAPH_SKIP:
772                 flag.enabled(layout_->isMultiPar());
773                 return true;
774
775         default:
776                 return InsetText::getStatus(cur, cmd, flag);
777         }
778 }
779
780
781 void InsetCollapsable::setLabel(docstring const & l)
782 {
783         labelstring_ = l;
784 }
785
786
787 void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
788 {
789         status_ = status;
790         setButtonLabel();
791         if (status_ == Collapsed)
792                 cur.leaveInset(*this);
793 }
794
795
796 docstring InsetCollapsable::floatName(
797                 string const & type, BufferParams const & bp) const
798 {
799         FloatList const & floats = bp.textClass().floats();
800         FloatList::const_iterator it = floats[type];
801         // FIXME UNICODE
802         return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());
803 }
804
805
806 InsetLayout::InsetDecoration InsetCollapsable::decoration() const
807 {
808         if (!layout_)
809                 return InsetLayout::Classic;
810         InsetLayout::InsetDecoration const dec = layout_->decoration();
811         switch (dec) {
812         case InsetLayout::Classic:
813         case InsetLayout::Minimalistic:
814         case InsetLayout::Conglomerate:
815                 return dec;
816         case InsetLayout::Default:
817                 break;
818         }
819         if (lyxCode() == FLEX_CODE)
820                 // FIXME: Is this really necessary?
821                 return InsetLayout::Conglomerate;
822         return InsetLayout::Classic;
823 }
824
825
826 bool InsetCollapsable::isMacroScope(Buffer const &) const
827 {
828         // layout_ == 0 leads to no latex output, so ignore 
829         // the macros outside
830         if (!layout_)
831                 return true;
832
833         // see InsetCollapsable::latex(...) below. In these case
834         // an environment is opened there
835         if (!layout_->latexname().empty())
836                 return true;
837
838         return false;
839 }
840
841
842 int InsetCollapsable::latex(Buffer const & buf, odocstream & os,
843                           OutputParams const & runparams) const
844 {
845         // FIXME: What should we do layout_ is 0?
846         // 1) assert
847         // 2) through an error
848         if (!layout_)
849                 return 0;
850
851         // This implements the standard way of handling the LaTeX output of
852         // a collapsable inset, either a command or an environment. Standard 
853         // collapsable insets should not redefine this, non-standard ones may
854         // call this.
855         if (!layout_->latexname().empty()) {
856                 if (layout_->latextype() == "command") {
857                         // FIXME UNICODE
858                         if (runparams.moving_arg)
859                                 os << "\\protect";
860                         os << '\\' << from_utf8(layout_->latexname());
861                         if (!layout_->latexparam().empty())
862                                 os << from_utf8(layout_->latexparam());
863                         os << '{';
864                 } else if (layout_->latextype() == "environment") {
865                         os << "%\n\\begin{" << from_utf8(layout_->latexname()) << "}\n";
866                         if (!layout_->latexparam().empty())
867                                 os << from_utf8(layout_->latexparam());
868                 }
869         }
870         OutputParams rp = runparams;
871         if (layout_->isPassThru())
872                 rp.verbatim = true;
873         if (layout_->isNeedProtect())
874                 rp.moving_arg = true;
875         int i = InsetText::latex(buf, os, rp);
876         if (!layout_->latexname().empty()) {
877                 if (layout_->latextype() == "command") {
878                         os << "}";
879                 } else if (layout_->latextype() == "environment") {
880                         os << "\n\\end{" << from_utf8(layout_->latexname()) << "}\n";
881                         i += 4;
882                 }
883         }
884         return i;
885 }
886
887
888 void InsetCollapsable::validate(LaTeXFeatures & features) const
889 {
890         if (!layout_)
891                 return;
892
893         // Force inclusion of preamble snippet in layout file
894         features.require(to_utf8(layout_->name()));
895         InsetText::validate(features);
896 }
897
898
899 bool InsetCollapsable::undefined() const
900 {
901         docstring const & n = getLayout().name();
902         return n.empty() || n == TextClass::emptyInsetLayout().name();
903 }
904
905
906 } // namespace lyx