]> git.lyx.org Git - lyx.git/blob - src/insets/InsetLabel.cpp
Split InsetLabel::updateCommand() into:
[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         if (!cursor)
99                 return;
100
101         cursor->recordUndo();
102         buffer().undo().beginUndoGroup();
103         buffer().markDirty();
104         setParam("name", label);
105         updateReferences(old_label, label);
106         buffer().undo().endUndoGroup();
107 }
108
109
110 void InsetLabel::updateReferences(docstring const & old_label,
111                 docstring const & new_label)
112 {
113         Buffer::References & refs = buffer().references(old_label);
114         Buffer::References::iterator it = refs.begin();
115         Buffer::References::iterator end = refs.end();
116         for (; it != end; ++it) {
117                 buffer().undo().recordUndo(it->second);
118                 if (it->first->lyxCode() == MATH_REF_CODE) {
119                         InsetMathHull * mi = it->first->asInsetMath()->asHullInset();
120                         mi->asRefInset()->changeTarget(new_label);
121                 } else {
122                         InsetCommand * ref = it->first->asInsetCommand();
123                         ref->setParam("reference", new_label);
124                 }
125         }
126 }
127
128
129 ParamInfo const & InsetLabel::findInfo(string const & /* cmdName */)
130 {
131         static ParamInfo param_info_;
132         if (param_info_.empty())
133                 param_info_.add("name", ParamInfo::LATEX_REQUIRED,
134                                 ParamInfo::HANDLING_ESCAPE);
135         return param_info_;
136 }
137
138
139 docstring InsetLabel::screenLabel() const
140 {
141         return screen_label_;
142 }
143
144
145 void InsetLabel::updateBuffer(ParIterator const & par, UpdateType utype)
146 {
147         docstring const & label = getParam("name");
148         if (buffer().insetLabel(label)) {
149                 // Problem: We already have an InsetLabel with the same name!
150                 screen_label_ = _("DUPLICATE: ") + label;
151                 return;
152         }
153         buffer().setInsetLabel(label, this);
154         screen_label_ = label;
155
156         if (utype == OutputUpdate) {
157                 // save info on the active counter
158                 Counters const & cnts = 
159                         buffer().masterBuffer()->params().documentClass().counters();
160                 active_counter_ = cnts.currentCounter();
161                 Language const * lang = par->getParLanguage(buffer().params());
162                 if (lang && !active_counter_.empty()) {
163                         counter_value_ = cnts.theCounter(active_counter_, lang->code());
164                         pretty_counter_ = cnts.prettyCounter(active_counter_, lang->code());
165                 } else {
166                         counter_value_ = from_ascii("#");
167                         pretty_counter_ = from_ascii("#");
168                 }
169         }
170 }
171
172
173 void InsetLabel::addToToc(DocIterator const & cpit) const
174 {
175         docstring const & label = getParam("name");
176         Toc & toc = buffer().tocBackend().toc("label");
177         if (buffer().insetLabel(label) != this) {
178                 toc.push_back(TocItem(cpit, 0, screen_label_));
179                 return;
180         }
181         toc.push_back(TocItem(cpit, 0, screen_label_));
182         Buffer::References const & refs = buffer().references(label);
183         Buffer::References::const_iterator it = refs.begin();
184         Buffer::References::const_iterator end = refs.end();
185         for (; it != end; ++it) {
186                 DocIterator const ref_pit(it->second);
187                 if (it->first->lyxCode() == MATH_REF_CODE)
188                         toc.push_back(TocItem(ref_pit, 1,
189                                 it->first->asInsetMath()->asRefInset()->screenLabel()));
190                 else
191                         toc.push_back(TocItem(ref_pit, 1,
192                                 static_cast<InsetRef *>(it->first)->screenLabel()));
193         }
194 }
195
196
197 bool InsetLabel::getStatus(Cursor & cur, FuncRequest const & cmd,
198                            FuncStatus & status) const
199 {
200         bool enabled;
201         switch (cmd.action()) {
202         case LFUN_LABEL_INSERT_AS_REF:
203         case LFUN_LABEL_COPY_AS_REF:
204                 enabled = true;
205                 break;
206         default:
207                 return InsetCommand::getStatus(cur, cmd, status);
208         }
209
210         status.setEnabled(enabled);
211         return true;
212 }
213
214
215 void InsetLabel::doDispatch(Cursor & cur, FuncRequest & cmd)
216 {
217         switch (cmd.action()) {
218
219         case LFUN_INSET_MODIFY: {
220                 InsetCommandParams p(LABEL_CODE);
221                 // FIXME UNICODE
222                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
223                 if (p.getCmdName().empty()) {
224                         cur.noScreenUpdate();
225                         break;
226                 }
227                 if (p["name"] != params()["name"]) {
228                         // undo is handled in updateLabelAndRefs
229                         updateLabelAndRefs(p["name"], &cur);
230                 }
231                 cur.forceBufferUpdate();
232                 break;
233         }
234
235         case LFUN_LABEL_COPY_AS_REF: {
236                 InsetCommandParams p(REF_CODE, "ref");
237                 p["reference"] = getParam("name");
238                 cap::clearSelection();
239                 cap::copyInset(cur, new InsetRef(buffer_, p), getParam("name"));
240                 break;
241         }
242
243         case LFUN_LABEL_INSERT_AS_REF: {
244                 InsetCommandParams p(REF_CODE, "ref");
245                 p["reference"] = getParam("name");
246                 string const data = InsetCommand::params2string(p);
247                 lyx::dispatch(FuncRequest(LFUN_INSET_INSERT, data));
248                 break;
249         }
250
251         default:
252                 InsetCommand::doDispatch(cur, cmd);
253                 break;
254         }
255 }
256
257
258 int InsetLabel::plaintext(odocstream & os, OutputParams const &) const
259 {
260         docstring const str = getParam("name");
261         os << '<' << str << '>';
262         return 2 + str.size();
263 }
264
265
266 int InsetLabel::docbook(odocstream & os, OutputParams const & runparams) const
267 {
268         os << "<!-- anchor id=\""
269            << sgml::cleanID(buffer(), runparams, getParam("name"))
270            << "\" -->";
271         return 0;
272 }
273
274
275 docstring InsetLabel::xhtml(XHTMLStream & xs, OutputParams const &) const
276 {
277         // FIXME XHTML
278         // Unfortunately, the name attribute has been deprecated, so we have to use
279         // id here to get the document to validate as XHTML 1.1. This will cause a 
280         // problem with some browsers, though, I'm sure. (Guess which!) So we will
281         // have to figure out what to do about this later. 
282         string const attr = "id=\"" + html::cleanAttr(to_utf8(getParam("name"))) + "\"";
283         xs << html::CompTag("a", attr);
284         return docstring();
285 }
286
287
288 } // namespace lyx