]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathSpace.cpp
fix bug 2558: hspace support in math
[lyx.git] / src / mathed / InsetMathSpace.cpp
1 /**
2  * \file InsetMathSpace.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetMathSpace.h"
14 #include "MathData.h"
15 #include "MathFactory.h"
16 #include "MathStream.h"
17 #include "MathSupport.h"
18
19 #include "BufferView.h"
20 #include "Cursor.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "LaTeXFeatures.h"
24
25 #include "insets/InsetSpace.h"
26
27 #include "frontends/Application.h"
28 #include "frontends/Painter.h"
29
30 #include "support/lassert.h"
31
32 using namespace std;
33
34 namespace lyx {
35
36 namespace {
37
38 struct SpaceInfo {
39         string name;
40         int width;
41         InsetSpaceParams::Kind kind;
42         bool negative;
43         bool visible;
44         bool custom;
45 };
46
47 SpaceInfo space_info[] = {
48         // name           width kind                         negative visible custom
49         {"!",              6,   InsetSpaceParams::NEGTHIN,   true,    true,   false},
50         {"negthinspace",   6,   InsetSpaceParams::NEGTHIN,   true,    true,   false},
51         {"negmedspace",    8,   InsetSpaceParams::NEGMEDIUM, true,    true,   false},
52         {"negthickspace", 10,   InsetSpaceParams::NEGTHICK,  true,    true,   false},
53         {",",              6,   InsetSpaceParams::THIN,      false,   true,   false},
54         {"thinspace",      6,   InsetSpaceParams::THIN,      false,   true,   false},
55         {":",              8,   InsetSpaceParams::MEDIUM,    false,   true,   false},
56         {"medspace",       8,   InsetSpaceParams::MEDIUM,    false,   true,   false},
57         {";",             10,   InsetSpaceParams::THICK,     false,   true,   false},
58         {"thickspace",    10,   InsetSpaceParams::THICK,     false,   true,   false},
59         {"enskip",        10,   InsetSpaceParams::ENSKIP,    false,   true,   false},
60         {"quad",          20,   InsetSpaceParams::QUAD,      false,   true,   false},
61         {"qquad",         40,   InsetSpaceParams::QQUAD,     false,   true,   false},
62         {"lyxnegspace",   -2,   InsetSpaceParams::NEGTHIN,   true,    false,  false},
63         {"lyxposspace",    2,   InsetSpaceParams::THIN,      false,   false,  false},
64         {"hspace",         0,   InsetSpaceParams::CUSTOM,    false,   true,   true},
65 };
66
67 int const nSpace = sizeof(space_info)/sizeof(SpaceInfo);
68 int const defaultSpace = 4;
69
70 } // anon namespace
71
72 InsetMathSpace::InsetMathSpace()
73         : space_(defaultSpace)
74 {
75 }
76
77
78 InsetMathSpace::InsetMathSpace(string const & name, string const & length)
79         : space_(defaultSpace)
80 {
81         for (int i = 0; i < nSpace; ++i)
82                 if (space_info[i].name == name) {
83                         space_ = i;
84                         break;
85                 }
86         if (space_info[space_].custom) {
87                 length_ = Length(length);
88                 if (length_.zero() || length_.empty()) {
89                         length_.value(1.0);
90                         length_.unit(Length::EM);
91                 }
92         }
93 }
94
95
96 InsetMathSpace::InsetMathSpace(Length const & length)
97         : space_(defaultSpace), length_(length)
98 {
99         for (int i = 0; i < nSpace; ++i)
100                 if (space_info[i].name == "hspace") {
101                         space_ = i;
102                         break;
103                 }
104 }
105
106
107 InsetMathSpace::~InsetMathSpace()
108 {
109         hideDialogs("mathspace", this);
110 }
111
112
113 Inset * InsetMathSpace::clone() const
114 {
115         return new InsetMathSpace(*this);
116 }
117
118
119 void InsetMathSpace::metrics(MetricsInfo & mi, Dimension & dim) const
120 {
121         dim.asc = 4;
122         dim.des = 0;
123         if (space_info[space_].custom)
124                 dim.wid = abs(length_.inPixels(
125                                 mi.base.textwidth,
126                                 mathed_char_width(mi.base.font, 'M')));
127         else
128                 dim.wid = space_info[space_].width;
129 }
130
131
132 void InsetMathSpace::draw(PainterInfo & pi, int x, int y) const
133 {
134         // Sadly, HP-UX CC can't handle that kind of initialization.
135         // XPoint p[4] = {{++x, y-3}, {x, y}, {x+width-2, y}, {x+width-2, y-3}};
136         if (!space_info[space_].visible)
137                 return;
138
139         Dimension const dim = dimension(*pi.base.bv);
140         int xp[4];
141         int yp[4];
142         int w = dim.wid;
143
144         xp[0] = ++x;        yp[0] = y - 3;
145         xp[1] = x;          yp[1] = y;
146         xp[2] = x + w - 2;  yp[2] = y;
147         xp[3] = x + w - 2;  yp[3] = y - 3;
148
149         pi.pain.lines(xp, yp, 4,
150                         space_info[space_].custom ?
151                         Color_special :
152                         (isNegative() ? Color_latex : Color_math));
153 }
154
155
156 void InsetMathSpace::incSpace()
157 {
158         int const oldwidth = space_info[space_].width;
159         do
160                 space_ = (space_ + 1) % nSpace;
161         while ((space_info[space_].width == oldwidth && !space_info[space_].custom) ||
162                !space_info[space_].visible);
163         if (space_info[space_].custom && (length_.zero() || length_.empty())) {
164                 length_.value(1.0);
165                 length_.unit(Length::EM);
166         }
167 }
168
169
170 void InsetMathSpace::validate(LaTeXFeatures & features) const
171 {
172         if (space_info[space_].name == "negmedspace" ||
173             space_info[space_].name == "negthickspace")
174                 features.require("amsmath");
175 }
176
177
178 void InsetMathSpace::maple(MapleStream & os) const
179 {
180         os << ' ';
181 }
182
183 void InsetMathSpace::mathematica(MathematicaStream & os) const
184 {
185         os << ' ';
186 }
187
188
189 void InsetMathSpace::octave(OctaveStream & os) const
190 {
191         os << ' ';
192 }
193
194
195 void InsetMathSpace::normalize(NormalStream & os) const
196 {
197         os << "[space " << int(space_) << "] ";
198 }
199
200
201 void InsetMathSpace::write(WriteStream & os) const
202 {
203         // no MathEnsurer - all kinds work in text and math mode
204         os << '\\' << space_info[space_].name.c_str();
205         if (space_info[space_].custom)
206                 os << '{' << length_.asLatexString().c_str() << '}';
207         else
208                 os.pendingSpace(true);
209 }
210
211
212 string const InsetMathSpace::createDialogStr() const
213 {
214         LASSERT(space_info[space_].visible, /**/);
215         InsetSpaceParams isp(true);
216         isp.kind = space_info[space_].kind;
217         isp.length = length_;
218         return InsetSpace::params2string(isp);
219 }
220
221
222 docstring InsetMathSpace::contextMenu(BufferView const &, int, int) const
223 {
224         return from_ascii("context-mathspace");
225 }
226
227
228 bool InsetMathSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
229                                FuncStatus & status) const
230 {
231         switch (cmd.action) {
232         // we handle these
233         case LFUN_INSET_MODIFY:
234         case LFUN_INSET_DIALOG_UPDATE:
235         case LFUN_MOUSE_RELEASE:
236         case LFUN_MOUSE_PRESS:
237         case LFUN_MOUSE_MOTION:
238                 status.setEnabled(true);
239                 return true;
240         default:
241                 bool retval = InsetMath::getStatus(cur, cmd, status);
242                 return retval;
243         }
244 }
245
246
247 void InsetMathSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
248 {
249         switch (cmd.action) {
250         case LFUN_INSET_MODIFY:
251                 if (cmd.getArg(0) == "mathspace") {
252                         MathData ar;
253                         if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
254                                 *this = *ar[0].nucleus()->asSpaceInset();
255                                 break;
256                         }
257                 }
258                 cur.undispatched();
259                 break;
260
261         case LFUN_INSET_DIALOG_UPDATE:
262                 cur.bv().updateDialog("mathspace", createDialogStr());
263                 break;
264
265         case LFUN_MOUSE_RELEASE:
266                 if (cmd.button() == mouse_button::button1) {
267                         string const data = createDialogStr();
268                         cur.bv().showDialog("mathspace", data, this);
269                         break;
270                 }
271                 cur.undispatched();
272                 break;
273
274         case LFUN_MOUSE_PRESS:
275         case LFUN_MOUSE_MOTION:
276                 // eat other mouse commands
277                 break;
278
279         default:
280                 InsetMath::doDispatch(cur, cmd);
281                 break;
282         }
283 }
284
285
286 bool InsetMathSpace::isNegative() const
287 {
288         if (space_info[space_].custom)
289                 return length_.value() < 0;
290         return space_info[space_].negative;
291 }
292
293 } // namespace lyx