2 * \file InsetCollapsable.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
8 * \author Lars Gullik Bjønnes
10 * Full author contact details are available in file CREDITS.
15 #include "InsetCollapsable.h"
18 #include "BufferParams.h"
19 #include "BufferView.h"
22 #include "DispatchResult.h"
23 #include "FloatList.h"
24 #include "FuncStatus.h"
28 #include "FuncRequest.h"
29 #include "MetricsInfo.h"
31 #include "frontends/FontMetrics.h"
32 #include "frontends/Painter.h"
37 using graphics::PreviewLoader;
46 InsetCollapsable::CollapseStatus InsetCollapsable::status() const
48 return autoOpen_ ? Open : status_;
52 InsetCollapsable::Geometry InsetCollapsable::geometry() const
54 switch (decoration()) {
56 if (status_ == Open || autoOpen_) {
68 return status_ == Open ? SubLabel : Corners;
71 // dummy return value to shut down a warning,
77 InsetCollapsable::InsetCollapsable
78 (BufferParams const & bp, CollapseStatus status)
79 : InsetText(bp), label(from_ascii("Label")), status_(status),
80 openinlined_(false), autoOpen_(false), mouse_hover_(false)
82 setAutoBreakRows(true);
84 setFrameColor(Color::collapsableframe);
89 InsetCollapsable::InsetCollapsable(InsetCollapsable const & rhs)
91 button_dim(rhs.button_dim),
93 topbaseline(rhs.topbaseline),
97 openinlined_(rhs.openinlined_),
98 autoOpen_(rhs.autoOpen_),
99 textdim_(rhs.textdim_),
100 // the sole purpose of this copy constructor
106 void InsetCollapsable::setLayout(BufferParams const & bp)
108 setLabelFont(getLayout(bp).labelfont);
109 setLabel(getLayout(bp).labelstring);
113 void InsetCollapsable::write(Buffer const & buf, ostream & os) const
125 text_.write(buf, os);
129 void InsetCollapsable::read(Buffer const & buf, Lexer & lex)
131 bool token_found = false;
134 string const token = lex.getString();
135 if (token == "status") {
137 string const tmp_token = lex.getString();
139 if (tmp_token == "collapsed") {
142 } else if (tmp_token == "open") {
146 lyxerr << "InsetCollapsable::read: Missing status!"
148 // Take countermeasures
149 lex.pushToken(token);
152 lyxerr << "InsetCollapsable::read: Missing 'status'-tag!"
154 // take countermeasures
155 lex.pushToken(token);
158 InsetText::read(buf, lex);
161 status_ = isOpen() ? Open : Collapsed;
167 Dimension InsetCollapsable::dimensionCollapsed() const
170 theFontMetrics(layout_.labelfont).buttonText(
171 label, dim.wid, dim.asc, dim.des);
176 bool InsetCollapsable::metrics(MetricsInfo & mi, Dimension & dim) const
178 autoOpen_ = mi.base.bv->cursor().isInside(this);
179 mi.base.textwidth -= (int) (1.5 * TEXT_TO_INSET_OFFSET);
181 switch (decoration()) {
184 InsetText::metrics(mi, dim);
187 dim = dimensionCollapsed();
188 if (geometry() == TopButton
189 || geometry() == LeftButton) {
190 InsetText::metrics(mi, textdim_);
191 // This expression should not contain mi.base.texwidth
192 openinlined_ = !hasFixedWidth()
193 && textdim_.wid < 0.5 * mi.base.bv->workWidth();
195 // Correct for button width, and re-fit
196 mi.base.textwidth -= dim.wid;
197 InsetText::metrics(mi, textdim_);
198 dim.wid += textdim_.wid;
199 dim.des = max(dim.des - textdim_.asc + dim.asc, textdim_.des);
200 dim.asc = textdim_.asc;
202 dim.des += textdim_.height() + TEXT_TO_BOTTOM_OFFSET;
203 dim.wid = max(dim.wid, textdim_.wid);
205 dim.wid = max(dim.wid, mi.base.textwidth);
210 dim.asc += TEXT_TO_INSET_OFFSET;
211 dim.des += TEXT_TO_INSET_OFFSET;
212 dim.wid += (int) (1.5 * TEXT_TO_INSET_OFFSET);
213 mi.base.textwidth += (int) (1.5 * TEXT_TO_INSET_OFFSET);
214 bool const changed = dim_ != dim;
220 bool InsetCollapsable::setMouseHover(bool mouse_hover)
222 mouse_hover_ = mouse_hover;
227 void InsetCollapsable::draw(PainterInfo & pi, int x, int y) const
229 const int xx = x + TEXT_TO_INSET_OFFSET;
231 // Draw button first -- top, left or only
232 Dimension dimc = dimensionCollapsed();
233 int const top = y - ascent() + TEXT_TO_INSET_OFFSET;
234 if (decoration() == Classic) {
235 button_dim.x1 = xx + 0;
236 button_dim.x2 = xx + dimc.width();
238 button_dim.y2 = top + dimc.height();
240 pi.pain.buttonText(xx, top + dimc.asc, label, layout_.labelfont, mouse_hover_);
244 switch (geometry()) {
246 textx = xx + dimc.width();
247 texty = top + textdim_.asc;
248 InsetText::draw(pi, textx, texty);
252 texty = top + dimc.height() + textdim_.asc;
253 InsetText::draw(pi, textx, texty);
259 texty = y + textdim_.asc;
260 InsetText::draw(pi, textx, texty);
264 // FIXME add handling of SubLabel, Corners
265 // still in CharStyle
267 texty = y + textdim_.asc;
268 const_cast<InsetCollapsable *>(this)->setDrawFrame(false);
269 InsetText::draw(pi, textx, texty);
270 const_cast<InsetCollapsable *>(this)->setDrawFrame(true);
273 setPosCache(pi, x, y);
277 void InsetCollapsable::drawSelection(PainterInfo & pi, int x, int y) const
279 x += TEXT_TO_INSET_OFFSET;
280 switch (geometry()) {
282 x += dimensionCollapsed().wid;
283 InsetText::drawSelection(pi, x, y);
286 y += dimensionCollapsed().des + textdim_.asc;
287 InsetText::drawSelection(pi, x, y);
294 InsetText::drawSelection(pi, x, y);
300 void InsetCollapsable::cursorPos(BufferView const & bv,
301 CursorSlice const & sl, bool boundary, int & x, int & y) const
303 BOOST_ASSERT(geometry() != ButtonOnly);
305 InsetText::cursorPos(bv, sl, boundary, x, y);
307 switch (geometry()) {
309 x += dimensionCollapsed().wid;
312 y += dimensionCollapsed().height() - ascent()
313 + TEXT_TO_INSET_OFFSET + textdim_.asc;
324 x += TEXT_TO_INSET_OFFSET;
328 Inset::EDITABLE InsetCollapsable::editable() const
330 return geometry() != ButtonOnly? HIGHLY_EDITABLE : IS_EDITABLE;
334 bool InsetCollapsable::descendable() const
336 return geometry() != ButtonOnly;
340 bool InsetCollapsable::hitButton(FuncRequest const & cmd) const
342 return button_dim.contains(cmd.x, cmd.y);
346 docstring const InsetCollapsable::getNewLabel(docstring const & l) const
349 pos_type const max_length = 15;
350 pos_type const p_siz = paragraphs().begin()->size();
351 pos_type const n = min(max_length, p_siz);
354 for (; i < n && j < p_siz; ++j) {
355 if (paragraphs().begin()->isInset(j))
357 label += paragraphs().begin()->getChar(j);
360 if (paragraphs().size() > 1 || (i > 0 && j < p_siz)) {
363 return label.empty() ? l : label;
367 void InsetCollapsable::edit(Cursor & cur, bool left)
369 //lyxerr << "InsetCollapsable: edit left/right" << endl;
371 InsetText::edit(cur, left);
375 Inset * InsetCollapsable::editXY(Cursor & cur, int x, int y)
377 //lyxerr << "InsetCollapsable: edit xy" << endl;
378 if (geometry() == ButtonOnly
379 || (button_dim.contains(x, y)
380 && decoration() != Minimalistic))
383 return InsetText::editXY(cur, x, y);
387 void InsetCollapsable::doDispatch(Cursor & cur, FuncRequest & cmd)
389 //lyxerr << "InsetCollapsable::doDispatch (begin): cmd: " << cmd
390 // << " cur: " << cur << " bvcur: " << cur.bv().cursor() << endl;
392 switch (cmd.action) {
393 case LFUN_MOUSE_PRESS:
394 if (cmd.button() == mouse_button::button1
396 && decoration() != Minimalistic) {
397 // reset selection if necessary (see bug 3060)
399 cur.bv().cursor().clearSelection();
405 if (decoration() == Minimalistic)
406 InsetText::doDispatch(cur, cmd);
407 else if (geometry() != ButtonOnly
409 InsetText::doDispatch(cur, cmd);
414 case LFUN_MOUSE_MOTION:
415 case LFUN_MOUSE_DOUBLE:
416 case LFUN_MOUSE_TRIPLE:
417 if (decoration() == Minimalistic)
418 InsetText::doDispatch(cur, cmd);
419 else if (geometry() != ButtonOnly
421 InsetText::doDispatch(cur, cmd);
426 case LFUN_MOUSE_RELEASE:
427 if (cmd.button() == mouse_button::button3) {
428 // Open the Inset configuration dialog
429 showInsetDialog(&cur.bv());
433 if (decoration() == Minimalistic) {
434 // The mouse click has to be within the inset!
435 InsetText::doDispatch(cur, cmd);
439 if (cmd.button() == mouse_button::button1 && hitButton(cmd)) {
440 // if we are selecting, we do not want to
444 // Left button is clicked, the user asks to
445 // toggle the inset visual state.
447 cur.updateFlags(Update::Force | Update::FitCursor);
448 if (geometry() == ButtonOnly) {
449 setStatus(cur, Open);
453 setStatus(cur, Collapsed);
455 cur.bv().cursor() = cur;
459 // The mouse click is within the opened inset.
460 if (geometry() == TopButton
461 || geometry() == LeftButton)
462 InsetText::doDispatch(cur, cmd);
465 case LFUN_INSET_TOGGLE:
466 if (cmd.argument() == "open")
467 setStatus(cur, Open);
468 else if (cmd.argument() == "close")
469 setStatus(cur, Collapsed);
470 else if (cmd.argument() == "toggle" || cmd.argument().empty())
472 setStatus(cur, Collapsed);
473 cur.top().forwardPos();
476 setStatus(cur, Open);
477 else // if assign or anything else
483 InsetText::doDispatch(cur, cmd);
489 bool InsetCollapsable::getStatus(Cursor & cur, FuncRequest const & cmd,
490 FuncStatus & flag) const
492 switch (cmd.action) {
494 case LFUN_INSET_TOGGLE:
495 if (cmd.argument() == "open" || cmd.argument() == "close" ||
496 cmd.argument() == "toggle")
503 return InsetText::getStatus(cur, cmd, flag);
508 void InsetCollapsable::setLabel(docstring const & l)
514 void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
518 if (status_ == Collapsed)
519 cur.leaveInset(*this);
520 // Because the collapse status is part of the inset and thus an
521 // integral part of the Buffer contents a changed status must be
522 // signaled to all views of current buffer.
523 cur.bv().buffer()->changed();
527 void InsetCollapsable::setLabelFont(Font const & font)
529 layout_.labelfont = font;
532 docstring InsetCollapsable::floatName(string const & type, BufferParams const & bp) const
534 FloatList const & floats = bp.getTextClass().floats();
535 FloatList::const_iterator it = floats[type];
537 return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());