]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathRef.cpp
082a341cf45947476c54c06ddb0fb3c391d31733
[lyx.git] / src / mathed / InsetMathRef.cpp
1 /**
2  * \file InsetMathRef.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 "InsetMathRef.h"
14
15 #include "BufferParams.h"
16 #include "BufferView.h"
17 #include "Buffer.h"
18 #include "Cursor.h"
19 #include "FuncRequest.h"
20 #include "FuncStatus.h"
21 #include "LaTeXFeatures.h"
22 #include "LyX.h"
23 #include "MathData.h"
24 #include "MathFactory.h"
25 #include "MathStream.h"
26 #include "MathSupport.h"
27 #include "ParIterator.h"
28 #include "xml.h"
29
30 #include "insets/InsetCommand.h"
31 #include "insets/InsetRef.h"
32
33 #include "support/debug.h"
34 #include "support/gettext.h"
35 #include "support/lstrings.h"
36 #include "support/textutils.h"
37
38 #include <ostream>
39
40 using namespace std;
41
42 namespace lyx {
43
44 InsetMathRef::InsetMathRef(Buffer * buf)
45         : InsetMathCommand(buf, from_ascii("ref"), false)
46 {}
47
48
49 InsetMathRef::InsetMathRef(Buffer * buf, docstring const & data)
50         : InsetMathCommand(buf, data, false)
51 {}
52
53
54 Inset * InsetMathRef::clone() const
55 {
56         return new InsetMathRef(*this);
57 }
58
59
60 void InsetMathRef::infoize(odocstream & os) const
61 {
62         os << "Ref: " << cell(0);
63 }
64
65
66 void InsetMathRef::doDispatch(Cursor & cur, FuncRequest & cmd)
67 {
68         // Ctrl + click: go to label
69         if (cmd.action() == LFUN_MOUSE_RELEASE && cmd.modifier() == ControlModifier) {
70                 LYXERR0("trying to goto ref '" << to_utf8(asString(cell(0))) << "'");
71                 //FIXME: use DispatchResult argument
72                 lyx::dispatch(FuncRequest(LFUN_LABEL_GOTO, asString(cell(0))));
73                 return;
74         }
75
76         switch (cmd.action()) {
77         case LFUN_INSET_MODIFY: {
78                 string const arg0 = cmd.getArg(0);
79                 string const arg1 = cmd.getArg(1);
80                 if (arg0 == "ref") {
81                         if (arg1 == "changetarget") {
82                                 string const oldtarget = cmd.getArg(2);
83                                 string const newtarget = cmd.getArg(3);
84                                 if (!oldtarget.empty() && !newtarget.empty()
85                                     && asString(cell(0)) == from_utf8(oldtarget))
86                                         changeTarget(from_utf8(newtarget));
87                                 cur.forceBufferUpdate();
88                                 break;
89                         }
90                         MathData ar;
91                         if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
92                                 cur.recordUndo();
93                                 Buffer & buf = buffer();
94                                 *this = *ar[0].nucleus()->asRefInset();
95                                 setBuffer(buf);
96                                 break;
97                         }
98                 } else if (arg0 == "changetype") {
99                         docstring const data = from_ascii(createDialogStr(arg1));
100                         MathData ar;
101                         if (createInsetMath_fromDialogStr(data, ar)) {
102                                 cur.recordUndo();
103                                 Buffer & buf = buffer();
104                                 *this = *ar[0].nucleus()->asRefInset();
105                                 setBuffer(buf);
106                                 break;
107                         }
108                 }
109                 cur.undispatched();
110                 break;
111         }
112
113         case LFUN_INSET_DIALOG_UPDATE: {
114                 string const data = createDialogStr();
115                 cur.bv().updateDialog("ref", data);
116                 break;
117         }
118
119         case LFUN_INSET_SETTINGS: {
120                 string const data = createDialogStr();
121                 cur.bv().showDialog("ref", data, this);
122                 cur.dispatched();
123                 break;
124         }
125
126         case LFUN_MOUSE_RELEASE:
127                 if (cur.selection()) {
128                         cur.undispatched();
129                         break;
130                 }
131                 if (cmd.button() == mouse_button::button1) {
132                         string const data = createDialogStr();
133                         cur.bv().showDialog("ref", data, this);
134                         break;
135                 }
136                 cur.undispatched();
137                 break;
138
139         case LFUN_MOUSE_PRESS: {
140                 bool do_selection = cmd.button() == mouse_button::button1
141                         && cmd.modifier() == ShiftModifier;
142                 // For some reason the cursor points inside the first cell, which is not
143                 // active.
144                 cur.leaveInset(*this);
145                 cur.bv().mouseSetCursor(cur, do_selection);
146                 break;
147         }
148
149         case LFUN_MOUSE_DOUBLE:
150         case LFUN_MOUSE_TRIPLE:
151                 // eat other mouse commands
152                 break;
153
154         default:
155                 InsetMathCommand::doDispatch(cur, cmd);
156                 break;
157         }
158 }
159
160
161 bool InsetMathRef::getStatus(Cursor & cur, FuncRequest const & cmd,
162                          FuncStatus & status) const
163 {
164         switch (cmd.action()) {
165         // we handle these
166         case LFUN_INSET_MODIFY:
167                 if (cmd.getArg(0) == "changetype")
168                         status.setOnOff(from_ascii(cmd.getArg(1)) == commandname());
169                 status.setEnabled(true);
170                 return true;
171         case LFUN_INSET_DIALOG_UPDATE:
172         case LFUN_INSET_SETTINGS:
173         case LFUN_MOUSE_RELEASE:
174         case LFUN_MOUSE_PRESS:
175         case LFUN_MOUSE_DOUBLE:
176         case LFUN_MOUSE_TRIPLE:
177                 status.setEnabled(true);
178                 return true;
179         default:
180                 return InsetMathCommand::getStatus(cur, cmd, status);
181         }
182 }
183
184
185 docstring const InsetMathRef::screenLabel() const
186 {
187         docstring str;
188         for (int i = 0; !types[i].latex_name.empty(); ++i) {
189                 if (commandname() == types[i].latex_name) {
190                         str = _(to_utf8(types[i].short_gui_name));
191                         break;
192                 }
193         }
194         str += asString(cell(0));
195
196         //if (/* !isLatex && */ !cell(0).empty()) {
197         //      str += "||";
198         //      str += asString(cell(1));
199         //}
200         return str;
201 }
202
203
204 void InsetMathRef::validate(LaTeXFeatures & features) const
205 {
206         // This really should not happen here but does.
207         if (!buffer_) {
208                 LYXERR0("Unassigned buffer_ in InsetMathRef::write!");
209                 LYXERR0("LaTeX output may be wrong!");
210         }
211         bool const use_refstyle =
212                 buffer_ && buffer().params().use_refstyle;
213
214         if (commandname() == "vref" || commandname() == "vpageref")
215                 features.require("varioref");
216         else if (commandname() == "formatted") {
217                 if (use_refstyle)
218                         features.require("refstyle");
219                 else
220                         features.require("prettyref");
221         }
222         // if eqref is used with refstyle, we do our own output
223         else if (commandname() == "eqref" && use_refstyle)
224                 features.require("amsmath");
225         else if (commandname() == "nameref")
226                 features.require("nameref");
227 }
228
229
230 void InsetMathRef::docbook(XMLStream & xs, OutputParams const &) const
231 {
232         if (cell(1).empty()) {
233                 docstring attr = from_utf8("linkend=\"") + xml::cleanID(asString(cell(0))) + from_utf8("\"");
234                 xs << xml::CompTag("xref", to_utf8(attr));
235         } else {
236                 // Link with linkend, as is it within the document (not outside, in which case xlink:href is better suited).
237                 docstring attr = from_utf8("linkend=\"") + xml::cleanID(asString(cell(0))) + from_utf8("\"");
238                 xs << xml::StartTag("link", to_utf8(attr))
239                    << asString(cell(1))
240                    << xml::EndTag("link");
241         }
242 }
243
244
245 void InsetMathRef::updateBuffer(ParIterator const & it, UpdateType /*utype*/, bool const /*deleted*/)
246 {
247         if (!buffer_) {
248                 LYXERR0("InsetMathRef::updateBuffer: no buffer_!");
249                 return;
250         }
251         // register this inset into the buffer reference cache.
252         buffer().addReference(getTarget(), this, it);
253 }
254
255
256 string const InsetMathRef::createDialogStr(string const & type) const
257 {
258         InsetCommandParams icp(REF_CODE, (type.empty()
259                         ?  to_ascii(commandname()) : type));
260         icp["reference"] = asString(cell(0));
261         if (!cell(1).empty())
262                 icp["name"] = asString(cell(1));
263         return InsetCommand::params2string(icp);
264 }
265
266
267 docstring const InsetMathRef::getTarget() const
268 {
269         return asString(cell(0));
270 }
271
272
273 void InsetMathRef::changeTarget(docstring const & target)
274 {
275         InsetCommandParams icp(REF_CODE, to_ascii(commandname()));
276         icp["reference"] = target;
277         if (!cell(1).empty())
278                 icp["name"] = asString(cell(1));
279         MathData ar;
280         Buffer & buf = buffer();
281         if (createInsetMath_fromDialogStr(
282             from_utf8(InsetCommand::params2string(icp)), ar)) {
283                 *this = *ar[0].nucleus()->asRefInset();
284                 // FIXME audit setBuffer calls
285                 setBuffer(buf);
286         }
287 }
288
289
290 void InsetMathRef::write(TeXMathStream & os) const
291 {
292         docstring const & cmd = commandname();
293         // This should not happen, but of course it does
294         if (!buffer_) {
295                 LYXERR0("Unassigned buffer_ in InsetMathRef::write!");
296                 LYXERR0("LaTeX output may be wrong!");
297         }
298         // are we writing to the LyX file?
299         if (!os.latex()) {
300                 // if so, then this is easy
301                 InsetMathCommand::write(os);
302                 return;
303         }
304         bool const use_refstyle =
305                 buffer_ && buffer().params().use_refstyle;
306         bool special_case =  cmd == "formatted" ||
307                         cmd == "labelonly" ||
308                         (cmd == "eqref" && use_refstyle);
309         // we need to translate 'formatted' to prettyref or refstyle-type
310         // commands and just output the label with labelonly
311         // most of this is borrowed from InsetRef and should be kept in 
312         // sync with that.
313         ModeSpecifier specifier(os, currentMode(), lockedMode(), asciiOnly());
314         MathEnsurer ensurer(os, false);
315         if (!special_case) {
316                 os << from_ascii("\\") << cmd << "{" << cell(0) << from_ascii("}");
317         }
318         else if (use_refstyle && cmd == "eqref") {
319                 // we advertise this as printing "(n)", so we'll do that, at least
320                 // for refstyle, since refstlye's own \eqref prints, by default,
321                 // "equation n". if one wants \eqref, one can get it by using a
322                 // formatted label in this case.
323                 os << '(' << from_ascii("\\ref{") << cell(0) << from_ascii("})");
324         }
325         else if (cmd == "formatted") {
326                 if (!use_refstyle)
327                         os << "\\prettyref{" << cell(0) << "}";
328                 else {
329                         odocstringstream ods;
330                         // get the label we are referencing
331                         for (auto const & d : cell(0)) {
332                                 ods << d;
333                         }
334                         docstring const ref = ods.str();
335
336                         /*
337                         At the moment, the 'plural' and 'caps' options will
338                         not work here. The reason is that we handle these as
339                         'internal' LyX argumemts, but those are not handled by
340                         InsetCommandParams::getCommand, which is what is used
341                         in createInsetMath_fromDialogStr to interpret the data
342                         coming from the dialog.
343                         If this is fixed, then what follows will get the info
344                         we need.
345                         Fixing it, I think, would mean sub-classing
346                         InsetCommandParams to InsetRefParams, and the overriding
347                         getCommand.
348                         *******************************************************
349                         // reset
350                         ods.str(docstring());
351                         ods.clear();
352                         // get the options from the optional argument
353                         for (auto const & d : cell(1))
354                                 ods << d;
355                         docstring const options = ods.str();
356                         bool const caps   = support::contains(options, 'C');
357                         bool const plural = support::contains(options, 's');
358                         */
359                         docstring label;
360                         docstring prefix;
361                         docstring const fcmd =
362                                 InsetRef::getFormattedCmd(ref, label, prefix, true);
363                         os << fcmd;
364                         //if (plural)
365                         //      os << "[s]";
366                         os << '{' << label << '}';
367                 }
368         }
369         else if (cmd == "labelonly") {
370                 // noprefix does not work here, for reasons given above.
371                 os << cell(0);
372         }
373 }
374
375
376 InsetMathRef::ref_type_info InsetMathRef::types[] = {
377         { from_ascii("ref"),       from_ascii(N_("Standard[[mathref]]")),   from_ascii(N_("Ref: "))},
378         { from_ascii("eqref"),     from_ascii(N_("Equation")),              from_ascii(N_("EqRef: "))},
379         { from_ascii("pageref"),   from_ascii(N_("Page Number")),           from_ascii(N_("Page: "))},
380         { from_ascii("vpageref"),  from_ascii(N_("Textual Page Number")),   from_ascii(N_("TextPage: "))},
381         { from_ascii("vref"),      from_ascii(N_("Standard+Textual Page")), from_ascii(N_("Ref+Text: "))},
382         { from_ascii("formatted"), from_ascii(N_("PrettyRef")),             from_ascii(N_("FormatRef: "))},
383         { from_ascii("nameref"),   from_ascii(N_("Reference to Name")),     from_ascii(N_("NameRef: "))},
384         { from_ascii("labelonly"), from_ascii(N_("Label Only")),             from_ascii(N_("Label Only: "))},
385         { from_ascii(""), from_ascii(""), from_ascii("") }
386 };
387
388
389 } // namespace lyx