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