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