]> git.lyx.org Git - features.git/blob - src/insets/insetcollapsable.C
namespace grfx -> lyx::graphics
[features.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(bv, 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(bv, 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 void InsetCollapsable::update(BufferView * bv, bool reinit)
293 {
294         if (in_update) {
295                 if (reinit && owner()) {
296                         owner()->update(bv, true);
297                 }
298                 return;
299         }
300         in_update = true;
301         inset.update(bv, reinit);
302         if (reinit && owner()) {
303                 owner()->update(bv, true);
304         }
305         in_update = false;
306 }
307
308
309 Inset::RESULT InsetCollapsable::localDispatch(FuncRequest const & cmd)
310 {
311         //lyxerr << "InsetCollapsable::localDispatch: " << cmd.action << "\n";
312         BufferView * bv = cmd.view();
313         switch (cmd.action) {
314                 case LFUN_INSET_EDIT: {
315                         if (!cmd.argument.empty()) {
316                                 UpdatableInset::localDispatch(cmd);
317                                 if (collapsed_) {
318                                         collapsed_ = false;
319                                         if (bv->lockInset(this)) {
320                                                 inset.setUpdateStatus(bv, InsetText::FULL);
321                                                 bv->updateInset(this);
322                                                 bv->buffer()->markDirty();
323                                                 inset.localDispatch(cmd);
324                                                 first_after_edit = true;
325                                         }
326                                 } else {
327                                         if (bv->lockInset(this))
328                                                 inset.localDispatch(cmd);
329                                 }
330                                 return DISPATCHED;
331                         }
332
333 #ifdef WITH_WARNINGS
334 #warning Fix this properly in BufferView_pimpl::workAreaButtonRelease
335 #endif
336                         if (cmd.button() == mouse_button::button3)
337                                 return DISPATCHED;
338
339                         UpdatableInset::localDispatch(cmd);
340
341                         if (collapsed_) {
342                                 collapsed_ = false;
343                                 // set this only here as it should be recollapsed only if
344                                 // it was already collapsed!
345                                 first_after_edit = true;
346                                 if (!bv->lockInset(this))
347                                         return DISPATCHED;
348                                 bv->updateInset(this);
349                                 bv->buffer()->markDirty();
350                                 inset.localDispatch(cmd);
351                         } else {
352                                 FuncRequest cmd1 = cmd;
353                                 if (!bv->lockInset(this))
354                                         return DISPATCHED;
355                                 if (cmd.y <= button_bottom_y) {
356                                         cmd1.y = 0;
357                                 } else {
358                                         LyXFont font(LyXFont::ALL_SANE);
359                                         cmd1.y = ascent(bv, font) + cmd.y -
360                                                 (height_collapsed() + inset.ascent(bv, font));
361                                 }
362                                 inset.localDispatch(cmd);
363                         }
364                         return DISPATCHED;
365                 }
366
367                 case LFUN_MOUSE_PRESS:
368                         if (!collapsed_ && cmd.y > button_bottom_y)
369                                 inset.localDispatch(adjustCommand(cmd));
370                         return DISPATCHED;
371
372                 case LFUN_MOUSE_MOTION:
373                         if (!collapsed_ && cmd.y > button_bottom_y)
374                                 inset.localDispatch(adjustCommand(cmd));
375                         return DISPATCHED;
376
377                 case LFUN_MOUSE_RELEASE:
378                         lfunMouseRelease(cmd);
379                         return DISPATCHED;
380
381                 default:
382                         UpdatableInset::RESULT result = inset.localDispatch(cmd);
383                         if (result >= FINISHED)
384                                 bv->unlockInset(this);
385                         first_after_edit = false;
386                         return result;
387         }
388 }
389
390
391 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
392 {
393         if (&inset == in)
394                 return true;
395         return inset.lockInsetInInset(bv, in);
396 }
397
398
399 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
400                                           bool lr)
401 {
402         if (&inset == in) {
403                 bv->unlockInset(this);
404                 return true;
405         }
406         return inset.unlockInsetInInset(bv, in, lr);
407 }
408
409
410 bool InsetCollapsable::updateInsetInInset(BufferView * bv, Inset *in)
411 {
412         if (in == this)
413                 return true;
414         return inset.updateInsetInInset(bv, in);
415 }
416
417
418 int InsetCollapsable::insetInInsetY() const
419 {
420         return inset.insetInInsetY() - (top_baseline - inset.y());
421 }
422
423
424 void InsetCollapsable::validate(LaTeXFeatures & features) const
425 {
426         inset.validate(features);
427 }
428
429
430 void InsetCollapsable::getCursor(BufferView & bv, int & x, int & y) const
431 {
432         inset.getCursor(bv, x, y);
433 }
434
435
436 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
437 {
438         inset.getCursorPos(bv, x , y);
439 }
440
441
442 UpdatableInset * InsetCollapsable::getLockingInset() const
443 {
444         UpdatableInset * in = inset.getLockingInset();
445         if (const_cast<InsetText *>(&inset) == in)
446                 return const_cast<InsetCollapsable *>(this);
447         return in;
448 }
449
450
451 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(Inset::Code c)
452 {
453         if (c == lyxCode())
454                 return this;
455         return inset.getFirstLockingInsetOfType(c);
456 }
457
458
459 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
460                                bool toggleall, bool selectall)
461 {
462         inset.setFont(bv, font, toggleall, selectall);
463 }
464
465
466 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
467                                        bool const recursive) const
468 {
469         return inset.getLyXText(bv, recursive);
470 }
471
472
473 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
474 {
475         inset.deleteLyXText(bv, recursive);
476 }
477
478
479 void InsetCollapsable::resizeLyXText(BufferView * bv, bool force) const
480 {
481         inset.resizeLyXText(bv, force);
482         LyXFont font(LyXFont::ALL_SANE);
483         oldWidth = width(bv, font);
484 }
485
486
487 void InsetCollapsable::getLabelList(std::vector<string> & list) const
488 {
489         inset.getLabelList(list);
490 }
491
492
493 bool InsetCollapsable::nodraw() const
494 {
495         return inset.nodraw();
496 }
497
498
499 int InsetCollapsable::scroll(bool recursive) const
500 {
501         int sx = UpdatableInset::scroll(false);
502
503         if (recursive)
504                 sx += inset.scroll(recursive);
505
506         return sx;
507 }
508
509
510 ParagraphList * InsetCollapsable::getParagraphs(int i) const
511 {
512         return inset.getParagraphs(i);
513 }
514
515
516 LyXCursor const & InsetCollapsable::cursor(BufferView * bv) const
517 {
518         return inset.cursor(bv);
519 }
520
521
522 Inset * InsetCollapsable::getInsetFromID(int id_arg) const
523 {
524         if (id_arg == id())
525                 return const_cast<InsetCollapsable *>(this);
526         return inset.getInsetFromID(id_arg);
527 }
528
529
530 void InsetCollapsable::open(BufferView * bv)
531 {
532         if (!collapsed_) return;
533
534         collapsed_ = false;
535         bv->updateInset(this);
536 }
537
538
539 void InsetCollapsable::close(BufferView * bv) const
540 {
541         if (collapsed_)
542                 return;
543
544         collapsed_ = true;
545         bv->updateInset(const_cast<InsetCollapsable *>(this));
546 }
547
548
549 void InsetCollapsable::setLabel(string const & l) const
550 {
551         label = l;
552 }
553
554
555 void InsetCollapsable::markErased()
556 {
557         inset.markErased();
558 }
559
560
561 bool InsetCollapsable::nextChange(BufferView * bv, lyx::pos_type & length)
562 {
563         bool found = inset.nextChange(bv, length);
564
565         if (first_after_edit && !found)
566                 close(bv);
567         else if (!found)
568                 first_after_edit = false;
569         return found;
570 }
571
572
573 bool InsetCollapsable::searchForward(BufferView * bv, string const & str,
574                                      bool cs, bool mw)
575 {
576         bool found = inset.searchForward(bv, str, cs, mw);
577         if (first_after_edit && !found)
578                 close(bv);
579         else if (!found)
580                 first_after_edit = false;
581         return found;
582 }
583
584
585 bool InsetCollapsable::searchBackward(BufferView * bv, string const & str,
586                                       bool cs, bool mw)
587 {
588         bool found = inset.searchBackward(bv, str, cs, mw);
589         if (first_after_edit && !found)
590                 close(bv);
591         else if (!found)
592                 first_after_edit = false;
593         return found;
594 }
595
596
597 WordLangTuple const
598 InsetCollapsable::selectNextWordToSpellcheck(BufferView * bv, float & value) const
599 {
600         WordLangTuple word = inset.selectNextWordToSpellcheck(bv, value);
601         if (first_after_edit && word.word().empty())
602                 close(bv);
603         first_after_edit = false;
604         return word;
605 }
606
607
608 void InsetCollapsable::addPreview(grfx::PreviewLoader & loader) const
609 {
610         inset.addPreview(loader);
611 }
612
613
614 void InsetCollapsable::cache(BufferView * bv) const
615 {
616         view_ = bv->owner()->view();
617 }
618
619
620 BufferView * InsetCollapsable::view() const
621 {
622         return view_.lock().get();
623 }