]> git.lyx.org Git - lyx.git/blob - src/insets/InsetLabel.cpp
tex2lyx/text.cpp: fix typos
[lyx.git] / src / insets / InsetLabel.cpp
1 /**
2  * \file InsetLabel.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetLabel.h"
14
15 #include "InsetRef.h"
16
17 #include "buffer_funcs.h"
18 #include "Buffer.h"
19 #include "BufferParams.h"
20 #include "BufferView.h"
21 #include "CutAndPaste.h"
22 #include "DispatchResult.h"
23 #include "FuncRequest.h"
24 #include "FuncStatus.h"
25 #include "InsetIterator.h"
26 #include "Language.h"
27 #include "LyX.h"
28 #include "output_xhtml.h"
29 #include "ParIterator.h"
30 #include "sgml.h"
31 #include "Text.h"
32 #include "TextClass.h"
33 #include "TocBackend.h"
34
35 #include "mathed/InsetMathHull.h"
36 #include "mathed/InsetMathRef.h"
37
38 #include "frontends/alert.h"
39
40 #include "support/convert.h"
41 #include "support/gettext.h"
42 #include "support/lstrings.h"
43 #include "support/lyxalgo.h"
44
45 using namespace std;
46 using namespace lyx::support;
47
48 namespace lyx {
49
50
51 InsetLabel::InsetLabel(Buffer * buf, InsetCommandParams const & p)
52         : InsetCommand(buf, p)
53 {}
54
55
56 void InsetLabel::initView()
57 {
58         // FIXME: This seems to be used only for inset creation so
59         // we probably just need to call updateLabel() here.
60         updateLabelAndRefs(getParam("name"));
61 }
62
63
64 void InsetLabel::uniqueLabel(docstring & label) const
65 {
66         docstring const new_label = label;
67         int i = 1;
68         while (buffer().insetLabel(label)) {
69                 label = new_label + '-' + convert<docstring>(i);
70                 ++i;
71         }
72         if (label != new_label) {
73                 // Warn the user that the label has been changed to something else.
74                 frontend::Alert::warning(_("Label names must be unique!"),
75                         bformat(_("The label %1$s already exists,\n"
76                         "it will be changed to %2$s."), new_label, label));
77         }
78 }
79
80
81 void InsetLabel::updateLabel(docstring const & new_label)
82 {
83         docstring label = new_label;
84         uniqueLabel(label);
85         setParam("name", label);
86 }
87
88
89 void InsetLabel::updateLabelAndRefs(docstring const & new_label,
90                 Cursor * cursor)
91 {
92         docstring const old_label = getParam("name");
93         docstring label = new_label;
94         uniqueLabel(label);
95         if (label == old_label)
96                 return;
97
98         buffer().undo().beginUndoGroup();
99         if (cursor)
100                 cursor->recordUndo();
101         setParam("name", label);
102         updateReferences(old_label, label);
103         buffer().undo().endUndoGroup();
104 }
105
106
107 void InsetLabel::updateReferences(docstring const & old_label,
108                 docstring const & new_label)
109 {
110         Buffer::References & refs = buffer().references(old_label);
111         Buffer::References::iterator it = refs.begin();
112         Buffer::References::iterator end = refs.end();
113         for (; it != end; ++it) {
114                 buffer().undo().recordUndo(it->second);
115                 if (it->first->lyxCode() == MATH_REF_CODE) {
116                         InsetMathRef * mi = it->first->asInsetMath()->asRefInset();
117                         mi->changeTarget(new_label);
118                 } else {
119                         InsetCommand * ref = it->first->asInsetCommand();
120                         ref->setParam("reference", new_label);
121                 }
122         }
123 }
124
125
126 ParamInfo const & InsetLabel::findInfo(string const & /* cmdName */)
127 {
128         static ParamInfo param_info_;
129         if (param_info_.empty())
130                 param_info_.add("name", ParamInfo::LATEX_REQUIRED,
131                                 ParamInfo::HANDLING_ESCAPE);
132         return param_info_;
133 }
134
135
136 docstring InsetLabel::screenLabel() const
137 {
138         return screen_label_;
139 }
140
141
142 void InsetLabel::updateBuffer(ParIterator const & par, UpdateType utype)
143 {
144         docstring const & label = getParam("name");
145         if (buffer().insetLabel(label)) {
146                 // Problem: We already have an InsetLabel with the same name!
147                 screen_label_ = _("DUPLICATE: ") + label;
148                 return;
149         }
150         buffer().setInsetLabel(label, this);
151         screen_label_ = label;
152
153         if (utype == OutputUpdate) {
154                 // save info on the active counter
155                 Counters const & cnts = 
156                         buffer().masterBuffer()->params().documentClass().counters();
157                 active_counter_ = cnts.currentCounter();
158                 Language const * lang = par->getParLanguage(buffer().params());
159                 if (lang && !active_counter_.empty()) {
160                         counter_value_ = cnts.theCounter(active_counter_, lang->code());
161                         pretty_counter_ = cnts.prettyCounter(active_counter_, lang->code());
162                 } else {
163                         counter_value_ = from_ascii("#");
164                         pretty_counter_ = from_ascii("#");
165                 }
166         }
167 }
168
169
170 void InsetLabel::addToToc(DocIterator const & cpit) const
171 {
172         docstring const & label = getParam("name");
173         Toc & toc = buffer().tocBackend().toc("label");
174         if (buffer().insetLabel(label) != this) {
175                 toc.push_back(TocItem(cpit, 0, screen_label_));
176                 return;
177         }
178         toc.push_back(TocItem(cpit, 0, screen_label_));
179         Buffer::References const & refs = buffer().references(label);
180         Buffer::References::const_iterator it = refs.begin();
181         Buffer::References::const_iterator end = refs.end();
182         for (; it != end; ++it) {
183                 DocIterator const ref_pit(it->second);
184                 if (it->first->lyxCode() == MATH_REF_CODE)
185                         toc.push_back(TocItem(ref_pit, 1,
186                                 it->first->asInsetMath()->asRefInset()->screenLabel()));
187                 else
188                         toc.push_back(TocItem(ref_pit, 1,
189                                 static_cast<InsetRef *>(it->first)->screenLabel()));
190         }
191 }
192
193
194 bool InsetLabel::getStatus(Cursor & cur, FuncRequest const & cmd,
195                            FuncStatus & status) const
196 {
197         bool enabled;
198         switch (cmd.action()) {
199         case LFUN_LABEL_INSERT_AS_REF:
200         case LFUN_LABEL_COPY_AS_REF:
201                 enabled = true;
202                 break;
203         default:
204                 return InsetCommand::getStatus(cur, cmd, status);
205         }
206
207         status.setEnabled(enabled);
208         return true;
209 }
210
211
212 void InsetLabel::doDispatch(Cursor & cur, FuncRequest & cmd)
213 {
214         switch (cmd.action()) {
215
216         case LFUN_INSET_MODIFY: {
217                 InsetCommandParams p(LABEL_CODE);
218                 // FIXME UNICODE
219                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
220                 if (p.getCmdName().empty()) {
221                         cur.noScreenUpdate();
222                         break;
223                 }
224                 if (p["name"] != params()["name"]) {
225                         // undo is handled in updateLabelAndRefs
226                         updateLabelAndRefs(p["name"], &cur);
227                 }
228                 cur.forceBufferUpdate();
229                 break;
230         }
231
232         case LFUN_LABEL_COPY_AS_REF: {
233                 InsetCommandParams p(REF_CODE, "ref");
234                 p["reference"] = getParam("name");
235                 cap::clearSelection();
236                 cap::copyInset(cur, new InsetRef(buffer_, p), getParam("name"));
237                 break;
238         }
239
240         case LFUN_LABEL_INSERT_AS_REF: {
241                 InsetCommandParams p(REF_CODE, "ref");
242                 p["reference"] = getParam("name");
243                 string const data = InsetCommand::params2string(p);
244                 lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, data));
245                 break;
246         }
247
248         default:
249                 InsetCommand::doDispatch(cur, cmd);
250                 break;
251         }
252 }
253
254
255 int InsetLabel::plaintext(odocstream & os, OutputParams const &) const
256 {
257         docstring const str = getParam("name");
258         os << '<' << str << '>';
259         return 2 + str.size();
260 }
261
262
263 int InsetLabel::docbook(odocstream & os, OutputParams const & runparams) const
264 {
265         os << "<!-- anchor id=\""
266            << sgml::cleanID(buffer(), runparams, getParam("name"))
267            << "\" -->";
268         return 0;
269 }
270
271
272 docstring InsetLabel::xhtml(XHTMLStream & xs, OutputParams const &) const
273 {
274         // FIXME XHTML
275         // Unfortunately, the name attribute has been deprecated, so we have to use
276         // id here to get the document to validate as XHTML 1.1. This will cause a 
277         // problem with some browsers, though, I'm sure. (Guess which!) So we will
278         // have to figure out what to do about this later. 
279         string const attr = "id=\"" + html::cleanAttr(to_utf8(getParam("name"))) + "\"";
280         xs << html::CompTag("a", attr);
281         return docstring();
282 }
283
284
285 } // namespace lyx