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"
26 #include "support/lstrings.h"
27 #include "frontends/LyXView.h"
28 #include "frontends/screen.h"
29 #include "frontends/WorkArea.h"
30 #include "insets/insetspecialchar.h"
31 #include "insets/insettext.h"
32 #include "insets/insetquotes.h"
33 #include "insets/insetcommand.h"
34 #include "undo_funcs.h"
41 extern string current_layout;
45 void moveCursorUpdate(BufferView * bv, bool selecting)
47 LyXText * lt = bv->getLyXText();
49 if (selecting || lt->selection.mark()) {
54 bv->updateInset(lt->inset_owner, false);
58 // bv->update(lt, BufferView::SELECT|BufferView::FITCUR);
60 bv->update(lt, BufferView::SELECT);
62 } else if (bv->text->status() != LyXText::UNCHANGED) {
63 bv->theLockingInset()->hideInsetCursor(bv);
64 bv->update(bv->text, BufferView::SELECT|BufferView::FITCUR);
68 if (!lt->selection.set())
69 bv->workarea().haveSelection(false);
75 void finishChange(BufferView * bv, bool selecting = false)
78 moveCursorUpdate(bv, selecting);
79 bv->owner()->view_state_changed();
85 bool LyXText::gotoNextInset(BufferView * bv,
86 vector<Inset::Code> const & codes, string const & contents) const
88 LyXCursor res = cursor;
91 if (res.pos() < res.par()->size() - 1) {
92 res.pos(res.pos() + 1);
94 res.par(res.par()->next());
99 !(res.par()->isInset(res.pos())
100 && (inset = res.par()->getInset(res.pos())) != 0
101 && find(codes.begin(), codes.end(), inset->lyxCode())
103 && (contents.empty() ||
104 static_cast<InsetCommand *>(
105 res.par()->getInset(res.pos()))->getContents()
109 setCursor(bv, res.par(), res.pos(), false);
116 void LyXText::gotoInset(BufferView * bv, vector<Inset::Code> const & codes,
120 bv->beforeChange(this);
124 if (same_content && cursor.par()->isInset(cursor.pos())) {
125 Inset const * inset = cursor.par()->getInset(cursor.pos());
126 if (find(codes.begin(), codes.end(), inset->lyxCode())
128 contents = static_cast<InsetCommand const *>(inset)->getContents();
131 if (!gotoNextInset(bv, codes, contents)) {
132 if (cursor.pos() || cursor.par() != ownerParagraph()) {
133 LyXCursor tmp = cursor;
134 cursor.par(ownerParagraph());
136 if (!gotoNextInset(bv, codes, contents)) {
138 bv->owner()->message(_("No more insets"));
141 bv->owner()->message(_("No more insets"));
145 selection.cursor = cursor;
149 void LyXText::gotoInset(BufferView * bv, Inset::Code code, bool same_content)
151 gotoInset(bv, vector<Inset::Code>(1, code), same_content);
155 void LyXText::cursorPrevious(BufferView * bv)
157 if (!cursor.row()->previous()) {
159 int new_y = bv->text->first_y - bv->workarea().workHeight();
160 bv->screen().draw(bv->text, bv, new_y < 0 ? 0 : new_y);
161 bv->updateScrollbar();
167 Row * cursorrow = cursor.row();
169 setCursorFromCoordinates(bv, cursor.x_fix(), y);
173 if (cursorrow == bv->text->cursor.row()) {
174 // we have a row which is higher than the workarea so we leave the
175 // cursor on the start of the row and move only the draw up as soon
176 // as we move the cursor or do something while inside the row (it may
177 // span several workarea-heights) we'll move to the top again, but this
178 // is better than just jump down and only display part of the row.
179 new_y = bv->text->first_y - bv->workarea().workHeight();
182 new_y = bv->text->cursor.iy()
183 + bv->theLockingInset()->insetInInsetY() + y
184 + cursor.row()->height()
185 - bv->workarea().workHeight() + 1;
188 - cursor.row()->baseline()
189 + cursor.row()->height()
190 - bv->workarea().workHeight() + 1;
193 bv->screen().draw(bv->text, bv, new_y < 0 ? 0 : new_y);
194 if (cursor.row()->previous()) {
196 setCursor(bv, cur, cursor.row()->previous()->par(),
197 cursor.row()->previous()->pos(), false);
198 if (cur.y() > first_y) {
202 bv->updateScrollbar();
206 void LyXText::cursorNext(BufferView * bv)
208 if (!cursor.row()->next()) {
209 int y = cursor.y() - cursor.row()->baseline() +
210 cursor.row()->height();
211 if (y > int(first_y + bv->workarea().workHeight())) {
212 bv->screen().draw(bv->text, bv,
213 bv->text->first_y + bv->workarea().workHeight());
214 bv->updateScrollbar();
219 int y = first_y + bv->workarea().workHeight();
220 if (inset_owner && !first_y) {
221 y -= (bv->text->cursor.iy()
223 + bv->theLockingInset()->insetInInsetY());
228 Row * cursorrow = cursor.row();
229 setCursorFromCoordinates(bv, cursor.x_fix(), y);
230 // + workarea().workHeight());
234 if (cursorrow == bv->text->cursor.row()) {
235 // we have a row which is higher than the workarea so we leave the
236 // cursor on the start of the row and move only the draw down as soon
237 // as we move the cursor or do something while inside the row (it may
238 // span several workarea-heights) we'll move to the top again, but this
239 // is better than just jump down and only display part of the row.
240 new_y = bv->text->first_y + bv->workarea().workHeight();
243 new_y = bv->text->cursor.iy()
244 + bv->theLockingInset()->insetInInsetY()
245 + y - cursor.row()->baseline();
247 new_y = cursor.y() - cursor.row()->baseline();
250 bv->screen().draw(bv->text, bv, new_y);
251 if (cursor.row()->next()) {
253 setCursor(bv, cur, cursor.row()->next()->par(),
254 cursor.row()->next()->pos(), false);
255 if (cur.y() < int(first_y + bv->workarea().workHeight())) {
256 cursorDown(bv, true);
259 bv->updateScrollbar();
263 void LyXText::update(BufferView * bv, bool changed)
265 BufferView::UpdateCodes c = BufferView::SELECT | BufferView::FITCUR;
267 bv->update(this, c | BufferView::CHANGE);
273 void specialChar(LyXText * lt, BufferView * bv, InsetSpecialChar::Kind kind)
277 InsetSpecialChar * new_inset = new InsetSpecialChar(kind);
278 if (!bv->insertInset(new_inset))
281 bv->updateInset(new_inset, true);
285 Inset::RESULT LyXText::dispatch(FuncRequest const & cmd)
287 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: action[" << cmd.action
288 <<"] arg[" << cmd.argument << "]" << endl;
290 BufferView * bv = cmd.view();
292 switch (cmd.action) {
294 case LFUN_APPENDIX: {
295 Paragraph * par = cursor.par();
296 bool start = !par->params().startOfAppendix();
298 // ensure that we have only one start_of_appendix in this document
299 Paragraph * tmp = ownerParagraph();
300 for (; tmp; tmp = tmp->next())
301 tmp->params().startOfAppendix(false);
303 par->params().startOfAppendix(start);
305 // we can set the refreshing parameters now
306 status(cmd.view(), LyXText::NEED_MORE_REFRESH);
308 refresh_row = 0; // not needed for full update
309 updateCounters(cmd.view());
310 setCursor(cmd.view(), cursor.par(), cursor.pos());
315 case LFUN_DELETE_WORD_FORWARD:
316 bv->beforeChange(this);
318 deleteWordForward(bv);
323 case LFUN_DELETE_WORD_BACKWARD:
324 bv->beforeChange(this);
326 deleteWordBackward(bv);
331 case LFUN_DELETE_LINE_FORWARD:
332 bv->beforeChange(this);
334 deleteLineForward(bv);
341 if (!selection.mark())
342 bv->beforeChange(this);
349 if (!selection.mark())
350 bv->beforeChange(this);
352 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
353 cursorLeftOneWord(bv);
355 cursorRightOneWord(bv);
360 if (!selection.mark())
361 bv->beforeChange(this);
363 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
364 cursorRightOneWord(bv);
366 cursorLeftOneWord(bv);
370 case LFUN_BEGINNINGBUF:
371 if (!selection.mark())
372 bv->beforeChange(this);
379 if (selection.mark())
380 bv->beforeChange(this);
388 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
392 finishChange(bv, true);
397 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
401 finishChange(bv, true);
407 finishChange(bv, true);
412 cursorDown(bv, true);
413 finishChange(bv, true);
416 case LFUN_UP_PARAGRAPHSEL:
418 cursorUpParagraph(bv);
419 finishChange(bv, true);
422 case LFUN_DOWN_PARAGRAPHSEL:
424 cursorDownParagraph(bv);
425 finishChange(bv, true);
431 finishChange(bv, true);
437 finishChange(bv, true);
443 finishChange(bv, true);
449 finishChange(bv, true);
452 case LFUN_WORDRIGHTSEL:
454 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
455 cursorLeftOneWord(bv);
457 cursorRightOneWord(bv);
458 finishChange(bv, true);
461 case LFUN_WORDLEFTSEL:
463 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
464 cursorRightOneWord(bv);
466 cursorLeftOneWord(bv);
467 finishChange(bv, true);
471 bool is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
472 if (!selection.mark())
473 bv->beforeChange(this);
476 cursorLeft(bv, false);
477 if (cursor.pos() < cursor.par()->size()
478 && cursor.par()->isInset(cursor.pos())
479 && isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
480 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
481 cmd.message(tmpinset->editMessage());
482 tmpinset->edit(bv, !is_rtl);
486 cursorRight(bv, false);
492 // This is soooo ugly. Isn`t it possible to make
494 bool const is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
495 if (!selection.mark())
496 bv->beforeChange(this);
498 LyXCursor const cur = cursor;
500 cursorLeft(bv, false);
501 if ((is_rtl || cur != cursor) && // only if really moved!
502 cursor.pos() < cursor.par()->size() &&
503 cursor.par()->isInset(cursor.pos()) &&
504 isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
505 Inset * tmpinset = cursor.par()->getInset(cursor.pos());
506 cmd.message(tmpinset->editMessage());
507 tmpinset->edit(bv, is_rtl);
511 cursorRight(bv, false);
517 if (!selection.mark())
518 bv->beforeChange(this);
519 bv->update(this, BufferView::UPDATE);
525 if (!selection.mark())
526 bv->beforeChange(this);
527 bv->update(this, BufferView::UPDATE);
532 case LFUN_UP_PARAGRAPH:
533 if (!selection.mark())
534 bv->beforeChange(this);
535 bv->update(this, BufferView::UPDATE);
536 cursorUpParagraph(bv);
540 case LFUN_DOWN_PARAGRAPH:
541 if (!selection.mark())
542 bv->beforeChange(this);
543 bv->update(this, BufferView::UPDATE);
544 cursorDownParagraph(bv);
545 finishChange(bv, false);
549 if (!selection.mark())
550 bv->beforeChange(this);
551 bv->update(this, BufferView::UPDATE);
553 finishChange(bv, false);
556 // moveCursorUpdate(bv, false, false);
557 // owner_->view_state_changed();
561 if (!selection.mark())
562 bv->beforeChange(this);
563 bv->update(this, BufferView::UPDATE);
565 finishChange(bv, false);
569 if (!selection.mark())
570 bv->beforeChange(this);
573 finishChange(bv, false);
577 if (!selection.mark())
578 bv->beforeChange(this);
581 finishChange(bv, false);
585 bv->beforeChange(this);
586 insertChar(bv, Paragraph::META_NEWLINE);
588 setCursor(bv, cursor.par(), cursor.pos());
589 moveCursorUpdate(bv, false);
593 if (!selection.set()) {
595 selection.cursor = cursor;
597 // It is possible to make it a lot faster still
598 // just comment out the line below...
602 cutSelection(bv, true);
605 moveCursorUpdate(bv, false);
606 bv->owner()->view_state_changed();
610 case LFUN_DELETE_SKIP:
611 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
612 if (!selection.set()) {
613 LyXCursor cur = cursor;
614 if (cur.pos() == cur.par()->size()) {
618 && !(cur.par()->params().spaceTop()
619 == VSpace (VSpace::NONE))) {
621 cur.par()->params().lineTop(),
622 cur.par()->params().lineBottom(),
623 cur.par()->params().pagebreakTop(),
624 cur.par()->params().pagebreakBottom(),
625 VSpace(VSpace::NONE),
626 cur.par()->params().spaceBottom(),
627 cur.par()->params().spacing(),
628 cur.par()->params().align(),
629 cur.par()->params().labelWidthString(), 0);
635 selection.cursor = cursor;
639 selection.cursor = cursor;
643 cutSelection(bv, true);
650 if (!selection.set()) {
651 if (bv->owner()->getIntl().getTransManager().backspace()) {
653 selection.cursor = cursor;
655 // It is possible to make it a lot faster still
656 // just comment out the line below...
661 cutSelection(bv, true);
664 bv->owner()->view_state_changed();
668 case LFUN_BACKSPACE_SKIP:
669 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
670 if (!selection.set()) {
671 LyXCursor cur = cursor;
673 && !(cur.par()->params().spaceTop()
674 == VSpace (VSpace::NONE))) {
676 cur.par()->params().lineTop(),
677 cur.par()->params().lineBottom(),
678 cur.par()->params().pagebreakTop(),
679 cur.par()->params().pagebreakBottom(),
680 VSpace(VSpace::NONE), cur.par()->params().spaceBottom(),
681 cur.par()->params().spacing(),
682 cur.par()->params().align(),
683 cur.par()->params().labelWidthString(), 0);
686 selection.cursor = cur;
690 cutSelection(bv, true);
695 case LFUN_BREAKPARAGRAPH:
696 bv->beforeChange(this);
697 breakParagraph(bv, 0);
699 selection.cursor = cursor;
701 bv->owner()->view_state_changed();
704 case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
705 bv->beforeChange(this);
706 breakParagraph(bv, 1);
708 selection.cursor = cursor;
710 bv->owner()->view_state_changed();
713 case LFUN_BREAKPARAGRAPH_SKIP: {
714 // When at the beginning of a paragraph, remove
715 // indentation and add a "defskip" at the top.
716 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
717 LyXCursor cur = cursor;
718 bv->beforeChange(this);
719 if (cur.pos() == 0) {
720 if (cur.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
722 cur.par()->params().lineTop(),
723 cur.par()->params().lineBottom(),
724 cur.par()->params().pagebreakTop(),
725 cur.par()->params().pagebreakBottom(),
726 VSpace(VSpace::DEFSKIP), cur.par()->params().spaceBottom(),
727 cur.par()->params().spacing(),
728 cur.par()->params().align(),
729 cur.par()->params().labelWidthString(), 1);
734 breakParagraph(bv, 0);
738 selection.cursor = cur;
740 bv->owner()->view_state_changed();
744 case LFUN_PARAGRAPH_SPACING: {
745 Paragraph * par = cursor.par();
746 Spacing::Space cur_spacing = par->params().spacing().getSpace();
747 float cur_value = 1.0;
748 if (cur_spacing == Spacing::Other)
749 cur_value = par->params().spacing().getValue();
751 istringstream is(cmd.argument.c_str());
754 Spacing::Space new_spacing = cur_spacing;
755 float new_value = cur_value;
757 lyxerr << "Missing argument to `paragraph-spacing'"
759 } else if (tmp == "single") {
760 new_spacing = Spacing::Single;
761 } else if (tmp == "onehalf") {
762 new_spacing = Spacing::Onehalf;
763 } else if (tmp == "double") {
764 new_spacing = Spacing::Double;
765 } else if (tmp == "other") {
766 new_spacing = Spacing::Other;
769 lyxerr << "new_value = " << tmpval << endl;
772 } else if (tmp == "default") {
773 new_spacing = Spacing::Default;
775 lyxerr << _("Unknown spacing argument: ")
776 << cmd.argument << endl;
778 if (cur_spacing != new_spacing || cur_value != new_value) {
779 par->params().spacing(Spacing(new_spacing, new_value));
786 case LFUN_INSET_TOGGLE:
788 bv->beforeChange(this);
795 case LFUN_PROTECTEDSPACE:
796 if (cursor.par()->layout()->free_spacing) {
800 specialChar(this, bv, InsetSpecialChar::PROTECTED_SEPARATOR);
802 moveCursorUpdate(bv, false);
805 case LFUN_HYPHENATION:
806 specialChar(this, bv, InsetSpecialChar::HYPHENATION);
809 case LFUN_LIGATURE_BREAK:
810 specialChar(this, bv, InsetSpecialChar::LIGATURE_BREAK);
814 specialChar(this, bv, InsetSpecialChar::LDOTS);
820 insertChar(bv, Paragraph::META_HFILL);
824 case LFUN_END_OF_SENTENCE:
825 specialChar(this, bv, InsetSpecialChar::END_OF_SENTENCE);
828 case LFUN_MENU_SEPARATOR:
829 specialChar(this, bv, InsetSpecialChar::MENU_SEPARATOR);
833 bv->beforeChange(this);
835 selection.cursor = cursor;
836 cmd.message(N_("Mark off"));
840 bv->beforeChange(this);
841 selection.mark(true);
843 selection.cursor = cursor;
844 cmd.message(N_("Mark on"));
848 bv->beforeChange(this);
849 if (selection.mark()) {
851 cmd.message(N_("Mark removed"));
853 selection.mark(true);
855 cmd.message(N_("Mark set"));
857 selection.cursor = cursor;
860 case LFUN_UPCASE_WORD:
862 changeCase(bv, LyXText::text_uppercase);
864 bv->updateInset(inset_owner, true);
868 case LFUN_LOWCASE_WORD:
870 changeCase(bv, LyXText::text_lowercase);
872 bv->updateInset(inset_owner, true);
876 case LFUN_CAPITALIZE_WORD:
878 changeCase(bv, LyXText::text_capitalization);
880 bv->updateInset(inset_owner, true);
884 case LFUN_TRANSPOSE_CHARS:
888 bv->updateInset(inset_owner, true);
893 cmd.message(_("Paste"));
895 // clear the selection
896 bv->toggleSelection();
900 clearSelection(); // bug 393
909 cutSelection(bv, true);
911 cmd.message(_("Cut"));
916 cmd.message(_("Copy"));
919 case LFUN_BEGINNINGBUFSEL:
921 return Inset::UNDISPATCHED;
924 finishChange(bv, true);
929 return Inset::UNDISPATCHED;
932 finishChange(bv, true);
936 cmd.message(tostr(cursor.x()) + ' ' + tostr(cursor.y()));
942 istringstream is(cmd.argument.c_str());
945 lyxerr << "SETXY: Could not parse coordinates in '"
946 << cmd.argument << std::endl;
948 setCursorFromCoordinates(bv, x, y);
953 if (current_font.shape() == LyXFont::ITALIC_SHAPE)
955 else if (current_font.shape() == LyXFont::SMALLCAPS_SHAPE)
962 cmd.message(tostr(cursor.par()->layout()));
966 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
967 << cmd.argument << endl;
969 // This is not the good solution to the empty argument
970 // problem, but it will hopefully suffice for 1.2.0.
971 // The correct solution would be to augument the
972 // function list/array with information about what
973 // functions needs arguments and their type.
974 if (cmd.argument.empty()) {
975 cmd.errorMessage(_("LyX function 'layout' needs an argument."));
979 // Derive layout number from given argument (string)
980 // and current buffer's textclass (number)
981 LyXTextClass const & tclass = bv->buffer()->params.getLyXTextClass();
982 bool hasLayout = tclass.hasLayout(cmd.argument);
983 string layout = cmd.argument;
985 // If the entry is obsolete, use the new one instead.
987 string const & obs = tclass[layout]->obsoleted_by();
993 cmd.errorMessage(string(N_("Layout ")) + cmd.argument +
998 bool change_layout = (current_layout != layout);
999 if (!change_layout && selection.set() &&
1000 selection.start.par() != selection.end.par())
1002 Paragraph * spar = selection.start.par();
1003 Paragraph * epar = selection.end.par()->next();
1004 while (spar != epar) {
1005 if (spar->layout()->name() != current_layout) {
1006 change_layout = true;
1011 if (change_layout) {
1013 current_layout = layout;
1015 setLayout(bv, layout);
1016 bv->owner()->setLayout(layout);
1023 case LFUN_PASTESELECTION: {
1027 // this was originally a beforeChange(bv->text), i.e
1028 // the outermost LyXText!
1029 bv->beforeChange(this);
1030 string const clip = bv->workarea().getClipboard();
1031 if (!clip.empty()) {
1032 if (cmd.argument == "paragraph")
1033 insertStringAsParagraphs(bv, clip);
1035 insertStringAsLines(bv, clip);
1042 case LFUN_GOTOERROR:
1043 gotoInset(bv, Inset::ERROR_CODE, false);
1047 gotoInset(bv, Inset::NOTE_CODE, false);
1050 case LFUN_REFERENCE_GOTO:
1052 vector<Inset::Code> tmp;
1053 tmp.push_back(Inset::LABEL_CODE);
1054 tmp.push_back(Inset::REF_CODE);
1055 gotoInset(bv, tmp, true);
1060 Paragraph const * par = cursor.par();
1061 lyx::pos_type pos = cursor.pos();
1065 else if (par->isInset(pos - 1) && par->getInset(pos - 1)->isSpace())
1068 c = par->getChar(pos - 1);
1071 LyXLayout_ptr const & style = par->layout();
1073 if (style->pass_thru ||
1074 par->getFontSettings(bv->buffer()->params,
1075 pos).language()->lang() == "hebrew" ||
1076 (!bv->insertInset(new InsetQuotes(c, bv->buffer()->params))))
1077 bv->owner()->dispatch(FuncRequest(LFUN_SELFINSERT, "\""));
1081 case LFUN_DATE_INSERT: { // jdblair: date-insert cmd
1082 time_t now_time_t = time(NULL);
1083 struct tm * now_tm = localtime(&now_time_t);
1084 setlocale(LC_TIME, "");
1086 if (!cmd.argument.empty())
1089 arg = lyxrc.date_insert_format;
1091 int const datetmp_len =
1092 ::strftime(datetmp, 32, arg.c_str(), now_tm);
1094 for (int i = 0; i < datetmp_len; i++) {
1095 insertChar(bv, datetmp[i]);
1098 selection.cursor = cursor;
1099 moveCursorUpdate(bv, false);
1103 case LFUN_SELFINSERT: {
1104 if (cmd.argument.empty())
1107 // Automatically delete the currently selected
1108 // text and replace it with what is being
1109 // typed in now. Depends on lyxrc settings
1110 // "auto_region_delete", which defaults to
1113 if (lyxrc.auto_region_delete) {
1114 if (selection.set()) {
1115 cutSelection(bv, false, false);
1118 bv->workarea().haveSelection(false);
1121 bv->beforeChange(this);
1122 LyXFont const old_font(real_current_font);
1124 string::const_iterator cit = cmd.argument.begin();
1125 string::const_iterator end = cmd.argument.end();
1126 for (; cit != end; ++cit)
1127 bv->owner()->getIntl().getTransManager().
1128 TranslateAndInsert(*cit, this);
1131 selection.cursor = cursor;
1132 moveCursorUpdate(bv, false);
1134 // real_current_font.number can change so we need to
1135 // update the minibuffer
1136 if (old_font != real_current_font)
1137 bv->owner()->view_state_changed();
1142 return Inset::UNDISPATCHED;
1145 return Inset::DISPATCHED;