]> git.lyx.org Git - lyx.git/blob - src/insets/insetcollapsable.C
factor out the detection of clicking on the inset button as 'hitButton'.
[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 using namespace lyx::graphics;
39
40 using std::vector;
41 using std::ostream;
42 using std::endl;
43 using std::max;
44
45
46 InsetCollapsable::InsetCollapsable(BufferParams const & bp, bool collapsed)
47         : UpdatableInset(), collapsed_(collapsed), inset(bp),
48           button_dim(0, 0, 0, 0), label("Label"),
49 #if 0
50         autocollapse(false),
51 #endif
52           oldWidth(0), in_update(false), first_after_edit(false)
53 {
54         inset.setOwner(this);
55         inset.setAutoBreakRows(true);
56         inset.setDrawFrame(0, InsetText::ALWAYS);
57         inset.setFrameColor(0, LColor::collapsableframe);
58         setInsetName("Collapsable");
59 }
60
61
62 InsetCollapsable::InsetCollapsable(InsetCollapsable const & in)
63         : UpdatableInset(in), collapsed_(in.collapsed_),
64           framecolor(in.framecolor), labelfont(in.labelfont), inset(in.inset),
65           button_dim(0, 0, 0, 0), label(in.label),
66 #if 0
67           autocollapse(in.autocollapse),
68 #endif
69           oldWidth(0), in_update(false), first_after_edit(false)
70 {
71         inset.init(&(in.inset));
72         inset.setOwner(this);
73 }
74
75
76 bool InsetCollapsable::insertInset(BufferView * bv, InsetOld * in)
77 {
78         if (!insetAllowed(in->lyxCode())) {
79                 lyxerr << "InsetCollapsable::InsertInset: "
80                         "Unable to insert inset." << endl;
81                 return false;
82         }
83         return inset.insertInset(bv, in);
84 }
85
86
87 void InsetCollapsable::write(Buffer const * buf, ostream & os) const
88 {
89         os << "collapsed " << (collapsed_ ? "true" : "false") << "\n";
90         inset.writeParagraphData(buf, os);
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 void InsetCollapsable::dimension_collapsed(Dimension & dim) const
114 {
115         font_metrics::buttonText(label, labelfont, dim.wid, dim.asc, dim.des);
116         dim.wid += 2 * TEXT_TO_INSET_OFFSET;
117 }
118
119
120 int InsetCollapsable::height_collapsed() const
121 {
122         Dimension dim;
123         font_metrics::buttonText(label, labelfont, dim.wid, dim.asc, dim.des);
124         return dim.asc + dim.des;
125 }
126
127
128 void InsetCollapsable::metrics(MetricsInfo & mi, Dimension & dim) const
129 {
130         //lyxerr << "InsetCollapsable::metrics:  width: " << mi.base.textwidth << "\n";
131         dimension_collapsed(dim);
132         if (!collapsed_) {
133                 Dimension insetdim;
134                 inset.metrics(mi, insetdim);
135                 dim.des += insetdim.height() + TEXT_TO_BOTTOM_OFFSET;
136                 dim.wid = max(dim.wid, insetdim.wid);
137         }
138         dim_ = dim;
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();
160         button_dim.x1 = 0;
161         button_dim.x2 = dim_collapsed.width();
162         button_dim.y1 = -aa;
163         button_dim.y2 = -aa + dim_collapsed.height();
164
165         if (!isOpen()) {
166                 draw_collapsed(pi, x, y);
167                 return;
168         }
169
170         int old_x = x;
171
172         if (!owner())
173                 x += scroll();
174
175         top_x = x;
176         top_baseline = y;
177
178         int const bl = y - aa + dim_collapsed.ascent();
179
180         if (inlined) {
181                 inset.draw(pi, x, y);
182         } else {
183                 draw_collapsed(pi, old_x, bl);
184                 int const yy = bl + dim_collapsed.descent() + inset.ascent();
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 InsetOld::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         FuncRequest cmd1 = cmd;
225         cmd1.y = ascent() + cmd.y - (height_collapsed() + inset.ascent());
226         return cmd1;
227 }
228
229
230 void InsetCollapsable::lfunMouseRelease(FuncRequest const & cmd)
231 {
232         bool ret = false;
233         BufferView * bv = cmd.view();
234
235         if (collapsed_ && cmd.button() != mouse_button::button3) {
236                 collapsed_ = false;
237                 inset.setUpdateStatus(InsetText::FULL);
238                 bv->updateInset(this);
239                 bv->buffer()->markDirty();
240                 return;
241         }
242
243         if (cmd.button() != mouse_button::button3 && hitButton(cmd))
244         {
245                 if (collapsed_) {
246                         collapsed_ = false;
247                         inset.setUpdateStatus(InsetText::FULL);
248                         bv->updateInset(this);
249                         bv->buffer()->markDirty();
250                 } else {
251                         collapsed_ = true;
252                         bv->unlockInset(this);
253                         bv->updateInset(this);
254                         bv->buffer()->markDirty();
255                 }
256         } else if (!collapsed_ && (cmd.y > button_dim.y2)) {
257                 ret = (inset.localDispatch(adjustCommand(cmd)) == DISPATCHED);
258         }
259         if (cmd.button() == mouse_button::button3 && !ret)
260                 showInsetDialog(bv);
261 }
262
263
264 int InsetCollapsable::latex(Buffer const * buf, ostream & os,
265                             LatexRunParams const & runparams) const
266 {
267         return inset.latex(buf, os, runparams);
268 }
269
270
271 int InsetCollapsable::ascii(Buffer const * buf, ostream & os, int ll) const
272 {
273         return inset.ascii(buf, os, ll);
274 }
275
276
277 int InsetCollapsable::linuxdoc(Buffer const * buf, ostream & os) const
278 {
279         return inset.linuxdoc(buf, os);
280 }
281
282
283 int InsetCollapsable::docbook(Buffer const * buf, ostream & os, bool mixcont) const
284 {
285         return inset.docbook(buf, os, mixcont);
286 }
287
288
289 bool InsetCollapsable::hitButton(FuncRequest const & cmd) const
290 {
291         return button_dim.contained(cmd.x, cmd.y);
292 }
293
294
295 InsetOld::RESULT InsetCollapsable::localDispatch(FuncRequest const & cmd)
296 {
297         lyxerr << "InsetCollapsable::localDispatch: "
298                 << cmd.action << " '" << cmd.argument << "'\n";
299         BufferView * bv = cmd.view();
300         switch (cmd.action) {
301                 case LFUN_INSET_EDIT: {
302                         if (!cmd.argument.empty()) {
303                                 UpdatableInset::localDispatch(cmd);
304                                 if (collapsed_) {
305                                         lyxerr << "branch collapsed_\n";
306                                         collapsed_ = false;
307                                         if (bv->lockInset(this)) {
308                                                 inset.setUpdateStatus(InsetText::FULL);
309                                                 bv->updateInset(this);
310                                                 bv->buffer()->markDirty();
311                                                 inset.localDispatch(cmd);
312                                                 first_after_edit = true;
313                                         }
314                                 } else {
315                                         lyxerr << "branch not collapsed_\n";
316                                         if (bv->lockInset(this))
317                                                 inset.localDispatch(cmd);
318                                 }
319                                 return DISPATCHED;
320                         }
321
322 #ifdef WITH_WARNINGS
323 #warning Fix this properly in BufferView_pimpl::workAreaButtonRelease
324 #endif
325                         if (cmd.button() == mouse_button::button3)
326                                 return DISPATCHED;
327
328                         UpdatableInset::localDispatch(cmd);
329
330                         if (collapsed_) {
331                                 collapsed_ = false;
332                                 // set this only here as it should be recollapsed only if
333                                 // it was already collapsed!
334                                 first_after_edit = true;
335                                 if (!bv->lockInset(this))
336                                         return DISPATCHED;
337                                 bv->updateInset(this);
338                                 bv->buffer()->markDirty();
339                                 inset.localDispatch(cmd);
340                         } else {
341                                 FuncRequest cmd1 = cmd;
342                                 if (!bv->lockInset(this))
343                                         return DISPATCHED;
344                                 if (cmd.y <= button_dim.y2) {
345                                         cmd1.y = 0;
346                                 } else {
347                                         cmd1.y = ascent() + cmd.y - (height_collapsed() + inset.ascent());
348                                 }
349                                 inset.localDispatch(cmd);
350                         }
351                         return DISPATCHED;
352                 }
353
354                 case LFUN_MOUSE_PRESS:
355                         if (!collapsed_ && cmd.y > button_dim.y2)
356                                 inset.localDispatch(adjustCommand(cmd));
357                         return DISPATCHED;
358
359                 case LFUN_MOUSE_MOTION:
360                         if (!collapsed_ && cmd.y > button_dim.y2)
361                                 inset.localDispatch(adjustCommand(cmd));
362                         return DISPATCHED;
363
364                 case LFUN_MOUSE_RELEASE:
365                         lfunMouseRelease(cmd);
366                         return DISPATCHED;
367
368                 default:
369                         UpdatableInset::RESULT result = inset.localDispatch(cmd);
370                         if (result >= FINISHED)
371                                 bv->unlockInset(this);
372                         first_after_edit = false;
373                         return result;
374         }
375 }
376
377
378 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
379 {
380         if (&inset == in)
381                 return true;
382         return inset.lockInsetInInset(bv, in);
383 }
384
385
386 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
387                                           bool lr)
388 {
389         if (&inset == in) {
390                 bv->unlockInset(this);
391                 return true;
392         }
393         return inset.unlockInsetInInset(bv, in, lr);
394 }
395
396
397 bool InsetCollapsable::updateInsetInInset(BufferView * bv, InsetOld *in)
398 {
399         if (in == this)
400                 return true;
401         return inset.updateInsetInInset(bv, in);
402 }
403
404
405 int InsetCollapsable::insetInInsetY() const
406 {
407         return inset.insetInInsetY() - (top_baseline - inset.y());
408 }
409
410
411 void InsetCollapsable::validate(LaTeXFeatures & features) const
412 {
413         inset.validate(features);
414 }
415
416
417 void InsetCollapsable::getCursor(BufferView & bv, int & x, int & y) const
418 {
419         inset.getCursor(bv, x, y);
420 }
421
422
423 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
424 {
425         inset.getCursorPos(bv, x , y);
426 }
427
428
429 UpdatableInset * InsetCollapsable::getLockingInset() const
430 {
431         UpdatableInset * in = inset.getLockingInset();
432         if (&inset == in)
433                 return const_cast<InsetCollapsable *>(this);
434         return in;
435 }
436
437
438 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(InsetOld::Code c)
439 {
440         if (c == lyxCode())
441                 return this;
442         return inset.getFirstLockingInsetOfType(c);
443 }
444
445
446 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
447                                bool toggleall, bool selectall)
448 {
449         inset.setFont(bv, font, toggleall, selectall);
450 }
451
452
453 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
454                                        bool const recursive) const
455 {
456         return inset.getLyXText(bv, recursive);
457 }
458
459
460 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
461 {
462         inset.deleteLyXText(bv, recursive);
463 }
464
465
466 void InsetCollapsable::resizeLyXText(BufferView * bv, bool force) const
467 {
468         inset.resizeLyXText(bv, force);
469         oldWidth = width();
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 InsetOld * 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(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 }