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"
33 extern string current_layout;
36 void LyXText::update(BufferView * bv, bool changed)
38 BufferView::UpdateCodes c = BufferView::SELECT | BufferView::FITCUR;
40 bv->update(this, c | BufferView::CHANGE);
46 void specialChar(LyXText * lt, BufferView * bv, InsetSpecialChar::Kind kind)
50 InsetSpecialChar * new_inset = new InsetSpecialChar(kind);
51 if (!bv->insertInset(new_inset))
54 bv->updateInset(new_inset, true);
58 Inset::RESULT LyXText::dispatch(FuncRequest const & cmd)
60 BufferView * bv = cmd.view();
65 Paragraph * par = cursor.par();
66 bool start = !par->params().startOfAppendix();
68 // ensure that we have only one start_of_appendix in this document
69 Paragraph * tmp = ownerParagraph();
70 for (; tmp; tmp = tmp->next())
71 tmp->params().startOfAppendix(false);
73 par->params().startOfAppendix(start);
75 // we can set the refreshing parameters now
76 status(cmd.view(), LyXText::NEED_MORE_REFRESH);
78 refresh_row = 0; // not needed for full update
79 updateCounters(cmd.view());
80 setCursor(cmd.view(), cursor.par(), cursor.pos());
85 case LFUN_DELETE_WORD_FORWARD:
86 bv->beforeChange(this);
88 deleteWordForward(bv);
93 case LFUN_DELETE_WORD_BACKWARD:
94 bv->beforeChange(this);
96 deleteWordBackward(bv);
101 case LFUN_DELETE_LINE_FORWARD:
102 bv->beforeChange(this);
104 deleteLineForward(bv);
111 if (!selection.mark())
112 bv->beforeChange(this);
119 if (!selection.mark())
120 bv->beforeChange(this);
122 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
123 cursorLeftOneWord(bv);
125 cursorRightOneWord(bv);
130 if (!selection.mark())
131 bv->beforeChange(this);
133 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
134 cursorRightOneWord(bv);
136 cursorLeftOneWord(bv);
140 case LFUN_BEGINNINGBUF:
141 if (!selection.mark())
142 bv->beforeChange(this);
149 if (selection.mark())
150 bv->beforeChange(this);
158 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
162 bv->finishChange(true);
167 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
171 bv->finishChange(true);
177 bv->finishChange(true);
182 cursorDown(bv, true);
183 bv->finishChange(true);
186 case LFUN_UP_PARAGRAPHSEL:
188 cursorUpParagraph(bv);
189 bv->finishChange(true);
192 case LFUN_DOWN_PARAGRAPHSEL:
194 cursorDownParagraph(bv);
195 bv->finishChange(true);
200 bv->cursorPrevious(this);
201 bv->finishChange(true);
206 bv->cursorNext(this);
213 bv->finishChange(true);
219 bv->finishChange(true);
222 case LFUN_WORDRIGHTSEL:
224 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
225 cursorLeftOneWord(bv);
227 cursorRightOneWord(bv);
228 bv->finishChange(true);
231 case LFUN_WORDLEFTSEL:
233 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
234 cursorRightOneWord(bv);
236 cursorLeftOneWord(bv);
237 bv->finishChange(true);
241 bool is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
242 if (!selection.mark())
243 bv->beforeChange(this);
246 cursorLeft(bv, false);
247 if (cursor.pos() < cursor.par()->size()
248 && cursor.par()->isInset(cursor.pos())
249 && isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
250 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
251 cmd.message(tmpinset->editMessage());
252 tmpinset->edit(bv, !is_rtl);
256 cursorRight(bv, false);
257 bv->finishChange(false);
262 // This is soooo ugly. Isn`t it possible to make
264 bool const is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
265 if (!selection.mark())
266 bv->beforeChange(this);
268 LyXCursor const cur = cursor;
270 cursorLeft(bv, false);
271 if ((is_rtl || cur != cursor) && // only if really moved!
272 cursor.pos() < cursor.par()->size() &&
273 cursor.par()->isInset(cursor.pos()) &&
274 isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
275 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
276 cmd.message(tmpinset->editMessage());
277 tmpinset->edit(bv, is_rtl);
281 cursorRight(bv, false);
282 bv->finishChange(false);
287 if (!selection.mark())
288 bv->beforeChange(this);
289 bv->update(this, BufferView::UPDATE);
291 bv->finishChange(false);
295 if (!selection.mark())
296 bv->beforeChange(this);
297 bv->update(this, BufferView::UPDATE);
302 case LFUN_UP_PARAGRAPH:
303 if (!selection.mark())
304 bv->beforeChange(this);
305 bv->update(this, BufferView::UPDATE);
306 cursorUpParagraph(bv);
310 case LFUN_DOWN_PARAGRAPH:
311 if (!selection.mark())
312 bv->beforeChange(this);
313 bv->update(this, BufferView::UPDATE);
314 cursorDownParagraph(bv);
315 bv->finishChange(false);
319 if (!selection.mark())
320 bv->beforeChange(this);
321 bv->update(this, BufferView::UPDATE);
322 bv->cursorPrevious(this);
323 bv->finishChange(false);
326 // moveCursorUpdate(false, false);
327 // owner_->view_state_changed();
331 if (!selection.mark())
332 bv->beforeChange(this);
333 bv->update(this, BufferView::UPDATE);
334 bv->cursorNext(this);
335 bv->finishChange(false);
339 if (!selection.mark())
340 bv->beforeChange(this);
343 bv->finishChange(false);
347 if (!selection.mark())
348 bv->beforeChange(this);
351 bv->finishChange(false);
355 bv->beforeChange(this);
356 insertChar(bv, Paragraph::META_NEWLINE);
358 setCursor(bv, cursor.par(), cursor.pos());
359 bv->moveCursorUpdate(false);
363 if (!selection.set()) {
365 selection.cursor = cursor;
367 // It is possible to make it a lot faster still
368 // just comment out the line below...
373 bv->moveCursorUpdate(false);
374 bv->owner()->view_state_changed();
378 case LFUN_DELETE_SKIP:
379 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
380 if (!selection.set()) {
381 LyXCursor cur = cursor;
382 if (cur.pos() == cur.par()->size()) {
386 && !(cur.par()->params().spaceTop()
387 == VSpace (VSpace::NONE))) {
389 cur.par()->params().lineTop(),
390 cur.par()->params().lineBottom(),
391 cur.par()->params().pagebreakTop(),
392 cur.par()->params().pagebreakBottom(),
393 VSpace(VSpace::NONE),
394 cur.par()->params().spaceBottom(),
395 cur.par()->params().spacing(),
396 cur.par()->params().align(),
397 cur.par()->params().labelWidthString(), 0);
403 selection.cursor = cursor;
408 selection.cursor = cursor;
417 if (!selection.set()) {
418 if (bv->owner()->getIntl().getTransManager().backspace()) {
420 selection.cursor = cursor;
422 // It is possible to make it a lot faster still
423 // just comment out the line below...
428 bv->owner()->view_state_changed();
432 case LFUN_BACKSPACE_SKIP:
433 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
434 if (!selection.set()) {
435 LyXCursor cur = cursor;
437 && !(cur.par()->params().spaceTop()
438 == VSpace (VSpace::NONE))) {
440 cur.par()->params().lineTop(),
441 cur.par()->params().lineBottom(),
442 cur.par()->params().pagebreakTop(),
443 cur.par()->params().pagebreakBottom(),
444 VSpace(VSpace::NONE), cur.par()->params().spaceBottom(),
445 cur.par()->params().spacing(),
446 cur.par()->params().align(),
447 cur.par()->params().labelWidthString(), 0);
451 selection.cursor = cur;
458 case LFUN_BREAKPARAGRAPH:
459 bv->beforeChange(this);
460 breakParagraph(bv, 0);
462 selection.cursor = cursor;
464 bv->owner()->view_state_changed();
467 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
468 bv->beforeChange(this);
469 breakParagraph(bv, 1);
471 selection.cursor = cursor;
473 bv->owner()->view_state_changed();
476 case LFUN_BREAKPARAGRAPH_SKIP: {
477 // When at the beginning of a paragraph, remove
478 // indentation and add a "defskip" at the top.
479 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
480 LyXCursor cur = cursor;
481 bv->beforeChange(this);
482 if (cur.pos() == 0) {
483 if (cur.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
485 cur.par()->params().lineTop(),
486 cur.par()->params().lineBottom(),
487 cur.par()->params().pagebreakTop(),
488 cur.par()->params().pagebreakBottom(),
489 VSpace(VSpace::DEFSKIP), cur.par()->params().spaceBottom(),
490 cur.par()->params().spacing(),
491 cur.par()->params().align(),
492 cur.par()->params().labelWidthString(), 1);
497 breakParagraph(bv, 0);
501 selection.cursor = cur;
503 bv->owner()->view_state_changed();
507 case LFUN_PARAGRAPH_SPACING: {
508 Paragraph * par = cursor.par();
509 Spacing::Space cur_spacing = par->params().spacing().getSpace();
510 float cur_value = 1.0;
511 if (cur_spacing == Spacing::Other)
512 cur_value = par->params().spacing().getValue();
514 istringstream is(cmd.argument.c_str());
517 Spacing::Space new_spacing = cur_spacing;
518 float new_value = cur_value;
520 lyxerr << "Missing argument to `paragraph-spacing'"
522 } else if (tmp == "single") {
523 new_spacing = Spacing::Single;
524 } else if (tmp == "onehalf") {
525 new_spacing = Spacing::Onehalf;
526 } else if (tmp == "double") {
527 new_spacing = Spacing::Double;
528 } else if (tmp == "other") {
529 new_spacing = Spacing::Other;
532 lyxerr << "new_value = " << tmpval << endl;
535 } else if (tmp == "default") {
536 new_spacing = Spacing::Default;
538 lyxerr << _("Unknown spacing argument: ")
539 << cmd.argument << endl;
541 if (cur_spacing != new_spacing || cur_value != new_value) {
542 par->params().spacing(Spacing(new_spacing, new_value));
549 case LFUN_INSET_TOGGLE:
551 bv->beforeChange(this);
558 case LFUN_PROTECTEDSPACE:
559 if (cursor.par()->layout()->free_spacing) {
563 specialChar(this, bv, InsetSpecialChar::PROTECTED_SEPARATOR);
565 bv->moveCursorUpdate(false);
568 case LFUN_HYPHENATION:
569 specialChar(this, bv, InsetSpecialChar::HYPHENATION);
572 case LFUN_LIGATURE_BREAK:
573 specialChar(this, bv, InsetSpecialChar::LIGATURE_BREAK);
577 specialChar(this, bv, InsetSpecialChar::LDOTS);
583 insertChar(bv, Paragraph::META_HFILL);
587 case LFUN_END_OF_SENTENCE:
588 specialChar(this, bv, InsetSpecialChar::END_OF_SENTENCE);
591 case LFUN_MENU_SEPARATOR:
592 specialChar(this, bv, InsetSpecialChar::MENU_SEPARATOR);
596 bv->beforeChange(this);
598 selection.cursor = cursor;
599 cmd.message(N_("Mark off"));
603 bv->beforeChange(this);
604 selection.mark(true);
606 selection.cursor = cursor;
607 cmd.message(N_("Mark on"));
611 bv->beforeChange(this);
612 if (selection.mark()) {
614 cmd.message(N_("Mark removed"));
616 selection.mark(true);
618 cmd.message(N_("Mark set"));
620 selection.cursor = cursor;
623 case LFUN_UPCASE_WORD:
625 changeCase(bv, LyXText::text_uppercase);
627 bv->updateInset(inset_owner, true);
631 case LFUN_LOWCASE_WORD:
633 changeCase(bv, LyXText::text_lowercase);
635 bv->updateInset(inset_owner, true);
639 case LFUN_CAPITALIZE_WORD:
641 changeCase(bv, LyXText::text_capitalization);
643 bv->updateInset(inset_owner, true);
647 case LFUN_TRANSPOSE_CHARS:
651 bv->updateInset(inset_owner, true);
655 case LFUN_BEGINNINGBUFSEL:
657 return Inset::UNDISPATCHED;
660 bv->finishChange(true);
665 return Inset::UNDISPATCHED;
668 bv->finishChange(true);
672 cmd.message(tostr(cursor.x()) + ' ' + tostr(cursor.y()));
678 istringstream is(cmd.argument.c_str());
681 lyxerr << "SETXY: Could not parse coordinates in '"
682 << cmd.argument << std::endl;
684 setCursorFromCoordinates(bv, x, y);
689 if (current_font.shape() == LyXFont::ITALIC_SHAPE)
691 else if (current_font.shape() == LyXFont::SMALLCAPS_SHAPE)
698 cmd.message(tostr(cursor.par()->layout()));
702 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
703 << cmd.argument << endl;
705 // This is not the good solution to the empty argument
706 // problem, but it will hopefully suffice for 1.2.0.
707 // The correct solution would be to augument the
708 // function list/array with information about what
709 // functions needs arguments and their type.
710 if (cmd.argument.empty()) {
711 cmd.errorMessage(_("LyX function 'layout' needs an argument."));
715 // Derive layout number from given argument (string)
716 // and current buffer's textclass (number)
717 LyXTextClass const & tclass = bv->buffer()->params.getLyXTextClass();
718 bool hasLayout = tclass.hasLayout(cmd.argument);
719 string layout = cmd.argument;
721 // If the entry is obsolete, use the new one instead.
723 string const & obs = tclass[layout]->obsoleted_by();
729 cmd.errorMessage(string(N_("Layout ")) + cmd.argument +
734 bool change_layout = (current_layout != layout);
735 if (!change_layout && selection.set() &&
736 selection.start.par() != selection.end.par())
738 Paragraph * spar = selection.start.par();
739 Paragraph * epar = selection.end.par()->next();
740 while (spar != epar) {
741 if (spar->layout()->name() != current_layout) {
742 change_layout = true;
749 current_layout = layout;
751 setLayout(bv, layout);
752 bv->owner()->setLayout(layout);
759 case LFUN_PASTESELECTION: {
763 // this was originally a beforeChange(bv->text), i.e
764 // the outermost LyXText!
765 bv->beforeChange(this);
766 string const clip(bv->workarea().getClipboard());
768 if (cmd.argument == "paragraph")
769 insertStringAsParagraphs(bv, clip);
771 insertStringAsLines(bv, clip);
778 case LFUN_SELFINSERT: {
779 if (cmd.argument.empty())
782 // Automatically delete the currently selected
783 // text and replace it with what is being
784 // typed in now. Depends on lyxrc settings
785 // "auto_region_delete", which defaults to
788 if (lyxrc.auto_region_delete) {
789 if (selection.set()) {
790 cutSelection(bv, false, false);
793 bv->workarea().haveSelection(false);
796 bv->beforeChange(this);
797 LyXFont const old_font(real_current_font);
799 string::const_iterator cit = cmd.argument.begin();
800 string::const_iterator end = cmd.argument.end();
801 for (; cit != end; ++cit)
802 bv->owner()->getIntl().getTransManager().
803 TranslateAndInsert(*cit, this);
806 selection.cursor = cursor;
807 bv->moveCursorUpdate(false);
809 // real_current_font.number can change so we need to
810 // update the minibuffer
811 if (old_font != real_current_font)
812 bv->owner()->view_state_changed();
817 return Inset::UNDISPATCHED;
820 return Inset::DISPATCHED;