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