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