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