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