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