]> git.lyx.org Git - lyx.git/blob - src/insets/InsetBranch.cpp
UI for separate control of master/child branch state (#7642, part of #7643)
[lyx.git] / src / insets / InsetBranch.cpp
1 /**
2  * \file InsetBranch.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Martin Vermeer
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetBranch.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "BufferView.h"
18 #include "BranchList.h"
19 #include "ColorSet.h"
20 #include "Counters.h"
21 #include "Cursor.h"
22 #include "DispatchResult.h"
23 #include "FuncRequest.h"
24 #include "FuncStatus.h"
25 #include "Lexer.h"
26 #include "OutputParams.h"
27 #include "output_xhtml.h"
28 #include "TextClass.h"
29 #include "TocBackend.h"
30
31 #include "support/debug.h"
32 #include "support/gettext.h"
33 #include "support/lstrings.h"
34
35 #include "frontends/Application.h"
36
37 #include <sstream>
38
39 using namespace std;
40
41
42 namespace lyx {
43
44 InsetBranch::InsetBranch(Buffer * buf, InsetBranchParams const & params)
45         : InsetCollapsable(buf, InsetText::DefaultLayout), params_(params)
46 {}
47
48
49 void InsetBranch::write(ostream & os) const
50 {
51         os << "Branch ";
52         params_.write(os);
53         os << '\n';
54         InsetCollapsable::write(os);
55 }
56
57
58 void InsetBranch::read(Lexer & lex)
59 {
60         params_.read(lex);
61         InsetCollapsable::read(lex);
62 }
63
64
65 docstring InsetBranch::toolTip(BufferView const & bv, int, int) const
66 {
67         docstring const masterstatus = isBranchSelected() ?
68                 _("active") : _("non-active");
69         docstring const childstatus = isBranchSelected(true) ?
70                 _("active") : _("non-active");
71         docstring const status = (masterstatus == childstatus) ?
72                 masterstatus :
73                 support::bformat(_("master: %1$s, child: %2$s"),
74                                                  masterstatus, childstatus);
75         docstring const heading = 
76                 support::bformat(_("Branch (%1$s): %2$s"), status, params_.branch);
77         if (isOpen(bv))
78                 return heading;
79         return toolTipText(heading + from_ascii("\n"));
80 }
81
82
83 docstring const InsetBranch::buttonLabel(BufferView const & bv) const
84 {
85         docstring s = _("Branch: ") + params_.branch;
86         Buffer const & realbuffer = *buffer().masterBuffer();
87         BranchList const & branchlist = realbuffer.params().branchlist();
88         bool const inmaster = branchlist.find(params_.branch);
89         bool const inchild = buffer().params().branchlist().find(params_.branch);
90         if (!inmaster && inchild)
91                 s = _("Branch (child only): ") + params_.branch;
92         else if (inmaster && !inchild)
93                 s = _("Branch (master only): ") + params_.branch;
94         else if (!inmaster)
95                 s = _("Branch (undefined): ") + params_.branch;
96         if (!params_.branch.empty()) {
97                 // FIXME UNICODE
98                 ColorCode c = lcolor.getFromLyXName(to_utf8(params_.branch));
99                 if (c == Color_none)
100                         s = _("Undef: ") + s;
101         }
102         bool const master_selected = isBranchSelected();
103         bool const child_selected = isBranchSelected(true);
104         docstring symb = docstring(1, char_type(master_selected ? 0x2714 : 0x2716));
105         if (inchild && master_selected != child_selected)
106                 symb += char_type(child_selected ? 0x2714 : 0x2716);
107         s = symb + s;
108         if (decoration() == InsetLayout::CLASSIC)
109                 return isOpen(bv) ? s : getNewLabel(s);
110         else
111                 return params_.branch + ": " + getNewLabel(s);
112 }
113
114
115 ColorCode InsetBranch::backgroundColor(PainterInfo const & pi) const
116 {
117         if (params_.branch.empty())
118                 return Inset::backgroundColor(pi);
119         // FIXME UNICODE
120         ColorCode c = lcolor.getFromLyXName(to_utf8(params_.branch));
121         if (c == Color_none)
122                 c = Color_error;
123         return c;
124 }
125
126
127 void InsetBranch::doDispatch(Cursor & cur, FuncRequest & cmd)
128 {
129         switch (cmd.action()) {
130         case LFUN_INSET_MODIFY: {
131                 InsetBranchParams params;
132                 InsetBranch::string2params(to_utf8(cmd.argument()), params);
133
134                 cur.recordUndoInset(ATOMIC_UNDO, this);
135                 params_.branch = params.branch;
136                 // what we really want here is a TOC update, but that means
137                 // a full buffer update
138                 cur.forceBufferUpdate();
139                 break;
140         }
141         case LFUN_BRANCH_ACTIVATE:
142         case LFUN_BRANCH_DEACTIVATE:
143         case LFUN_BRANCH_MASTER_ACTIVATE:
144         case LFUN_BRANCH_MASTER_DEACTIVATE: {
145                 bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE
146                                                          || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE);
147                 Buffer * buf = master ? const_cast<Buffer *>(buffer().masterBuffer())
148                                                           : &buffer();
149
150                 Branch * our_branch = buf->params().branchlist().find(params_.branch);
151                 if (!our_branch)
152                         break;
153
154                 bool const activate = (cmd.action() == LFUN_BRANCH_ACTIVATE
155                                                            || cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE);
156                 if (our_branch->isSelected() != activate) {
157                         // FIXME If the branch is in the master document, we cannot
158                         // call recordUndo..., because the master may be hidden, and
159                         // the code presently assumes that hidden documents can never
160                         // be dirty. See GuiView::closeBufferAll(), for example.
161                         if (!master)
162                                 buffer().undo().recordUndoFullDocument(cur);
163                         our_branch->setSelected(activate);
164                         cur.forceBufferUpdate();
165                 }
166                 break;
167         }
168         case LFUN_INSET_TOGGLE:
169                 if (cmd.argument() == "assign")
170                         setStatus(cur, isBranchSelected() ? Open : Collapsed);
171                 else
172                         InsetCollapsable::doDispatch(cur, cmd);
173                 break;
174
175         default:
176                 InsetCollapsable::doDispatch(cur, cmd);
177                 break;
178         }
179 }
180
181
182 bool InsetBranch::getStatus(Cursor & cur, FuncRequest const & cmd,
183                 FuncStatus & flag) const
184 {
185         switch (cmd.action()) {
186         case LFUN_INSET_MODIFY:
187                 flag.setEnabled(true);
188                 break;
189
190         case LFUN_BRANCH_ACTIVATE:
191                 flag.setEnabled(!isBranchSelected(true));
192                 break;
193
194         case LFUN_BRANCH_DEACTIVATE:
195                 flag.setEnabled(isBranchSelected(true));
196                 break;
197
198         case LFUN_BRANCH_MASTER_ACTIVATE:
199                 flag.setEnabled(buffer().parent() && !isBranchSelected());
200                 break;
201
202         case LFUN_BRANCH_MASTER_DEACTIVATE:
203                 flag.setEnabled(buffer().parent() && isBranchSelected());
204                 break;
205
206         case LFUN_INSET_TOGGLE:
207                 if (cmd.argument() == "assign")
208                         flag.setEnabled(true);
209                 else
210                         return InsetCollapsable::getStatus(cur, cmd, flag);     
211                 break;
212
213         default:
214                 return InsetCollapsable::getStatus(cur, cmd, flag);
215         }
216         return true;
217 }
218
219
220 bool InsetBranch::isBranchSelected(bool const child) const
221 {
222         Buffer const & realbuffer = child ? buffer() : *buffer().masterBuffer();
223         BranchList const & branchlist = realbuffer.params().branchlist();
224         Branch const * ourBranch = branchlist.find(params_.branch);
225
226         if (!ourBranch) {
227                 // this branch is defined in child only
228                 ourBranch = buffer().params().branchlist().find(params_.branch);
229                 if (!ourBranch)
230                         return false;
231         }
232         return ourBranch->isSelected();
233 }
234
235
236 void InsetBranch::latex(otexstream & os, OutputParams const & runparams) const
237 {
238         if (isBranchSelected())
239                 InsetText::latex(os, runparams);
240 }
241
242
243 int InsetBranch::plaintext(odocstream & os,
244                            OutputParams const & runparams) const
245 {
246         if (!isBranchSelected())
247                 return 0;
248
249         int len = InsetText::plaintext(os, runparams);
250         return len;
251 }
252
253
254 int InsetBranch::docbook(odocstream & os,
255                          OutputParams const & runparams) const
256 {
257         return isBranchSelected() ?  InsetText::docbook(os, runparams) : 0;
258 }
259
260
261 docstring InsetBranch::xhtml(XHTMLStream & xs, OutputParams const & rp) const
262 {
263         if (isBranchSelected()) {
264                 OutputParams newrp = rp;
265                 newrp.par_begin = 0;
266                 newrp.par_end = text().paragraphs().size();
267                 xhtmlParagraphs(text(), buffer(), xs, newrp);
268         }
269         return docstring();
270 }
271
272
273 void InsetBranch::toString(odocstream & os) const
274 {
275         if (isBranchSelected())
276                 InsetCollapsable::toString(os);
277 }
278
279
280 void InsetBranch::forToc(docstring & os, size_t maxlen) const
281 {
282         if (isBranchSelected())
283                 InsetCollapsable::forToc(os, maxlen);
284 }
285
286
287 void InsetBranch::validate(LaTeXFeatures & features) const
288 {
289         if (isBranchSelected())
290                 InsetCollapsable::validate(features);
291 }
292
293
294 string InsetBranch::contextMenuName() const
295 {
296         return "context-branch";
297 }
298
299
300 bool InsetBranch::isMacroScope() const 
301 {
302         // Its own scope if not selected by buffer
303         return !isBranchSelected();
304 }
305
306
307 string InsetBranch::params2string(InsetBranchParams const & params)
308 {
309         ostringstream data;
310         params.write(data);
311         return data.str();
312 }
313
314
315 void InsetBranch::string2params(string const & in, InsetBranchParams & params)
316 {
317         params = InsetBranchParams();
318         if (in.empty())
319                 return;
320
321         istringstream data(in);
322         Lexer lex;
323         lex.setStream(data);
324         lex.setContext("InsetBranch::string2params");
325         params.read(lex);
326 }
327
328
329 void InsetBranch::addToToc(DocIterator const & cpit) const
330 {
331         DocIterator pit = cpit;
332         pit.push_back(CursorSlice(const_cast<InsetBranch &>(*this)));
333
334         Toc & toc = buffer().tocBackend().toc("branch");
335         docstring str = params_.branch + ": ";
336         text().forToc(str, TOC_ENTRY_LENGTH);
337         toc.push_back(TocItem(pit, 0, str, toolTipText(docstring(), 3, 60)));
338         // Proceed with the rest of the inset.
339         InsetCollapsable::addToToc(cpit);
340 }
341
342
343 void InsetBranchParams::write(ostream & os) const
344 {
345         os << to_utf8(branch);
346 }
347
348
349 void InsetBranchParams::read(Lexer & lex)
350 {
351         lex.eatLine();
352         branch = lex.getDocString();
353 }
354
355 } // namespace lyx