]> git.lyx.org Git - lyx.git/blob - src/insets/insetcollapsable.C
The "I want this in now" patch.
[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 "dimension.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 #include "buffer.h"
28
29 #include "frontends/font_metrics.h"
30 #include "frontends/Painter.h"
31 #include "frontends/LyXView.h"
32
33 #include "support/LAssert.h"
34 #include "support/LOstream.h"
35
36 using std::vector;
37 using std::ostream;
38 using std::endl;
39 using std::max;
40
41
42 InsetCollapsable::InsetCollapsable(BufferParams const & bp, bool collapsed)
43         : UpdatableInset(), collapsed_(collapsed), inset(bp),
44           button_length(0), button_top_y(0), button_bottom_y(0),
45           label("Label"),
46 #if 0
47         autocollapse(false),
48 #endif
49           oldWidth(0), in_update(false), first_after_edit(false)
50 {
51         inset.setOwner(this);
52         inset.setAutoBreakRows(true);
53         inset.setDrawFrame(0, InsetText::ALWAYS);
54         inset.setFrameColor(0, LColor::collapsableframe);
55         setInsetName("Collapsable");
56 }
57
58
59 InsetCollapsable::InsetCollapsable(InsetCollapsable const & in, bool same_id)
60         : UpdatableInset(in, same_id), collapsed_(in.collapsed_),
61           framecolor(in.framecolor), labelfont(in.labelfont), inset(in.inset),
62           button_length(0), button_top_y(0), button_bottom_y(0),
63           label(in.label),
64 #if 0
65           autocollapse(in.autocollapse),
66 #endif
67           oldWidth(0), in_update(false), first_after_edit(false)
68 {
69         inset.init(&(in.inset), same_id);
70         inset.setOwner(this);
71 }
72
73
74 bool InsetCollapsable::insertInset(BufferView * bv, Inset * in)
75 {
76         if (!insetAllowed(in->lyxCode())) {
77                 lyxerr << "InsetCollapsable::InsertInset: "
78                         "Unable to insert inset." << endl;
79                 return false;
80         }
81         return inset.insertInset(bv, in);
82 }
83
84
85 void InsetCollapsable::write(Buffer const * buf, ostream & os) const
86 {
87         os << "collapsed " << (collapsed_ ? "true" : "false") << "\n";
88         inset.writeParagraphData(buf, os);
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 void InsetCollapsable::dimension_collapsed(Dimension & dim) const
112 {
113         font_metrics::buttonText(label, labelfont, dim.w, dim.a, dim.d);
114         dim.w += 2 * TEXT_TO_INSET_OFFSET;
115 }
116
117
118 int InsetCollapsable::height_collapsed() const
119 {
120         Dimension dim;
121         font_metrics::buttonText(label, labelfont, dim.w, dim.a, dim.d);
122         return dim.a + dim.d;
123 }
124
125
126 void InsetCollapsable::dimension(BufferView * bv, LyXFont const & font,
127         Dimension & dim) const
128 {
129         dimension_collapsed(dim);
130         if (collapsed_)
131                 return;
132         Dimension insetdim;
133         inset.dimension(bv, font, insetdim);
134         dim.d += insetdim.height() + TEXT_TO_BOTTOM_OFFSET;
135         dim.w = max(dim.w, insetdim.width());
136 }
137
138
139 void InsetCollapsable::draw_collapsed(Painter & pain,
140                                       int baseline, float & x) const
141 {
142         pain.buttonText(int(x) + TEXT_TO_INSET_OFFSET,
143                         baseline, label, labelfont);
144         Dimension dim;
145         dimension_collapsed(dim);
146         x += dim.w;
147 }
148
149
150 void InsetCollapsable::draw(BufferView * bv, LyXFont const & f,
151                             int baseline, float & x, bool inlined) const
152 {
153         lyx::Assert(bv);
154         cache(bv);
155
156         if (nodraw())
157                 return;
158
159         Dimension dim_collapsed;
160         dimension_collapsed(dim_collapsed);
161
162         Painter & pain = bv->painter();
163
164         button_length   = dim_collapsed.width();
165         button_top_y    = -ascent(bv, f);
166         button_bottom_y = -ascent(bv, f) + dim_collapsed.height();
167
168         if (!isOpen()) {
169                 draw_collapsed(pain, baseline, x);
170                 return;
171         }
172
173         float old_x = x;
174
175         if (!owner())
176                 x += scroll();
177
178         top_x = int(x);
179         top_baseline = baseline;
180
181         int const bl = baseline - ascent(bv, f) + dim_collapsed.ascent();
182
183         if (inlined) {
184                 inset.draw(bv, f, baseline, x);
185         } else {
186                 draw_collapsed(pain, bl, old_x);
187                 int const yy = bl + dim_collapsed.descent() + inset.ascent(bv, f);
188                 inset.draw(bv, f, yy, x);
189                 // contained inset may be shorter than the button
190                 if (x < top_x + button_length + TEXT_TO_INSET_OFFSET)
191                         x = top_x + button_length + TEXT_TO_INSET_OFFSET;
192         }
193 }
194
195
196 void InsetCollapsable::draw(BufferView * bv, LyXFont const & f,
197                             int baseline, float & x) const
198 {
199         // by default, we are not inlined-drawing
200         draw(bv, f, baseline, x, false);
201 }
202
203
204 Inset::EDITABLE InsetCollapsable::editable() const
205 {
206         return collapsed_ ? IS_EDITABLE : HIGHLY_EDITABLE;
207 }
208
209
210 void InsetCollapsable::insetUnlock(BufferView * bv)
211 {
212 #if 0
213         if (autocollapse) {
214                 if (change_label_with_text) {
215                         draw_label = get_new_label();
216                 } else {
217                         draw_label = label;
218                 }
219                 collapsed_ = true;
220         }
221 #endif
222         inset.insetUnlock(bv);
223         if (scroll())
224                 scroll(bv, 0.0F);
225         bv->updateInset(this);
226 }
227
228
229 FuncRequest InsetCollapsable::adjustCommand(FuncRequest const & cmd)
230 {
231         LyXFont font(LyXFont::ALL_SANE);
232         FuncRequest cmd1 = cmd;
233         cmd1.y = ascent(cmd.view(), font) + cmd.y -
234             (height_collapsed() + inset.ascent(cmd.view(), font));
235         return cmd1;
236 }
237
238
239 void InsetCollapsable::lfunMouseRelease(FuncRequest const & cmd)
240 {
241         bool ret = false;
242         BufferView * bv = cmd.view();
243
244         if (collapsed_ && cmd.button() != mouse_button::button3) {
245                 collapsed_ = false;
246                 inset.setUpdateStatus(bv, InsetText::FULL);
247                 bv->updateInset(this);
248                 bv->buffer()->markDirty();
249                 return;
250         }
251
252         if ((cmd.button() != mouse_button::button3) && (cmd.x < button_length) &&
253             (cmd.y >= button_top_y) && (cmd.y <= button_bottom_y))
254         {
255                 if (collapsed_) {
256                         collapsed_ = false;
257                         inset.setUpdateStatus(bv, InsetText::FULL);
258                         bv->updateInset(this);
259                         bv->buffer()->markDirty();
260                 } else {
261                         collapsed_ = true;
262                         bv->unlockInset(this);
263                         bv->updateInset(this);
264                         bv->buffer()->markDirty();
265                 }
266         } else if (!collapsed_ && (cmd.y > button_bottom_y)) {
267                 ret = (inset.localDispatch(adjustCommand(cmd)) == DISPATCHED);
268         }
269         if (cmd.button() == mouse_button::button3 && !ret)
270                 showInsetDialog(bv);
271 }
272
273
274 int InsetCollapsable::latex(Buffer const * buf, ostream & os,
275                             LatexRunParams const & runparams,
276                             bool free_spc) const
277 {
278         return inset.latex(buf, os, runparams, free_spc);
279 }
280
281
282 int InsetCollapsable::ascii(Buffer const * buf, ostream & os, int ll) const
283 {
284         return inset.ascii(buf, os, ll);
285 }
286
287
288 int InsetCollapsable::linuxdoc(Buffer const * buf, ostream & os) const
289 {
290         return inset.linuxdoc(buf, os);
291 }
292
293
294 int InsetCollapsable::docbook(Buffer const * buf, ostream & os, bool mixcont) const
295 {
296         return inset.docbook(buf, os, mixcont);
297 }
298
299
300 void InsetCollapsable::update(BufferView * bv, bool reinit)
301 {
302         if (in_update) {
303                 if (reinit && owner()) {
304                         owner()->update(bv, true);
305                 }
306                 return;
307         }
308         in_update = true;
309         inset.update(bv, reinit);
310         if (reinit && owner()) {
311                 owner()->update(bv, true);
312         }
313         in_update = false;
314 }
315
316
317 Inset::RESULT InsetCollapsable::localDispatch(FuncRequest const & cmd)
318 {
319         //lyxerr << "InsetCollapsable::localDispatch: " << cmd.action << "\n";
320         BufferView * bv = cmd.view();
321         switch (cmd.action) {
322                 case LFUN_INSET_EDIT: {
323                         if (!cmd.argument.empty()) {
324                                 UpdatableInset::localDispatch(cmd);
325                                 if (collapsed_) {
326                                         collapsed_ = false;
327                                         if (bv->lockInset(this)) {
328                                                 inset.setUpdateStatus(bv, InsetText::FULL);
329                                                 bv->updateInset(this);
330                                                 bv->buffer()->markDirty();
331                                                 inset.localDispatch(cmd);
332                                                 first_after_edit = true;
333                                         }
334                                 } else {
335                                         if (bv->lockInset(this))
336                                                 inset.localDispatch(cmd);
337                                 }
338                                 return DISPATCHED;
339                         }
340
341 #ifdef WITH_WARNINGS
342 #warning Fix this properly in BufferView_pimpl::workAreaButtonRelease
343 #endif
344                         if (cmd.button() == mouse_button::button3)
345                                 return DISPATCHED;
346
347                         UpdatableInset::localDispatch(cmd);
348
349                         if (collapsed_) {
350                                 collapsed_ = false;
351                                 // set this only here as it should be recollapsed only if
352                                 // it was already collapsed!
353                                 first_after_edit = true;
354                                 if (!bv->lockInset(this))
355                                         return DISPATCHED;
356                                 bv->updateInset(this);
357                                 bv->buffer()->markDirty();
358                                 inset.localDispatch(cmd);
359                         } else {
360                                 FuncRequest cmd1 = cmd;
361                                 if (!bv->lockInset(this))
362                                         return DISPATCHED;
363                                 if (cmd.y <= button_bottom_y) {
364                                         cmd1.y = 0;
365                                 } else {
366                                         LyXFont font(LyXFont::ALL_SANE);
367                                         cmd1.y = ascent(bv, font) + cmd.y -
368                                                 (height_collapsed() + inset.ascent(bv, font));
369                                 }
370                                 inset.localDispatch(cmd);
371                         }
372                         return DISPATCHED;
373                 }
374
375                 case LFUN_MOUSE_PRESS:
376                         if (!collapsed_ && cmd.y > button_bottom_y)
377                                 inset.localDispatch(adjustCommand(cmd));
378                         return DISPATCHED;
379
380                 case LFUN_MOUSE_MOTION:
381                         if (!collapsed_ && cmd.y > button_bottom_y)
382                                 inset.localDispatch(adjustCommand(cmd));
383                         return DISPATCHED;
384
385                 case LFUN_MOUSE_RELEASE:
386                         lfunMouseRelease(cmd);
387                         return DISPATCHED;
388
389                 default:
390                         UpdatableInset::RESULT result = inset.localDispatch(cmd);
391                         if (result >= FINISHED)
392                                 bv->unlockInset(this);
393                         first_after_edit = false;
394                         return result;
395         }
396 }
397
398
399 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
400 {
401         if (&inset == in)
402                 return true;
403         return inset.lockInsetInInset(bv, in);
404 }
405
406
407 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
408                                           bool lr)
409 {
410         if (&inset == in) {
411                 bv->unlockInset(this);
412                 return true;
413         }
414         return inset.unlockInsetInInset(bv, in, lr);
415 }
416
417
418 bool InsetCollapsable::updateInsetInInset(BufferView * bv, Inset *in)
419 {
420         if (in == this)
421                 return true;
422         return inset.updateInsetInInset(bv, in);
423 }
424
425
426 int InsetCollapsable::insetInInsetY() const
427 {
428         return inset.insetInInsetY() - (top_baseline - inset.y());
429 }
430
431
432 void InsetCollapsable::validate(LaTeXFeatures & features) const
433 {
434         inset.validate(features);
435 }
436
437
438 void InsetCollapsable::getCursor(BufferView & bv, int & x, int & y) const
439 {
440         inset.getCursor(bv, x, y);
441 }
442
443
444 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
445 {
446         inset.getCursorPos(bv, x , y);
447 }
448
449
450 UpdatableInset * InsetCollapsable::getLockingInset() const
451 {
452         UpdatableInset * in = inset.getLockingInset();
453         if (const_cast<InsetText *>(&inset) == in)
454                 return const_cast<InsetCollapsable *>(this);
455         return in;
456 }
457
458
459 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(Inset::Code c)
460 {
461         if (c == lyxCode())
462                 return this;
463         return inset.getFirstLockingInsetOfType(c);
464 }
465
466
467 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
468                                bool toggleall, bool selectall)
469 {
470         inset.setFont(bv, font, toggleall, selectall);
471 }
472
473
474 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
475                                        bool const recursive) const
476 {
477         return inset.getLyXText(bv, recursive);
478 }
479
480
481 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
482 {
483         inset.deleteLyXText(bv, recursive);
484 }
485
486
487 void InsetCollapsable::resizeLyXText(BufferView * bv, bool force) const
488 {
489         inset.resizeLyXText(bv, force);
490         LyXFont font(LyXFont::ALL_SANE);
491         oldWidth = width(bv, font);
492 }
493
494
495 vector<string> const InsetCollapsable::getLabelList() const
496 {
497         return inset.getLabelList();
498 }
499
500
501 bool InsetCollapsable::nodraw() const
502 {
503         return inset.nodraw();
504 }
505
506
507 int InsetCollapsable::scroll(bool recursive) const
508 {
509         int sx = UpdatableInset::scroll(false);
510
511         if (recursive)
512                 sx += inset.scroll(recursive);
513
514         return sx;
515 }
516
517
518 ParagraphList * InsetCollapsable::getParagraphs(int i) const
519 {
520         return inset.getParagraphs(i);
521 }
522
523
524 LyXCursor const & InsetCollapsable::cursor(BufferView * bv) const
525 {
526         return inset.cursor(bv);
527 }
528
529
530 Inset * InsetCollapsable::getInsetFromID(int id_arg) const
531 {
532         if (id_arg == id())
533                 return const_cast<InsetCollapsable *>(this);
534         return inset.getInsetFromID(id_arg);
535 }
536
537
538 void InsetCollapsable::open(BufferView * bv)
539 {
540         if (!collapsed_) return;
541
542         collapsed_ = false;
543         bv->updateInset(this);
544 }
545
546
547 void InsetCollapsable::close(BufferView * bv) const
548 {
549         if (collapsed_)
550                 return;
551
552         collapsed_ = true;
553         bv->updateInset(const_cast<InsetCollapsable *>(this));
554 }
555
556
557 void InsetCollapsable::setLabel(string const & l) const
558 {
559         label = l;
560 }
561
562
563 void InsetCollapsable::markErased()
564 {
565         inset.markErased();
566 }
567
568
569 bool InsetCollapsable::nextChange(BufferView * bv, lyx::pos_type & length)
570 {
571         bool found = inset.nextChange(bv, length);
572
573         if (first_after_edit && !found)
574                 close(bv);
575         else if (!found)
576                 first_after_edit = false;
577         return found;
578 }
579
580
581 bool InsetCollapsable::searchForward(BufferView * bv, string const & str,
582                                      bool cs, bool mw)
583 {
584         bool found = inset.searchForward(bv, str, cs, mw);
585         if (first_after_edit && !found)
586                 close(bv);
587         else if (!found)
588                 first_after_edit = false;
589         return found;
590 }
591
592
593 bool InsetCollapsable::searchBackward(BufferView * bv, string const & str,
594                                       bool cs, bool mw)
595 {
596         bool found = inset.searchBackward(bv, str, cs, mw);
597         if (first_after_edit && !found)
598                 close(bv);
599         else if (!found)
600                 first_after_edit = false;
601         return found;
602 }
603
604
605 WordLangTuple const
606 InsetCollapsable::selectNextWordToSpellcheck(BufferView * bv, float & value) const
607 {
608         WordLangTuple word = inset.selectNextWordToSpellcheck(bv, value);
609         if (first_after_edit && word.word().empty())
610                 close(bv);
611         first_after_edit = false;
612         return word;
613 }
614
615
616 void InsetCollapsable::addPreview(grfx::PreviewLoader & loader) const
617 {
618         inset.addPreview(loader);
619 }
620
621
622 void InsetCollapsable::cache(BufferView * bv) const
623 {
624         view_ = bv->owner()->view();
625 }
626
627
628 BufferView * InsetCollapsable::view() const
629 {
630         return view_.lock().get();
631 }