]> git.lyx.org Git - lyx.git/blob - src/insets/insetcollapsable.C
playing around with insets
[lyx.git] / src / insets / insetcollapsable.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *          Copyright 1998-2001 The LyX Team.
7  *
8  * ======================================================
9  */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "insetcollapsable.h"
18 #include "gettext.h"
19 #include "lyxfont.h"
20 #include "BufferView.h"
21 #include "Painter.h"
22 #include "insets/insettext.h"
23 #include "support/LOstream.h"
24 #include "support/lstrings.h"
25 #include "debug.h"
26 #include "lyxtext.h"
27 #include "font.h"
28
29 class LyXText;
30
31 using std::ostream;
32 using std::endl;
33 using std::max;
34
35
36 InsetCollapsable::InsetCollapsable(bool collapsed)
37         : UpdatableInset(), collapsed_(collapsed), 
38           button_length(0), button_top_y(0), button_bottom_y(0),
39           label("Label"), draw_label(label), autocollapse(true), 
40           oldWidth(0), need_update(FULL),
41           inlined(false), change_label_with_text(false)
42           
43
44 {
45         inset.setOwner(this);
46         inset.setAutoBreakRows(true);
47         inset.setDrawFrame(0, InsetText::ALWAYS);
48         inset.setFrameColor(0, LColor::collapsableframe);
49         setInsetName("Collapsable");
50 }
51
52
53 bool InsetCollapsable::insertInset(BufferView * bv, Inset * in)
54 {
55         if (!insetAllowed(in->lyxCode())) {
56                 lyxerr << "InsetCollapsable::InsertInset: "
57                         "Unable to insert inset." << endl;
58                 return false;
59         }
60         return inset.insertInset(bv, in);
61 }
62
63
64 void InsetCollapsable::write(Buffer const * buf, ostream & os) const
65 {
66         os << "collapsed " << tostr(collapsed_) << "\n";
67         inset.writeParagraphData(buf, os);
68 }
69
70
71
72 void InsetCollapsable::read(Buffer const * buf, LyXLex & lex)
73 {
74         if (lex.IsOK()) {
75                 lex.next();
76                 string const token = lex.GetString();
77                 if (token == "collapsed") {
78                         lex.next();
79                         collapsed_ = lex.GetBool();
80                 } else {
81                         lyxerr << "InsetCollapsable::Read: Missing collapsed!"
82                                << endl;
83                 }
84         }
85         inset.read(buf, lex);
86         if (collapsed_ && change_label_with_text) {
87                 draw_label = get_new_label();
88         } else {
89                 draw_label = label;
90         }
91 }
92
93
94 //int InsetCollapsable::ascent_collapsed(Painter & pain) const
95 int InsetCollapsable::ascent_collapsed(Painter & /*pain*/) const
96 {
97         int width = 0;
98         int ascent = 0;
99         int descent = 0;
100 #if 0
101         pain.buttonText(0, 0, draw_label, labelfont, false, 
102                         width, ascent, descent);
103 #else
104         lyxfont::buttonText(draw_label, labelfont, width, ascent, descent);
105 #endif
106         return ascent;
107 }
108
109
110 //int InsetCollapsable::descent_collapsed(Painter & pain) const
111 int InsetCollapsable::descent_collapsed(Painter & /*pain*/) const
112 {
113         int width = 0;
114         int ascent = 0;
115         int descent = 0;
116 #if 0
117         pain.buttonText(0, 0, draw_label, labelfont, false, 
118                         width, ascent, descent);
119 #else
120         lyxfont::buttonText(draw_label, labelfont, width, ascent, descent);
121 #endif
122         return descent;
123 }
124
125
126 //int InsetCollapsable::width_collapsed(Painter & pain) const
127 int InsetCollapsable::width_collapsed(Painter & /*pain*/) const
128 {
129         int width;
130         int ascent;
131         int descent;
132 #if 0
133         pain.buttonText(TEXT_TO_INSET_OFFSET, 0, draw_label, labelfont, false,
134                         width, ascent, descent);
135 #else
136         lyxfont::buttonText(draw_label, labelfont, width, ascent, descent);
137 #endif
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(bv->painter());
145 }
146
147
148 int InsetCollapsable::descent(BufferView * bv, LyXFont const & font) const
149 {
150         if (collapsed_) 
151                 return descent_collapsed(bv->painter());
152
153         return descent_collapsed(bv->painter())
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(bv->painter());
164
165         int widthCollapsed = width_collapsed(bv->painter());
166
167         return (inset.width(bv, font) > widthCollapsed) ?
168                 inset.width(bv, font) : widthCollapsed;
169 }
170
171
172 void InsetCollapsable::draw_collapsed(Painter & pain, int baseline, float & x) const
173 {
174 #if 0
175         int width = 0;
176         pain.buttonText(int(x) + TEXT_TO_INSET_OFFSET,
177                         baseline, draw_label, labelfont, true, width);
178 #else
179         pain.buttonText(int(x) + TEXT_TO_INSET_OFFSET,
180                         baseline, draw_label, labelfont);
181 #endif
182 #if 0
183         x += width + TEXT_TO_INSET_OFFSET;
184 #else
185         x += width_collapsed(pain) + TEXT_TO_INSET_OFFSET;
186 #endif
187 }
188
189
190 void InsetCollapsable::draw(BufferView * bv, LyXFont const & f, 
191                             int baseline, float & x, bool cleared) const
192 {
193         if (nodraw())
194                 return;
195
196         Painter & pain = bv->painter();
197
198         button_length = width_collapsed(pain);
199         button_top_y = -ascent(bv, f);
200         button_bottom_y = -ascent(bv, f) + ascent_collapsed(pain) +
201                 descent_collapsed(pain);
202
203         if (collapsed_) {
204                 draw_collapsed(pain, baseline, x);
205                 x += TEXT_TO_INSET_OFFSET;
206                 return;
207         }
208
209         float old_x = x;
210
211 #if 0
212         UpdatableInset::draw(bv, f, baseline, x, cleared);
213 #else
214         if (!owner())
215                 x += static_cast<float>(scroll());
216 #endif
217         if (!cleared && (inset.need_update == InsetText::FULL ||
218                          inset.need_update == InsetText::INIT ||
219                          top_x != int(x) ||
220                          top_baseline != baseline))
221         {
222 #if 1
223                 // we don't need anymore to clear here we just have to tell
224                 // the underlying LyXText that it should do the RowClear!
225                 inset.setUpdateStatus(bv, InsetText::FULL);
226                 bv->text->status(bv, LyXText::CHANGED_IN_DRAW);
227                 return;
228 #else
229                 int w =  owner() ? width(bv, f) : pain.paperWidth();
230                 int h = ascent(bv, f) + descent(bv, f);
231                 int const tx = (needFullRow() && !owner()) ? 0 : int(x);
232                 int const ty = max(0, baseline - ascent(bv, f));
233
234                 if ((ty + h) > pain.paperHeight())
235                         h = pain.paperHeight();
236                 if ((top_x + w) > pain.paperWidth())
237                         w = pain.paperWidth();
238                 if (baseline < 0)
239                         h += (baseline - ascent(bv, f));
240                 pain.fillRectangle(tx, ty - 1, w, h + 2);
241                 cleared = true;
242 #endif
243         }
244
245         top_x = int(x);
246         top_baseline = baseline;
247
248         int const bl = baseline - ascent(bv, f) + ascent_collapsed(pain);
249
250         draw_collapsed(pain, bl, old_x);
251         inset.draw(bv, f, 
252                    bl + descent_collapsed(pain) + inset.ascent(bv, f),
253                    x, cleared);
254         need_update = NONE;
255 }
256
257
258 void InsetCollapsable::edit(BufferView * bv, int xp, int yp,
259                             unsigned int button)
260 {
261         UpdatableInset::edit(bv, xp, yp, button);
262
263         if (collapsed_) {
264                 draw_label = label;
265                 collapsed_ = false;
266                 if (!bv->lockInset(this))
267                         return;
268                 bv->updateInset(this, false);
269                 inset.edit(bv, 0, 0, button);
270         } else {
271                 if (!bv->lockInset(this))
272                         return;
273                 LyXFont font(LyXFont::ALL_SANE);
274                 int yy = ascent(bv, font) + yp -
275                     (ascent_collapsed(bv->painter()) +
276                      descent_collapsed(bv->painter()) +
277                      inset.ascent(bv, font));
278                 inset.edit(bv, xp, yy, button);
279         }
280 }
281
282
283 void InsetCollapsable::edit(BufferView * bv, bool front)
284 {
285         UpdatableInset::edit(bv, front);
286
287         if (collapsed_) {
288                 draw_label = label;
289                 collapsed_ = false;
290                 if (!bv->lockInset(this))
291                         return;
292                 inset.setUpdateStatus(bv, InsetText::FULL);
293                 bv->updateInset(this, false);
294                 inset.edit(bv, front);
295         } else {
296                 if (!bv->lockInset(this))
297                         return;
298                 inset.edit(bv, front);
299         }
300 }
301
302
303 Inset::EDITABLE InsetCollapsable::editable() const
304 {
305         if (collapsed_)
306                 return IS_EDITABLE;
307         return HIGHLY_EDITABLE;
308 }
309
310
311 void InsetCollapsable::insetUnlock(BufferView * bv)
312 {
313         if (autocollapse) {
314                 if (change_label_with_text) {
315                         draw_label = get_new_label();
316                 } else {
317                         draw_label = label;
318                 }
319                 collapsed_ = true;
320         }
321         inset.insetUnlock(bv);
322         if (scroll())
323                 scroll(bv, 0.0F);
324         bv->updateInset(this, false);
325 }
326
327
328 void InsetCollapsable::insetButtonPress(BufferView * bv, int x, int y,
329                                         int button)
330 {
331         if (!collapsed_ && (y > button_bottom_y)) {
332                 LyXFont font(LyXFont::ALL_SANE);
333                 int yy = ascent(bv, font) + y -
334                     (ascent_collapsed(bv->painter()) +
335                      descent_collapsed(bv->painter()) +
336                      inset.ascent(bv, font));
337                 inset.insetButtonPress(bv, x, yy, button);
338         }
339 }
340
341
342 void InsetCollapsable::insetButtonRelease(BufferView * bv,
343                                           int x, int y, int button)
344 {
345         if ((x >= 0)  && (x < button_length) &&
346             (y >= button_top_y) &&  (y <= button_bottom_y)) {
347                 if (collapsed_) {
348                         draw_label = label;
349                         collapsed_ = false;
350                         inset.insetButtonRelease(bv, 0, 0, button);
351                         inset.setUpdateStatus(bv, InsetText::FULL);
352                         bv->updateInset(this, false);
353                 } else {
354                         if (change_label_with_text) {
355                                 draw_label = get_new_label();
356                         } else {
357                                 draw_label = label;
358                         }
359                         collapsed_ = true;
360                         bv->unlockInset(this);
361                         bv->updateInset(this, false);
362                 }
363         } else if (!collapsed_ && (y > button_top_y)) {
364                 LyXFont font(LyXFont::ALL_SANE);
365                 int yy = ascent(bv, font) + y -
366                     (ascent_collapsed(bv->painter()) +
367                      descent_collapsed(bv->painter()) +
368                      inset.ascent(bv, font));
369                 inset.insetButtonRelease(bv, x, yy, button);
370         }
371 }
372
373
374 void InsetCollapsable::insetMotionNotify(BufferView * bv,
375                                          int x, int y, int state)
376 {
377         if (x > button_bottom_y) {
378                 LyXFont font(LyXFont::ALL_SANE);
379                 int yy = ascent(bv, font) + y -
380                     (ascent_collapsed(bv->painter()) +
381                      descent_collapsed(bv->painter()) +
382                      inset.ascent(bv, font));
383                 inset.insetMotionNotify(bv, x, yy, state);
384         }
385 }
386
387
388 void InsetCollapsable::insetKeyPress(XKeyEvent * xke)
389 {
390         inset.insetKeyPress(xke);
391 }
392
393
394 int InsetCollapsable::latex(Buffer const * buf, ostream & os,
395                             bool fragile, bool free_spc) const
396 {
397         return inset.latex(buf, os, fragile, free_spc);
398 }
399
400
401 int InsetCollapsable::getMaxWidth(BufferView * bv,
402                                   UpdatableInset const * inset) const
403 {
404         int const w = UpdatableInset::getMaxWidth(bv, inset);
405
406         if (w < 0) {
407                 // What does a negative max width signify? (Lgb)
408                 // Use the max width of the draw-area (Jug)
409                 return w;
410         }
411         // should be at least 30 pixels !!!
412         return max(30, w - width_collapsed(bv->painter()));
413 }
414
415
416 void InsetCollapsable::update(BufferView * bv, LyXFont const & font,
417                               bool reinit)
418 {
419         inset.update(bv, font, reinit);
420 }
421
422
423 UpdatableInset::RESULT
424 InsetCollapsable::localDispatch(BufferView * bv, kb_action action,
425                                 string const & arg)
426 {
427         UpdatableInset::RESULT result = inset.localDispatch(bv, action, arg);
428         if (result == FINISHED)
429                 bv->unlockInset(this);
430         return result;
431 }
432
433
434 bool InsetCollapsable::lockInsetInInset(BufferView * bv, UpdatableInset * in)
435 {
436         if (&inset == in)
437                 return true;
438         return inset.lockInsetInInset(bv, in);
439 }
440
441
442 bool InsetCollapsable::unlockInsetInInset(BufferView * bv, UpdatableInset * in,
443                                           bool lr)
444 {
445         if (&inset == in) {
446                 bv->unlockInset(this);
447                 return true;
448         }
449         return inset.unlockInsetInInset(bv, in, lr);
450 }
451
452
453 bool InsetCollapsable::updateInsetInInset(BufferView * bv, Inset *in)
454 {
455         if (&inset == in)
456                 return true;
457         return inset.updateInsetInInset(bv, in);
458 }
459
460
461 unsigned int InsetCollapsable::insetInInsetY()
462 {
463         return inset.insetInInsetY() - (top_baseline - inset.y());
464 }
465
466
467 void InsetCollapsable::validate(LaTeXFeatures & features) const
468 {
469         inset.validate(features);
470 }
471
472
473 void InsetCollapsable::getCursorPos(BufferView * bv, int & x, int & y) const
474 {
475         inset.getCursorPos(bv, x , y);
476 }
477
478
479 void InsetCollapsable::toggleInsetCursor(BufferView * bv)
480 {
481         inset.toggleInsetCursor(bv);
482 }
483
484
485 void InsetCollapsable::showInsetCursor(BufferView * bv, bool show)
486 {
487         inset.showInsetCursor(bv, show);
488 }
489
490
491 void InsetCollapsable::hideInsetCursor(BufferView * bv)
492 {
493         inset.hideInsetCursor(bv);
494 }
495
496
497 UpdatableInset * InsetCollapsable::getLockingInset() const
498 {
499         UpdatableInset * in = inset.getLockingInset();
500         if (const_cast<InsetText *>(&inset) == in)
501                 return const_cast<InsetCollapsable *>(this);
502         return in;
503 }
504
505
506 UpdatableInset * InsetCollapsable::getFirstLockingInsetOfType(Inset::Code c)
507 {
508         if (c == lyxCode())
509                 return this;
510         return inset.getFirstLockingInsetOfType(c);
511 }
512
513
514 void InsetCollapsable::setFont(BufferView * bv, LyXFont const & font,
515                                bool toggleall, bool selectall)
516 {
517         inset.setFont(bv, font, toggleall, selectall);
518 }
519
520
521 bool InsetCollapsable::doClearArea() const
522 {
523         return inset.doClearArea();
524 }
525
526
527 LyXText * InsetCollapsable::getLyXText(BufferView const * bv,
528                                        bool const recursive) const
529 {
530         return inset.getLyXText(bv, recursive);
531 }
532
533
534 void InsetCollapsable::deleteLyXText(BufferView * bv, bool recursive) const
535 {
536         inset.deleteLyXText(bv, recursive);
537 }
538
539
540 void InsetCollapsable::resizeLyXText(BufferView * bv, bool force) const
541 {
542         inset.resizeLyXText(bv, force);
543         LyXFont font(LyXFont::ALL_SANE);
544         oldWidth = width(bv, font);
545 }
546
547
548 std::vector<string> const InsetCollapsable::getLabelList() const
549 {
550         return inset.getLabelList();
551 }
552
553
554 bool InsetCollapsable::nodraw() const
555 {
556         return inset.nodraw();
557 }
558
559  
560 int InsetCollapsable::scroll(bool recursive) const
561 {
562         int sx = UpdatableInset::scroll(false);
563
564         if (recursive)
565                 sx += inset.scroll(recursive);
566
567         return sx;
568 }
569
570
571 Paragraph * InsetCollapsable::getParFromID(int id) const
572 {
573         return inset.getParFromID(id);
574 }
575
576
577 Paragraph * InsetCollapsable::firstParagraph() const
578 {
579         return inset.firstParagraph();
580 }
581
582
583 LyXCursor const & InsetCollapsable::cursor(BufferView * bv) const
584 {
585         return inset.cursor(bv);
586 }
587
588
589 Inset * InsetCollapsable::getInsetFromID(int id_arg) const
590 {
591         if (id_arg == id())
592                 return const_cast<InsetCollapsable *>(this);
593         return inset.getInsetFromID(id_arg);
594 }
595
596 void InsetCollapsable::open(BufferView * bv, bool flag)
597 {
598         if (flag == !collapsed_)
599                 return;
600         collapsed_ = !flag;
601         if (collapsed_ && change_label_with_text) {
602                 draw_label = get_new_label();
603         } else {
604                 draw_label = label;
605         }
606         bv->updateInset(this, false);
607 }
608
609
610 void InsetCollapsable::setLabel(string const & l, bool flag)
611 {
612         label = l;
613         change_label_with_text = flag;
614         if (collapsed_ && change_label_with_text) {
615                 draw_label = get_new_label();
616         } else {
617                 draw_label = label;
618         }
619 }
620
621
622 string InsetCollapsable::get_new_label() const
623 {
624         string la;
625         Paragraph::size_type const max_length = 10;
626
627         int n = std::min(max_length, inset.paragraph()->size());
628         int i,j;
629         for(i=0,j=0; i < n && j < inset.paragraph()->size(); ++j) {
630                 if (inset.paragraph()->isInset(j))
631                         continue;
632                 la += inset.paragraph()->getChar(j);
633                 ++i;
634         }
635         if ((i > 0) && (j < inset.paragraph()->size()))
636                 la += "...";
637         if (la.empty())
638                 la = label;
639         return la;
640 }