]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathRef.cpp
Fix context menu of InsetMathRef
[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         bool const use_refstyle =
299                 buffer_ && buffer().params().use_refstyle;
300         bool special_case =  cmd == "formatted" ||
301                         cmd == "labelonly" ||
302                         (cmd == "eqref" && use_refstyle);
303         // are we writing to the LyX file or not in a special case?
304         if (!os.latex() || !special_case) {
305                 // if so, then this is easy
306                 InsetMathCommand::write(os);
307                 return;
308         }
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
316         if (use_refstyle && cmd == "eqref") {
317                 // we advertise this as printing "(n)", so we'll do that, at least
318                 // for refstyle, since refstlye's own \eqref prints, by default,
319                 // "equation n". if one wants \eqref, one can get it by using a
320                 // formatted label in this case.
321                 os << '(' << from_ascii("\\ref{") << cell(0) << from_ascii("})");
322         }
323         else if (cmd == "formatted") {
324                 if (!use_refstyle)
325                         os << "\\prettyref{" << cell(0) << "}";
326                 else {
327                          odocstringstream ods;
328                         // get the label we are referencing
329                         for (auto const & d : cell(0)) {
330                                 ods << d;
331                         }
332                         docstring const ref = ods.str();
333
334                         /*
335                         At the moment, the 'plural' and 'caps' options will
336                         not work here. The reason is that we handle these as
337                         'internal' LyX argumemts, but those are not handled by
338                         InsetCommandParams::getCommand, which is what is used
339                         in createInsetMath_fromDialogStr to interpret the data
340                         coming from the dialog.
341                         If this is fixed, then what follows will get the info
342                         we need.
343                         Fixing it, I think, would mean sub-classing
344                         InsetCommandParams to InsetRefParams, and the overriding
345                         getCommand.
346                         *******************************************************
347                         // reset
348                         ods.str(docstring());
349                         ods.clear();
350                         // get the options from the optional argument
351                         for (auto const & d : cell(1))
352                                 ods << d;
353                         docstring const options = ods.str();
354                         bool const caps   = support::contains(options, 'C');
355                         bool const plural = support::contains(options, 's');
356                         */
357                         docstring label;
358                         docstring prefix;
359                         docstring const fcmd =
360                                 InsetRef::getFormattedCmd(ref, label, prefix, true);
361                         os << fcmd;
362                         //if (plural)
363                         //      os << "[s]";
364                         os << '{' << label << '}';
365                 }
366         }
367         else if (cmd == "labelonly") {
368                 // noprefix does not work here, for reasons given above.
369                 os << cell(0);
370         }
371 }
372
373
374 InsetMathRef::ref_type_info InsetMathRef::types[] = {
375         { from_ascii("ref"),       from_ascii(N_("Standard[[mathref]]")),   from_ascii(N_("Ref: "))},
376         { from_ascii("eqref"),     from_ascii(N_("Equation")),              from_ascii(N_("EqRef: "))},
377         { from_ascii("pageref"),   from_ascii(N_("Page Number")),           from_ascii(N_("Page: "))},
378         { from_ascii("vpageref"),  from_ascii(N_("Textual Page Number")),   from_ascii(N_("TextPage: "))},
379         { from_ascii("vref"),      from_ascii(N_("Standard+Textual Page")), from_ascii(N_("Ref+Text: "))},
380         { from_ascii("formatted"), from_ascii(N_("PrettyRef")),             from_ascii(N_("FormatRef: "))},
381         { from_ascii("nameref"),   from_ascii(N_("Reference to Name")),     from_ascii(N_("NameRef: "))},
382         { from_ascii("labelonly"), from_ascii(N_("Label Only")),             from_ascii(N_("Label Only: "))},
383         { from_ascii(""), from_ascii(""), from_ascii("") }
384 };
385
386
387 } // namespace lyx