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