]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiCompleter.cpp
On Linux show in crash message box the backtrace
[lyx.git] / src / frontends / qt4 / GuiCompleter.cpp
index 884bde6d873143d7705c74874ba2a84d94f81a05..87c5aa9007790871d43e44128ec337e4b305a4c5 100644 (file)
 #include "CompletionList.h"
 #include "Cursor.h"
 #include "Dimension.h"
-#include "FuncRequest.h"
 #include "GuiWorkArea.h"
 #include "GuiView.h"
-#include "LyXFunc.h"
+#include "LyX.h"
 #include "LyXRC.h"
 #include "Paragraph.h"
+#include "qt_helpers.h"
 #include "version.h"
 
 #include "support/lassert.h"
@@ -30,6 +30,7 @@
 
 #include <QApplication>
 #include <QHeaderView>
+#include <QKeyEvent>
 #include <QPainter>
 #include <QPixmapCache>
 #include <QScrollBar>
@@ -90,9 +91,10 @@ public:
        ~GuiCompletionModel() { delete list_; }
        ///
        void setList(CompletionList const * l) {
+               beginResetModel();
                delete list_;
                list_ = l;
-               reset();
+               endResetModel();
        }
        ///
        bool sorted() const
@@ -111,8 +113,7 @@ public:
        {
                if (list_ == 0)
                        return 0;
-               else
-                       return list_->size();
+               return list_->size();
        }
 
        ///
@@ -130,24 +131,23 @@ public:
                if (index.column() == 0)
                        return toqstr(list_->data(index.row()));
 
-               if (index.column() == 1) {
-                       // get icon from cache
-                       QPixmap scaled;
-                       QString const name = ":" + toqstr(list_->icon(index.row()));
-                       if (!QPixmapCache::find("completion" + name, scaled)) {
-                               // load icon from disk
-                               QPixmap p = QPixmap(name);
-                               if (!p.isNull()) {
-                                       // scale it to 16x16 or smaller
-                                       scaled = p.scaled(min(16, p.width()), min(16, p.height()), 
-                                               Qt::KeepAspectRatio, Qt::SmoothTransformation);
-                               }
-
-                               QPixmapCache::insert("completion" + name, scaled);
+               if (index.column() != 1)
+                       return QVariant();
+       
+               // get icon from cache
+               QPixmap scaled;
+               QString const name = ":" + toqstr(list_->icon(index.row()));
+               if (!QPixmapCache::find("completion" + name, scaled)) {
+                       // load icon from disk
+                       QPixmap p = QPixmap(name);
+                       if (!p.isNull()) {
+                               // scale it to 16x16 or smaller
+                               scaled = p.scaled(min(16, p.width()), min(16, p.height()), 
+                                       Qt::KeepAspectRatio, Qt::SmoothTransformation);
                        }
-                       return scaled;
+                       QPixmapCache::insert("completion" + name, scaled);
                }
-               return QVariant();
+               return scaled;
        }
 
 private:
@@ -165,14 +165,15 @@ GuiCompleter::GuiCompleter(GuiWorkArea * gui, QObject * parent)
        model_ = new GuiCompletionModel(this, 0);
        setModel(model_);
        setCompletionMode(QCompleter::PopupCompletion);
+       setCaseSensitivity(Qt::CaseSensitive);
        setWidget(gui_);
        
        // create the popup
        QTreeView *listView = new QTreeView;
-        listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
-        listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-        listView->setSelectionBehavior(QAbstractItemView::SelectRows);
-        listView->setSelectionMode(QAbstractItemView::SingleSelection);
+       listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+       listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+       listView->setSelectionBehavior(QAbstractItemView::SelectRows);
+       listView->setSelectionMode(QAbstractItemView::SingleSelection);
        listView->header()->hide();
        listView->setIndentation(0);
        listView->setUniformRowHeights(true);
@@ -286,19 +287,19 @@ bool GuiCompleter::inlineVisible() const
 }
 
 
-void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cursorInView)
+void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep)
 {
        // parameters which affect the completion
        bool moved = cur != old_cursor_;
        if (moved)
                old_cursor_ = cur;
 
-       bool possiblePopupState = popupPossible(cur) && cursorInView;
-       bool possibleInlineState = inlinePossible(cur) && cursorInView;
+       bool const possiblePopupState = popupPossible(cur);
+       bool const possibleInlineState = inlinePossible(cur);
 
        // we moved or popup state is not ok for popup?
        if ((moved && !keep) || !possiblePopupState)
-               hidePopup(cur);
+               hidePopup();
 
        // we moved or inline state is not ok for inline completion?
        if ((moved && !keep) || !possibleInlineState)
@@ -313,6 +314,11 @@ void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cu
        if (!inlineVisible() && possibleInlineState && start
                && cur.inset().automaticInlineCompletion())
                inline_timer_.start(int(lyxrc.completion_inline_delay * 1000));
+       else if (cur.inMathed() && !lyxrc.completion_inline_math) {
+               // no inline completion, hence a metrics update is needed
+               if (!(cur.result().screenUpdate() & Update::Force))
+                       cur.screenUpdateFlags(cur.result().screenUpdate() | Update::SinglePar);
+       }
 
        // update prefix if any completion is possible
        bool modelActive = modelActive_ && model()->rowCount() > 0;
@@ -328,16 +334,16 @@ void GuiCompleter::updateVisibility(Cursor & cur, bool start, bool keep, bool cu
 void GuiCompleter::updateVisibility(bool start, bool keep)
 {
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        updateVisibility(cur, start, keep);
        
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
 }
 
 
-void GuiCompleter::updatePrefix(Cursor & cur)
+void GuiCompleter::updatePrefix(Cursor const & cur)
 {
        // get new prefix. Do nothing if unchanged
        QString newPrefix = toqstr(cur.inset().completionPrefix(cur));
@@ -376,7 +382,7 @@ void GuiCompleter::updatePrefix(Cursor & cur)
 }
 
 
-void GuiCompleter::updateInline(Cursor & cur, QString const & completion)
+void GuiCompleter::updateInline(Cursor const & cur, QString const & completion)
 {
        if (!cur.inset().inlineCompletionSupported(cur))
                return;
@@ -397,7 +403,7 @@ void GuiCompleter::updateInline(Cursor & cur, QString const & completion)
 }
 
 
-void GuiCompleter::updatePopup(Cursor & cur)
+void GuiCompleter::updatePopup(Cursor const & cur)
 {
        if (!cur.inset().completionSupported(cur))
                return;
@@ -416,7 +422,8 @@ void GuiCompleter::updatePopup(Cursor & cur)
 void GuiCompleter::asyncUpdatePopup()
 {
        Cursor cur = gui_->bufferView().cursor();
-       if (!cur.inset().completionSupported(cur)) {
+       if (!cur.inset().completionSupported(cur)
+                 || !cur.bv().paragraphVisible(cur)) {
                popupVisible_ = false;
                return;
        }
@@ -439,8 +446,8 @@ void GuiCompleter::asyncUpdatePopup()
        // has a bad memory about it and we have to tell him again and again.
        QTreeView * listView = static_cast<QTreeView *>(popup());
        listView->header()->setStretchLastSection(false);
-       listView->header()->setResizeMode(0, QHeaderView::Stretch);
-       listView->header()->setResizeMode(1, QHeaderView::Fixed);
+       setSectionResizeMode(listView->header(), 0, QHeaderView::Stretch);
+       setSectionResizeMode(listView->header(), 1, QHeaderView::Fixed);
        listView->header()->resizeSection(1, 22);
        
        // show/update popup
@@ -454,7 +461,7 @@ void GuiCompleter::updateAvailability()
        // visible yet, i.e. especially if automatic completion is disabled.
        if (inlineVisible() || popupVisible())
                return;
-       Cursor & cur = gui_->bufferView().cursor();
+       Cursor const & cur = gui_->bufferView().cursor();
        if (!popupPossible(cur) && !inlinePossible(cur))
                return;
        
@@ -462,7 +469,7 @@ void GuiCompleter::updateAvailability()
 }
        
 
-void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate)
+void GuiCompleter::updateModel(Cursor const & cur, bool popupUpdate, bool inlineUpdate)
 {
        // value which should be kept selected
        QString old = currentCompletion();
@@ -516,7 +523,7 @@ void GuiCompleter::updateModel(Cursor & cur, bool popupUpdate, bool inlineUpdate
 }
 
 
-void GuiCompleter::showPopup(Cursor & cur)
+void GuiCompleter::showPopup(Cursor const & cur)
 {
        if (!popupPossible(cur))
                return;
@@ -525,26 +532,6 @@ void GuiCompleter::showPopup(Cursor & cur)
 }
 
 
-void GuiCompleter::hidePopup(Cursor &)
-{
-       popupVisible_ = false;
-
-       if (popup_timer_.isActive())
-               popup_timer_.stop();
-
-       // hide popup asynchronously because we might be here inside of
-       // LFUN dispatchers. Hiding a popup can trigger a focus event on the 
-       // workarea which then redisplays the cursor. But the metrics are not
-       // yet up to date such that the coord cache has not all insets yet. The
-       // cursorPos methods would triggers asserts in the coord cache then.
-       QTimer::singleShot(0, this, SLOT(asyncHidePopup()));
-       
-       // mark that the asynchronous part will reset the model
-       if (!inlineVisible())
-               modelActive_ = false;
-}
-
-
 void GuiCompleter::asyncHidePopup()
 {
        popup()->hide();
@@ -553,7 +540,7 @@ void GuiCompleter::asyncHidePopup()
 }
 
 
-void GuiCompleter::showInline(Cursor & cur)
+void GuiCompleter::showInline(Cursor const & cur)
 {
        if (!inlinePossible(cur))
                return;
@@ -562,7 +549,7 @@ void GuiCompleter::showInline(Cursor & cur)
 }
 
 
-void GuiCompleter::hideInline(Cursor & cur)
+void GuiCompleter::hideInline(Cursor const & cur)
 {
        gui_->bufferView().setInlineCompletion(cur, DocIterator(cur.buffer()), docstring());
        inlineVisible_ = false;
@@ -591,52 +578,59 @@ void GuiCompleter::asyncHideInline()
 void GuiCompleter::showPopup()
 {
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        showPopup(cur);
 
        // redraw if needed
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
 }
 
 
 void GuiCompleter::showInline()
 {
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        showInline(cur);
 
        // redraw if needed
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
 }
 
 
 void GuiCompleter::hidePopup()
 {
-       Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
-       
-       hidePopup(cur);
+       popupVisible_ = false;
+
+       if (popup_timer_.isActive())
+               popup_timer_.stop();
+
+       // hide popup asynchronously because we might be here inside of
+       // LFUN dispatchers. Hiding a popup can trigger a focus event on the
+       // workarea which then redisplays the cursor. But the metrics are not
+       // yet up to date such that the coord cache has not all insets yet. The
+       // cursorPos methods would triggers asserts in the coord cache then.
+       QTimer::singleShot(0, this, SLOT(asyncHidePopup()));
        
-       // redraw if needed
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       // mark that the asynchronous part will reset the model
+       if (!inlineVisible())
+               modelActive_ = false;
 }
 
 
 void GuiCompleter::hideInline()
 {
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        hideInline(cur);
        
        // redraw if needed
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
 }
 
 
@@ -653,7 +647,7 @@ void GuiCompleter::tab()
 {
        BufferView * bv = &gui_->bufferView();
        Cursor cur = bv->cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        // check that inline completion is active
        if (!inlineVisible() && !uniqueCompletionAvailable()) {
@@ -679,6 +673,7 @@ void GuiCompleter::tab()
        }
        
        // Make undo possible
+       cur.beginUndoGroup();
        cur.recordUndo();
 
        // If completion is active, at least complete by one character
@@ -689,14 +684,17 @@ void GuiCompleter::tab()
                cur.inset().insertCompletion(cur, docstring(), true);
                
                // hide popup and inline completion
-               hidePopup(cur);
+               hidePopup();
                hideInline(cur);
                updateVisibility(false, false);
+               cur.endUndoGroup();
                return;
        }
        docstring nextchar = completion.substr(prefix.size(), 1);
-       if (!cur.inset().insertCompletion(cur, nextchar, false))
+       if (!cur.inset().insertCompletion(cur, nextchar, false)) {
+               cur.endUndoGroup();
                return;
+       }
        updatePrefix(cur);
 
        // try to complete as far as it is unique
@@ -714,8 +712,9 @@ void GuiCompleter::tab()
                popup_timer_.start(0);
 
        // redraw if needed
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
+       cur.endUndoGroup();
 }
 
 
@@ -789,7 +788,8 @@ void GuiCompleter::setCurrentCompletion(QString const & s)
                        i = n;
                else
                        i = l;
-               LASSERT(i <= n, /**/);
+               // we can try to recover
+               LASSERT(i <= n, i = 0);
        }
 
        // select the first if none was found
@@ -870,18 +870,20 @@ docstring GuiCompleter::longestUniqueCompletion() const
 void GuiCompleter::popupActivated(const QString & completion)
 {
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
-       
+       cur.screenUpdateFlags(Update::None);
+
+       cur.beginUndoGroup();
        cur.recordUndo();
 
        docstring prefix = cur.inset().completionPrefix(cur);
        docstring postfix = qstring_to_ucs4(completion.mid(prefix.length()));
        cur.inset().insertCompletion(cur, postfix, true);
-       hidePopup(cur);
+       hidePopup();
        hideInline(cur);
        
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
+       cur.endUndoGroup();
 }
 
 
@@ -891,13 +893,13 @@ void GuiCompleter::popupHighlighted(const QString & completion)
                return;
 
        Cursor cur = gui_->bufferView().cursor();
-       cur.updateFlags(Update::None);
+       cur.screenUpdateFlags(Update::None);
        
        if (inlineVisible())
                updateInline(cur, completion);
        
-       if (cur.disp_.update())
-               gui_->bufferView().processUpdateFlags(cur.disp_.update());
+       if (cur.result().screenUpdate())
+               gui_->bufferView().processUpdateFlags(cur.result().screenUpdate());
 }
 
 } // namespace frontend