]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiCompleter.cpp
* fix assert with 4.2.x when setting a null item delegate on an item view.
[lyx.git] / src / frontends / qt4 / GuiCompleter.cpp
index a6049ba81873030753963685a5e4f746cbb46ef1..a56df94e464d2175fa659c6f2d821b102e3a1169 100644 (file)
@@ -46,16 +46,27 @@ public:
        explicit RtlItemDelegate(QObject * parent = 0)
                : QItemDelegate(parent) {}
 
+       void setEnabled(bool enabled = true)
+       {
+               enabled_ = enabled;
+       }
+       
 protected:
        virtual void drawDisplay(QPainter * painter,
                QStyleOptionViewItem const & option,
                QRect const & rect, QString const & text) const
        {
+               if (!enabled_)
+                       return QItemDelegate::drawDisplay(painter, option, rect, text);
+
                // FIXME: do this more elegantly
                docstring stltext = qstring_to_ucs4(text);
                reverse(stltext.begin(), stltext.end());
                QItemDelegate::drawDisplay(painter, option, rect, toqstr(stltext));
        }
+       
+private:
+       bool enabled_;
 };
 
 
@@ -179,7 +190,8 @@ GuiCompleter::GuiCompleter(GuiWorkArea * gui, QObject * parent)
        setPopup(listView);
        popup()->setItemDelegateForColumn(1, new PixmapItemDelegate(this));
        rtlItemDelegate_ = new RtlItemDelegate(this);
-       
+       popup()->setItemDelegateForColumn(0, rtlItemDelegate_);
+
        // create timeout timers
        popup_timer_.setSingleShot(true);
        inline_timer_.setSingleShot(true);
@@ -233,6 +245,19 @@ bool GuiCompleter::inlinePossible(Cursor const & cur) const
 }
 
 
+bool GuiCompleter::completionAvailable() const
+{
+       size_t n = popup()->model()->rowCount();
+
+       // if there is exactly one, we have to check whether it is a 
+       // real completion, i.e. longer than the current prefix.
+       if (n == 1 && completionPrefix() == currentCompletion())
+           return false;
+
+       return n > 0;
+}
+
+
 bool GuiCompleter::popupVisible() const
 {
        return popup()->isVisible();
@@ -262,28 +287,12 @@ void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cu
        bool possibleInlineState = inlinePossible(cur) && cursorInView;
 
        // we moved or popup state is not ok for popup?
-       if ((moved && !keep) || !possiblePopupState) {
-               // stop an old completion timer
-               if (popup_timer_.isActive())
-                       popup_timer_.stop();
-
-               // hide old popup
-               if (popupVisible())
-                       popup()->hide();
-       }
+       if ((moved && !keep) || !possiblePopupState)
+               hidePopup(cur);
 
        // we moved or inline state is not ok for inline completion?
-       if ((moved && !keep) || !possibleInlineState) {
-               // stop an old completion timer
-               if (inline_timer_.isActive())
-                       inline_timer_.stop();
-
-               // hide old inline completion
-               if (inlineVisible()) {
-                       gui_->bufferView().setInlineCompletion(cur, DocIterator(), docstring());
-                       inlineVisible_ = false;
-               }
-       }
+       if ((moved && !keep) || !possibleInlineState)
+               hideInline(cur);
 
        // we inserted something and are in a possible popup state?
        if (!popupVisible() && possiblePopupState && start
@@ -295,10 +304,14 @@ void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cu
                && cur.inset().automaticInlineCompletion())
                inline_timer_.start(int(lyxrc.completion_inline_delay * 1000));
 
-       // update prefix if popup is visible or if it will be visible soon
-       if (popupVisible() || inlineVisible()
-           || popup_timer_.isActive() || inline_timer_.isActive())
-               updatePrefix(cur);
+       // update prefix if any completion is possible
+       bool modelActive = model()->rowCount() > 0;
+       if (possiblePopupState || possibleInlineState) {
+               if (modelActive)
+                       updatePrefix(cur);
+               else
+                       updateAvailability();
+       }
 }
 
 
@@ -339,11 +352,13 @@ void GuiCompleter::updatePrefix(Cursor & cur)
        // if popup is not empty, the new selection will
        // be our last valid one
        QString const & s = currentCompletion();
-       if (s.length() > 0)
-               last_selection_ = s;
-       else
-               last_selection_ = old;
-       
+       if (popupVisible() || inlineVisible()) {
+               if (s.length() > 0)
+                       last_selection_ = s;
+               else
+                       last_selection_ = old;
+       }
+
        // update inline completion because the default
        // completion string might have changed
        if (inlineVisible())
@@ -394,12 +409,27 @@ void GuiCompleter::updatePopup(Cursor & cur)
                rect = QRect(x, y - dim.ascent() - 3, 200, dim.height() + 6);
        
        // show/update popup
-       complete(rect);
        QTreeView * p = static_cast<QTreeView *>(popup());
        p->setColumnWidth(0, popup()->width() - 22 - p->verticalScrollBar()->width());
+
+       complete(rect);
 }
 
 
+void GuiCompleter::updateAvailability()
+{
+       // this should really only be of interest if no completion is
+       // visible yet, i.e. especially if automatic completion is disabled.
+       if (inlineVisible() || popupVisible())
+               return;
+       Cursor & cur = gui_->bufferView().cursor();
+       if (!popupPossible(cur) && !inlinePossible(cur))
+               return;
+       
+       updateModel(cur, false, false);
+}
+       
+
 void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate)
 {
        // value which should be kept selected
@@ -419,7 +449,7 @@ void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate
 
        // turn the direction of the strings in the popup.
        // Qt does not do that itself.
-       popup()->setItemDelegateForColumn(0, rtl ? rtlItemDelegate_ : 0);
+       rtlItemDelegate_->setEnabled(rtl);
 
        // set new model
        Inset::CompletionList const * list = cur.inset().createCompletionList(cur);
@@ -443,11 +473,13 @@ void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate
        
        // if popup is not empty, the new selection will
        // be our last valid one
-       QString const & s = currentCompletion();
-       if (s.length() > 0)
-               last_selection_ = s;
-       else
-               last_selection_ = old;
+       if (popupVisible() || inlineVisible()) {
+               QString const & s = currentCompletion();
+               if (s.length() > 0)
+                       last_selection_ = s;
+               else
+                       last_selection_ = old;
+       }
 
        // show inline completion
        if (inlineUpdate)
@@ -464,6 +496,17 @@ void GuiCompleter::showPopup(Cursor & cur)
 }
 
 
+void GuiCompleter::hidePopup(Cursor & cur)
+{
+       popup()->hide();
+       if (popup_timer_.isActive())
+               popup_timer_.stop();
+       
+       if (!inlineVisible())
+               setModel(new GuiCompletionModel(this, 0));
+}
+
+
 void GuiCompleter::showInline(Cursor & cur)
 {
        if (!inlinePossible(cur))
@@ -473,6 +516,16 @@ void GuiCompleter::showInline(Cursor & cur)
 }
 
 
+void GuiCompleter::hideInline(Cursor & cur)
+{
+       gui_->bufferView().setInlineCompletion(cur, DocIterator(), docstring());
+       inlineVisible_ = false;
+       
+       if (!popupVisible())
+               setModel(new GuiCompletionModel(this, 0));
+}
+
+
 void GuiCompleter::showPopup()
 {
        Cursor cur = gui_->bufferView().cursor();
@@ -499,15 +552,38 @@ void GuiCompleter::showInline()
 }
 
 
+void GuiCompleter::hidePopup()
+{
+       Cursor cur = gui_->bufferView().cursor();
+       cur.updateFlags(Update::None);
+       
+       hidePopup(cur);
+       
+       // redraw if needed
+       if (cur.disp_.update())
+               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+}
+
+
+void GuiCompleter::hideInline()
+{
+       Cursor cur = gui_->bufferView().cursor();
+       cur.updateFlags(Update::None);
+       
+       hideInline(cur);
+       
+       // redraw if needed
+       if (cur.disp_.update())
+               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+}
+
+
 void GuiCompleter::activate()
 {
        if (!popupVisible() && !inlineVisible())
                return;
 
-       // Complete with current selection in the popup.
-       QString s = currentCompletion();
-       popup()->hide();
-       popupActivated(s);
+       popupActivated(currentCompletion());
 }
 
 
@@ -548,9 +624,8 @@ void GuiCompleter::tab()
                cur.inset().insertCompletion(cur, docstring(), true);
                
                // hide popup and inline completion
-               popup()->hide();
-               gui_->bufferView().setInlineCompletion(cur, DocIterator(), docstring());
-               inlineVisible_ = false;
+               hidePopup(cur);
+               hideInline(cur);
                updateVisibility(false, false);
                return;
        }
@@ -618,10 +693,10 @@ void GuiCompleter::setCurrentCompletion(QString const & s)
                }
        } else {
                // In sorted models, do binary search for s.
-               i = 0;
-               size_t r = n - 1;
-               while (r >= i && i < n) {
-                       size_t mid = (r + i) / 2;
+               int l = 0;
+               int r = n - 1;
+               while (r >= l && l < int(n)) {
+                       size_t mid = (r + l) / 2;
                        QString const & mids
                        = model.data(model.index(mid, 0),
                                     Qt::EditRole).toString();
@@ -631,22 +706,25 @@ void GuiCompleter::setCurrentCompletion(QString const & s)
                        // from the CompletionList has?
                        int c = s.compare(mids, Qt::CaseSensitive);
                        if (c == 0) {
-                               i = mid;
+                               l = mid;
                                break;
-                       } else if (i == r) {
-                               i = n;
+                       } else if (l == r) {
+                               l = n;
                                break;
                        } else if (c > 0)
                                // middle is not far enough
-                               i = mid + 1;
+                               l = mid + 1;
                        else
                                // middle is too far
                                r = mid - 1;
                }
 
                // loop was left without finding anything
-               if (r < i)
+               if (r < l)
                        i = n;
+               else
+                       i = l;
+               BOOST_ASSERT(0 <= i && i <= n);
        }
 
        // select the first if none was found
@@ -676,9 +754,11 @@ size_t commonPrefix(QString const & s1, QString const & s2)
 docstring GuiCompleter::longestUniqueCompletion() const
 {
        QAbstractItemModel const & model = *popup()->model();
-       QString s = currentCompletion();
        size_t n = model.rowCount();
-
+       if (n == 0)
+               return docstring();
+       QString s = model.data(model.index(0, 0), Qt::EditRole).toString();
+       
        if (modelSorting() == QCompleter::UnsortedModel) {
                // For unsorted model we cannot do more than iteration.
                // Iterate through the completions and cut off where s differs
@@ -730,7 +810,8 @@ void GuiCompleter::popupActivated(const QString & completion)
        docstring prefix = cur.inset().completionPrefix(cur);
        docstring postfix = from_utf8(fromqstr(completion.mid(prefix.length())));
        cur.inset().insertCompletion(cur, postfix, true);
-       updateVisibility(cur, false);
+       hidePopup(cur);
+       hideInline(cur);
        
        if (cur.disp_.update())
                gui_->bufferView().processUpdateFlags(cur.disp_.update());
@@ -745,7 +826,8 @@ void GuiCompleter::popupHighlighted(const QString & completion)
        Cursor cur = gui_->bufferView().cursor();
        cur.updateFlags(Update::None);
        
-       updateInline(cur, completion);
+       if (inlineVisible())
+               updateInline(cur, completion);
        
        if (cur.disp_.update())
                gui_->bufferView().processUpdateFlags(cur.disp_.update());