]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
merge InsetText::setParagraphData and InsetText::init
[lyx.git] / src / insets / insettext.C
1 /**
2  * \file insettext.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS
9  */
10
11 #include <config.h>
12
13 #include "insettext.h"
14
15 #include "buffer.h"
16 #include "BufferView.h"
17 #include "bufferview_funcs.h"
18 #include "CutAndPaste.h"
19 #include "debug.h"
20 #include "dimension.h"
21 #include "funcrequest.h"
22 #include "gettext.h"
23 #include "errorlist.h"
24 #include "intl.h"
25 #include "LaTeXFeatures.h"
26 #include "LColor.h"
27 #include "Lsstream.h"
28 #include "lyxfont.h"
29 #include "lyxcursor.h"
30 #include "lyxfind.h"
31 #include "lyxlex.h"
32 #include "lyxrow.h"
33 #include "lyxrc.h"
34 #include "lyxtext.h"
35 #include "paragraph.h"
36 #include "ParagraphParameters.h"
37 #include "trans_mgr.h"
38 #include "undo_funcs.h"
39 #include "WordLangTuple.h"
40 #include "paragraph_funcs.h"
41 #include "sgml.h"
42 #include "rowpainter.h"
43 #include "insetnewline.h"
44 #include "metricsinfo.h"
45
46 #include "frontends/Alert.h"
47 #include "frontends/Dialogs.h"
48 #include "frontends/font_metrics.h"
49 #include "frontends/LyXView.h"
50 #include "frontends/Painter.h"
51 #include "frontends/screen.h"
52
53 #include "support/textutils.h"
54 #include "support/LAssert.h"
55 #include "support/lstrings.h"
56 #include "support/lyxalgo.h" // lyx::count
57
58 #include <boost/bind.hpp>
59
60 #include <fstream>
61 #include <algorithm>
62 #include <cstdlib>
63
64 using std::ostream;
65 using std::ifstream;
66 using std::endl;
67 using std::min;
68 using std::max;
69 using std::make_pair;
70 using std::vector;
71 using std::pair;
72 using std::for_each;
73 using std::auto_ptr;
74
75 using namespace lyx::support;
76 using namespace lyx::graphics;
77 using namespace bv_funcs;
78
79 using lyx::pos_type;
80 using lyx::textclass_type;
81
82
83 // we should get rid of these (Andre')
84
85 void InsetText::saveLyXTextState() const
86 {
87         // check if my paragraphs are still valid
88         ParagraphList::iterator it = const_cast<ParagraphList&>(paragraphs).begin();
89         ParagraphList::iterator end = const_cast<ParagraphList&>(paragraphs).end();
90         for (; it != end; ++it)
91                 if (it == text_.cursor.par())
92                         break;
93
94         if (it != end && text_.cursor.pos() <= it->size())
95                 sstate = text_; // slicing intended
96         else
97                 sstate.cursor.par(end);
98 }
99
100
101 void InsetText::restoreLyXTextState() const
102 {
103         if (sstate.cursor.par() == const_cast<ParagraphList&>(paragraphs).end())
104                 return;
105
106         text_.selection.set(true);
107         // at this point just to avoid the DEPM when setting the cursor
108         text_.selection.mark(sstate.selection.mark());
109         if (sstate.selection.set()) {
110                 text_.setCursor(sstate.selection.start.par(),
111                         sstate.selection.start.pos(),
112                         true, sstate.selection.start.boundary());
113                 text_.selection.cursor = text_.cursor;
114                 text_.setCursor(sstate.selection.end.par(), sstate.selection.end.pos(),
115                         true, sstate.selection.end.boundary());
116                 text_.setSelection();
117                 text_.setCursor(sstate.cursor.par(), sstate.cursor.pos());
118         } else {
119                 text_.setCursor(sstate.cursor.par(), sstate.cursor.pos(),
120                         true, sstate.cursor.boundary());
121                 text_.selection.cursor = text_.cursor;
122                 text_.selection.set(false);
123         }
124 }
125
126
127 InsetText::InsetText(BufferParams const & bp)
128         : UpdatableInset(), text_(0, this)
129 {
130         paragraphs.push_back(Paragraph());
131         paragraphs.begin()->layout(bp.getLyXTextClass().defaultLayout());
132         if (bp.tracking_changes)
133                 paragraphs.begin()->trackChanges();
134         init(0);
135 }
136
137
138 InsetText::InsetText(InsetText const & in)
139         : UpdatableInset(in), text_(0, this)
140 {
141         init(&in);
142 }
143
144
145 InsetText & InsetText::operator=(InsetText const & it)
146 {
147         init(&it);
148         return *this;
149 }
150
151
152 void InsetText::init(InsetText const * ins)
153 {
154         if (ins) {
155                 textwidth_ = ins->textwidth_;
156                 text_.bv_owner = ins->text_.bv_owner;
157
158                 paragraphs = ins->paragraphs;
159
160                 ParagraphList::iterator pit = paragraphs.begin();
161                 ParagraphList::iterator end = paragraphs.end();
162                 for (; pit != end; ++pit)
163                         pit->setInsetOwner(this);
164
165                 reinitLyXText();
166
167                 autoBreakRows = ins->autoBreakRows;
168                 drawFrame_ = ins->drawFrame_;
169                 frame_color = ins->frame_color;
170         } else {
171                 textwidth_ = 0; // broken
172                 drawFrame_ = NEVER;
173                 frame_color = LColor::insetframe;
174                 autoBreakRows = false;
175         }
176         the_locking_inset = 0;
177         for_each(paragraphs.begin(), paragraphs.end(),
178                  boost::bind(&Paragraph::setInsetOwner, _1, this));
179         top_y = 0;
180         no_selection = true;
181         drawTextXOffset = 0;
182         drawTextYOffset = 0;
183         locked = false;
184         old_par = paragraphs.end();
185         sstate.cursor.par(paragraphs.end());
186         in_insetAllowed = false;
187 }
188
189
190 void InsetText::clear(bool just_mark_erased)
191 {
192         if (just_mark_erased) {
193                 ParagraphList::iterator it = paragraphs.begin();
194                 ParagraphList::iterator end = paragraphs.end();
195                 for (; it != end; ++it) {
196                         it->markErased();
197                 }
198                 return;
199         }
200
201         // This is a gross hack...
202         LyXLayout_ptr old_layout = paragraphs.begin()->layout();
203
204         paragraphs.clear();
205         paragraphs.push_back(Paragraph());
206         paragraphs.begin()->setInsetOwner(this);
207         paragraphs.begin()->layout(old_layout);
208
209         reinitLyXText();
210 }
211
212
213 auto_ptr<InsetBase> InsetText::clone() const
214 {
215         return auto_ptr<InsetBase>(new InsetText(*this));
216 }
217
218
219 void InsetText::write(Buffer const * buf, ostream & os) const
220 {
221         os << "Text\n";
222         writeParagraphData(buf, os);
223 }
224
225
226 void InsetText::writeParagraphData(Buffer const * buf, ostream & os) const
227 {
228         ParagraphList::const_iterator it = paragraphs.begin();
229         ParagraphList::const_iterator end = paragraphs.end();
230         Paragraph::depth_type dth = 0;
231         for (; it != end; ++it) {
232                 it->write(buf, os, buf->params, dth);
233         }
234 }
235
236
237 void InsetText::read(Buffer const * buf, LyXLex & lex)
238 {
239         string token;
240         Paragraph::depth_type depth = 0;
241
242         clear(false);
243
244         if (buf->params.tracking_changes)
245                 paragraphs.begin()->trackChanges();
246
247         // delete the initial paragraph
248         paragraphs.clear();
249         ParagraphList::iterator pit = paragraphs.begin();
250
251         while (lex.isOK()) {
252                 lex.nextToken();
253                 token = lex.getString();
254                 if (token.empty())
255                         continue;
256                 if (token == "\\end_inset") {
257                         break;
258                 }
259
260                 if (token == "\\end_document") {
261                         lex.printError("\\end_document read in inset! Error in document!");
262                         return;
263                 }
264
265                 // FIXME: ugly.
266                 const_cast<Buffer*>(buf)->readParagraph(lex, token, paragraphs, pit, depth);
267         }
268
269         pit = paragraphs.begin();
270         ParagraphList::iterator const end = paragraphs.end();
271         for (; pit != end; ++pit)
272                 pit->setInsetOwner(this);
273
274         if (token != "\\end_inset") {
275                 lex.printError("Missing \\end_inset at this point. "
276                                            "Read: `$$Token'");
277         }
278 }
279
280
281 void InsetText::metrics(MetricsInfo & mi, Dimension & dim) const
282 {
283         //lyxerr << "InsetText::metrics: width: " << mi.base.textwidth << endl;
284
285         textwidth_ = mi.base.textwidth;
286         BufferView * bv = mi.base.bv;
287         setViewCache(bv);
288         text_.metrics(mi, dim);
289         dim.asc += TEXT_TO_INSET_OFFSET;
290         dim.des += TEXT_TO_INSET_OFFSET;
291         dim.wid += 2 * TEXT_TO_INSET_OFFSET;
292         dim.wid = max(dim.wid, 10);
293         dim_ = dim;
294 }
295
296
297 int InsetText::textWidth() const
298 {
299         return textwidth_;
300 }
301
302
303 void InsetText::draw(PainterInfo & pi, int x, int baseline) const
304 {
305         // update our idea of where we are. Clearly, we should
306         // not have to know this information.
307         top_x = x;
308
309         int const start_x = x;
310
311         BufferView * bv = pi.base.bv;
312         Painter & pain = pi.pain;
313
314         // repaint the background if needed
315         if (backgroundColor() != LColor::background)
316                 clearInset(bv, start_x + TEXT_TO_INSET_OFFSET, baseline);
317
318         // no draw is necessary !!!
319         if (drawFrame_ == LOCKED && !locked && paragraphs.begin()->empty()) {
320                 top_baseline = baseline;
321                 return;
322         }
323
324         bv->hideCursor();
325
326         if (!owner())
327                 x += scroll();
328
329         top_baseline = baseline;
330         top_y = baseline - dim_.asc;
331
332         if (the_locking_inset && cpar() == inset_par && cpos() == inset_pos) {
333                 inset_x = cix() - x + drawTextXOffset;
334                 inset_y = ciy() + drawTextYOffset;
335         }
336
337         x += TEXT_TO_INSET_OFFSET;
338
339         RowList::iterator rit = text_.rows().begin();
340         RowList::iterator end = text_.rows().end();
341
342         int y_offset = baseline - rit->ascent_of_text();
343         int y2 = pain.paperHeight();
344         int first = 0;
345         int y = y_offset;
346         while (rit != end && y + rit->height() <= 0) {
347                 y += rit->height();
348                 first += rit->height();
349                 ++rit;
350         }
351         if (y_offset < 0) {
352                 text_.top_y(-y_offset);
353                 first = y;
354                 y_offset = 0;
355         } else {
356                 text_.top_y(first);
357                 first = 0;
358         }
359
360         int yo = y_offset + first;
361
362         paintRows(*bv, text_, rit, x, 0, yo, y2, yo);
363
364         if (drawFrame_ == ALWAYS || (drawFrame_ == LOCKED && locked))
365                 drawFrame(pain, int(start_x));
366 }
367
368
369 void InsetText::drawFrame(Painter & pain, int x) const
370 {
371         int const ttoD2 = TEXT_TO_INSET_OFFSET / 2;
372         int const frame_x = x + ttoD2;
373         int const frame_y = top_baseline - dim_.asc + ttoD2;
374         int const frame_w = dim_.wid - TEXT_TO_INSET_OFFSET;
375         int const frame_h = dim_.asc + dim_.des - TEXT_TO_INSET_OFFSET;
376         pain.rectangle(frame_x, frame_y, frame_w, frame_h, frame_color);
377 }
378
379
380 void InsetText::updateLocal(BufferView * bv, bool /*mark_dirty*/)
381 {
382         if (!bv)
383                 return;
384
385         if (!autoBreakRows && paragraphs.size() > 1)
386                 collapseParagraphs(bv);
387
388         text_.partialRebreak();
389         if (!text_.selection.set())
390                 text_.selection.cursor = text_.cursor;
391
392         bv->fitCursor();
393         bv->updateInset();
394         bv->owner()->view_state_changed();
395         bv->owner()->updateMenubar();
396         bv->owner()->updateToolbar();
397         if (old_par != cpar()) {
398                 bv->owner()->setLayout(cpar()->layout()->name());
399                 old_par = cpar();
400         }
401 }
402
403
404 string const InsetText::editMessage() const
405 {
406         return _("Opened Text Inset");
407 }
408
409
410 void InsetText::insetUnlock(BufferView * bv)
411 {
412         if (the_locking_inset) {
413                 the_locking_inset->insetUnlock(bv);
414                 the_locking_inset = 0;
415                 updateLocal(bv, false);
416         }
417         no_selection = true;
418         locked = false;
419
420         if (text_.selection.set()) {
421                 text_.clearSelection();
422         } else if (owner()) {
423                 bv->owner()->setLayout(owner()->getLyXText(bv)
424                                        ->cursor.par()->layout()->name());
425         } else
426                 bv->owner()->setLayout(bv->text->cursor.par()->layout()->name());
427         // hack for deleteEmptyParMech
428         ParagraphList::iterator first_par = paragraphs.begin();
429         if (!first_par->empty()) {
430                 text_.setCursor(first_par, 0);
431         } else if (paragraphs.size() > 1) {
432                 text_.setCursor(boost::next(first_par), 0);
433         }
434 }
435
436
437 void InsetText::lockInset(BufferView * bv)
438 {
439         locked = true;
440         the_locking_inset = 0;
441         inset_pos = inset_x = inset_y = 0;
442         inset_boundary = false;
443         inset_par = paragraphs.end();
444         old_par = paragraphs.end();
445         text_.setCursorIntern(paragraphs.begin(), 0);
446         text_.clearSelection();
447         finishUndo();
448         // If the inset is empty set the language of the current font to the
449         // language to the surronding text (if different).
450         if (paragraphs.begin()->empty() && paragraphs.size() == 1 &&
451                 bv->getParentLanguage(this) != text_.current_font.language()) {
452                 LyXFont font(LyXFont::ALL_IGNORE);
453                 font.setLanguage(bv->getParentLanguage(this));
454                 setFont(bv, font, false);
455         }
456 }
457
458
459 void InsetText::lockInset(BufferView * /*bv*/, UpdatableInset * inset)
460 {
461         the_locking_inset = inset;
462         inset_x = cix() - top_x + drawTextXOffset;
463         inset_y = ciy() + drawTextYOffset;
464         inset_pos = cpos();
465         inset_par = cpar();
466         inset_boundary = cboundary();
467 }
468
469
470 bool InsetText::lockInsetInInset(BufferView * bv, UpdatableInset * inset)
471 {
472         lyxerr[Debug::INSETS] << "InsetText::LockInsetInInset("
473                               << inset << "): " << endl;
474         if (!inset)
475                 return false;
476         if (!the_locking_inset) {
477                 ParagraphList::iterator pit = paragraphs.begin();
478                 ParagraphList::iterator pend = paragraphs.end();
479
480                 int const id = inset->id();
481                 for (; pit != pend; ++pit) {
482                         InsetList::iterator it = pit->insetlist.begin();
483                         InsetList::iterator const end = pit->insetlist.end();
484                         for (; it != end; ++it) {
485                                 if (it->inset == inset) {
486                                         lyxerr << "InsetText::lockInsetInInset: 1 a" << endl;
487                                         text_.setCursorIntern(pit, it->pos);
488                                         lyxerr << "InsetText::lockInsetInInset: 1 b" << endl;
489                                         lyxerr << "bv: " << bv << " inset: " << inset << endl;
490                                         lockInset(bv, inset);
491                                         lyxerr << "InsetText::lockInsetInInset: 1 c" << endl;
492                                         return true;
493                                 }
494                                 if (it->inset->getInsetFromID(id)) {
495                                         lyxerr << "InsetText::lockInsetInInset: 2" << endl;
496                                         text_.setCursorIntern(pit, it->pos);
497                                         it->inset->localDispatch(FuncRequest(bv, LFUN_INSET_EDIT));
498                                         return the_locking_inset->lockInsetInInset(bv, inset);
499                                 }
500                         }
501                 }
502                 lyxerr << "InsetText::lockInsetInInset: 3" << endl;
503                 return false;
504         }
505         if (inset == cpar()->getInset(cpos())) {
506                 lyxerr[Debug::INSETS] << "OK" << endl;
507                 lockInset(bv, inset);
508                 return true;
509         }
510
511         if (the_locking_inset && the_locking_inset == inset) {
512                 if (cpar() == inset_par && cpos() == inset_pos) {
513                         lyxerr[Debug::INSETS] << "OK" << endl;
514                         inset_x = cix() - top_x + drawTextXOffset;
515                         inset_y = ciy() + drawTextYOffset;
516                 } else {
517                         lyxerr[Debug::INSETS] << "cursor.pos != inset_pos" << endl;
518                 }
519         } else if (the_locking_inset) {
520                 lyxerr[Debug::INSETS] << "MAYBE" << endl;
521                 return the_locking_inset->lockInsetInInset(bv, inset);
522         }
523         lyxerr[Debug::INSETS] << "NOT OK" << endl;
524         return false;
525 }
526
527
528 bool InsetText::unlockInsetInInset(BufferView * bv, UpdatableInset * inset,
529                                    bool lr)
530 {
531         if (!the_locking_inset)
532                 return false;
533         if (the_locking_inset == inset) {
534                 the_locking_inset->insetUnlock(bv);
535                 getLyXText(bv)->updateInset(inset);
536                 the_locking_inset = 0;
537                 if (lr)
538                         moveRightIntern(bv, true, false);
539                 old_par = paragraphs.end(); // force layout setting
540                 if (scroll())
541                         scroll(bv, 0.0F);
542                 else
543                         updateLocal(bv, false);
544                 return true;
545         }
546         return the_locking_inset->unlockInsetInInset(bv, inset, lr);
547 }
548
549
550 void InsetText::lfunMousePress(FuncRequest const & cmd)
551 {
552         no_selection = true;
553
554         // use this to check mouse motion for selection!
555         mouse_x = cmd.x;
556         mouse_y = cmd.y;
557
558         BufferView * bv = cmd.view();
559         FuncRequest cmd1 = cmd;
560         cmd1.x -= inset_x;
561         cmd1.y -= inset_y;
562         if (!locked)
563                 lockInset(bv);
564
565         int tmp_x = cmd.x - drawTextXOffset;
566         int tmp_y = cmd.y + dim_.asc - getLyXText(bv)->top_y();
567         InsetOld * inset = getLyXText(bv)->checkInsetHit(tmp_x, tmp_y);
568
569         if (the_locking_inset) {
570                 if (the_locking_inset == inset) {
571                         the_locking_inset->localDispatch(cmd1);
572                         return;
573                 }
574                 // otherwise only unlock the_locking_inset
575                 the_locking_inset->insetUnlock(bv);
576                 the_locking_inset = 0;
577         }
578         if (!inset)
579                 no_selection = false;
580
581         if (bv->theLockingInset()) {
582                 if (isHighlyEditableInset(inset)) {
583                         // We just have to lock the inset before calling a
584                         // PressEvent on it!
585                         UpdatableInset * uinset = static_cast<UpdatableInset*>(inset);
586                         if (!bv->lockInset(uinset)) {
587                                 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
588                         }
589                         inset->localDispatch(cmd1);
590                         if (the_locking_inset)
591                                 updateLocal(bv, false);
592                         return;
593                 }
594         }
595         if (!inset) {
596                 bool paste_internally = false;
597                 if (cmd.button() == mouse_button::button2 && getLyXText(bv)->selection.set()) {
598                         localDispatch(FuncRequest(bv, LFUN_COPY));
599                         paste_internally = true;
600                 }
601                 int old_top_y = text_.top_y();
602
603                 text_.setCursorFromCoordinates(cmd.x - drawTextXOffset,
604                                              cmd.y + dim_.asc);
605                 // set the selection cursor!
606                 text_.selection.cursor = text_.cursor;
607                 text_.cursor.x_fix(text_.cursor.x());
608
609                 text_.clearSelection();
610                 updateLocal(bv, false);
611
612                 bv->owner()->setLayout(cpar()->layout()->name());
613
614                 // we moved the view we cannot do mouse selection in this case!
615                 if (getLyXText(bv)->top_y() != old_top_y)
616                         no_selection = true;
617                 old_par = cpar();
618                 // Insert primary selection with middle mouse
619                 // if there is a local selection in the current buffer,
620                 // insert this
621                 if (cmd.button() == mouse_button::button2) {
622                         if (paste_internally)
623                                 localDispatch(FuncRequest(bv, LFUN_PASTE));
624                         else
625                                 localDispatch(FuncRequest(bv, LFUN_PASTESELECTION, "paragraph"));
626                 }
627         } else {
628                 getLyXText(bv)->clearSelection();
629         }
630 }
631
632
633 bool InsetText::lfunMouseRelease(FuncRequest const & cmd)
634 {
635         BufferView * bv = cmd.view();
636         FuncRequest cmd1 = cmd;
637         cmd1.x -= inset_x;
638         cmd1.y -= inset_y;
639
640         no_selection = true;
641         if (the_locking_inset)
642                 return the_locking_inset->localDispatch(cmd1);
643
644         int tmp_x = cmd.x - drawTextXOffset;
645         int tmp_y = cmd.y + dim_.asc - getLyXText(bv)->top_y();
646         InsetOld * inset = getLyXText(bv)->checkInsetHit(tmp_x, tmp_y);
647         bool ret = false;
648         if (inset) {
649 // This code should probably be removed now. Simple insets
650 // (!highlyEditable) can actually take the localDispatch,
651 // and turn it into edit() if necessary. But we still
652 // need to deal properly with the whole relative vs.
653 // absolute mouse co-ords thing in a realiable, sensible way
654 #if 0
655                 if (isHighlyEditableInset(inset))
656                         ret = inset->localDispatch(cmd1);
657                 else {
658                         inset_x = cix(bv) - top_x + drawTextXOffset;
659                         inset_y = ciy() + drawTextYOffset;
660                         cmd1.x = cmd.x - inset_x;
661                         cmd1.y = cmd.x - inset_y;
662                         inset->edit(bv, cmd1.x, cmd1.y, cmd.button());
663                         ret = true;
664                 }
665 #endif
666                 ret = inset->localDispatch(cmd1);
667                 updateLocal(bv, false);
668
669         }
670         return ret;
671 }
672
673
674 void InsetText::lfunMouseMotion(FuncRequest const & cmd)
675 {
676         FuncRequest cmd1 = cmd;
677         cmd1.x -= inset_x;
678         cmd1.y -= inset_y;
679
680         if (the_locking_inset) {
681                 the_locking_inset->localDispatch(cmd1);
682                 return;
683         }
684
685         if (no_selection || (mouse_x == cmd.x && mouse_y == cmd.y))
686                 return;
687
688         BufferView * bv = cmd.view();
689         LyXCursor cur = text_.cursor;
690         text_.setCursorFromCoordinates
691                 (cmd.x - drawTextXOffset, cmd.y + dim_.asc);
692         text_.cursor.x_fix(text_.cursor.x());
693         if (cur == text_.cursor)
694                 return;
695         text_.setSelection();
696         updateLocal(bv, false);
697 }
698
699
700 InsetOld::RESULT InsetText::localDispatch(FuncRequest const & cmd)
701 {
702         BufferView * bv = cmd.view();
703         setViewCache(bv);
704
705         switch (cmd.action) {
706         case LFUN_INSET_EDIT: {
707                 UpdatableInset::localDispatch(cmd);
708
709                 if (!bv->lockInset(this)) {
710                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
711                         return DISPATCHED;
712                 }
713
714                 locked = true;
715                 the_locking_inset = 0;
716                 inset_pos = 0;
717                 inset_x = 0;
718                 inset_y = 0;
719                 inset_boundary = false;
720                 inset_par = paragraphs.end();
721                 old_par = paragraphs.end();
722
723
724                 if (cmd.argument.size()) {
725                         if (cmd.argument == "left")
726                                 text_.setCursorIntern(paragraphs.begin(), 0);
727                         else {
728                                 ParagraphList::iterator it = boost::prior(paragraphs.end());
729                                 text_.setCursor(it, it->size());
730                         }
731                 } else {
732                         int tmp_y = (cmd.y < 0) ? 0 : cmd.y;
733                         // we put here -1 and not button as now the button in the
734                         // edit call should not be needed we will fix this in 1.3.x
735                         // cycle hopefully (Jug 20020509)
736                         // FIXME: GUII I've changed this to none: probably WRONG
737                         if (!checkAndActivateInset(bv, cmd.x, tmp_y, mouse_button::none)) {
738                                 text_.setCursorFromCoordinates(cmd.x - drawTextXOffset,
739                                                                         cmd.y + dim_.asc);
740                                 text_.cursor.x_fix(text_.cursor.x());
741                         }
742                 }
743
744                 text_.clearSelection();
745                 finishUndo();
746
747                 // If the inset is empty set the language of the current font to the
748                 // language to the surronding text (if different).
749                 if (paragraphs.begin()->empty() &&
750                     paragraphs.size() == 1 &&
751                     bv->getParentLanguage(this) != text_.current_font.language())
752                 {
753                         LyXFont font(LyXFont::ALL_IGNORE);
754                         font.setLanguage(bv->getParentLanguage(this));
755                         setFont(bv, font, false);
756                 }
757
758                 updateLocal(bv, false);
759                 // Tell the paragraph dialog that we've entered an insettext.
760                 bv->dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
761                 return DISPATCHED;
762         }
763
764         case LFUN_MOUSE_PRESS:
765                 lfunMousePress(cmd);
766                 return DISPATCHED;
767
768         case LFUN_MOUSE_MOTION:
769                 lfunMouseMotion(cmd);
770                 return DISPATCHED;
771
772         case LFUN_MOUSE_RELEASE:
773                 return lfunMouseRelease(cmd) ? DISPATCHED : UNDISPATCHED;
774
775         default:
776                 break;
777         }
778
779         bool was_empty = (paragraphs.begin()->empty() &&
780                           paragraphs.size() == 1);
781
782         no_selection = false;
783         RESULT result = UpdatableInset::localDispatch(cmd);
784         if (result != UNDISPATCHED)
785                 return DISPATCHED;
786
787         result = DISPATCHED;
788         if (cmd.action < 0 && cmd.argument.empty())
789                 return FINISHED;
790
791         if (the_locking_inset) {
792                 result = the_locking_inset->localDispatch(cmd);
793                 if (result == DISPATCHED_NOUPDATE)
794                         return result;
795                 if (result == DISPATCHED) {
796                         updateLocal(bv, false);
797                         return result;
798                 }
799                 if (result >= FINISHED) {
800                         switch (result) {
801                         case FINISHED_RIGHT:
802                                 moveRightIntern(bv, false, false);
803                                 result = DISPATCHED;
804                                 break;
805                         case FINISHED_UP:
806                                 result = moveUp(bv);
807                                 if (result >= FINISHED) {
808                                         updateLocal(bv, false);
809                                         bv->unlockInset(this);
810                                 }
811                                 break;
812                         case FINISHED_DOWN:
813                                 result = moveDown(bv);
814                                 if (result >= FINISHED) {
815                                         updateLocal(bv, false);
816                                         bv->unlockInset(this);
817                                 }
818                                 break;
819                         default:
820                                 result = DISPATCHED;
821                                 break;
822                         }
823                         the_locking_inset = 0;
824                         updateLocal(bv, false);
825                         // make sure status gets reset immediately
826                         bv->owner()->clearMessage();
827                         return result;
828                 }
829         }
830         bool updflag = false;
831
832         switch (cmd.action) {
833
834         // Normal chars
835         case LFUN_SELFINSERT:
836                 if (bv->buffer()->isReadonly()) {
837 //          setErrorMessage(N_("Document is read only"));
838                         break;
839                 }
840                 if (!cmd.argument.empty()) {
841                         /* Automatically delete the currently selected
842                          * text and replace it with what is being
843                          * typed in now. Depends on lyxrc settings
844                          * "auto_region_delete", which defaults to
845                          * true (on). */
846 #if 0
847                         // This should not be needed here and is also WRONG!
848                         recordUndo(bv, Undo::INSERT, text_.cursor.par());
849 #endif
850                         bv->switchKeyMap();
851                         if (lyxrc.auto_region_delete) {
852                                 if (text_.selection.set()) {
853                                         text_.cutSelection(false, false);
854                                 }
855                         }
856                         text_.clearSelection();
857                         for (string::size_type i = 0; i < cmd.argument.length(); ++i) {
858                                 bv->owner()->getIntl().getTransManager().
859                                         TranslateAndInsert(cmd.argument[i], &text_);
860                         }
861                 }
862                 text_.selection.cursor = text_.cursor;
863                 updflag = true;
864                 result = DISPATCHED_NOUPDATE;
865                 break;
866
867         // cursor movements that need special handling
868
869         case LFUN_RIGHT:
870                 result = moveRight(bv);
871                 finishUndo();
872                 break;
873         case LFUN_LEFT:
874                 finishUndo();
875                 result = moveLeft(bv);
876                 break;
877         case LFUN_DOWN:
878                 finishUndo();
879                 result = moveDown(bv);
880                 break;
881         case LFUN_UP:
882                 finishUndo();
883                 result = moveUp(bv);
884                 break;
885
886         case LFUN_PRIOR:
887                 if (crow() == text_.rows().begin())
888                         result = FINISHED_UP;
889                 else {
890                         text_.cursorPrevious();
891                         text_.clearSelection();
892                         result = DISPATCHED_NOUPDATE;
893                 }
894                 break;
895
896         case LFUN_NEXT:
897                 if (boost::next(crow()) == text_.rows().end())
898                         result = FINISHED_DOWN;
899                 else {
900                         text_.cursorNext();
901                         text_.clearSelection();
902                         result = DISPATCHED_NOUPDATE;
903                 }
904                 break;
905
906         case LFUN_BACKSPACE: {
907                 if (text_.selection.set())
908                         text_.cutSelection(true, false);
909                 else
910                         text_.backspace();
911                 updflag = true;
912                 break;
913         }
914
915         case LFUN_DELETE: {
916                 if (text_.selection.set()) {
917                         text_.cutSelection(true, false);
918                 } else {
919                         text_.Delete();
920                 }
921                 updflag = true;
922                 break;
923         }
924
925         case LFUN_CUT: {
926                 text_.cutSelection(true, true);
927                 updflag = true;
928                 break;
929         }
930
931         case LFUN_COPY:
932                 finishUndo();
933                 text_.copySelection();
934                 break;
935
936         case LFUN_PASTESELECTION:
937         {
938                 string const clip(bv->getClipboard());
939
940                 if (clip.empty())
941                         break;
942                 if (cmd.argument == "paragraph") {
943                         text_.insertStringAsParagraphs(clip);
944                 } else {
945                         text_.insertStringAsLines(clip);
946                 }
947                 // bug 393
948                 text_.clearSelection();
949
950                 updflag = true;
951                 break;
952         }
953
954         case LFUN_PASTE: {
955                 if (!autoBreakRows) {
956                         if (CutAndPaste::nrOfParagraphs() > 1) {
957 #ifdef WITH_WARNINGS
958 #warning FIXME horrendously bad UI
959 #endif
960                                 Alert::error(_("Paste failed"), _("Cannot include more than one paragraph."));
961                                 break;
962                         }
963                 }
964
965                 replaceSelection(bv->getLyXText());
966                 size_t sel_index = 0;
967                 string const & arg = cmd.argument;
968                 if (isStrUnsignedInt(arg)) {
969                         size_t const paste_arg = strToUnsignedInt(arg);
970 #warning FIXME Check if the arg is in the domain of available selections.
971                         sel_index = paste_arg;
972                 }
973                 text_.pasteSelection(sel_index);
974                 // bug 393
975                 text_.clearSelection();
976                 updflag = true;
977                 break;
978         }
979
980         case LFUN_BREAKPARAGRAPH:
981                 if (!autoBreakRows) {
982                         result = DISPATCHED;
983                         break;
984                 }
985                 replaceSelection(bv->getLyXText());
986                 text_.breakParagraph(paragraphs, 0);
987                 updflag = true;
988                 break;
989
990         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
991                 if (!autoBreakRows) {
992                         result = DISPATCHED;
993                         break;
994                 }
995                 replaceSelection(bv->getLyXText());
996                 text_.breakParagraph(paragraphs, 1);
997                 updflag = true;
998                 break;
999
1000         case LFUN_BREAKLINE: {
1001                 if (!autoBreakRows) {
1002                         result = DISPATCHED;
1003                         break;
1004                 }
1005
1006                 replaceSelection(bv->getLyXText());
1007                 text_.insertInset(new InsetNewline);
1008                 updflag = true;
1009                 break;
1010         }
1011
1012         case LFUN_LAYOUT:
1013                 // do not set layouts on non breakable textinsets
1014                 if (autoBreakRows) {
1015                         string cur_layout = cpar()->layout()->name();
1016
1017                         // Derive layout number from given argument (string)
1018                         // and current buffer's textclass (number). */
1019                         LyXTextClass const & tclass =
1020                                 bv->buffer()->params.getLyXTextClass();
1021                         string layout = cmd.argument;
1022                         bool hasLayout = tclass.hasLayout(layout);
1023
1024                         // If the entry is obsolete, use the new one instead.
1025                         if (hasLayout) {
1026                                 string const & obs =
1027                                         tclass[layout]->obsoleted_by();
1028                                 if (!obs.empty())
1029                                         layout = obs;
1030                         }
1031
1032                         // see if we found the layout number:
1033                         if (!hasLayout) {
1034                                 FuncRequest lf(LFUN_MESSAGE, N_("Layout ") + cmd.argument + N_(" not known"));
1035                                 bv->owner()->dispatch(lf);
1036                                 break;
1037                         }
1038
1039                         if (cur_layout != layout) {
1040                                 cur_layout = layout;
1041                                 text_.setLayout(layout);
1042                                 bv->owner()->setLayout(cpar()->layout()->name());
1043                                 updflag = true;
1044                         }
1045                 } else {
1046                         // reset the layout box
1047                         bv->owner()->setLayout(cpar()->layout()->name());
1048                 }
1049                 break;
1050         case LFUN_PARAGRAPH_SPACING:
1051                 // This one is absolutely not working. When fiddling with this
1052                 // it also seems to me that the paragraphs inside the insettext
1053                 // inherit bufferparams/paragraphparams in a strange way. (Lgb)
1054                 // FIXME: how old is this comment ? ...
1055         {
1056                 ParagraphList::iterator pit = text_.cursor.par();
1057                 Spacing::Space cur_spacing = pit->params().spacing().getSpace();
1058                 float cur_value = 1.0;
1059                 if (cur_spacing == Spacing::Other) {
1060                         cur_value = pit->params().spacing().getValue();
1061                 }
1062
1063                 istringstream istr(STRCONV(cmd.argument));
1064                 string tmp;
1065                 istr >> tmp;
1066                 Spacing::Space new_spacing = cur_spacing;
1067                 float new_value = cur_value;
1068                 if (tmp.empty()) {
1069                         lyxerr << "Missing argument to `paragraph-spacing'"
1070                                    << endl;
1071                 } else if (tmp == "single") {
1072                         new_spacing = Spacing::Single;
1073                 } else if (tmp == "onehalf") {
1074                         new_spacing = Spacing::Onehalf;
1075                 } else if (tmp == "double") {
1076                         new_spacing = Spacing::Double;
1077                 } else if (tmp == "other") {
1078                         new_spacing = Spacing::Other;
1079                         float tmpval = 0.0;
1080                         istr >> tmpval;
1081                         lyxerr << "new_value = " << tmpval << endl;
1082                         if (tmpval != 0.0)
1083                                 new_value = tmpval;
1084                 } else if (tmp == "default") {
1085                         new_spacing = Spacing::Default;
1086                 } else {
1087                         lyxerr << _("Unknown spacing argument: ")
1088                                    << cmd.argument << endl;
1089                 }
1090                 if (cur_spacing != new_spacing || cur_value != new_value) {
1091                         pit->params().spacing(Spacing(new_spacing, new_value));
1092                         updflag = true;
1093                 }
1094         }
1095         break;
1096
1097         default:
1098                 if (!bv->dispatch(cmd))
1099                         result = UNDISPATCHED;
1100                 break;
1101         }
1102
1103         updateLocal(bv, updflag);
1104
1105         /// If the action has deleted all text in the inset, we need to change the
1106         // language to the language of the surronding text.
1107         if (!was_empty && paragraphs.begin()->empty() &&
1108             paragraphs.size() == 1) {
1109                 LyXFont font(LyXFont::ALL_IGNORE);
1110                 font.setLanguage(bv->getParentLanguage(this));
1111                 setFont(bv, font, false);
1112         }
1113
1114         if (result >= FINISHED)
1115                 bv->unlockInset(this);
1116
1117         if (result == DISPATCHED_NOUPDATE)
1118                 result = DISPATCHED;
1119         return result;
1120 }
1121
1122
1123 int InsetText::latex(Buffer const * buf, ostream & os,
1124                      LatexRunParams const & runparams) const
1125 {
1126         TexRow texrow;
1127         latexParagraphs(buf, paragraphs, os, texrow, runparams);
1128         return texrow.rows();
1129 }
1130
1131
1132 int InsetText::ascii(Buffer const * buf, ostream & os, int linelen) const
1133 {
1134         unsigned int lines = 0;
1135
1136         ParagraphList::const_iterator beg = paragraphs.begin();
1137         ParagraphList::const_iterator end = paragraphs.end();
1138         ParagraphList::const_iterator it = beg;
1139         for (; it != end; ++it) {
1140                 string const tmp = buf->asciiParagraph(*it, linelen, it == beg);
1141                 lines += lyx::count(tmp.begin(), tmp.end(), '\n');
1142                 os << tmp;
1143         }
1144         return lines;
1145 }
1146
1147
1148 int InsetText::linuxdoc(Buffer const * buf, ostream & os) const
1149 {
1150         ParagraphList::iterator pit = const_cast<ParagraphList&>(paragraphs).begin();
1151         ParagraphList::iterator pend = const_cast<ParagraphList&>(paragraphs).end();
1152
1153         // There is a confusion between the empty paragraph and the default paragraph
1154         // The default paragraph is <p></p>, the empty paragraph is *empty*
1155         // Since none of the floats of linuxdoc accepts standard paragraphs
1156         // I disable them. I don't expect problems. (jamatos 2003/07/27)
1157         for (; pit != pend; ++pit) {
1158                 const string name = pit->layout()->latexname();
1159                 if (name != "p")
1160                         sgml::openTag(os, 1, 0, name);
1161                 buf->simpleLinuxDocOnePar(os, pit, 0);
1162                 if (name != "p")
1163                         sgml::closeTag(os, 1, 0, name);
1164         }
1165         return 0;
1166 }
1167
1168
1169 int InsetText::docbook(Buffer const * buf, ostream & os, bool mixcont) const
1170 {
1171         unsigned int lines = 0;
1172
1173         vector<string> environment_stack(10);
1174         vector<string> environment_inner(10);
1175
1176         int const command_depth = 0;
1177         string item_name;
1178
1179         Paragraph::depth_type depth = 0; // paragraph depth
1180
1181         ParagraphList::iterator pit = const_cast<ParagraphList&>(paragraphs).begin();
1182         ParagraphList::iterator pend = const_cast<ParagraphList&>(paragraphs).end();
1183
1184         for (; pit != pend; ++pit) {
1185                 string sgmlparam;
1186                 int desc_on = 0; // description mode
1187
1188                 LyXLayout_ptr const & style = pit->layout();
1189
1190                 // environment tag closing
1191                 for (; depth > pit->params().depth(); --depth) {
1192                         if (environment_inner[depth] != "!-- --") {
1193                                 item_name = "listitem";
1194                                 lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1195                                 if (environment_inner[depth] == "varlistentry")
1196                                         lines += sgml::closeTag(os, depth+command_depth, mixcont, environment_inner[depth]);
1197                         }
1198                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1199                         environment_stack[depth].erase();
1200                         environment_inner[depth].erase();
1201                 }
1202
1203                 if (depth == pit->params().depth()
1204                    && environment_stack[depth] != style->latexname()
1205                    && !environment_stack[depth].empty()) {
1206                         if (environment_inner[depth] != "!-- --") {
1207                                 item_name= "listitem";
1208                                 lines += sgml::closeTag(os, command_depth+depth, mixcont, item_name);
1209                                 if (environment_inner[depth] == "varlistentry")
1210                                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1211                         }
1212
1213                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1214
1215                         environment_stack[depth].erase();
1216                         environment_inner[depth].erase();
1217                 }
1218
1219                 // Write opening SGML tags.
1220                 switch (style->latextype) {
1221                 case LATEX_PARAGRAPH:
1222                         lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexname());
1223                         break;
1224
1225                 case LATEX_COMMAND:
1226                         buf->error(ErrorItem(_("Error"), _("LatexType Command not allowed here.\n"), pit->id(), 0, pit->size()));
1227                         return -1;
1228                         break;
1229
1230                 case LATEX_ENVIRONMENT:
1231                 case LATEX_ITEM_ENVIRONMENT:
1232                         if (depth < pit->params().depth()) {
1233                                 depth = pit->params().depth();
1234                                 environment_stack[depth].erase();
1235                         }
1236
1237                         if (environment_stack[depth] != style->latexname()) {
1238                                 if (environment_stack.size() == depth + 1) {
1239                                         environment_stack.push_back("!-- --");
1240                                         environment_inner.push_back("!-- --");
1241                                 }
1242                                 environment_stack[depth] = style->latexname();
1243                                 environment_inner[depth] = "!-- --";
1244                                 lines += sgml::openTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1245                         } else {
1246                                 if (environment_inner[depth] != "!-- --") {
1247                                         item_name= "listitem";
1248                                         lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1249                                         if (environment_inner[depth] == "varlistentry")
1250                                                 lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1251                                 }
1252                         }
1253
1254                         if (style->latextype == LATEX_ENVIRONMENT) {
1255                                 if (!style->latexparam().empty()) {
1256                                         if (style->latexparam() == "CDATA")
1257                                                 os << "<![CDATA[";
1258                                         else
1259                                           lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexparam());
1260                                 }
1261                                 break;
1262                         }
1263
1264                         desc_on = (style->labeltype == LABEL_MANUAL);
1265
1266                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
1267                         lines += sgml::openTag(os, depth + 1 + command_depth, mixcont, environment_inner[depth]);
1268
1269                         item_name = desc_on ? "term" : "para";
1270                         lines += sgml::openTag(os, depth + 1 + command_depth, mixcont, item_name);
1271
1272                         break;
1273                 default:
1274                         lines += sgml::openTag(os, depth + command_depth, mixcont, style->latexname());
1275                         break;
1276                 }
1277
1278                 buf->simpleDocBookOnePar(os, pit, desc_on, depth + 1 + command_depth);
1279
1280                 string end_tag;
1281                 // write closing SGML tags
1282                 switch (style->latextype) {
1283                 case LATEX_ENVIRONMENT:
1284                         if (!style->latexparam().empty()) {
1285                                 if (style->latexparam() == "CDATA")
1286                                         os << "]]>";
1287                                 else
1288                                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexparam());
1289                         }
1290                         break;
1291                 case LATEX_ITEM_ENVIRONMENT:
1292                         if (desc_on == 1) break;
1293                         end_tag= "para";
1294                         lines += sgml::closeTag(os, depth + 1 + command_depth, mixcont, end_tag);
1295                         break;
1296                 case LATEX_PARAGRAPH:
1297                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexname());
1298                         break;
1299                 default:
1300                         lines += sgml::closeTag(os, depth + command_depth, mixcont, style->latexname());
1301                         break;
1302                 }
1303         }
1304
1305         // Close open tags
1306         for (int d = depth; d >= 0; --d) {
1307                 if (!environment_stack[depth].empty()) {
1308                         if (environment_inner[depth] != "!-- --") {
1309                                 item_name = "listitem";
1310                                 lines += sgml::closeTag(os, command_depth + depth, mixcont, item_name);
1311                                if (environment_inner[depth] == "varlistentry")
1312                                        lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_inner[depth]);
1313                         }
1314
1315                         lines += sgml::closeTag(os, depth + command_depth, mixcont, environment_stack[depth]);
1316                 }
1317         }
1318
1319         return lines;
1320 }
1321
1322
1323 void InsetText::validate(LaTeXFeatures & features) const
1324 {
1325         for_each(paragraphs.begin(), paragraphs.end(),
1326                  boost::bind(&Paragraph::validate, _1, boost::ref(features)));
1327 }
1328
1329
1330 void InsetText::getCursor(BufferView & bv, int & x, int & y) const
1331 {
1332         if (the_locking_inset) {
1333                 the_locking_inset->getCursor(bv, x, y);
1334                 return;
1335         }
1336         x = cx();
1337         y = cy() + InsetText::y();
1338 }
1339
1340
1341 void InsetText::getCursorPos(BufferView * bv, int & x, int & y) const
1342 {
1343         if (the_locking_inset) {
1344                 the_locking_inset->getCursorPos(bv, x, y);
1345                 return;
1346         }
1347         x = cx() - top_x - TEXT_TO_INSET_OFFSET;
1348         y = cy() - TEXT_TO_INSET_OFFSET;
1349 }
1350
1351
1352 int InsetText::insetInInsetY() const
1353 {
1354         if (!the_locking_inset)
1355                 return 0;
1356
1357         return inset_y + the_locking_inset->insetInInsetY();
1358 }
1359
1360
1361 void InsetText::fitInsetCursor(BufferView * bv) const
1362 {
1363         if (the_locking_inset) {
1364                 the_locking_inset->fitInsetCursor(bv);
1365                 return;
1366         }
1367
1368         LyXFont const font = text_.getFont(cpar(), cpos());
1369
1370         int const asc = font_metrics::maxAscent(font);
1371         int const desc = font_metrics::maxDescent(font);
1372
1373         bv->fitLockedInsetCursor(cx(), cy(), asc, desc);
1374 }
1375
1376
1377 InsetOld::RESULT InsetText::moveRight(BufferView * bv)
1378 {
1379         if (text_.cursor.par()->isRightToLeftPar(bv->buffer()->params))
1380                 return moveLeftIntern(bv, false, true, false);
1381         else
1382                 return moveRightIntern(bv, true, true, false);
1383 }
1384
1385
1386 InsetOld::RESULT InsetText::moveLeft(BufferView * bv)
1387 {
1388         if (text_.cursor.par()->isRightToLeftPar(bv->buffer()->params))
1389                 return moveRightIntern(bv, true, true, false);
1390         else
1391                 return moveLeftIntern(bv, false, true, false);
1392 }
1393
1394
1395 InsetOld::RESULT
1396 InsetText::moveRightIntern(BufferView * bv, bool front,
1397                            bool activate_inset, bool selecting)
1398 {
1399         ParagraphList::iterator c_par = cpar();
1400
1401         if (boost::next(c_par) == paragraphs.end() && cpos() >= c_par->size())
1402                 return FINISHED_RIGHT;
1403         if (activate_inset && checkAndActivateInset(bv, front))
1404                 return DISPATCHED;
1405         text_.cursorRight(bv);
1406         if (!selecting)
1407                 text_.clearSelection();
1408         return DISPATCHED_NOUPDATE;
1409 }
1410
1411
1412 InsetOld::RESULT
1413 InsetText::moveLeftIntern(BufferView * bv, bool front,
1414                           bool activate_inset, bool selecting)
1415 {
1416         if (cpar() == paragraphs.begin() && cpos() <= 0)
1417                 return FINISHED;
1418         text_.cursorLeft(bv);
1419         if (!selecting)
1420                 text_.clearSelection();
1421         if (activate_inset && checkAndActivateInset(bv, front))
1422                 return DISPATCHED;
1423         return DISPATCHED_NOUPDATE;
1424 }
1425
1426
1427 InsetOld::RESULT InsetText::moveUp(BufferView * bv)
1428 {
1429         if (crow() == text_.rows().begin())
1430                 return FINISHED_UP;
1431         text_.cursorUp(bv);
1432         text_.clearSelection();
1433         return DISPATCHED_NOUPDATE;
1434 }
1435
1436
1437 InsetOld::RESULT InsetText::moveDown(BufferView * bv)
1438 {
1439         if (boost::next(crow()) == text_.rows().end())
1440                 return FINISHED_DOWN;
1441         text_.cursorDown(bv);
1442         text_.clearSelection();
1443         return DISPATCHED_NOUPDATE;
1444 }
1445
1446
1447 bool InsetText::insertInset(BufferView * bv, InsetOld * inset)
1448 {
1449         if (the_locking_inset) {
1450                 if (the_locking_inset->insetAllowed(inset))
1451                         return the_locking_inset->insertInset(bv, inset);
1452                 return false;
1453         }
1454         inset->setOwner(this);
1455         text_.insertInset(inset);
1456         bv->fitCursor();
1457         updateLocal(bv, true);
1458         return true;
1459 }
1460
1461
1462 bool InsetText::insetAllowed(InsetOld::Code code) const
1463 {
1464         // in_insetAllowed is a really gross hack,
1465         // to allow us to call the owner's insetAllowed
1466         // without stack overflow, which can happen
1467         // when the owner uses InsetCollapsable::insetAllowed()
1468         bool ret = true;
1469         if (in_insetAllowed)
1470                 return ret;
1471         in_insetAllowed = true;
1472         if (the_locking_inset)
1473                 ret = the_locking_inset->insetAllowed(code);
1474         else if (owner())
1475                 ret = owner()->insetAllowed(code);
1476         in_insetAllowed = false;
1477         return ret;
1478 }
1479
1480
1481 UpdatableInset * InsetText::getLockingInset() const
1482 {
1483         return the_locking_inset ? the_locking_inset->getLockingInset() :
1484                 const_cast<InsetText *>(this);
1485 }
1486
1487
1488 UpdatableInset * InsetText::getFirstLockingInsetOfType(InsetOld::Code c)
1489 {
1490         if (c == lyxCode())
1491                 return this;
1492         if (the_locking_inset)
1493                 return the_locking_inset->getFirstLockingInsetOfType(c);
1494         return 0;
1495 }
1496
1497
1498 bool InsetText::showInsetDialog(BufferView * bv) const
1499 {
1500         if (the_locking_inset)
1501                 return the_locking_inset->showInsetDialog(bv);
1502         return false;
1503 }
1504
1505
1506 void InsetText::getLabelList(std::vector<string> & list) const
1507 {
1508         ParagraphList::const_iterator pit = paragraphs.begin();
1509         ParagraphList::const_iterator pend = paragraphs.end();
1510         for (; pit != pend; ++pit) {
1511                 InsetList::const_iterator beg = pit->insetlist.begin();
1512                 InsetList::const_iterator end = pit->insetlist.end();
1513                 for (; beg != end; ++beg)
1514                         beg->inset->getLabelList(list);
1515         }
1516 }
1517
1518
1519 void InsetText::setFont(BufferView * bv, LyXFont const & font, bool toggleall,
1520                         bool selectall)
1521 {
1522         if (the_locking_inset) {
1523                 the_locking_inset->setFont(bv, font, toggleall, selectall);
1524                 return;
1525         }
1526
1527         if ((paragraphs.size() == 1 && paragraphs.begin()->empty())
1528             || cpar()->empty()) {
1529                 text_.setFont(font, toggleall);
1530                 return;
1531         }
1532
1533
1534         if (text_.selection.set())
1535                 recordUndo(bv, Undo::ATOMIC, text_.cursor.par());
1536
1537         if (selectall) {
1538                 text_.cursorTop();
1539                 text_.selection.cursor = text_.cursor;
1540                 text_.cursorBottom();
1541                 text_.setSelection();
1542         }
1543
1544         text_.toggleFree(font, toggleall);
1545
1546         if (selectall)
1547                 text_.clearSelection();
1548
1549         bv->fitCursor();
1550         updateLocal(bv, true);
1551 }
1552
1553
1554 bool InsetText::checkAndActivateInset(BufferView * bv, bool front)
1555 {
1556         InsetOld * inset = cpar()->getInset(cpos());
1557         if (!isHighlyEditableInset(inset))
1558                 return false;
1559         FuncRequest cmd(bv, LFUN_INSET_EDIT, front ? "left" : "right");
1560         inset->localDispatch(cmd);
1561         if (!the_locking_inset)
1562                 return false;
1563         updateLocal(bv, false);
1564         return true;
1565 }
1566
1567
1568 bool InsetText::checkAndActivateInset(BufferView * bv, int x, int y,
1569                                       mouse_button::state button)
1570 {
1571         x -= drawTextXOffset;
1572         int dummyx = x;
1573         int dummyy = y + dim_.asc;
1574         InsetOld * inset = getLyXText(bv)->checkInsetHit(dummyx, dummyy);
1575         // we only do the edit() call if the inset was hit by the mouse
1576         // or if it is a highly editable inset. So we should call this
1577         // function from our own edit with button < 0.
1578         // FIXME: GUII jbl. I've changed this to ::none for now which is probably
1579         // WRONG
1580         if (button == mouse_button::none && !isHighlyEditableInset(inset))
1581                 return false;
1582
1583         if (!inset)
1584                 return false;
1585         if (x < 0)
1586                 x = dim_.wid;
1587         if (y < 0)
1588                 y = dim_.des;
1589         inset_x = cix() - top_x + drawTextXOffset;
1590         inset_y = ciy() + drawTextYOffset;
1591         FuncRequest cmd(bv, LFUN_INSET_EDIT, x - inset_x, y - inset_y, button);
1592         inset->localDispatch(cmd);
1593         if (!the_locking_inset)
1594                 return false;
1595         updateLocal(bv, false);
1596         return true;
1597 }
1598
1599
1600 void InsetText::markNew(bool track_changes)
1601 {
1602         ParagraphList::iterator pit = paragraphs.begin();
1603         ParagraphList::iterator end = paragraphs.end();
1604         for (; pit != end; ++pit) {
1605                 if (track_changes) {
1606                         pit->trackChanges();
1607                 } else {
1608                         // no-op when not tracking
1609                         pit->cleanChanges();
1610                 }
1611         }
1612 }
1613
1614
1615 void InsetText::setText(string const & data, LyXFont const & font)
1616 {
1617         clear(false);
1618         for (unsigned int i = 0; i < data.length(); ++i)
1619                 paragraphs.begin()->insertChar(i, data[i], font);
1620         reinitLyXText();
1621 }
1622
1623
1624 void InsetText::setAutoBreakRows(bool flag)
1625 {
1626         if (flag != autoBreakRows) {
1627                 autoBreakRows = flag;
1628                 if (!flag)
1629                         removeNewlines();
1630         }
1631 }
1632
1633
1634 void InsetText::setDrawFrame(BufferView * bv, DrawFrame how)
1635 {
1636         if (how != drawFrame_) {
1637                 drawFrame_ = how;
1638                 updateLocal(bv, false);
1639         }
1640 }
1641
1642
1643 void InsetText::setFrameColor(BufferView * bv, LColor::color col)
1644 {
1645         if (frame_color != col) {
1646                 frame_color = col;
1647                 updateLocal(bv, false);
1648         }
1649 }
1650
1651
1652 int InsetText::cx() const
1653 {
1654         int x = text_.cursor.x() + top_x + TEXT_TO_INSET_OFFSET;
1655         if (the_locking_inset) {
1656                 LyXFont font = text_.getFont(text_.cursor.par(), text_.cursor.pos());
1657                 if (font.isVisibleRightToLeft())
1658                         x -= the_locking_inset->width();
1659         }
1660         return x;
1661 }
1662
1663
1664 int InsetText::cix() const
1665 {
1666         int x = text_.cursor.ix() + top_x + TEXT_TO_INSET_OFFSET;
1667         if (the_locking_inset) {
1668                 LyXFont font = text_.getFont(text_.cursor.par(), text_.cursor.pos());
1669                 if (font.isVisibleRightToLeft())
1670                         x -= the_locking_inset->width();
1671         }
1672         return x;
1673 }
1674
1675
1676 int InsetText::cy() const
1677 {
1678         return text_.cursor.y() - dim_.asc + TEXT_TO_INSET_OFFSET;
1679 }
1680
1681
1682 int InsetText::ciy() const
1683 {
1684         return text_.cursor.iy() - dim_.asc + TEXT_TO_INSET_OFFSET;
1685 }
1686
1687
1688 pos_type InsetText::cpos() const
1689 {
1690         return text_.cursor.pos();
1691 }
1692
1693
1694 ParagraphList::iterator InsetText::cpar() const
1695 {
1696         return text_.cursor.par();
1697 }
1698
1699
1700 bool InsetText::cboundary() const
1701 {
1702         return text_.cursor.boundary();
1703 }
1704
1705
1706 RowList::iterator InsetText::crow() const
1707 {
1708         return text_.cursorRow();
1709 }
1710
1711
1712 LyXText * InsetText::getLyXText(BufferView const * bv,
1713                                 bool const recursive) const
1714 {
1715         setViewCache(bv);
1716         if (recursive && the_locking_inset)
1717                 return the_locking_inset->getLyXText(bv, true);
1718         return &text_;
1719 }
1720
1721
1722 void InsetText::setViewCache(BufferView const * bv) const
1723 {
1724         if (bv)
1725                 text_.bv_owner = const_cast<BufferView *>(bv);
1726 }
1727
1728
1729 void InsetText::deleteLyXText(BufferView * bv, bool recursive) const
1730 {
1731         if (recursive) {
1732                 /// then remove all LyXText in text-insets
1733                 for_each(const_cast<ParagraphList&>(paragraphs).begin(),
1734                          const_cast<ParagraphList&>(paragraphs).end(),
1735                          boost::bind(&Paragraph::deleteInsetsLyXText, _1, bv));
1736         }
1737 }
1738
1739
1740 void InsetText::resizeLyXText(BufferView * bv, bool /*force*/) const
1741 {
1742         if (paragraphs.size() == 1 && paragraphs.begin()->empty())
1743                 return;
1744
1745         if (!bv)
1746                 return;
1747
1748         Assert(bv);
1749         setViewCache(bv);
1750
1751         for_each(const_cast<ParagraphList&>(paragraphs).begin(),
1752                  const_cast<ParagraphList&>(paragraphs).end(),
1753                  boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
1754
1755         saveLyXTextState();
1756         text_.init(bv);
1757         restoreLyXTextState();
1758
1759         // seems to be unneeded
1760 #if 1
1761         if (the_locking_inset) {
1762                 inset_x = cix() - top_x + drawTextXOffset;
1763                 inset_y = ciy() + drawTextYOffset;
1764         }
1765 #endif
1766
1767 #if 1
1768         text_.top_y(bv->screen().topCursorVisible(&text_));
1769         if (!owner()) {
1770                 const_cast<InsetText*>(this)->updateLocal(bv, false);
1771                 // this will scroll the screen such that the cursor becomes visible
1772                 bv->updateScrollbar();
1773         }
1774 #endif
1775 }
1776
1777
1778 void InsetText::reinitLyXText() const
1779 {
1780         BufferView * bv = text_.bv_owner;
1781
1782         if (!bv)
1783                 return;
1784
1785         saveLyXTextState();
1786
1787         for_each(const_cast<ParagraphList&>(paragraphs).begin(),
1788                  const_cast<ParagraphList&>(paragraphs).end(),
1789                  boost::bind(&Paragraph::resizeInsetsLyXText, _1, bv));
1790
1791         text_.init(bv);
1792         restoreLyXTextState();
1793         if (the_locking_inset) {
1794                 inset_x = cix() - top_x + drawTextXOffset;
1795                 inset_y = ciy() + drawTextYOffset;
1796         }
1797         text_.top_y(bv->screen().topCursorVisible(&text_));
1798         if (!owner()) {
1799                 // this will scroll the screen such that the cursor becomes visible
1800                 bv->updateScrollbar();
1801         }
1802 }
1803
1804
1805 void InsetText::removeNewlines()
1806 {
1807         bool changed = false;
1808
1809         ParagraphList::iterator it = paragraphs.begin();
1810         ParagraphList::iterator end = paragraphs.end();
1811         for (; it != end; ++it) {
1812                 for (int i = 0; i < it->size(); ++i) {
1813                         if (it->isNewline(i)) {
1814                                 changed = true;
1815                                 it->erase(i);
1816                         }
1817                 }
1818         }
1819         if (changed)
1820                 reinitLyXText();
1821 }
1822
1823
1824 int InsetText::scroll(bool recursive) const
1825 {
1826         int sx = UpdatableInset::scroll(false);
1827
1828         if (recursive && the_locking_inset)
1829                 sx += the_locking_inset->scroll(recursive);
1830
1831         return sx;
1832 }
1833
1834
1835 void InsetText::clearSelection(BufferView * bv)
1836 {
1837         getLyXText(bv)->clearSelection();
1838 }
1839
1840
1841 void InsetText::clearInset(BufferView * bv, int start_x, int baseline) const
1842 {
1843         Painter & pain = bv->painter();
1844         int w = dim_.wid;
1845         int h = dim_.asc + dim_.des;
1846         int ty = baseline - dim_.asc;
1847
1848         if (ty < 0) {
1849                 h += ty;
1850                 ty = 0;
1851         }
1852         if (ty + h > pain.paperHeight())
1853                 h = pain.paperHeight();
1854         if (top_x + drawTextXOffset + w > pain.paperWidth())
1855                 w = pain.paperWidth();
1856         pain.fillRectangle(start_x + 1, ty + 1, w - 3, h - 1, backgroundColor());
1857 }
1858
1859
1860 ParagraphList * InsetText::getParagraphs(int i) const
1861 {
1862         return (i == 0) ? const_cast<ParagraphList*>(&paragraphs) : 0;
1863 }
1864
1865
1866 LyXCursor const & InsetText::cursor(BufferView * bv) const
1867 {
1868         if (the_locking_inset)
1869                 return the_locking_inset->cursor(bv);
1870         return getLyXText(bv)->cursor;
1871 }
1872
1873
1874 InsetOld * InsetText::getInsetFromID(int id_arg) const
1875 {
1876         if (id_arg == id())
1877                 return const_cast<InsetText *>(this);
1878
1879         ParagraphList::const_iterator pit = paragraphs.begin();
1880         ParagraphList::const_iterator pend = paragraphs.end();
1881         for (; pit != pend; ++pit) {
1882                 InsetList::const_iterator it = pit->insetlist.begin();
1883                 InsetList::const_iterator end = pit->insetlist.end();
1884                 for (; it != end; ++it) {
1885                         if (it->inset->id() == id_arg)
1886                                 return it->inset;
1887                         InsetOld * in = it->inset->getInsetFromID(id_arg);
1888                         if (in)
1889                                 return in;
1890                 }
1891         }
1892         return 0;
1893 }
1894
1895
1896 WordLangTuple const
1897 InsetText::selectNextWordToSpellcheck(BufferView * bv, float & value) const
1898 {
1899         WordLangTuple word;
1900         if (the_locking_inset) {
1901                 word = the_locking_inset->selectNextWordToSpellcheck(bv, value);
1902                 if (!word.word().empty()) {
1903                         value += cy();
1904                         return word;
1905                 }
1906                 // we have to go on checking so move cursor to the next char
1907                 text_.cursor.pos(text_.cursor.pos() + 1);
1908         }
1909         word = text_.selectNextWordToSpellcheck(value);
1910         if (word.word().empty())
1911                 bv->unlockInset(const_cast<InsetText *>(this));
1912         else
1913                 value = cy();
1914         return word;
1915 }
1916
1917
1918 void InsetText::selectSelectedWord(BufferView * bv)
1919 {
1920         if (the_locking_inset) {
1921                 the_locking_inset->selectSelectedWord(bv);
1922                 return;
1923         }
1924         getLyXText(bv)->selectSelectedWord();
1925         updateLocal(bv, false);
1926 }
1927
1928
1929 bool InsetText::nextChange(BufferView * bv, lyx::pos_type & length)
1930 {
1931         if (the_locking_inset) {
1932                 if (the_locking_inset->nextChange(bv, length))
1933                         return true;
1934                 text_.cursorRight(true);
1935         }
1936         lyx::find::SearchResult result =
1937                 lyx::find::findNextChange(bv, &text_, length);
1938
1939         if (result == lyx::find::SR_FOUND) {
1940                 LyXCursor cur = text_.cursor;
1941                 bv->unlockInset(bv->theLockingInset());
1942                 if (bv->lockInset(this))
1943                         locked = true;
1944                 text_.cursor = cur;
1945                 text_.setSelectionRange(length);
1946                 updateLocal(bv, false);
1947         }
1948         return result != lyx::find::SR_NOT_FOUND;
1949 }
1950
1951
1952 bool InsetText::searchForward(BufferView * bv, string const & str,
1953                               bool cs, bool mw)
1954 {
1955         if (the_locking_inset) {
1956                 if (the_locking_inset->searchForward(bv, str, cs, mw))
1957                         return true;
1958                 text_.cursorRight(true);
1959         }
1960         lyx::find::SearchResult result =
1961                 lyx::find::find(bv, &text_, str, true, cs, mw);
1962
1963         if (result == lyx::find::SR_FOUND) {
1964                 LyXCursor cur = text_.cursor;
1965                 bv->unlockInset(bv->theLockingInset());
1966                 if (bv->lockInset(this))
1967                         locked = true;
1968                 text_.cursor = cur;
1969                 text_.setSelectionRange(str.length());
1970                 updateLocal(bv, false);
1971         }
1972         return result != lyx::find::SR_NOT_FOUND;
1973 }
1974
1975
1976 bool InsetText::searchBackward(BufferView * bv, string const & str,
1977                                bool cs, bool mw)
1978 {
1979         if (the_locking_inset) {
1980                 if (the_locking_inset->searchBackward(bv, str, cs, mw))
1981                         return true;
1982         }
1983         if (!locked) {
1984                 ParagraphList::iterator pit = paragraphs.begin();
1985                 ParagraphList::iterator pend = paragraphs.end();
1986
1987                 while (boost::next(pit) != pend)
1988                         ++pit;
1989
1990                 text_.setCursor(pit, pit->size());
1991         }
1992         lyx::find::SearchResult result =
1993                 lyx::find::find(bv, &text_, str, false, cs, mw);
1994
1995         if (result == lyx::find::SR_FOUND) {
1996                 LyXCursor cur = text_.cursor;
1997                 bv->unlockInset(bv->theLockingInset());
1998                 if (bv->lockInset(this))
1999                         locked = true;
2000                 text_.cursor = cur;
2001                 text_.setSelectionRange(str.length());
2002                 updateLocal(bv, false);
2003         }
2004         return result != lyx::find::SR_NOT_FOUND;
2005 }
2006
2007
2008 bool InsetText::checkInsertChar(LyXFont & font)
2009 {
2010         return owner() ? owner()->checkInsertChar(font) : true;
2011 }
2012
2013
2014 void InsetText::collapseParagraphs(BufferView * bv)
2015 {
2016         while (paragraphs.size() > 1) {
2017                 ParagraphList::iterator first_par = paragraphs.begin();
2018                 ParagraphList::iterator next_par = boost::next(first_par);
2019                 size_t const first_par_size = first_par->size();
2020
2021                 if (!first_par->empty() &&
2022                     !next_par->empty() &&
2023                     !first_par->isSeparator(first_par_size - 1)) {
2024                         first_par->insertChar(first_par_size, ' ');
2025                 }
2026
2027                 if (text_.selection.set()) {
2028                         if (text_.selection.start.par() == next_par) {
2029                                 text_.selection.start.par(first_par);
2030                                 text_.selection.start.pos(
2031                                         text_.selection.start.pos() + first_par_size);
2032                         }
2033                         if (text_.selection.end.par() == next_par) {
2034                                 text_.selection.end.par(first_par);
2035                                 text_.selection.end.pos(
2036                                         text_.selection.end.pos() + first_par_size);
2037                         }
2038                 }
2039
2040                 mergeParagraph(bv->buffer()->params, paragraphs, first_par);
2041         }
2042         reinitLyXText();
2043 }
2044
2045
2046 void InsetText::getDrawFont(LyXFont & font) const
2047 {
2048         if (!owner())
2049                 return;
2050         owner()->getDrawFont(font);
2051 }
2052
2053
2054 void InsetText::appendParagraphs(Buffer * buffer, ParagraphList & plist)
2055 {
2056 #warning FIXME Check if Changes stuff needs changing here. (Lgb)
2057 // And it probably does. You have to take a look at this John. (Lgb)
2058 #warning John, have a look here. (Lgb)
2059         ParagraphList::iterator pit = plist.begin();
2060         ParagraphList::iterator ins = paragraphs.insert(paragraphs.end(), *pit);
2061         ++pit;
2062         mergeParagraph(buffer->params, paragraphs, boost::prior(ins));
2063
2064         ParagraphList::iterator pend = plist.end();
2065         for (; pit != pend; ++pit)
2066                 paragraphs.push_back(*pit);
2067
2068         reinitLyXText();
2069 }
2070
2071
2072 void InsetText::addPreview(PreviewLoader & loader) const
2073 {
2074         ParagraphList::const_iterator pit = paragraphs.begin();
2075         ParagraphList::const_iterator pend = paragraphs.end();
2076
2077         for (; pit != pend; ++pit) {
2078                 InsetList::const_iterator it  = pit->insetlist.begin();
2079                 InsetList::const_iterator end = pit->insetlist.end();
2080                 for (; it != end; ++it)
2081                         it->inset->addPreview(loader);
2082         }
2083 }