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