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