#include "Buffer.h"
#include "BufferView.h"
+#include "CompletionList.h"
#include "Cursor.h"
#include "Dimension.h"
#include "FuncRequest.h"
#include "Paragraph.h"
#include "version.h"
+#include "support/assert.h"
#include "support/debug.h"
#include <QApplication>
-#include <QAbstractListModel>
#include <QHeaderView>
#include <QPainter>
#include <QPixmapCache>
namespace lyx {
namespace frontend {
-class RtlItemDelegate : public QItemDelegate {
+class RtlItemDelegate : public QItemDelegate
+{
public:
- explicit RtlItemDelegate(QObject * parent = 0)
- : QItemDelegate(parent) {}
+ explicit RtlItemDelegate(QObject * parent)
+ : QItemDelegate(parent), enabled_(false)
+ {}
+ void setEnabled(bool enabled = true)
+ {
+ enabled_ = enabled;
+ }
+
protected:
- virtual void drawDisplay(QPainter * painter,
+ void drawDisplay(QPainter * painter,
QStyleOptionViewItem const & option,
QRect const & rect, QString const & text) const
{
+ if (!enabled_) {
+ QItemDelegate::drawDisplay(painter, option, rect, text);
+ return;
+ }
+
// 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_;
};
-class PixmapItemDelegate : public QItemDelegate {
+class PixmapItemDelegate : public QItemDelegate
+{
public:
- explicit PixmapItemDelegate(QObject *parent = 0)
- : QItemDelegate(parent) {}
+ explicit PixmapItemDelegate(QObject * parent)
+ : QItemDelegate(parent)
+ {}
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
};
-class GuiCompletionModel : public QAbstractListModel {
+class GuiCompletionModel : public QAbstractListModel
+{
public:
///
- GuiCompletionModel(QObject * parent,
- Inset::CompletionList const * l)
- : QAbstractListModel(parent), list_(l) {}
+ GuiCompletionModel(QObject * parent, CompletionList const * l)
+ : QAbstractListModel(parent), list_(l)
+ {}
///
- ~GuiCompletionModel()
- { delete list_; }
+ ~GuiCompletionModel() { delete list_; }
///
bool sorted() const
{
if (list_)
return list_->sorted();
- else
- return false;
+ return false;
}
///
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
- if (index.column() == 0) {
- docstring const word = list_->data(index.row());
- return toqstr(word);
- } else if (index.column() == 1) {
+ 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()));
}
private:
- ///
- Inset::CompletionList const * list_;
+ /// owned by us
+ CompletionList const * list_;
};
GuiCompleter::GuiCompleter(GuiWorkArea * gui, QObject * parent)
: QCompleter(parent), gui_(gui), updateLock_(0),
- inlineVisible_(false)
+ inlineVisible_(false), popupVisible_(false),
+ modelActive_(false)
{
// Setup the completion popup
setModel(new GuiCompletionModel(this, 0));
listView->setIndentation(0);
listView->setUniformRowHeights(true);
setPopup(listView);
- popup()->setItemDelegateForColumn(1, new PixmapItemDelegate(this));
+
rtlItemDelegate_ = new RtlItemDelegate(this);
+ popup()->setItemDelegateForColumn(0, rtlItemDelegate_);
+ popup()->setItemDelegateForColumn(1, new PixmapItemDelegate(this));
// create timeout timers
popup_timer_.setSingleShot(true);
bool GuiCompleter::completionAvailable() const
{
+ if (!modelActive_)
+ return false;
+
size_t n = popup()->model()->rowCount();
// if there is exactly one, we have to check whether it is a
bool GuiCompleter::popupVisible() const
{
- return popup()->isVisible();
+ return popupVisible_;
}
inline_timer_.start(int(lyxrc.completion_inline_delay * 1000));
// update prefix if any completion is possible
- bool modelActive = model()->rowCount() > 0;
+ bool modelActive = modelActive_ && model()->rowCount() > 0;
if (possiblePopupState || possibleInlineState) {
if (modelActive)
updatePrefix(cur);
if (!cur.inset().completionSupported(cur))
return;
- if (completionCount() == 0)
+ popupVisible_ = true;
+
+ if (completionCount() == 0) {
+ QTimer::singleShot(0, popup(), SLOT(hide()));
return;
-
+ }
+
+ QTimer::singleShot(0, this, SLOT(asyncUpdatePopup()));
+}
+
+
+void GuiCompleter::asyncUpdatePopup()
+{
+ Cursor cur = gui_->bufferView().cursor();
+ if (!cur.inset().completionSupported(cur)) {
+ popupVisible_ = false;
+ return;
+ }
+
// get dimensions of completion prefix
Dimension dim;
int x;
else
rect = QRect(x, y - dim.ascent() - 3, 200, dim.height() + 6);
+ // Resize the columns in the popup.
+ // This should really be in the constructor. But somehow the treeview
+ // 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);
+ listView->header()->resizeSection(1, 22);
+
// show/update popup
- QTreeView * p = static_cast<QTreeView *>(popup());
- p->setColumnWidth(0, popup()->width() - 22 - p->verticalScrollBar()->width());
-
complete(rect);
}
// 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);
+ CompletionList const * list = cur.inset().createCompletionList(cur);
setModel(new GuiCompletionModel(this, list));
+ modelActive_ = true;
if (list->sorted())
setModelSorting(QCompleter::CaseSensitivelySortedModel);
else
}
-void GuiCompleter::hidePopup(Cursor & cur)
+void GuiCompleter::hidePopup(Cursor &)
{
- popup()->hide();
+ 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();
if (!inlineVisible())
setModel(new GuiCompletionModel(this, 0));
}
gui_->bufferView().setInlineCompletion(cur, DocIterator(), docstring());
inlineVisible_ = false;
+ if (inline_timer_.isActive())
+ inline_timer_.stop();
+
+ // Trigger asynchronous part of hideInline. We might be
+ // in a dispatcher here and the setModel call might
+ // trigger focus events which is are not healthy here.
+ QTimer::singleShot(0, this, SLOT(asyncHideInline()));
+
+ // mark that the asynchronous part will reset the model
+ if (!popupVisible())
+ modelActive_ = false;
+}
+
+
+void GuiCompleter::asyncHideInline()
+{
if (!popupVisible())
setModel(new GuiCompletionModel(this, 0));
}
i = n;
else
i = l;
- BOOST_ASSERT(0 <= i && i <= n);
+ LASSERT(i <= n, /**/);
}
// select the first if none was found
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