]> git.lyx.org Git - lyx.git/blob - src/insets/insetcollapsable.C
dispatchresult -> DispatchResult
[lyx.git] / src / insets / insetcollapsable.C
1 /**
2  * \file insetcollapsable.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Jürgen Vigna
8  * \author Lars Gullik Bjønnes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "insetcollapsable.h"
16
17 #include "buffer.h"
18 #include "BufferView.h"
19 #include "debug.h"
20 #include "dispatchresult.h"
21 #include "LColor.h"
22 #include "lyxlex.h"
23 #include "funcrequest.h"
24 #include "metricsinfo.h"
25 #include "paragraph.h"
26 #include "WordLangTuple.h"
27
28 #include "frontends/font_metrics.h"
29 #include "frontends/Painter.h"
30 #include "frontends/LyXView.h"
31
32
33 using lyx::graphics::PreviewLoader;
34
35 using std::endl;
36 using std::string;
37 using std::max;
38 using std::ostream;
39
40
41 InsetCollapsable::InsetCollapsable(BufferParams const & bp, bool collapsed)
42         : UpdatableInset(), inset(bp), collapsed_(collapsed),
43           button_dim(0, 0, 0, 0), label("Label"),
44 #if 0
45         autocollapse(false),
46 #endif
47           in_update(false), first_after_edit(false)
48 {
49         inset.setOwner(this);
50         inset.setAutoBreakRows(true);
51         inset.setDrawFrame(InsetText::ALWAYS);
52         inset.setFrameColor(LColor::collapsableframe);
53         setInsetName("Collapsable");
54 }
55
56
57 InsetCollapsable::InsetCollapsable(InsetCollapsable const & in)
58         : UpdatableInset(in), inset(in.inset), collapsed_(in.collapsed_),
59           labelfont_(in.labelfont_), button_dim(0, 0, 0, 0), label(in.label),
60 #if 0
61           autocollapse(in.autocollapse),
62 #endif
63           in_update(false), first_after_edit(false)
64 {
65         inset.setOwner(this);
66 }
67
68
69 bool InsetCollapsable::insertInset(BufferView * bv, InsetOld * in)
70 {
71         if (!insetAllowed(in->lyxCode())) {
72                 lyxerr << "InsetCollapsable::InsertInset: "
73                         "Unable to insert inset." << endl;
74                 return false;
75         }
76         return inset.insertInset(bv, in);
77 }
78
79
80 void InsetCollapsable::write(Buffer const & buf, ostream & os) const
81 {
82         os << "collapsed " << (collapsed_ ? "true" : "false") << "\n";
83         inset.writeParagraphData(buf, os);
84 }
85
86
87 void InsetCollapsable::read(Buffer const & buf, LyXLex & lex)
88 {
89         if (lex.isOK()) {
90                 lex.next();
91                 string const token = lex.getString();
92                 if (token == "collapsed") {
93                         lex.next();
94                         collapsed_ = lex.getBool();
95                 } else {
96                         lyxerr << "InsetCollapsable::Read: Missing collapsed!"
97                                << endl;
98                         // Take countermeasures
99                         lex.pushToken(token);
100                 }
101         }
102         inset.read(buf, lex);
103 }
104
105
106 void InsetCollapsable::dimension_collapsed(Dimension & dim) const
107 {
108         font_metrics::buttonText(label, labelfont_, dim.wid, dim.asc, dim.des);
109 }
110
111
112 int InsetCollapsable::height_collapsed() const
113 {
114         Dimension dim;
115         font_metrics::buttonText(label, labelfont_, dim.wid, dim.asc, dim.des);
116         return dim.asc + dim.des;
117 }
118
119
120 void InsetCollapsable::metrics(MetricsInfo & mi, Dimension & dim) const
121 {
122         //lyxerr << "InsetCollapsable::metrics:  width: " << mi.base.textwidth << endl;
123         dimension_collapsed(dim);
124         if (!collapsed_) {
125                 Dimension insetdim;
126                 inset.metrics(mi, insetdim);
127                 dim.des += insetdim.height() + TEXT_TO_BOTTOM_OFFSET;
128                 dim.wid = max(dim.wid, insetdim.wid);
129         }
130         dim_ = dim;
131 }
132
133
134 void InsetCollapsable::draw_collapsed(PainterInfo & pi, int x, int y) const
135 {
136         pi.pain.buttonText(x, y, label, labelfont_);
137 }
138
139
140 void InsetCollapsable::draw(PainterInfo & pi, int x, int y, bool inlined) const
141 {
142         Dimension dim_collapsed;
143         dimension_collapsed(dim_collapsed);
144
145         int const aa  = ascent();
146         button_dim.x1 = 0;
147         button_dim.x2 = dim_collapsed.width();
148         button_dim.y1 = -aa;
149         button_dim.y2 = -aa + dim_collapsed.height();
150
151         if (!isOpen()) {
152                 draw_collapsed(pi, x, y);
153                 return;
154         }
155
156         int old_x = x;
157
158         if (!owner())
159                 x += scroll();
160
161         top_x = x;
162         top_baseline = y;
163
164         int const bl = y - aa + dim_collapsed.ascent();
165
166         if (inlined) {
167                 inset.draw(pi, x, y);
168         } else {
169                 draw_collapsed(pi, old_x, bl);
170                 inset.draw(pi, x, bl + dim_collapsed.descent() + inset.ascent());
171         }
172 }
173
174
175 void InsetCollapsable::draw(PainterInfo & pi, int x, int y) const
176 {
177         // by default, we are not inlined-drawing
178         draw(pi, x, y, false);
179 }
180
181
182 InsetOld::EDITABLE InsetCollapsable::editable() const
183 {
184         return collapsed_ ? IS_EDITABLE : HIGHLY_EDITABLE;
185 }
186
187
188 void InsetCollapsable::insetUnlock(BufferView * bv)
189 {
190 #if 0
191         if (autocollapse) {
192                 if (change_label_with_text) {
193                         draw_label = get_new_label();
194                 } else {
195                         draw_label = label;
196                 }
197                 collapsed_ = true;
198         }
199 #endif
200         inset.insetUnlock(bv);
201         if (scroll())
202                 scroll(bv, 0.0F);
203         bv->updateInset(this);
204 }
205
206
207 FuncRequest InsetCollapsable::adjustCommand(FuncRequest const & cmd)
208 {
209         FuncRequest cmd1 = cmd;
210         cmd1.y = ascent() + cmd.y - (height_collapsed() + inset.ascent());
211         return cmd1;
212 }
213
214
215 void InsetCollapsable::lfunMouseRelease(FuncRequest const & cmd)
216 {
217         bool ret = false;
218         BufferView * bv = cmd.view();
219
220         if (collapsed_ && cmd.button() != mouse_button::button3) {
221                 collapsed_ = false;
222                 bv->updateInset(this);
223                 bv->buffer()->markDirty();
224                 return;
225         }
226
227         if (cmd.button() != mouse_button::button3 && hitButton(cmd)) {
228                 if (collapsed_) {
229                         collapsed_ = false;
230                 } else {
231                         collapsed_ = true;
232                         bv->unlockInset(this);
233                 }
234                 bv->updateInset(this);
235                 bv->buffer()->markDirty();
236         } else if (!collapsed_ && cmd.y > button_dim.y2) {
237                 ret = inset.dispatch(adjustCommand(cmd)) == DISPATCHED;
238         }
239         if (cmd.button() == mouse_button::button3 && !ret)
240                 showInsetDialog(bv);
241 }
242
243
244 int InsetCollapsable::latex(Buffer const & buf, ostream & os,
245                             LatexRunParams const & runparams) const
246 {
247         return inset.latex(buf, os, runparams);
248 }
249
250
251 int InsetCollapsable::ascii(Buffer const & buf, ostream & os, int ll) const
252 {
253         return inset.ascii(buf, os, ll);
254 }
255
256
257 int InsetCollapsable::linuxdoc(Buffer const & buf, ostream & os) const
258 {
259         return inset.linuxdoc(buf, os);
260 }
261
262
263 int InsetCollapsable::docbook(Buffer const & buf, ostream & os, bool mixcont) const
264 {
265         return inset.docbook(buf, os, mixcont);
266 }
267
268
269 bool InsetCollapsable::hitButton(FuncRequest const & cmd) const
270 {
271         return button_dim.contains(cmd.x, cmd.y);
272 }
273
274
275 void InsetCollapsable::edit(BufferView * bv, int index)
276 {
277         lyxerr << "InsetCollapsable: edit" << endl;
278         if (!bv->lockInset(this))
279                 lyxerr << "InsetCollapsable: can't lock index " << index << endl;
280         inset.dispatch(FuncRequest(bv, LFUN_INSET_EDIT, "left"));
281         first_after_edit = true;
282 }
283
284
285 DispatchResult
286 InsetCollapsable::priv_dispatch(FuncRequest const & cmd,
287                                 idx_type & idx, pos_type & pos)
288 {
289         //lyxerr << "InsetCollapsable::localDispatch: "
290         //      << cmd.action << " '" << cmd.argument << "'\n";
291         BufferView * bv = cmd.view();
292         switch (cmd.action) {
293                 case LFUN_INSET_EDIT: {
294                         if (!cmd.argument.empty()) {
295                                 UpdatableInset::priv_dispatch(cmd, idx, pos);
296                                 if (collapsed_) {
297                                         lyxerr << "branch collapsed_" << endl;
298                                         collapsed_ = false;
299                                         if (bv->lockInset(this)) {
300                                                 bv->updateInset(this);
301                                                 bv->buffer()->markDirty();
302                                                 inset.dispatch(cmd);
303                                                 first_after_edit = true;
304                                         }
305                                 } else {
306                                         lyxerr << "branch not collapsed_" << endl;
307                                         if (bv->lockInset(this))
308                                                 inset.dispatch(cmd);
309                                 }
310                                 return DISPATCHED;
311                         }
312
313 #ifdef WITH_WARNINGS
314 #warning Fix this properly in BufferView_pimpl::workAreaButtonRelease
315 #endif
316                         if (cmd.button() == mouse_button::button3)
317                                 return DISPATCHED;
318
319                         UpdatableInset::priv_dispatch(cmd, idx, pos);
320
321                         if (collapsed_) {
322                                 collapsed_ = false;
323                                 // set this only here as it should be recollapsed only if
324                                 // it was already collapsed!
325                                 first_after_edit = true;
326                                 if (!bv->lockInset(this))
327                                         return DISPATCHED;
328                                 bv->updateInset(this);
329                                 bv->buffer()->markDirty();
330                                 inset.dispatch(cmd);
331                         } else {
332                                 if (!bv->lockInset(this))
333                                         return DISPATCHED;
334                                 if (cmd.y <= button_dim.y2) {
335                                         FuncRequest cmd1 = cmd;
336                                         cmd1.y = 0;
337                                         inset.dispatch(cmd1);
338                                 } else
339                                         inset.dispatch(adjustCommand(cmd));
340                         }
341                         return DISPATCHED;
342                 }
343
344                 case LFUN_MOUSE_PRESS:
345                         if (!collapsed_ && cmd.y > button_dim.y2)
346                                 inset.dispatch(adjustCommand(cmd));
347                         return DISPATCHED;
348
349                 case LFUN_MOUSE_MOTION:
350                         if (!collapsed_ && cmd.y > button_dim.y2)
351                                 inset.dispatch(adjustCommand(cmd));
352                         return DISPATCHED;
353
354                 case LFUN_MOUSE_RELEASE:
355                         lfunMouseRelease(cmd);
356                         return DISPATCHED;
357
358                 default:
359                         DispatchResult result = inset.dispatch(cmd);
360                         if (result >= FINISHED)
361                                 bv->unlockInset(this);
362                         first_after_edit = false;
363                         return result;
364         }
365 }
366
367
368 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
369 {
370         if (&inset == in)
371                 return true;
372         return inset.lockInsetInInset(bv, in);
373 }
374
375
376 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
377                                           bool lr)
378 {
379         if (&inset == in) {
380                 bv->unlockInset(this);
381                 return true;
382         }
383         return inset.unlockInsetInInset(bv, in, lr);
384 }
385
386
387 int InsetCollapsable::insetInInsetY() const
388 {
389         return inset.insetInInsetY() - (top_baseline - inset.y());
390 }
391
392
393 void InsetCollapsable::validate(LaTeXFeatures & features) const
394 {
395         inset.validate(features);
396 }
397
398
399 void InsetCollapsable::getCursor(BufferView & bv, int & x, int & y) const
400 {
401         inset.getCursor(bv, x, y);
402 }
403
404
405 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
406 {
407         inset.getCursorPos(bv, x , y);
408 }
409
410
411 UpdatableInset * InsetCollapsable::getLockingInset() const
412 {
413         UpdatableInset * in = inset.getLockingInset();
414         if (&inset == in)
415                 return const_cast<InsetCollapsable *>(this);
416         return in;
417 }
418
419
420 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(InsetOld::Code c)
421 {
422         if (c == lyxCode())
423                 return this;
424         return inset.getFirstLockingInsetOfType(c);
425 }
426
427
428 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
429                                bool toggleall, bool selectall)
430 {
431         inset.setFont(bv, font, toggleall, selectall);
432 }
433
434
435 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
436                                        bool const recursive) const
437 {
438         return inset.getLyXText(bv, recursive);
439 }
440
441
442 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
443 {
444         inset.deleteLyXText(bv, recursive);
445 }
446
447
448 void InsetCollapsable::getLabelList(Buffer const & buffer,
449                                     std::vector<string> & list) const
450 {
451         inset.getLabelList(buffer, list);
452 }
453
454
455 int InsetCollapsable::scroll(bool recursive) const
456 {
457         int sx = UpdatableInset::scroll(false);
458
459         if (recursive)
460                 sx += inset.scroll(recursive);
461
462         return sx;
463 }
464
465
466 ParagraphList * InsetCollapsable::getParagraphs(int i) const
467 {
468         return inset.getParagraphs(i);
469 }
470
471
472 LyXText * InsetCollapsable::getText(int i) const
473 {
474         return inset.getText(i);
475 }
476
477
478 LyXCursor const & InsetCollapsable::cursor(BufferView * bv) const
479 {
480         return inset.cursor(bv);
481 }
482
483
484 InsetOld * InsetCollapsable::getInsetFromID(int id_arg) const
485 {
486         if (id_arg == id())
487                 return const_cast<InsetCollapsable *>(this);
488         return inset.getInsetFromID(id_arg);
489 }
490
491
492 void InsetCollapsable::open(BufferView * bv)
493 {
494         if (!collapsed_)
495                 return;
496
497         collapsed_ = false;
498         bv->updateInset(this);
499 }
500
501
502 void InsetCollapsable::close(BufferView * bv) const
503 {
504         if (collapsed_)
505                 return;
506
507         collapsed_ = true;
508         bv->updateInset(this);
509 }
510
511
512 void InsetCollapsable::setLabel(string const & l) const
513 {
514         label = l;
515 }
516
517
518 void InsetCollapsable::setCollapsed(bool c) const
519 {
520         collapsed_ = c;
521 }
522
523
524 void InsetCollapsable::markErased()
525 {
526         inset.markErased();
527 }
528
529
530 bool InsetCollapsable::nextChange(BufferView * bv, lyx::pos_type & length)
531 {
532         bool found = inset.nextChange(bv, length);
533
534         if (first_after_edit && !found)
535                 close(bv);
536         else if (!found)
537                 first_after_edit = false;
538         return found;
539 }
540
541
542 bool InsetCollapsable::searchForward(BufferView * bv, string const & str,
543                                      bool cs, bool mw)
544 {
545         bool found = inset.searchForward(bv, str, cs, mw);
546         if (first_after_edit && !found)
547                 close(bv);
548         else if (!found)
549                 first_after_edit = false;
550         return found;
551 }
552
553
554 bool InsetCollapsable::searchBackward(BufferView * bv, string const & str,
555                                       bool cs, bool mw)
556 {
557         bool found = inset.searchBackward(bv, str, cs, mw);
558         if (first_after_edit && !found)
559                 close(bv);
560         else if (!found)
561                 first_after_edit = false;
562         return found;
563 }
564
565
566 WordLangTuple const
567 InsetCollapsable::selectNextWordToSpellcheck(BufferView * bv, float & value) const
568 {
569         WordLangTuple word = inset.selectNextWordToSpellcheck(bv, value);
570         if (first_after_edit && word.word().empty())
571                 close(bv);
572         first_after_edit = false;
573         return word;
574 }
575
576
577 void InsetCollapsable::addPreview(PreviewLoader & loader) const
578 {
579         inset.addPreview(loader);
580 }