1 /**
2  * \file InsetVSpace.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author various
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 
12 #include <config.h>
13 
14 #include "InsetVSpace.h"
15 
16 #include "Buffer.h"
17 #include "BufferView.h"
18 #include "Cursor.h"
19 #include "Dimension.h"
20 #include "DispatchResult.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "Lexer.h"
24 #include "MetricsInfo.h"
25 #include "OutputParams.h"
26 #include "output_xhtml.h"
27 #include "texstream.h"
28 #include "Text.h"
29 
30 #include "support/debug.h"
31 #include "support/docstream.h"
32 #include "support/gettext.h"
33 #include "support/lassert.h"
34 
35 #include "frontends/Application.h"
36 #include "frontends/FontMetrics.h"
37 #include "frontends/Painter.h"
38 
39 #include <sstream>
40 
41 using namespace std;
42 
43 namespace lyx {
44 
45 namespace {
46 
47 int const ADD_TO_VSPACE_WIDTH = 5;
48 
49 } // namespace
50 
51 
InsetVSpace(VSpace const & space)52 InsetVSpace::InsetVSpace(VSpace const & space)
53 	: Inset(0), space_(space)
54 {}
55 
56 
doDispatch(Cursor & cur,FuncRequest & cmd)57 void InsetVSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
58 {
59 	switch (cmd.action()) {
60 
61 	case LFUN_INSET_MODIFY: {
62 		cur.recordUndo();
63 		string arg = to_utf8(cmd.argument());
64 		if (arg == "vspace custom")
65 			arg = (space_.kind() == VSpace::LENGTH)
66 				? "vspace " + space_.length().asString()
67 				: "vspace 1" + string(stringFromUnit(Length::defaultUnit()));
68 		InsetVSpace::string2params(arg, space_);
69 		break;
70 	}
71 
72 	default:
73 		Inset::doDispatch(cur, cmd);
74 		break;
75 	}
76 }
77 
78 
getStatus(Cursor & cur,FuncRequest const & cmd,FuncStatus & status) const79 bool InsetVSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
80 	FuncStatus & status) const
81 {
82 	switch (cmd.action()) {
83 	// we handle these
84 	case LFUN_INSET_MODIFY:
85 		if (cmd.getArg(0) == "vspace") {
86 			VSpace vspace;
87 			string arg = to_utf8(cmd.argument());
88 			if (arg == "vspace custom")
89 				arg = (space_.kind() == VSpace::LENGTH)
90 				? "vspace " + space_.length().asString()
91 				: "vspace 1" + string(stringFromUnit(Length::defaultUnit()));
92 			InsetVSpace::string2params(arg, vspace);
93 			status.setOnOff(vspace == space_);
94 		}
95 		status.setEnabled(true);
96 		return true;
97 
98 	default:
99 		return Inset::getStatus(cur, cmd, status);
100 	}
101 }
102 
103 
read(Lexer & lex)104 void InsetVSpace::read(Lexer & lex)
105 {
106 	LASSERT(lex.isOK(), return);
107 	string vsp;
108 	lex >> vsp;
109 	if (lex)
110 		space_ = VSpace(vsp);
111 	lex >> "\\end_inset";
112 }
113 
114 
write(ostream & os) const115 void InsetVSpace::write(ostream & os) const
116 {
117 	os << "VSpace " << space_.asLyXCommand();
118 }
119 
120 
label() const121 docstring const InsetVSpace::label() const
122 {
123 	static docstring const label = _("Vertical Space");
124 	return label + " (" + space_.asGUIName() + ')';
125 }
126 
127 
128 namespace {
129 int const vspace_arrow_size = 4;
130 }
131 
132 
metrics(MetricsInfo & mi,Dimension & dim) const133 void InsetVSpace::metrics(MetricsInfo & mi, Dimension & dim) const
134 {
135 	int height = 3 * vspace_arrow_size;
136 	if (space_.length().len().value() >= 0.0)
137 		height = max(height, space_.inPixels(*mi.base.bv));
138 
139 	FontInfo font;
140 	font.decSize();
141 	font.decSize();
142 
143 	int w = 0;
144 	int a = 0;
145 	int d = 0;
146 	theFontMetrics(font).rectText(label(), w, a, d);
147 
148 	height = max(height, a + d);
149 
150 	dim.asc = height / 2 + (a - d) / 2; // align cursor with the
151 	dim.des = height - dim.asc;         // label text
152 	dim.wid = ADD_TO_VSPACE_WIDTH + 2 * vspace_arrow_size + 5 + w;
153 }
154 
155 
draw(PainterInfo & pi,int x,int y) const156 void InsetVSpace::draw(PainterInfo & pi, int x, int y) const
157 {
158 	Dimension const dim = dimension(*pi.base.bv);
159 	x += ADD_TO_VSPACE_WIDTH;
160 	int const start = y - dim.asc;
161 	int const end   = y + dim.des;
162 
163 	// y-values for top arrow
164 	int ty1, ty2;
165 	// y-values for bottom arrow
166 	int by1, by2;
167 
168 	if (space_.kind() == VSpace::VFILL) {
169 		ty1 = ty2 = start;
170 		by1 = by2 = end;
171 	} else {
172 		// adding or removing space
173 		bool const added = space_.kind() != VSpace::LENGTH ||
174 				   space_.length().len().value() >= 0.0;
175 		ty1 = added ? (start + vspace_arrow_size) : start;
176 		ty2 = added ? start : (start + vspace_arrow_size);
177 		by1 = added ? (end - vspace_arrow_size) : end;
178 		by2 = added ? end : (end - vspace_arrow_size);
179 	}
180 
181 	int const midx = x + vspace_arrow_size;
182 	int const rightx = midx + vspace_arrow_size;
183 
184 	// first the string
185 	int w = 0;
186 	int a = 0;
187 	int d = 0;
188 
189 	FontInfo font;
190 	font.setColor(Color_added_space);
191 	font.decSize();
192 	font.decSize();
193 	docstring const lab = label();
194 	theFontMetrics(font).rectText(lab, w, a, d);
195 
196 	pi.pain.rectText(x + 2 * vspace_arrow_size + 5,
197 			 start + (end - start) / 2 + (a - d) / 2,
198 			 lab, font, Color_none, Color_none);
199 
200 	// top arrow
201 	pi.pain.line(x, ty1, midx, ty2, Color_added_space);
202 	pi.pain.line(midx, ty2, rightx, ty1, Color_added_space);
203 
204 	// bottom arrow
205 	pi.pain.line(x, by1, midx, by2, Color_added_space);
206 	pi.pain.line(midx, by2, rightx, by1, Color_added_space);
207 
208 	// joining line
209 	pi.pain.line(midx, ty2, midx, by2, Color_added_space);
210 }
211 
212 
latex(otexstream & os,OutputParams const &) const213 void InsetVSpace::latex(otexstream & os, OutputParams const &) const
214 {
215 	os << from_ascii(space_.asLatexCommand(buffer().params())) << '\n';
216 }
217 
218 
plaintext(odocstringstream & os,OutputParams const &,size_t) const219 int InsetVSpace::plaintext(odocstringstream & os,
220         OutputParams const &, size_t) const
221 {
222 	os << "\n\n";
223 	return PLAINTEXT_NEWLINE;
224 }
225 
226 
docbook(odocstream & os,OutputParams const &) const227 int InsetVSpace::docbook(odocstream & os, OutputParams const &) const
228 {
229 	os << '\n';
230 	return 1;
231 }
232 
233 
xhtml(XHTMLStream & os,OutputParams const &) const234 docstring InsetVSpace::xhtml(XHTMLStream & os, OutputParams const &) const
235 {
236 	string const len = space_.asHTMLLength();
237 	string const attr = "style='height:" + (len.empty() ? "1em" : len) + "'";
238 	os << html::StartTag("div", attr, true) << html::EndTag("div");
239 	return docstring();
240 }
241 
242 
contextMenuName() const243 string InsetVSpace::contextMenuName() const
244 {
245 	return "context-vspace";
246 }
247 
248 
string2params(string const & in,VSpace & vspace)249 void InsetVSpace::string2params(string const & in, VSpace & vspace)
250 {
251 	vspace = VSpace();
252 	if (in.empty())
253 		return;
254 
255 	istringstream data(in);
256 	Lexer lex;
257 	lex.setStream(data);
258 	lex.setContext("InsetVSpace::string2params");
259 	lex >> "vspace" >> vspace;
260 }
261 
262 
params2string(VSpace const & vspace)263 string InsetVSpace::params2string(VSpace const & vspace)
264 {
265 	ostringstream data;
266 	data << "vspace" << ' ' << vspace.asLyXCommand();
267 	return data.str();
268 }
269 
270 
271 } // namespace lyx
272