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