]> git.lyx.org Git - lyx.git/blob - src/insets/insetcollapsable.C
The markDirty() and fitCursor() changes
[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           need_update(NONE), 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           need_update(NONE), 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 widthCollapsed = width_collapsed();
171
172         return (inset.width(bv, font) > widthCollapsed) ?
173                 inset.width(bv, font) : widthCollapsed;
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) const
188 {
189         lyx::Assert(bv);
190         cache(bv);
191
192         if (need_update != NONE) {
193                 const_cast<InsetText *>(&inset)->update(bv, f, true);
194                 bv->text->postChangedInDraw();
195                 need_update = NONE;
196                 return;
197         }
198         if (nodraw())
199                 return;
200
201         Painter & pain = bv->painter();
202
203         button_length = width_collapsed();
204         button_top_y = -ascent(bv, f);
205         button_bottom_y = -ascent(bv, f) + ascent_collapsed() +
206                 descent_collapsed();
207
208         if (!isOpen()) {
209                 draw_collapsed(pain, baseline, x);
210                 return;
211         }
212
213         float old_x = x;
214
215         if (!owner())
216                 x += static_cast<float>(scroll());
217
218         top_x = int(x);
219         topx_set = true;
220         top_baseline = baseline;
221
222         int const bl = baseline - ascent(bv, f) + ascent_collapsed();
223
224         draw_collapsed(pain, bl, old_x);
225         inset.draw(bv, f, bl + descent_collapsed() + inset.ascent(bv, f), x);
226         if (x < (top_x + button_length + TEXT_TO_INSET_OFFSET))
227                 x = top_x + button_length + TEXT_TO_INSET_OFFSET;
228 }
229
230
231 void InsetCollapsable::edit(BufferView * bv, int xp, int yp,
232                             mouse_button::state button)
233 {
234 #ifdef WITH_WARNINGS
235 #warning Fix this properly in BufferView_pimpl::workAreaButtonRelease
236 #endif
237         if (button == mouse_button::button3)
238                 return;
239
240         UpdatableInset::edit(bv, xp, yp, button);
241
242         if (collapsed_) {
243                 collapsed_ = false;
244                 // set this only here as it should be recollapsed only if
245                 // it was already collapsed!
246                 first_after_edit = true;
247                 if (!bv->lockInset(this))
248                         return;
249                 bv->updateInset(this);
250                 bv->buffer()->markDirty();
251                 inset.edit(bv);
252         } else {
253                 if (!bv->lockInset(this))
254                         return;
255                 if (yp <= button_bottom_y) {
256                         inset.edit(bv, xp, 0, button);
257                 } else {
258                         LyXFont font(LyXFont::ALL_SANE);
259                         int yy = ascent(bv, font) + yp -
260                                 (ascent_collapsed() +
261                                  descent_collapsed() +
262                                  inset.ascent(bv, font));
263                         inset.edit(bv, xp, yy, button);
264                 }
265         }
266 }
267
268
269 void InsetCollapsable::edit(BufferView * bv, bool front)
270 {
271         UpdatableInset::edit(bv, front);
272
273         if (collapsed_) {
274                 collapsed_ = false;
275                 if (!bv->lockInset(this))
276                         return;
277                 inset.setUpdateStatus(bv, InsetText::FULL);
278                 bv->updateInset(this);
279                 bv->buffer()->markDirty();
280                 inset.edit(bv, front);
281                 first_after_edit = true;
282         } else {
283                 if (!bv->lockInset(this))
284                         return;
285                 inset.edit(bv, front);
286         }
287 }
288
289
290 Inset::EDITABLE InsetCollapsable::editable() const
291 {
292         if (collapsed_)
293                 return IS_EDITABLE;
294         return HIGHLY_EDITABLE;
295 }
296
297
298 void InsetCollapsable::insetUnlock(BufferView * bv)
299 {
300 #if 0
301         if (autocollapse) {
302                 if (change_label_with_text) {
303                         draw_label = get_new_label();
304                 } else {
305                         draw_label = label;
306                 }
307                 collapsed_ = true;
308         }
309 #endif
310         inset.insetUnlock(bv);
311         if (scroll())
312                 scroll(bv, 0.0F);
313         bv->updateInset(this);
314 }
315
316
317 void InsetCollapsable::lfunMousePress(FuncRequest const & cmd)
318 {
319         if (!collapsed_ && (cmd.y > button_bottom_y)) {
320                 LyXFont font(LyXFont::ALL_SANE);
321                 FuncRequest cmd1 = cmd;
322                 cmd1.y = ascent(cmd.view(), font) + cmd.y -
323                     (ascent_collapsed() +
324                      descent_collapsed() +
325                      inset.ascent(cmd.view(), font));
326                 inset.localDispatch(cmd1);
327         }
328 }
329
330
331 bool InsetCollapsable::lfunMouseRelease(FuncRequest const & cmd)
332 {
333         bool ret = false;
334         BufferView * bv = cmd.view();
335         if ((cmd.button() != mouse_button::button3) && (cmd.x < button_length) &&
336             (cmd.y >= button_top_y) && (cmd.y <= button_bottom_y))
337         {
338                 if (collapsed_) {
339                         collapsed_ = false;
340 // should not be called on inset open!
341 //                      inset.insetButtonRelease(bv, 0, 0, button);
342                         inset.setUpdateStatus(bv, InsetText::FULL);
343                         bv->updateInset(this);
344                         bv->buffer()->markDirty();
345                 } else {
346                         collapsed_ = true;
347                         bv->unlockInset(this);
348                         bv->updateInset(this);
349                         bv->buffer()->markDirty();
350                 }
351         } else if (!collapsed_ && (cmd.y > button_bottom_y)) {
352                 LyXFont font(LyXFont::ALL_SANE);
353                 FuncRequest cmd1 = cmd;
354                 cmd1.y = ascent(cmd.view(), font) + cmd.y -
355                     (ascent_collapsed() +
356                      descent_collapsed() +
357                      inset.ascent(cmd.view(), font));
358                 ret = (inset.localDispatch(cmd1) == DISPATCHED);
359         }
360         if (cmd.button() == mouse_button::button3 && !ret)
361                 return showInsetDialog(bv);
362         return ret;
363 }
364
365
366 void InsetCollapsable::lfunMouseMotion(FuncRequest const & cmd)
367 {
368         if (cmd.y > button_bottom_y) {
369                 LyXFont font(LyXFont::ALL_SANE);
370                 FuncRequest cmd1 = cmd;
371                 cmd1.y = ascent(cmd.view(), font) + cmd.y -
372                     (ascent_collapsed() +
373                      descent_collapsed() +
374                      inset.ascent(cmd.view(), font));
375                 inset.localDispatch(cmd1);
376         }
377 }
378
379
380 int InsetCollapsable::latex(Buffer const * buf, ostream & os,
381                             bool fragile, bool free_spc) const
382 {
383         return inset.latex(buf, os, fragile, free_spc);
384 }
385
386
387 int InsetCollapsable::ascii(Buffer const * buf, ostream & os, int ll) const
388 {
389         return inset.ascii(buf, os, ll);
390 }
391
392
393 int InsetCollapsable::linuxdoc(Buffer const * buf, ostream & os) const
394 {
395         return inset.linuxdoc(buf, os);
396 }
397
398
399 int InsetCollapsable::docbook(Buffer const * buf, ostream & os, bool mixcont) const
400 {
401         return inset.docbook(buf, os, mixcont);
402 }
403
404 #if 0
405 int InsetCollapsable::getMaxWidth(BufferView * bv,
406                                   UpdatableInset const * in) const
407 {
408 #if 0
409         int const w = UpdatableInset::getMaxWidth(bv, in);
410
411         if (w < 0) {
412                 // What does a negative max width signify? (Lgb)
413                 // Use the max width of the draw-area (Jug)
414                 return w;
415         }
416         // should be at least 30 pixels !!!
417         return max(30, w - width_collapsed());
418 #else
419         return UpdatableInset::getMaxWidth(bv, in);
420 #endif
421 }
422 #endif
423
424
425 void InsetCollapsable::update(BufferView * bv, LyXFont const & font,
426                               bool reinit)
427 {
428         if (in_update) {
429                 if (reinit && owner()) {
430                         owner()->update(bv, font, true);
431                 }
432                 return;
433         }
434         in_update = true;
435         inset.update(bv, font, reinit);
436         if (reinit && owner()) {
437                 owner()->update(bv, font, true);
438         }
439         in_update = false;
440 }
441
442
443 Inset::RESULT InsetCollapsable::localDispatch(FuncRequest const & cmd)
444 {
445         switch (cmd.action) {
446
447                 case LFUN_MOUSE_PRESS:
448                         lfunMousePress(cmd);
449                         return DISPATCHED;
450
451                 case LFUN_MOUSE_MOTION:
452                         lfunMouseMotion(cmd);
453                         return DISPATCHED;
454
455                 case LFUN_MOUSE_RELEASE:
456                         lfunMouseRelease(cmd);
457                         return DISPATCHED;
458
459                 default:
460                         UpdatableInset::RESULT result = inset.localDispatch(cmd);
461                         if (result >= FINISHED)
462                                 cmd.view()->unlockInset(this);
463                         first_after_edit = false;
464                         return result;
465         }
466         return UNDISPATCHED;
467 }
468
469
470 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
471 {
472         if (&inset == in)
473                 return true;
474         return inset.lockInsetInInset(bv, in);
475 }
476
477
478 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
479                                           bool lr)
480 {
481         if (&inset == in) {
482                 bv->unlockInset(this);
483                 return true;
484         }
485         return inset.unlockInsetInInset(bv, in, lr);
486 }
487
488
489 bool InsetCollapsable::updateInsetInInset(BufferView * bv, Inset *in)
490 {
491         if (in == this)
492                 return true;
493         return inset.updateInsetInInset(bv, in);
494 }
495
496
497 int InsetCollapsable::insetInInsetY() const
498 {
499         return inset.insetInInsetY() - (top_baseline - inset.y());
500 }
501
502
503 void InsetCollapsable::validate(LaTeXFeatures & features) const
504 {
505         inset.validate(features);
506 }
507
508
509 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
510 {
511         inset.getCursorPos(bv, x , y);
512 }
513
514
515 void InsetCollapsable::toggleInsetCursor(BufferView * bv)
516 {
517         inset.toggleInsetCursor(bv);
518 }
519
520
521 void InsetCollapsable::showInsetCursor(BufferView * bv, bool show)
522 {
523         inset.showInsetCursor(bv, show);
524 }
525
526
527 void InsetCollapsable::hideInsetCursor(BufferView * bv)
528 {
529         inset.hideInsetCursor(bv);
530 }
531
532
533 UpdatableInset * InsetCollapsable::getLockingInset() const
534 {
535         UpdatableInset * in = inset.getLockingInset();
536         if (const_cast<InsetText *>(&inset) == in)
537                 return const_cast<InsetCollapsable *>(this);
538         return in;
539 }
540
541
542 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(Inset::Code c)
543 {
544         if (c == lyxCode())
545                 return this;
546         return inset.getFirstLockingInsetOfType(c);
547 }
548
549
550 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
551                                bool toggleall, bool selectall)
552 {
553         inset.setFont(bv, font, toggleall, selectall);
554 }
555
556
557 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
558                                        bool const recursive) const
559 {
560         return inset.getLyXText(bv, recursive);
561 }
562
563
564 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
565 {
566         inset.deleteLyXText(bv, recursive);
567 }
568
569
570 void InsetCollapsable::resizeLyXText(BufferView * bv, bool force) const
571 {
572         inset.resizeLyXText(bv, force);
573         LyXFont font(LyXFont::ALL_SANE);
574         oldWidth = width(bv, font);
575 }
576
577
578 vector<string> const InsetCollapsable::getLabelList() const
579 {
580         return inset.getLabelList();
581 }
582
583
584 bool InsetCollapsable::nodraw() const
585 {
586         return inset.nodraw();
587 }
588
589
590 int InsetCollapsable::scroll(bool recursive) const
591 {
592         int sx = UpdatableInset::scroll(false);
593
594         if (recursive)
595                 sx += inset.scroll(recursive);
596
597         return sx;
598 }
599
600
601 Paragraph * InsetCollapsable::firstParagraph() const
602 {
603         return inset.firstParagraph();
604 }
605
606
607 Paragraph * InsetCollapsable::getFirstParagraph(int i) const
608 {
609         return inset.getFirstParagraph(i);
610 }
611
612
613 LyXCursor const & InsetCollapsable::cursor(BufferView * bv) const
614 {
615         return inset.cursor(bv);
616 }
617
618
619 Inset * InsetCollapsable::getInsetFromID(int id_arg) const
620 {
621         if (id_arg == id())
622                 return const_cast<InsetCollapsable *>(this);
623         return inset.getInsetFromID(id_arg);
624 }
625
626
627 void InsetCollapsable::open(BufferView * bv)
628 {
629         if (!collapsed_) return;
630
631         collapsed_ = false;
632         bv->updateInset(this);
633 }
634
635
636 void InsetCollapsable::close(BufferView * bv) const
637 {
638         if (collapsed_)
639                 return;
640
641         collapsed_ = true;
642         bv->updateInset(const_cast<InsetCollapsable *>(this));
643 }
644
645
646 void InsetCollapsable::setLabel(string const & l) const
647 {
648         label = l;
649 }
650
651
652 void InsetCollapsable::markErased()
653 {
654         inset.markErased();
655 }
656
657
658 bool InsetCollapsable::nextChange(BufferView * bv, lyx::pos_type & length)
659 {
660         bool found = inset.nextChange(bv, length);
661
662         if (first_after_edit && !found)
663                 close(bv);
664         else if (!found)
665                 first_after_edit = false;
666         return found;
667 }
668
669
670 bool InsetCollapsable::searchForward(BufferView * bv, string const & str,
671                                      bool cs, bool mw)
672 {
673         bool found = inset.searchForward(bv, str, cs, mw);
674         if (first_after_edit && !found)
675                 close(bv);
676         else if (!found)
677                 first_after_edit = false;
678         return found;
679 }
680
681
682 bool InsetCollapsable::searchBackward(BufferView * bv, string const & str,
683                                       bool cs, bool mw)
684 {
685         bool found = inset.searchBackward(bv, str, cs, mw);
686         if (first_after_edit && !found)
687                 close(bv);
688         else if (!found)
689                 first_after_edit = false;
690         return found;
691 }
692
693
694 WordLangTuple const
695 InsetCollapsable::selectNextWordToSpellcheck(BufferView * bv, float & value) const
696 {
697         WordLangTuple word = inset.selectNextWordToSpellcheck(bv, value);
698         if (first_after_edit && word.word().empty())
699                 close(bv);
700         first_after_edit = false;
701         return word;
702 }
703
704
705 void InsetCollapsable::addPreview(grfx::PreviewLoader & loader) const
706 {
707         inset.addPreview(loader);
708 }
709
710
711 void InsetCollapsable::cache(BufferView * bv) const
712 {
713         view_ = bv->owner()->view();
714 }
715
716
717 BufferView * InsetCollapsable::view() const
718 {
719         return view_.lock().get();
720 }