1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2002 The LyX Team.
9 * ====================================================== */
15 #include "paragraph.h"
16 #include "BufferView.h"
17 #include "funcrequest.h"
20 #include "bufferparams.h"
22 #include "ParagraphParameters.h"
25 #include "support/lstrings.h"
26 #include "frontends/LyXView.h"
27 #include "frontends/WorkArea.h"
28 #include "insets/insetspecialchar.h"
29 #include "insets/insettext.h"
32 extern string current_layout;
35 void LyXText::update(BufferView * bv, bool changed)
37 BufferView::UpdateCodes c = BufferView::SELECT | BufferView::FITCUR;
39 bv->update(this, c | BufferView::CHANGE);
45 void specialChar(LyXText * lt, BufferView * bv, InsetSpecialChar::Kind kind)
49 InsetSpecialChar * new_inset = new InsetSpecialChar(kind);
50 if (!bv->insertInset(new_inset))
53 bv->updateInset(new_inset, true);
57 Inset::RESULT LyXText::dispatch(FuncRequest const & cmd)
59 BufferView * bv = cmd.view();
64 Paragraph * par = cursor.par();
65 bool start = !par->params().startOfAppendix();
67 // ensure that we have only one start_of_appendix in this document
68 Paragraph * tmp = ownerParagraph();
69 for (; tmp; tmp = tmp->next())
70 tmp->params().startOfAppendix(false);
72 par->params().startOfAppendix(start);
74 // we can set the refreshing parameters now
75 status(cmd.view(), LyXText::NEED_MORE_REFRESH);
77 refresh_row = 0; // not needed for full update
78 updateCounters(cmd.view());
79 setCursor(cmd.view(), cursor.par(), cursor.pos());
84 case LFUN_DELETE_WORD_FORWARD:
85 bv->beforeChange(this);
87 deleteWordForward(bv);
92 case LFUN_DELETE_WORD_BACKWARD:
93 bv->beforeChange(this);
95 deleteWordBackward(bv);
100 case LFUN_DELETE_LINE_FORWARD:
101 bv->beforeChange(this);
103 deleteLineForward(bv);
110 if (!selection.mark())
111 bv->beforeChange(this);
118 if (!selection.mark())
119 bv->beforeChange(this);
121 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
122 cursorLeftOneWord(bv);
124 cursorRightOneWord(bv);
129 if (!selection.mark())
130 bv->beforeChange(this);
132 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
133 cursorRightOneWord(bv);
135 cursorLeftOneWord(bv);
139 case LFUN_BEGINNINGBUF:
140 if (!selection.mark())
141 bv->beforeChange(this);
148 if (selection.mark())
149 bv->beforeChange(this);
157 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
161 bv->finishChange(true);
166 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
170 bv->finishChange(true);
176 bv->finishChange(true);
181 cursorDown(bv, true);
182 bv->finishChange(true);
185 case LFUN_UP_PARAGRAPHSEL:
187 cursorUpParagraph(bv);
188 bv->finishChange(true);
191 case LFUN_DOWN_PARAGRAPHSEL:
193 cursorDownParagraph(bv);
194 bv->finishChange(true);
199 bv->cursorPrevious(this);
200 bv->finishChange(true);
205 bv->cursorNext(this);
212 bv->finishChange(true);
218 bv->finishChange(true);
221 case LFUN_WORDRIGHTSEL:
223 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
224 cursorLeftOneWord(bv);
226 cursorRightOneWord(bv);
227 bv->finishChange(true);
230 case LFUN_WORDLEFTSEL:
232 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
233 cursorRightOneWord(bv);
235 cursorLeftOneWord(bv);
236 bv->finishChange(true);
240 bool is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
241 if (!selection.mark())
242 bv->beforeChange(this);
245 cursorLeft(bv, false);
246 if (cursor.pos() < cursor.par()->size()
247 && cursor.par()->isInset(cursor.pos())
248 && isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
249 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
250 cmd.message(tmpinset->editMessage());
251 tmpinset->edit(bv, !is_rtl);
255 cursorRight(bv, false);
256 bv->finishChange(false);
261 // This is soooo ugly. Isn`t it possible to make
263 bool const is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
264 if (!selection.mark())
265 bv->beforeChange(this);
267 LyXCursor const cur = cursor;
269 cursorLeft(bv, false);
270 if ((is_rtl || cur != cursor) && // only if really moved!
271 cursor.pos() < cursor.par()->size() &&
272 cursor.par()->isInset(cursor.pos()) &&
273 isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
274 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
275 cmd.message(tmpinset->editMessage());
276 tmpinset->edit(bv, is_rtl);
280 cursorRight(bv, false);
281 bv->finishChange(false);
286 if (!selection.mark())
287 bv->beforeChange(this);
288 bv->update(this, BufferView::UPDATE);
290 bv->finishChange(false);
294 if (!selection.mark())
295 bv->beforeChange(this);
296 bv->update(this, BufferView::UPDATE);
301 case LFUN_UP_PARAGRAPH:
302 if (!selection.mark())
303 bv->beforeChange(this);
304 bv->update(this, BufferView::UPDATE);
305 cursorUpParagraph(bv);
309 case LFUN_DOWN_PARAGRAPH:
310 if (!selection.mark())
311 bv->beforeChange(this);
312 bv->update(this, BufferView::UPDATE);
313 cursorDownParagraph(bv);
314 bv->finishChange(false);
318 if (!selection.mark())
319 bv->beforeChange(this);
320 bv->update(this, BufferView::UPDATE);
321 bv->cursorPrevious(this);
322 bv->finishChange(false);
325 // moveCursorUpdate(false, false);
326 // owner_->view_state_changed();
330 if (!selection.mark())
331 bv->beforeChange(this);
332 bv->update(this, BufferView::UPDATE);
333 bv->cursorNext(this);
334 bv->finishChange(false);
338 if (!selection.mark())
339 bv->beforeChange(this);
342 bv->finishChange(false);
346 if (!selection.mark())
347 bv->beforeChange(this);
350 bv->finishChange(false);
354 bv->beforeChange(this);
355 insertChar(bv, Paragraph::META_NEWLINE);
357 setCursor(bv, cursor.par(), cursor.pos());
358 bv->moveCursorUpdate(false);
362 if (!selection.set()) {
364 selection.cursor = cursor;
366 // It is possible to make it a lot faster still
367 // just comment out the line below...
372 bv->moveCursorUpdate(false);
373 bv->owner()->view_state_changed();
377 case LFUN_DELETE_SKIP:
378 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
379 if (!selection.set()) {
380 LyXCursor cur = cursor;
381 if (cur.pos() == cur.par()->size()) {
385 && !(cur.par()->params().spaceTop()
386 == VSpace (VSpace::NONE))) {
388 cur.par()->params().lineTop(),
389 cur.par()->params().lineBottom(),
390 cur.par()->params().pagebreakTop(),
391 cur.par()->params().pagebreakBottom(),
392 VSpace(VSpace::NONE),
393 cur.par()->params().spaceBottom(),
394 cur.par()->params().spacing(),
395 cur.par()->params().align(),
396 cur.par()->params().labelWidthString(), 0);
402 selection.cursor = cursor;
407 selection.cursor = cursor;
416 if (!selection.set()) {
417 if (bv->owner()->getIntl().getTransManager().backspace()) {
419 selection.cursor = cursor;
421 // It is possible to make it a lot faster still
422 // just comment out the line below...
427 bv->owner()->view_state_changed();
431 case LFUN_BACKSPACE_SKIP:
432 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
433 if (!selection.set()) {
434 LyXCursor cur = cursor;
436 && !(cur.par()->params().spaceTop()
437 == VSpace (VSpace::NONE))) {
439 cur.par()->params().lineTop(),
440 cur.par()->params().lineBottom(),
441 cur.par()->params().pagebreakTop(),
442 cur.par()->params().pagebreakBottom(),
443 VSpace(VSpace::NONE), cur.par()->params().spaceBottom(),
444 cur.par()->params().spacing(),
445 cur.par()->params().align(),
446 cur.par()->params().labelWidthString(), 0);
450 selection.cursor = cur;
457 case LFUN_BREAKPARAGRAPH:
458 bv->beforeChange(this);
459 breakParagraph(bv, 0);
461 selection.cursor = cursor;
463 bv->owner()->view_state_changed();
466 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
467 bv->beforeChange(this);
468 breakParagraph(bv, 1);
470 selection.cursor = cursor;
472 bv->owner()->view_state_changed();
475 case LFUN_BREAKPARAGRAPH_SKIP: {
476 // When at the beginning of a paragraph, remove
477 // indentation and add a "defskip" at the top.
478 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
479 LyXCursor cur = cursor;
480 bv->beforeChange(this);
481 if (cur.pos() == 0) {
482 if (cur.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
484 cur.par()->params().lineTop(),
485 cur.par()->params().lineBottom(),
486 cur.par()->params().pagebreakTop(),
487 cur.par()->params().pagebreakBottom(),
488 VSpace(VSpace::DEFSKIP), cur.par()->params().spaceBottom(),
489 cur.par()->params().spacing(),
490 cur.par()->params().align(),
491 cur.par()->params().labelWidthString(), 1);
496 breakParagraph(bv, 0);
500 selection.cursor = cur;
502 bv->owner()->view_state_changed();
506 case LFUN_PARAGRAPH_SPACING: {
507 Paragraph * par = cursor.par();
508 Spacing::Space cur_spacing = par->params().spacing().getSpace();
509 float cur_value = 1.0;
510 if (cur_spacing == Spacing::Other)
511 cur_value = par->params().spacing().getValue();
513 istringstream is(cmd.argument.c_str());
516 Spacing::Space new_spacing = cur_spacing;
517 float new_value = cur_value;
519 lyxerr << "Missing argument to `paragraph-spacing'"
521 } else if (tmp == "single") {
522 new_spacing = Spacing::Single;
523 } else if (tmp == "onehalf") {
524 new_spacing = Spacing::Onehalf;
525 } else if (tmp == "double") {
526 new_spacing = Spacing::Double;
527 } else if (tmp == "other") {
528 new_spacing = Spacing::Other;
531 lyxerr << "new_value = " << tmpval << endl;
534 } else if (tmp == "default") {
535 new_spacing = Spacing::Default;
537 lyxerr << _("Unknown spacing argument: ")
538 << cmd.argument << endl;
540 if (cur_spacing != new_spacing || cur_value != new_value) {
541 par->params().spacing(Spacing(new_spacing, new_value));
548 case LFUN_INSET_TOGGLE:
550 bv->beforeChange(this);
557 case LFUN_PROTECTEDSPACE:
558 if (cursor.par()->layout()->free_spacing) {
562 specialChar(this, bv, InsetSpecialChar::PROTECTED_SEPARATOR);
564 bv->moveCursorUpdate(false);
567 case LFUN_HYPHENATION:
568 specialChar(this, bv, InsetSpecialChar::HYPHENATION);
571 case LFUN_LIGATURE_BREAK:
572 specialChar(this, bv, InsetSpecialChar::LIGATURE_BREAK);
576 specialChar(this, bv, InsetSpecialChar::LDOTS);
582 insertChar(bv, Paragraph::META_HFILL);
586 case LFUN_END_OF_SENTENCE:
587 specialChar(this, bv, InsetSpecialChar::END_OF_SENTENCE);
590 case LFUN_MENU_SEPARATOR:
591 specialChar(this, bv, InsetSpecialChar::MENU_SEPARATOR);
595 bv->beforeChange(this);
597 selection.cursor = cursor;
598 cmd.message(N_("Mark off"));
602 bv->beforeChange(this);
603 selection.mark(true);
605 selection.cursor = cursor;
606 cmd.message(N_("Mark on"));
610 bv->beforeChange(this);
611 if (selection.mark()) {
613 cmd.message(N_("Mark removed"));
615 selection.mark(true);
617 cmd.message(N_("Mark set"));
619 selection.cursor = cursor;
622 case LFUN_UPCASE_WORD:
624 changeCase(bv, LyXText::text_uppercase);
626 bv->updateInset(inset_owner, true);
630 case LFUN_LOWCASE_WORD:
632 changeCase(bv, LyXText::text_lowercase);
634 bv->updateInset(inset_owner, true);
638 case LFUN_CAPITALIZE_WORD:
640 changeCase(bv, LyXText::text_capitalization);
642 bv->updateInset(inset_owner, true);
646 case LFUN_TRANSPOSE_CHARS:
650 bv->updateInset(inset_owner, true);
654 case LFUN_BEGINNINGBUFSEL:
656 return Inset::UNDISPATCHED;
659 bv->finishChange(true);
664 return Inset::UNDISPATCHED;
667 bv->finishChange(true);
671 cmd.message(tostr(cursor.x()) + ' ' + tostr(cursor.y()));
677 if (::sscanf(cmd.argument.c_str(), " %d %d", &x, &y) != 2)
678 lyxerr << "SETXY: Could not parse coordinates in '"
679 << cmd.argument << std::endl;
680 setCursorFromCoordinates(bv, x, y);
685 if (current_font.shape() == LyXFont::ITALIC_SHAPE)
687 else if (current_font.shape() == LyXFont::SMALLCAPS_SHAPE)
694 cmd.message(tostr(cursor.par()->layout()));
698 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
699 << cmd.argument << endl;
701 // This is not the good solution to the empty argument
702 // problem, but it will hopefully suffice for 1.2.0.
703 // The correct solution would be to augument the
704 // function list/array with information about what
705 // functions needs arguments and their type.
706 if (cmd.argument.empty()) {
707 cmd.errorMessage(_("LyX function 'layout' needs an argument."));
711 // Derive layout number from given argument (string)
712 // and current buffer's textclass (number)
713 LyXTextClass const & tclass = bv->buffer()->params.getLyXTextClass();
714 bool hasLayout = tclass.hasLayout(cmd.argument);
715 string layout = cmd.argument;
717 // If the entry is obsolete, use the new one instead.
719 string const & obs = tclass[layout]->obsoleted_by();
725 cmd.errorMessage(string(N_("Layout ")) + cmd.argument +
730 bool change_layout = (current_layout != layout);
731 if (!change_layout && selection.set() &&
732 selection.start.par() != selection.end.par())
734 Paragraph * spar = selection.start.par();
735 Paragraph * epar = selection.end.par()->next();
736 while (spar != epar) {
737 if (spar->layout()->name() != current_layout) {
738 change_layout = true;
745 current_layout = layout;
747 setLayout(bv, layout);
748 bv->owner()->setLayout(layout);
755 case LFUN_PASTESELECTION: {
759 // this was originally a beforeChange(bv->text), i.e
760 // the outermost LyXText!
761 bv->beforeChange(this);
762 string const clip(bv->workarea().getClipboard());
764 if (cmd.argument == "paragraph")
765 insertStringAsParagraphs(bv, clip);
767 insertStringAsLines(bv, clip);
774 case LFUN_SELFINSERT: {
775 if (cmd.argument.empty())
778 // Automatically delete the currently selected
779 // text and replace it with what is being
780 // typed in now. Depends on lyxrc settings
781 // "auto_region_delete", which defaults to
784 if (lyxrc.auto_region_delete) {
785 if (selection.set()) {
786 cutSelection(bv, false, false);
789 bv->workarea().haveSelection(false);
792 bv->beforeChange(this);
793 LyXFont const old_font(real_current_font);
795 string::const_iterator cit = cmd.argument.begin();
796 string::const_iterator end = cmd.argument.end();
797 for (; cit != end; ++cit)
798 bv->owner()->getIntl().getTransManager().
799 TranslateAndInsert(*cit, this);
802 selection.cursor = cursor;
803 bv->moveCursorUpdate(false);
805 // real_current_font.number can change so we need to
806 // update the minibuffer
807 if (old_font != real_current_font)
808 bv->owner()->view_state_changed();
813 return Inset::UNDISPATCHED;
816 return Inset::DISPATCHED;