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