]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiCompleter.cpp
aa54d93d1d406146031b2d365a7c94f1fd87ac80
[lyx.git] / src / frontends / qt4 / GuiCompleter.cpp
1 /**
2  * \file GuiCompleter.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Stefan Schimanski
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "GuiWorkArea.h"
14
15 #include "Buffer.h"
16 #include "BufferView.h"
17 #include "Cursor.h"
18 #include "Dimension.h"
19 #include "FuncRequest.h"
20 #include "GuiView.h"
21 #include "LyXFunc.h"
22 #include "LyXRC.h"
23 #include "Paragraph.h"
24 #include "version.h"
25
26 #include "support/debug.h"
27
28 #include <QApplication>
29 #include <QAbstractListModel>
30 #include <QHeaderView>
31 #include <QPainter>
32 #include <QPixmapCache>
33 #include <QScrollBar>
34 #include <QItemDelegate>
35 #include <QTreeView>
36 #include <QTimer>
37
38 using namespace std;
39 using namespace lyx::support;
40
41 namespace lyx {
42 namespace frontend {
43
44 class RtlItemDelegate : public QItemDelegate {
45 public:
46         explicit RtlItemDelegate(QObject * parent = 0)
47                 : QItemDelegate(parent) {}
48
49 protected:
50         virtual void drawDisplay(QPainter * painter,
51                 QStyleOptionViewItem const & option,
52                 QRect const & rect, QString const & text) const
53         {
54                 // FIXME: do this more elegantly
55                 docstring stltext = qstring_to_ucs4(text);
56                 reverse(stltext.begin(), stltext.end());
57                 QItemDelegate::drawDisplay(painter, option, rect, toqstr(stltext));
58         }
59 };
60
61
62 class PixmapItemDelegate : public QItemDelegate {
63 public:
64         explicit PixmapItemDelegate(QObject *parent = 0)
65         : QItemDelegate(parent) {}
66
67 protected:
68         void paint(QPainter *painter, const QStyleOptionViewItem &option,
69                    const QModelIndex &index) const
70         {
71                 QStyleOptionViewItem opt = setOptions(index, option);
72                 QVariant value = index.data(Qt::DisplayRole);
73                 QPixmap pixmap = qvariant_cast<QPixmap>(value);
74                 
75                 // draw
76                 painter->save();
77                 drawBackground(painter, opt, index);
78                 if (!pixmap.isNull()) {
79                         const QSize size = pixmap.size();
80                         painter->drawPixmap(option.rect.left() + (16 - size.width()) / 2,
81                                 option.rect.top() + (option.rect.height() - size.height()) / 2,
82                                 pixmap);
83                 }
84                 drawFocus(painter, opt, option.rect);
85                 painter->restore();
86         }
87 };
88
89
90 class GuiCompletionModel : public QAbstractListModel {
91 public:
92         ///
93         GuiCompletionModel(QObject * parent,
94                 Inset::CompletionList const * l)
95                 : QAbstractListModel(parent), list_(l) {}
96         ///
97         ~GuiCompletionModel()
98                 { delete list_; }
99         ///
100         bool sorted() const
101         {
102                 if (list_)
103                         return list_->sorted();
104                 else
105                         return false;
106         }
107         ///
108         int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const
109         {
110                 return 2;
111         }
112         ///
113         int rowCount(const QModelIndex & /*parent*/ = QModelIndex()) const
114         {
115                 if (list_ == 0)
116                         return 0;
117                 else
118                         return list_->size();
119         }
120
121         ///
122         QVariant data(const QModelIndex & index, int role) const
123         {
124                 if (list_ == 0)
125                         return QVariant();
126
127                 if (index.row() < 0 || index.row() >= rowCount())
128                         return QVariant();
129
130                 if (role != Qt::DisplayRole && role != Qt::EditRole)
131                     return QVariant();
132                     
133                 if (index.column() == 0) {
134                         docstring const word = list_->data(index.row());
135                         return toqstr(word);
136                 } else if (index.column() == 1) {
137                         // get icon from cache
138                         QPixmap scaled;
139                         QString const name = ":" + toqstr(list_->icon(index.row()));
140                         if (!QPixmapCache::find("completion" + name, scaled)) {
141                                 // load icon from disk
142                                 QPixmap p = QPixmap(name);
143                                 if (!p.isNull()) {
144                                         // scale it to 16x16 or smaller
145                                         scaled = p.scaled(min(16, p.width()), min(16, p.height()), 
146                                                 Qt::KeepAspectRatio, Qt::SmoothTransformation);
147                                 }
148
149                                 QPixmapCache::insert("completion" + name, scaled);
150                         }
151                         return scaled;
152                 }
153                 return QVariant();
154         }
155
156 private:
157         ///
158         Inset::CompletionList const * list_;
159 };
160
161
162 GuiCompleter::GuiCompleter(GuiWorkArea * gui, QObject * parent)
163         : QCompleter(parent), gui_(gui), updateLock_(0),
164           inlineVisible_(false)
165 {
166         // Setup the completion popup
167         setModel(new GuiCompletionModel(this, 0));
168         setCompletionMode(QCompleter::PopupCompletion);
169         setWidget(gui_);
170         
171         // create the popup
172         QTreeView *listView = new QTreeView;
173         listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
174         listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
175         listView->setSelectionBehavior(QAbstractItemView::SelectRows);
176         listView->setSelectionMode(QAbstractItemView::SingleSelection);
177         listView->header()->hide();
178         listView->setIndentation(0);
179         listView->setUniformRowHeights(true);
180         setPopup(listView);
181         popup()->setItemDelegateForColumn(1, new PixmapItemDelegate(this));
182         rtlItemDelegate_ = new RtlItemDelegate(this);
183         
184         // create timeout timers
185         popup_timer_.setSingleShot(true);
186         inline_timer_.setSingleShot(true);
187         connect(this, SIGNAL(highlighted(const QString &)),
188                 this, SLOT(popupHighlighted(const QString &)));
189         connect(this, SIGNAL(activated(const QString &)),
190                 this, SLOT(popupActivated(const QString &)));
191         connect(&popup_timer_, SIGNAL(timeout()),
192                 this, SLOT(showPopup()));
193         connect(&inline_timer_, SIGNAL(timeout()),
194                 this, SLOT(showInline()));
195 }
196
197
198 GuiCompleter::~GuiCompleter()
199 {
200         popup()->hide();
201 }
202
203
204 bool GuiCompleter::eventFilter(QObject * watched, QEvent * e)
205 {
206         // hijack back the tab key from the popup
207         // (which stole it from the workspace before)
208         if (e->type() == QEvent::KeyPress && popupVisible()) {
209                 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
210                 switch (ke->key()) {
211                 case Qt::Key_Tab:
212                         tab();
213                         ke->accept();
214                         return true;
215                 default: break;
216                 }
217         }
218         
219         return QCompleter::eventFilter(watched, e);
220 }
221
222
223 bool GuiCompleter::popupPossible(Cursor const & cur) const
224 {
225         return QApplication::activeWindow()
226                 && gui_->hasFocus()
227                 && cur.inset().completionSupported(cur);
228 }
229
230
231 bool GuiCompleter::inlinePossible(Cursor const & cur) const
232 {
233         return cur.inset().inlineCompletionSupported(cur);
234 }
235
236
237 bool GuiCompleter::completionAvailable() const
238 {
239         size_t n = popup()->model()->rowCount();
240
241         // if there is exactly one, we have to check whether it is a 
242         // real completion, i.e. longer than the current prefix.
243         if (n == 1 && completionPrefix() == currentCompletion())
244             return false;
245
246         return n > 0;
247 }
248
249
250 bool GuiCompleter::popupVisible() const
251 {
252         return popup()->isVisible();
253 }
254
255
256 bool GuiCompleter::inlineVisible() const
257 {
258         // In fact using BufferView::inlineCompletionPos.empty() should be
259         // here. But unfortunately this information is not good enough
260         // because destructive operations like backspace might invalidate
261         // inlineCompletionPos. But then the completion should stay visible
262         // (i.e. reshown on the next update). Hence be keep this information
263         // in the inlineVisible_ variable.
264         return inlineVisible_;
265 }
266
267
268 void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cursorInView)
269 {
270         // parameters which affect the completion
271         bool moved = cur != old_cursor_;
272         if (moved)
273                 old_cursor_ = cur;
274
275         bool possiblePopupState = popupPossible(cur) && cursorInView;
276         bool possibleInlineState = inlinePossible(cur) && cursorInView;
277
278         // we moved or popup state is not ok for popup?
279         if ((moved && !keep) || !possiblePopupState)
280                 hidePopup(cur);
281
282         // we moved or inline state is not ok for inline completion?
283         if ((moved && !keep) || !possibleInlineState)
284                 hideInline(cur);
285
286         // we inserted something and are in a possible popup state?
287         if (!popupVisible() && possiblePopupState && start
288                 && cur.inset().automaticPopupCompletion())
289                 popup_timer_.start(int(lyxrc.completion_popup_delay * 1000));
290
291         // we inserted something and are in a possible inline completion state?
292         if (!inlineVisible() && possibleInlineState && start
293                 && cur.inset().automaticInlineCompletion())
294                 inline_timer_.start(int(lyxrc.completion_inline_delay * 1000));
295
296         // update prefix if any completion is possible
297         bool modelActive = model()->rowCount() > 0;
298         if (possiblePopupState || possibleInlineState) {
299                 if (modelActive)
300                         updatePrefix(cur);
301                 else
302                         updateAvailability();
303         }
304 }
305
306
307 void GuiCompleter::updateVisibility(bool start, bool keep)
308 {
309         Cursor cur = gui_->bufferView().cursor();
310         cur.updateFlags(Update::None);
311         
312         updateVisibility(cur, start, keep);
313         
314         if (cur.disp_.update())
315                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
316 }
317
318
319 void GuiCompleter::updatePrefix(Cursor & cur)
320 {
321         // get new prefix. Do nothing if unchanged
322         QString newPrefix = toqstr(cur.inset().completionPrefix(cur));
323         if (newPrefix == completionPrefix())
324                 return;
325         
326         // value which should be kept selected
327         QString old = currentCompletion();
328         if (old.length() == 0)
329                 old = last_selection_;
330         
331         // update completer to new prefix
332         setCompletionPrefix(newPrefix);
333
334         // update popup because its size might have changed
335         if (popupVisible())
336                 updatePopup(cur);
337
338         // restore old selection
339         setCurrentCompletion(old);
340         
341         // if popup is not empty, the new selection will
342         // be our last valid one
343         QString const & s = currentCompletion();
344         if (popupVisible() || inlineVisible()) {
345                 if (s.length() > 0)
346                         last_selection_ = s;
347                 else
348                         last_selection_ = old;
349         }
350
351         // update inline completion because the default
352         // completion string might have changed
353         if (inlineVisible())
354                 updateInline(cur, s);
355 }
356
357
358 void GuiCompleter::updateInline(Cursor & cur, QString const & completion)
359 {
360         if (!cur.inset().inlineCompletionSupported(cur))
361                 return;
362         
363         // compute postfix
364         docstring prefix = cur.inset().completionPrefix(cur);
365         docstring postfix = from_utf8(fromqstr(completion.mid(prefix.length())));
366         
367         // shorten it if necessary
368         if (lyxrc.completion_inline_dots != -1
369             && postfix.size() > unsigned(lyxrc.completion_inline_dots))
370                 postfix = postfix.substr(0, lyxrc.completion_inline_dots - 1) + "...";
371
372         // set inline completion at cursor position
373         size_t uniqueTo = max(longestUniqueCompletion().size(), prefix.size());
374         gui_->bufferView().setInlineCompletion(cur, cur, postfix, uniqueTo - prefix.size());
375         inlineVisible_ = true;
376 }
377
378
379 void GuiCompleter::updatePopup(Cursor & cur)
380 {
381         if (!cur.inset().completionSupported(cur))
382                 return;
383         
384         if (completionCount() == 0)
385                 return;
386         
387         // get dimensions of completion prefix
388         Dimension dim;
389         int x;
390         int y;
391         cur.inset().completionPosAndDim(cur, x, y, dim);
392         
393         // and calculate the rect of the popup
394         QRect rect;
395         if (popup()->layoutDirection() == Qt::RightToLeft)
396                 rect = QRect(x + dim.width() - 200, y - dim.ascent() - 3, 200, dim.height() + 6);
397         else
398                 rect = QRect(x, y - dim.ascent() - 3, 200, dim.height() + 6);
399         
400         // show/update popup
401         QTreeView * p = static_cast<QTreeView *>(popup());
402         p->setColumnWidth(0, popup()->width() - 22 - p->verticalScrollBar()->width());
403
404         complete(rect);
405 }
406
407
408 void GuiCompleter::updateAvailability()
409 {
410         // this should really only be of interest if no completion is
411         // visible yet, i.e. especially if automatic completion is disabled.
412         if (inlineVisible() || popupVisible())
413                 return;
414         Cursor & cur = gui_->bufferView().cursor();
415         if (!popupPossible(cur) && !inlinePossible(cur))
416                 return;
417         
418         updateModel(cur, false, false);
419 }
420         
421
422 void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate)
423 {
424         // value which should be kept selected
425         QString old = currentCompletion();
426         if (old.length() == 0)
427                 old = last_selection_;
428
429         // set whether rtl
430         bool rtl = false;
431         if (cur.inTexted()) {
432                 Paragraph const & par = cur.paragraph();
433                 Font const font =
434                 par.getFontSettings(cur.bv().buffer().params(), cur.pos());
435                 rtl = font.isVisibleRightToLeft();
436         }
437         popup()->setLayoutDirection(rtl ? Qt::RightToLeft : Qt::LeftToRight);
438
439         // turn the direction of the strings in the popup.
440         // Qt does not do that itself.
441         popup()->setItemDelegateForColumn(0, rtl ? rtlItemDelegate_ : 0);
442
443         // set new model
444         Inset::CompletionList const * list = cur.inset().createCompletionList(cur);
445         setModel(new GuiCompletionModel(this, list));
446         if (list->sorted())
447                 setModelSorting(QCompleter::CaseSensitivelySortedModel);
448         else
449                 setModelSorting(QCompleter::UnsortedModel);
450
451         // set prefix
452         QString newPrefix = toqstr(cur.inset().completionPrefix(cur));
453         if (newPrefix != completionPrefix())
454                 setCompletionPrefix(newPrefix);
455
456         // show popup
457         if (popupUpdate)
458                 updatePopup(cur);
459
460         // restore old selection
461         setCurrentCompletion(old);
462         
463         // if popup is not empty, the new selection will
464         // be our last valid one
465         if (popupVisible() || inlineVisible()) {
466                 QString const & s = currentCompletion();
467                 if (s.length() > 0)
468                         last_selection_ = s;
469                 else
470                         last_selection_ = old;
471         }
472
473         // show inline completion
474         if (inlineUpdate)
475                 updateInline(cur, currentCompletion());
476 }
477
478
479 void GuiCompleter::showPopup(Cursor & cur)
480 {
481         if (!popupPossible(cur))
482                 return;
483         
484         updateModel(cur, true, inlineVisible());
485 }
486
487
488 void GuiCompleter::hidePopup(Cursor & cur)
489 {
490         popup()->hide();
491         if (popup_timer_.isActive())
492                 popup_timer_.stop();
493         
494         if (!inlineVisible())
495                 setModel(new GuiCompletionModel(this, 0));
496 }
497
498
499 void GuiCompleter::showInline(Cursor & cur)
500 {
501         if (!inlinePossible(cur))
502                 return;
503         
504         updateModel(cur, popupVisible(), true);
505 }
506
507
508 void GuiCompleter::hideInline(Cursor & cur)
509 {
510         gui_->bufferView().setInlineCompletion(cur, DocIterator(), docstring());
511         inlineVisible_ = false;
512         
513         if (!popupVisible())
514                 setModel(new GuiCompletionModel(this, 0));
515 }
516
517
518 void GuiCompleter::showPopup()
519 {
520         Cursor cur = gui_->bufferView().cursor();
521         cur.updateFlags(Update::None);
522         
523         showPopup(cur);
524
525         // redraw if needed
526         if (cur.disp_.update())
527                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
528 }
529
530
531 void GuiCompleter::showInline()
532 {
533         Cursor cur = gui_->bufferView().cursor();
534         cur.updateFlags(Update::None);
535         
536         showInline(cur);
537
538         // redraw if needed
539         if (cur.disp_.update())
540                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
541 }
542
543
544 void GuiCompleter::hidePopup()
545 {
546         Cursor cur = gui_->bufferView().cursor();
547         cur.updateFlags(Update::None);
548         
549         hidePopup(cur);
550         
551         // redraw if needed
552         if (cur.disp_.update())
553                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
554 }
555
556
557 void GuiCompleter::hideInline()
558 {
559         Cursor cur = gui_->bufferView().cursor();
560         cur.updateFlags(Update::None);
561         
562         hideInline(cur);
563         
564         // redraw if needed
565         if (cur.disp_.update())
566                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
567 }
568
569
570 void GuiCompleter::activate()
571 {
572         if (!popupVisible() && !inlineVisible())
573                 return;
574
575         popupActivated(currentCompletion());
576 }
577
578
579 void GuiCompleter::tab()
580 {
581         BufferView * bv = &gui_->bufferView();
582         Cursor cur = bv->cursor();
583         cur.updateFlags(Update::None);
584         
585         // check that inline completion is active
586         if (!inlineVisible()) {
587                 // try to activate the inline completion
588                 if (cur.inset().inlineCompletionSupported(cur)) {
589                         showInline();
590                         
591                         // show popup without delay because the completion was not unique
592                         if (lyxrc.completion_popup_after_complete
593                             && !popupVisible()
594                             && popup()->model()->rowCount() > 1)
595                                 popup_timer_.start(0);
596
597                         return;
598                 }
599                 // or try popup
600                 if (!popupVisible() && cur.inset().completionSupported(cur)) {
601                         showPopup();
602                         return;
603                 }
604                 
605                 return;
606         }
607         
608         // If completion is active, at least complete by one character
609         docstring prefix = cur.inset().completionPrefix(cur);
610         docstring completion = from_utf8(fromqstr(currentCompletion()));
611         if (completion.size() <= prefix.size()) {
612                 // finalize completion
613                 cur.inset().insertCompletion(cur, docstring(), true);
614                 
615                 // hide popup and inline completion
616                 hidePopup(cur);
617                 hideInline(cur);
618                 updateVisibility(false, false);
619                 return;
620         }
621         docstring nextchar = completion.substr(prefix.size(), 1);
622         if (!cur.inset().insertCompletion(cur, nextchar, false))
623                 return;
624         updatePrefix(cur);
625
626         // try to complete as far as it is unique
627         docstring longestCompletion = longestUniqueCompletion();
628         prefix = cur.inset().completionPrefix(cur);
629         docstring postfix = longestCompletion.substr(min(longestCompletion.size(), prefix.size()));
630         cur.inset().insertCompletion(cur, postfix, false);
631         old_cursor_ = bv->cursor();
632         updatePrefix(cur);
633
634         // show popup without delay because the completion was not unique
635         if (lyxrc.completion_popup_after_complete
636             && !popupVisible()
637             && popup()->model()->rowCount() > 1)
638                 popup_timer_.start(0);
639
640         // redraw if needed
641         if (cur.disp_.update())
642                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
643 }
644
645
646 QString GuiCompleter::currentCompletion() const
647 {
648         if (!popup()->selectionModel()->hasSelection())
649                 return QString();
650
651         // Not sure if this is bug in Qt: currentIndex() always 
652         // return the first element in the list.
653         QModelIndex idx = popup()->currentIndex();
654         return popup()->model()->data(idx, Qt::EditRole).toString();
655 }
656
657
658 void GuiCompleter::setCurrentCompletion(QString const & s)
659 {       
660         QAbstractItemModel const & model = *popup()->model();
661         size_t n = model.rowCount();
662         if (n == 0)
663                 return;
664
665         // select the first if s is empty
666         if (s.length() == 0) {
667                 updateLock_++;
668                 popup()->setCurrentIndex(model.index(0, 0));
669                 updateLock_--;
670                 return;
671         }
672
673         // find old selection in model
674         size_t i;
675         if (modelSorting() == QCompleter::UnsortedModel) {
676                 // In unsorted models, iterate through list until the s is found
677                 for (i = 0; i < n; ++i) {
678                         QString const & is
679                         = model.data(model.index(i, 0), Qt::EditRole).toString();
680                         if (is == s)
681                                 break;
682                 }
683         } else {
684                 // In sorted models, do binary search for s.
685                 int l = 0;
686                 int r = n - 1;
687                 while (r >= l && l < int(n)) {
688                         size_t mid = (r + l) / 2;
689                         QString const & mids
690                         = model.data(model.index(mid, 0),
691                                      Qt::EditRole).toString();
692
693                         // left or right?
694                         // FIXME: is this really the same order that the docstring
695                         // from the CompletionList has?
696                         int c = s.compare(mids, Qt::CaseSensitive);
697                         if (c == 0) {
698                                 l = mid;
699                                 break;
700                         } else if (l == r) {
701                                 l = n;
702                                 break;
703                         } else if (c > 0)
704                                 // middle is not far enough
705                                 l = mid + 1;
706                         else
707                                 // middle is too far
708                                 r = mid - 1;
709                 }
710
711                 // loop was left without finding anything
712                 if (r < l)
713                         i = n;
714                 else
715                         i = l;
716                 BOOST_ASSERT(0 <= i && i <= n);
717         }
718
719         // select the first if none was found
720         if (i == n)
721                 i = 0;
722
723         updateLock_++;
724         popup()->setCurrentIndex(model.index(i, 0));
725         updateLock_--;
726 }
727
728
729 size_t commonPrefix(QString const & s1, QString const & s2)
730 {
731         // find common prefix
732         size_t j;
733         size_t n1 = s1.length();
734         size_t n2 = s2.length();
735         for (j = 0; j < n1 && j < n2; ++j) {
736                 if (s1.at(j) != s2.at(j))
737                         break;
738         }
739         return j;
740 }
741
742
743 docstring GuiCompleter::longestUniqueCompletion() const
744 {
745         QAbstractItemModel const & model = *popup()->model();
746         size_t n = model.rowCount();
747         if (n == 0)
748                 return docstring();
749         QString s = model.data(model.index(0, 0), Qt::EditRole).toString();
750         
751         if (modelSorting() == QCompleter::UnsortedModel) {
752                 // For unsorted model we cannot do more than iteration.
753                 // Iterate through the completions and cut off where s differs
754                 for (size_t i = 0; i < n && s.length() > 0; ++i) {
755                         QString const & is
756                         = model.data(model.index(i, 0), Qt::EditRole).toString();
757
758                         s = s.left(commonPrefix(is, s));
759                 }
760         } else {
761                 // For sorted models we can do binary search multiple times,
762                 // each time to find the first string which has s not as prefix.
763                 size_t i = 0;
764                 while (i < n && s.length() > 0) {
765                         // find first string that does not have s as prefix
766                         // via binary search in [i,n-1]
767                         size_t r = n - 1;
768                         do {
769                                 // get common prefix with the middle string
770                                 size_t mid = (r + i) / 2;
771                                 QString const & mids
772                                 = model.data(model.index(mid, 0), 
773                                         Qt::EditRole).toString();
774                                 size_t oldLen = s.length();
775                                 size_t len = commonPrefix(mids, s);
776                                 s = s.left(len);
777
778                                 // left or right?
779                                 if (oldLen == len) {
780                                         // middle is not far enough
781                                         i = mid + 1;
782                                 } else {
783                                         // middle is maybe too far
784                                         r = mid;
785                                 }
786                         } while (r - i > 0 && i < n);
787                 }
788         }
789
790         return from_utf8(fromqstr(s));
791 }
792
793
794 void GuiCompleter::popupActivated(const QString & completion)
795 {
796         Cursor cur = gui_->bufferView().cursor();
797         cur.updateFlags(Update::None);
798         
799         docstring prefix = cur.inset().completionPrefix(cur);
800         docstring postfix = from_utf8(fromqstr(completion.mid(prefix.length())));
801         cur.inset().insertCompletion(cur, postfix, true);
802         hidePopup(cur);
803         hideInline(cur);
804         
805         if (cur.disp_.update())
806                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
807 }
808
809
810 void GuiCompleter::popupHighlighted(const QString & completion)
811 {
812         if (updateLock_ > 0)
813                 return;
814
815         Cursor cur = gui_->bufferView().cursor();
816         cur.updateFlags(Update::None);
817         
818         if (inlineVisible())
819                 updateInline(cur, completion);
820         
821         if (cur.disp_.update())
822                 gui_->bufferView().processUpdateFlags(cur.disp_.update());
823 }
824
825 } // namespace frontend
826 } // namespace lyx
827
828 #include "GuiCompleter_moc.cpp"