#include "InsetCollapsable.h"
#include "Buffer.h"
-#include "BufferParams.h"
#include "BufferView.h"
#include "Cursor.h"
#include "Dimension.h"
-#include "FloatList.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "InsetLayout.h"
#include "Lexer.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
+#include "TocBackend.h"
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/lstrings.h"
+#include "support/RefChanger.h"
using namespace std;
namespace lyx {
-InsetCollapsable::InsetCollapsable(Buffer const & buf, InsetText::UsePlain ltype)
- : InsetText(buf, ltype), status_(Inset::Open),
- openinlined_(false), mouse_hover_(false)
+InsetCollapsable::InsetCollapsable(Buffer * buf, InsetText::UsePlain ltype)
+ : InsetText(buf, ltype), status_(Open)
{
- setAutoBreakRows(true);
setDrawFrame(true);
setFrameColor(Color_collapsableframe);
}
+// The sole purpose of this copy constructor is to make sure
+// that the view_ map is not copied and remains empty.
InsetCollapsable::InsetCollapsable(InsetCollapsable const & rhs)
: InsetText(rhs),
status_(rhs.status_),
- labelstring_(rhs.labelstring_),
- button_dim(rhs.button_dim),
- openinlined_(rhs.openinlined_),
- auto_open_(rhs.auto_open_),
- // the sole purpose of this copy constructor
- mouse_hover_(false)
+ labelstring_(rhs.labelstring_)
+{}
+
+
+InsetCollapsable::~InsetCollapsable()
{
+ map<BufferView const *, View>::iterator it = view_.begin();
+ map<BufferView const *, View>::iterator end = view_.end();
+ for (; it != end; ++it)
+ if (it->second.mouse_hover_)
+ it->first->clearLastInset(this);
}
{
if (decoration() == InsetLayout::CONGLOMERATE)
return status_;
- return auto_open_[&bv] ? Open : status_;
+ return view_[&bv].auto_open_ ? Open : status_;
}
switch (decoration()) {
case InsetLayout::CLASSIC:
if (status(bv) == Open)
- return openinlined_ ? LeftButton : TopButton;
+ return view_[&bv].openinlined_ ? LeftButton : TopButton;
return ButtonOnly;
case InsetLayout::MINIMALISTIC:
}
-InsetCollapsable::Geometry InsetCollapsable::geometry() const
-{
- switch (decoration()) {
- case InsetLayout::CLASSIC:
- if (status_ == Open)
- return openinlined_ ? LeftButton : TopButton;
- return ButtonOnly;
-
- case InsetLayout::MINIMALISTIC:
- return status_ == Open ? NoButton : ButtonOnly ;
-
- case InsetLayout::CONGLOMERATE:
- return status_ == Open ? SubLabel : Corners ;
-
- case InsetLayout::DEFAULT:
- break; // this shouldn't happen
- }
-
- // dummy return value to shut down a warning,
- // this is dead code.
- return NoButton;
-}
-
-
docstring InsetCollapsable::toolTip(BufferView const & bv, int x, int y) const
{
- Dimension dim = dimensionCollapsed(bv);
+ Dimension const dim = dimensionCollapsed(bv);
if (geometry(bv) == NoButton)
return translateIfPossible(getLayout().labelstring());
if (x > xo(bv) + dim.wid || y > yo(bv) + dim.des || isOpen(bv))
return docstring();
- OutputParams rp(&buffer().params().encoding());
- odocstringstream ods;
- InsetText::plaintext(ods, rp);
- docstring const content_tip = ods.str();
- return support::wrapParas(content_tip, 4);
+ return toolTipText();
}
break;
}
os << "\n";
- text().write(buffer(), os);
+ text().write(os);
}
Dimension InsetCollapsable::dimensionCollapsed(BufferView const & bv) const
{
Dimension dim;
- theFontMetrics(getLayout().labelfont()).buttonText(
+ FontInfo labelfont(getLabelfont());
+ labelfont.realize(sane_font);
+ theFontMetrics(labelfont).buttonText(
buttonLabel(bv), dim.wid, dim.asc, dim.des);
return dim;
}
void InsetCollapsable::metrics(MetricsInfo & mi, Dimension & dim) const
{
- auto_open_[mi.base.bv] = mi.base.bv->cursor().isInside(this);
+ view_[mi.base.bv].auto_open_ = mi.base.bv->cursor().isInside(this);
FontInfo tmpfont = mi.base.font;
- mi.base.font = getLayout().font();
+ mi.base.font = getFont();
mi.base.font.realize(tmpfont);
BufferView const & bv = *mi.base.bv;
case SubLabel: {
InsetText::metrics(mi, dim);
// consider width of the inset label
- FontInfo font(getLayout().labelfont());
+ FontInfo font(getLabelfont());
font.realize(sane_font);
font.decSize();
font.decSize();
case TopButton:
case LeftButton:
case ButtonOnly:
+ if (hasFixedWidth()){
+ int const mindim = view_[&bv].button_dim_.x2 - view_[&bv].button_dim_.x1;
+ if (mi.base.textwidth < mindim)
+ mi.base.textwidth = mindim;
+ }
dim = dimensionCollapsed(bv);
- if (geometry(bv) == TopButton
- || geometry(bv) == LeftButton) {
+ if (geometry(bv) == TopButton || geometry(bv) == LeftButton) {
Dimension textdim;
InsetText::metrics(mi, textdim);
- openinlined_ = (textdim.wid + dim.wid) < mi.base.textwidth;
- if (openinlined_) {
+ view_[&bv].openinlined_ = (textdim.wid + dim.wid) < mi.base.textwidth;
+ if (view_[&bv].openinlined_) {
// Correct for button width.
dim.wid += textdim.wid;
dim.des = max(dim.des - textdim.asc + dim.asc, textdim.des);
}
-bool InsetCollapsable::setMouseHover(bool mouse_hover)
+bool InsetCollapsable::setMouseHover(BufferView const * bv, bool mouse_hover)
+ const
{
- mouse_hover_ = mouse_hover;
+ view_[bv].mouse_hover_ = mouse_hover;
return true;
}
{
BufferView const & bv = *pi.base.bv;
- auto_open_[&bv] = bv.cursor().isInside(this);
+ view_[&bv].auto_open_ = bv.cursor().isInside(this);
- FontInfo tmpfont = pi.base.font;
- pi.base.font = getLayout().font();
- pi.base.font.realize(tmpfont);
+ Changer dummy = pi.base.font.change(getFont(), true);
// Draw button first -- top, left or only
Dimension dimc = dimensionCollapsed(bv);
if (geometry(bv) == TopButton ||
geometry(bv) == LeftButton ||
geometry(bv) == ButtonOnly) {
- button_dim.x1 = x + 0;
- button_dim.x2 = x + dimc.width();
- button_dim.y1 = y - dimc.asc;
- button_dim.y2 = y + dimc.des;
+ view_[&bv].button_dim_.x1 = x + 0;
+ view_[&bv].button_dim_.x2 = x + dimc.width();
+ view_[&bv].button_dim_.y1 = y - dimc.asc;
+ view_[&bv].button_dim_.y2 = y + dimc.des;
- FontInfo labelfont = getLayout().labelfont();
+ FontInfo labelfont = getLabelfont();
labelfont.setColor(labelColor());
+ labelfont.realize(pi.base.font);
pi.pain.buttonText(x, y, buttonLabel(bv), labelfont,
- mouse_hover_);
+ view_[&bv].mouse_hover_);
+ // Draw the change tracking cue on the label, unless RowPainter already
+ // takes care of it.
+ if (canPaintChange(bv))
+ pi.change_.paintCue(pi, x, y, x + dimc.width(), labelfont);
} else {
- button_dim.x1 = 0;
- button_dim.y1 = 0;
- button_dim.x2 = 0;
- button_dim.y2 = 0;
+ view_[&bv].button_dim_.x1 = 0;
+ view_[&bv].button_dim_.y1 = 0;
+ view_[&bv].button_dim_.x2 = 0;
+ view_[&bv].button_dim_.y2 = 0;
}
- Dimension const textdim = InsetText::dimension(bv);
+ Dimension const textdim = dimensionHelper(bv);
int const baseline = y;
int textx, texty;
- switch (geometry(bv)) {
+ Geometry g = geometry(bv);
+ switch (g) {
case LeftButton:
- textx = x + dimc.width();
- texty = baseline;
- InsetText::draw(pi, textx, texty);
- break;
- case TopButton:
- textx = x;
- texty = baseline + dimc.des + textdim.asc;
+ case TopButton: {
+ if (g == LeftButton) {
+ textx = x + dimc.width();
+ texty = baseline;
+ } else {
+ textx = x;
+ texty = baseline + dimc.des + textdim.asc;
+ }
+ // Do not draw the cue for INSERTED -- it is already in the button and
+ // that's enough.
+ Changer dummy = (pi.change_.type == Change::INSERTED)
+ ? make_change(pi.change_, Change())
+ : Changer();
InsetText::draw(pi, textx, texty);
break;
+ }
case ButtonOnly:
break;
case NoButton:
case Corners:
textx = x;
texty = baseline;
- const_cast<InsetCollapsable *>(this)->setDrawFrame(false);
- InsetText::draw(pi, textx, texty);
- const_cast<InsetCollapsable *>(this)->setDrawFrame(true);
+ { // We will take care of the frame and the change tracking cue
+ // ourselves, below.
+ Changer dummy = make_change(pi.change_, Change());
+ const_cast<InsetCollapsable *>(this)->setDrawFrame(false);
+ InsetText::draw(pi, textx, texty);
+ const_cast<InsetCollapsable *>(this)->setDrawFrame(true);
+ }
int desc = textdim.descent();
- if (geometry(bv) == Corners)
+ if (g == Corners)
desc -= 3;
+ // Colour the frame according to the change type. (Like for tables.)
+ Color colour = pi.change_.changed() ? pi.change_.color()
+ : Color_foreground;
const int xx1 = x + TEXT_TO_INSET_OFFSET - 1;
const int xx2 = x + textdim.wid - TEXT_TO_INSET_OFFSET + 1;
- pi.pain.line(xx1, y + desc - 4,
- xx1, y + desc,
- labelColor());
+ pi.pain.line(xx1, y + desc - 4,
+ xx1, y + desc, colour);
if (status_ == Open)
- pi.pain.line(xx1, y + desc,
- xx2, y + desc,
- labelColor());
+ pi.pain.line(xx1, y + desc,
+ xx2, y + desc, colour);
else {
// Make status_ value visible:
pi.pain.line(xx1, y + desc,
- xx1 + 4, y + desc,
- labelColor());
+ xx1 + 4, y + desc, colour);
pi.pain.line(xx2 - 4, y + desc,
- xx2, y + desc,
- labelColor());
+ xx2, y + desc, colour);
}
- pi.pain.line(x + textdim.wid - 3, y + desc, x + textdim.wid - 3,
- y + desc - 4, labelColor());
+ pi.pain.line(x + textdim.wid - 3, y + desc, x + textdim.wid - 3,
+ y + desc - 4, colour);
// the label below the text. Can be toggled.
- if (geometry(bv) == SubLabel) {
- FontInfo font(getLayout().labelfont());
+ if (g == SubLabel) {
+ FontInfo font(getLabelfont());
+ if (pi.change_.changed())
+ font.setPaintColor(colour);
font.realize(sane_font);
font.decSize();
font.decSize();
int w = 0;
int a = 0;
int d = 0;
+ Color const col = pi.full_repaint ? Color_none : pi.backgroundColor(this);
theFontMetrics(font).rectText(buttonLabel(bv), w, a, d);
int const ww = max(textdim.wid, w);
pi.pain.rectText(x + (ww - w) / 2, y + desc + a,
- buttonLabel(bv), font, Color_none, Color_none);
- desc += d;
+ buttonLabel(bv), font, col, Color_none);
}
+ int const y1 = y - textdim.asc + 3;
// a visual cue when the cursor is inside the inset
Cursor const & cur = bv.cursor();
if (cur.isInside(this)) {
- y -= textdim.asc;
- y += 3;
- pi.pain.line(xx1, y + 4, xx1, y, labelColor());
- pi.pain.line(xx1 + 4, y, xx1, y, labelColor());
- pi.pain.line(xx2, y + 4, xx2, y,
- labelColor());
- pi.pain.line(xx2 - 4, y, xx2, y,
- labelColor());
+ pi.pain.line(xx1, y1 + 4, xx1, y1, colour);
+ pi.pain.line(xx1 + 4, y1, xx1, y1, colour);
+ pi.pain.line(xx2, y1 + 4, xx2, y1, colour);
+ pi.pain.line(xx2 - 4, y1, xx2, y1, colour);
}
+ // Strike through the inset if deleted and not already handled by
+ // RowPainter.
+ if (pi.change_.deleted() && canPaintChange(bv))
+ pi.change_.paintCue(pi, xx1, y1, xx2, y + desc);
break;
}
-
- pi.base.font = tmpfont;
}
status_ = Open;
InsetText::cursorPos(bv, sl, boundary, x, y);
- Dimension const textdim = InsetText::dimension(bv);
+ Dimension const textdim = dimensionHelper(bv);
switch (geometry(bv)) {
case LeftButton:
bool InsetCollapsable::editable() const
{
- return geometry() != ButtonOnly;
+ switch (decoration()) {
+ case InsetLayout::CLASSIC:
+ case InsetLayout::MINIMALISTIC:
+ return status_ == Open;
+ default:
+ return true;
+ }
}
-bool InsetCollapsable::descendable() const
+bool InsetCollapsable::descendable(BufferView const & bv) const
{
- return geometry() != ButtonOnly;
+ return geometry(bv) != ButtonOnly;
}
-bool InsetCollapsable::hitButton(FuncRequest const & cmd) const
+bool InsetCollapsable::clickable(BufferView const & bv, int x, int y) const
{
- return button_dim.contains(cmd.x, cmd.y);
+ return view_[&bv].button_dim_.contains(x, y);
}
{
//lyxerr << "InsetCollapsable: edit xy" << endl;
if (geometry(cur.bv()) == ButtonOnly
- || (button_dim.contains(x, y)
- && geometry(cur.bv()) != NoButton))
+ || (view_[&cur.bv()].button_dim_.contains(x, y)
+ && geometry(cur.bv()) != NoButton))
return this;
cur.push(*this);
return InsetText::editXY(cur, x, y);
//lyxerr << "InsetCollapsable::doDispatch (begin): cmd: " << cmd
// << " cur: " << cur << " bvcur: " << cur.bv().cursor() << endl;
- switch (cmd.action) {
+ bool const hitButton = clickable(cur.bv(), cmd.x(), cmd.y());
+
+ switch (cmd.action()) {
case LFUN_MOUSE_PRESS:
- if (hitButton(cmd)) {
+ if (hitButton) {
switch (cmd.button()) {
case mouse_button::button1:
case mouse_button::button3:
case mouse_button::button4:
case mouse_button::button5:
// Nothing to do.
- cur.noUpdate();
+ cur.noScreenUpdate();
break;
}
} else if (geometry(cur.bv()) != ButtonOnly)
case LFUN_MOUSE_MOTION:
case LFUN_MOUSE_DOUBLE:
case LFUN_MOUSE_TRIPLE:
- if (hitButton(cmd))
- cur.noUpdate();
+ if (hitButton)
+ cur.noScreenUpdate();
else if (geometry(cur.bv()) != ButtonOnly)
InsetText::doDispatch(cur, cmd);
else
break;
case LFUN_MOUSE_RELEASE:
- if (!hitButton(cmd)) {
+ if (!hitButton) {
// The mouse click has to be within the inset!
if (geometry(cur.bv()) != ButtonOnly)
InsetText::doDispatch(cur, cmd);
else
- cur.undispatched();
+ cur.undispatched();
break;
}
if (cmd.button() != mouse_button::button1) {
// Nothing to do.
- cur.noUpdate();
+ cur.noScreenUpdate();
break;
}
// if we are selecting, we do not want to
// Left button is clicked, the user asks to
// toggle the inset visual state.
cur.dispatched();
- cur.updateFlags(Update::Force | Update::FitCursor);
+ cur.screenUpdateFlags(Update::Force | Update::FitCursor);
if (geometry(cur.bv()) == ButtonOnly) {
setStatus(cur, Open);
edit(cur, true);
else if (cmd.argument() == "close")
setStatus(cur, Collapsed);
else if (cmd.argument() == "toggle" || cmd.argument().empty())
- if (status_ == Open) {
+ if (status_ == Open)
setStatus(cur, Collapsed);
- if (geometry(cur.bv()) == ButtonOnly)
- cur.top().forwardPos();
- } else
+ else
setStatus(cur, Open);
else // if assign or anything else
cur.undispatched();
bool InsetCollapsable::getStatus(Cursor & cur, FuncRequest const & cmd,
FuncStatus & flag) const
{
- switch (cmd.action) {
+ switch (cmd.action()) {
case LFUN_INSET_TOGGLE:
if (cmd.argument() == "open")
flag.setEnabled(status_ != Open);
}
-docstring const InsetCollapsable::buttonLabel(BufferView const &) const
+docstring InsetCollapsable::getLabel() const
{
- return labelstring_.empty() ? getLayout().labelstring() : labelstring_;
+ InsetLayout const & il = getLayout();
+ return labelstring_.empty() ?
+ translateIfPossible(il.labelstring()) : labelstring_;
}
-void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
+docstring const InsetCollapsable::buttonLabel(BufferView const & bv) const
{
- status_ = status;
- setButtonLabel();
- if (status_ == Collapsed) {
- cur.leaveInset(*this);
- mouse_hover_ = false;
- }
+ InsetLayout const & il = getLayout();
+ docstring const label = getLabel();
+ if (!il.contentaslabel() || geometry(bv) != ButtonOnly)
+ return label;
+ return getNewLabel(label);
}
-docstring InsetCollapsable::floatName(string const & type) const
+void InsetCollapsable::setStatus(Cursor & cur, CollapseStatus status)
{
- BufferParams const & bp = buffer().params();
- FloatList const & floats = bp.documentClass().floats();
- FloatList::const_iterator it = floats[type];
- // FIXME UNICODE
- return (it == floats.end()) ? from_ascii(type) : bp.B_(it->second.name());
+ status_ = status;
+ setButtonLabel();
+ if (status_ == Collapsed)
+ cur.leaveInset(*this);
}
}
-docstring InsetCollapsable::contextMenu(BufferView const & bv, int x,
+string InsetCollapsable::contextMenu(BufferView const & bv, int x,
int y) const
{
+ string context_menu = contextMenuName();
+ string const it_context_menu = InsetText::contextMenuName();
if (decoration() == InsetLayout::CONGLOMERATE)
- return from_ascii("context-conglomerate");
+ return context_menu + ";" + it_context_menu;
+
+ string const ic_context_menu = InsetCollapsable::contextMenuName();
+ if (ic_context_menu != context_menu)
+ context_menu += ";" + ic_context_menu;
if (geometry(bv) == NoButton)
- return from_ascii("context-collapsable");
+ return context_menu + ";" + it_context_menu;
Dimension dim = dimensionCollapsed(bv);
if (x < xo(bv) + dim.wid && y < yo(bv) + dim.des)
- return from_ascii("context-collapsable");
+ return context_menu;
+
+ return it_context_menu;
+}
+
+
+string InsetCollapsable::contextMenuName() const
+{
+ if (decoration() == InsetLayout::CONGLOMERATE)
+ return "context-conglomerate";
+ else
+ return "context-collapsable";
+}
+
+
+bool InsetCollapsable::canPaintChange(BufferView const & bv) const
+{
+ // return false to let RowPainter draw the change tracking cue consistently
+ // with the surrounding text, when the inset is inline: for buttons, for
+ // non-allowMultiPar insets.
+ switch (geometry(bv)) {
+ case Corners:
+ case SubLabel:
+ return allowMultiPar();
+ case ButtonOnly:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
- return InsetText::contextMenu(bv, x, y);
+
+void InsetCollapsable::addToToc(DocIterator const & cpit, bool output_active,
+ UpdateType utype) const
+{
+ bool doing_output = output_active && producesOutput();
+ InsetLayout const & layout = getLayout();
+ if (layout.addToToc()) {
+ TocBuilder & b = buffer().tocBackend().builder(layout.tocType());
+ // Cursor inside the inset
+ DocIterator pit = cpit;
+ pit.push_back(CursorSlice(const_cast<InsetCollapsable &>(*this)));
+ docstring const label = getLabel();
+ b.pushItem(pit, label + (label.empty() ? "" : ": "), output_active);
+ // Proceed with the rest of the inset.
+ InsetText::addToToc(cpit, doing_output, utype);
+ if (layout.isTocCaption()) {
+ docstring str;
+ text().forOutliner(str, TOC_ENTRY_LENGTH);
+ b.argumentItem(str);
+ }
+ b.pop();
+ } else
+ InsetText::addToToc(cpit, doing_output, utype);
}
+
+
} // namespace lyx