2011-04-15 16:19:23 +02:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
|
|
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
|
|
**
|
2011-11-02 15:59:12 +01:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2011-04-15 16:19:23 +02:00
|
|
|
**
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
|
|
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
|
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
|
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
|
|
|
|
** Please review the following information to ensure the GNU Lesser General
|
|
|
|
|
** Public License version 2.1 requirements will be met:
|
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
|
|
|
|
** Other Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
|
**
|
|
|
|
|
** If you have questions regarding the use of this file, please contact
|
2011-11-02 15:59:12 +01:00
|
|
|
** Nokia at qt-info@nokia.com.
|
2011-04-15 16:19:23 +02:00
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "functionhintproposalwidget.h"
|
|
|
|
|
#include "ifunctionhintproposalmodel.h"
|
|
|
|
|
#include "codeassistant.h"
|
|
|
|
|
|
|
|
|
|
#include <utils/faketooltip.h>
|
|
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtGui/QApplication>
|
|
|
|
|
#include <QtGui/QLabel>
|
|
|
|
|
#include <QtGui/QToolButton>
|
|
|
|
|
#include <QtGui/QHBoxLayout>
|
|
|
|
|
#include <QtGui/QVBoxLayout>
|
|
|
|
|
#include <QtGui/QDesktopWidget>
|
|
|
|
|
#include <QtGui/QKeyEvent>
|
|
|
|
|
#include <QtGui/QShortcutEvent>
|
|
|
|
|
|
|
|
|
|
namespace TextEditor {
|
|
|
|
|
|
|
|
|
|
// -------------------------
|
|
|
|
|
// HintProposalWidgetPrivate
|
|
|
|
|
// -------------------------
|
|
|
|
|
struct FunctionHintProposalWidgetPrivate
|
|
|
|
|
{
|
|
|
|
|
FunctionHintProposalWidgetPrivate();
|
|
|
|
|
|
|
|
|
|
const QWidget *m_underlyingWidget;
|
|
|
|
|
CodeAssistant *m_assistant;
|
|
|
|
|
IFunctionHintProposalModel *m_model;
|
|
|
|
|
Utils::FakeToolTip *m_popupFrame;
|
|
|
|
|
QLabel *m_numberLabel;
|
|
|
|
|
QLabel *m_hintLabel;
|
|
|
|
|
QWidget *m_pager;
|
|
|
|
|
QRect m_displayRect;
|
|
|
|
|
int m_currentHint;
|
|
|
|
|
int m_totalHints;
|
|
|
|
|
int m_currentArgument;
|
|
|
|
|
bool m_escapePressed;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FunctionHintProposalWidgetPrivate::FunctionHintProposalWidgetPrivate()
|
|
|
|
|
: m_underlyingWidget(0)
|
|
|
|
|
, m_assistant(0)
|
|
|
|
|
, m_model(0)
|
|
|
|
|
, m_popupFrame(new Utils::FakeToolTip)
|
|
|
|
|
, m_numberLabel(new QLabel)
|
|
|
|
|
, m_hintLabel(new QLabel)
|
|
|
|
|
, m_pager(new QWidget)
|
|
|
|
|
, m_currentHint(-1)
|
|
|
|
|
, m_totalHints(0)
|
|
|
|
|
, m_currentArgument(-1)
|
|
|
|
|
, m_escapePressed(false)
|
|
|
|
|
{
|
|
|
|
|
m_hintLabel->setTextFormat(Qt::RichText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------
|
|
|
|
|
// HintProposalWidget
|
|
|
|
|
// ------------------
|
|
|
|
|
FunctionHintProposalWidget::FunctionHintProposalWidget()
|
2011-09-16 13:10:06 +02:00
|
|
|
: d(new FunctionHintProposalWidgetPrivate)
|
2011-04-15 16:19:23 +02:00
|
|
|
{
|
|
|
|
|
QToolButton *downArrow = new QToolButton;
|
|
|
|
|
downArrow->setArrowType(Qt::DownArrow);
|
|
|
|
|
downArrow->setFixedSize(16, 16);
|
|
|
|
|
downArrow->setAutoRaise(true);
|
|
|
|
|
|
|
|
|
|
QToolButton *upArrow = new QToolButton;
|
|
|
|
|
upArrow->setArrowType(Qt::UpArrow);
|
|
|
|
|
upArrow->setFixedSize(16, 16);
|
|
|
|
|
upArrow->setAutoRaise(true);
|
|
|
|
|
|
2011-09-16 13:10:06 +02:00
|
|
|
QHBoxLayout *pagerLayout = new QHBoxLayout(d->m_pager);
|
2011-04-15 16:19:23 +02:00
|
|
|
pagerLayout->setMargin(0);
|
|
|
|
|
pagerLayout->setSpacing(0);
|
|
|
|
|
pagerLayout->addWidget(upArrow);
|
2011-09-16 13:10:06 +02:00
|
|
|
pagerLayout->addWidget(d->m_numberLabel);
|
2011-04-15 16:19:23 +02:00
|
|
|
pagerLayout->addWidget(downArrow);
|
|
|
|
|
|
2011-09-16 13:10:06 +02:00
|
|
|
QHBoxLayout *popupLayout = new QHBoxLayout(d->m_popupFrame);
|
2011-04-15 16:19:23 +02:00
|
|
|
popupLayout->setMargin(0);
|
|
|
|
|
popupLayout->setSpacing(0);
|
2011-09-16 13:10:06 +02:00
|
|
|
popupLayout->addWidget(d->m_pager);
|
|
|
|
|
popupLayout->addWidget(d->m_hintLabel);
|
2011-04-15 16:19:23 +02:00
|
|
|
|
|
|
|
|
connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
|
|
|
|
|
connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
|
|
|
|
|
|
|
|
|
|
qApp->installEventFilter(this);
|
|
|
|
|
|
|
|
|
|
setFocusPolicy(Qt::NoFocus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FunctionHintProposalWidget::~FunctionHintProposalWidget()
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
delete d->m_model;
|
|
|
|
|
delete d;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setAssistant(CodeAssistant *assistant)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_assistant = assistant;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
2011-06-23 15:04:01 +02:00
|
|
|
void FunctionHintProposalWidget::setReason(AssistReason)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setKind(AssistKind)
|
|
|
|
|
{}
|
2011-04-15 16:19:23 +02:00
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setUnderlyingWidget(const QWidget *underlyingWidget)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_underlyingWidget = underlyingWidget;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setModel(IAssistProposalModel *model)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_model = static_cast<IFunctionHintProposalModel *>(model);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setDisplayRect(const QRect &rect)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_displayRect = rect;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::setIsSynchronized(bool)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::showProposal(const QString &prefix)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_totalHints = d->m_model->size();
|
|
|
|
|
if (d->m_totalHints == 0) {
|
2011-04-15 16:19:23 +02:00
|
|
|
abort();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_pager->setVisible(d->m_totalHints > 1);
|
|
|
|
|
d->m_currentHint = 0;
|
2011-04-15 16:19:23 +02:00
|
|
|
if (!updateAndCheck(prefix)) {
|
|
|
|
|
abort();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_popupFrame->show();
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::updateProposal(const QString &prefix)
|
|
|
|
|
{
|
|
|
|
|
updateAndCheck(prefix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::closeProposal()
|
|
|
|
|
{
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::abort()
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
if (d->m_popupFrame->isVisible())
|
|
|
|
|
d->m_popupFrame->close();
|
2011-04-15 16:19:23 +02:00
|
|
|
deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FunctionHintProposalWidget::eventFilter(QObject *obj, QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
switch (e->type()) {
|
|
|
|
|
case QEvent::ShortcutOverride:
|
|
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_escapePressed = true;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case QEvent::KeyPress:
|
|
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_escapePressed = true;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
2011-09-16 13:10:06 +02:00
|
|
|
if (d->m_model->size() > 1) {
|
2011-04-15 16:19:23 +02:00
|
|
|
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
|
|
|
|
|
if (ke->key() == Qt::Key_Up) {
|
|
|
|
|
previousPage();
|
|
|
|
|
return true;
|
|
|
|
|
} else if (ke->key() == Qt::Key_Down) {
|
|
|
|
|
nextPage();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case QEvent::KeyRelease:
|
2011-09-16 13:10:06 +02:00
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_escapePressed) {
|
2011-04-15 16:19:23 +02:00
|
|
|
abort();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_assistant->notifyChange();
|
2011-04-15 16:19:23 +02:00
|
|
|
break;
|
|
|
|
|
case QEvent::WindowDeactivate:
|
|
|
|
|
case QEvent::FocusOut:
|
2011-09-16 13:10:06 +02:00
|
|
|
if (obj != d->m_underlyingWidget) {
|
2011-04-15 16:19:23 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
abort();
|
|
|
|
|
break;
|
|
|
|
|
case QEvent::MouseButtonPress:
|
|
|
|
|
case QEvent::MouseButtonRelease:
|
|
|
|
|
case QEvent::MouseButtonDblClick:
|
|
|
|
|
case QEvent::Wheel: {
|
|
|
|
|
QWidget *widget = qobject_cast<QWidget *>(obj);
|
2011-11-03 13:14:39 -04:00
|
|
|
if (!d->m_popupFrame->isAncestorOf(widget)) {
|
2011-04-15 16:19:23 +02:00
|
|
|
abort();
|
2011-11-03 13:14:39 -04:00
|
|
|
} else if (e->type() == QEvent::Wheel) {
|
|
|
|
|
if (static_cast<QWheelEvent*>(e)->delta() > 0) {
|
|
|
|
|
previousPage();
|
|
|
|
|
} else {
|
|
|
|
|
nextPage();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::nextPage()
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_currentHint = (d->m_currentHint + 1) % d->m_totalHints;
|
2011-04-15 16:19:23 +02:00
|
|
|
updateContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::previousPage()
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
if (d->m_currentHint == 0)
|
|
|
|
|
d->m_currentHint = d->m_totalHints - 1;
|
2011-04-15 16:19:23 +02:00
|
|
|
else
|
2011-09-16 13:10:06 +02:00
|
|
|
--d->m_currentHint;
|
2011-04-15 16:19:23 +02:00
|
|
|
updateContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FunctionHintProposalWidget::updateAndCheck(const QString &prefix)
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
const int activeArgument = d->m_model->activeArgument(prefix);
|
2011-04-15 16:19:23 +02:00
|
|
|
if (activeArgument == -1) {
|
|
|
|
|
abort();
|
|
|
|
|
return false;
|
2011-09-16 13:10:06 +02:00
|
|
|
} else if (activeArgument != d->m_currentArgument) {
|
|
|
|
|
d->m_currentArgument = activeArgument;
|
2011-04-15 16:19:23 +02:00
|
|
|
updateContent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::updateContent()
|
|
|
|
|
{
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_hintLabel->setText(d->m_model->text(d->m_currentHint));
|
|
|
|
|
d->m_numberLabel->setText(tr("%1 of %2").arg(d->m_currentHint + 1).arg(d->m_totalHints));
|
2011-04-15 16:19:23 +02:00
|
|
|
updatePosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FunctionHintProposalWidget::updatePosition()
|
|
|
|
|
{
|
|
|
|
|
const QDesktopWidget *desktop = QApplication::desktop();
|
|
|
|
|
#ifdef Q_WS_MAC
|
2011-09-16 13:10:06 +02:00
|
|
|
const QRect &screen = desktop->availableGeometry(desktop->screenNumber(d->m_underlyingWidget));
|
2011-04-15 16:19:23 +02:00
|
|
|
#else
|
2011-09-16 13:10:06 +02:00
|
|
|
const QRect &screen = desktop->screenGeometry(desktop->screenNumber(d->m_underlyingWidget));
|
2011-04-15 16:19:23 +02:00
|
|
|
#endif
|
|
|
|
|
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_pager->setFixedWidth(d->m_pager->minimumSizeHint().width());
|
2011-04-15 16:19:23 +02:00
|
|
|
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_hintLabel->setWordWrap(false);
|
2011-04-15 16:19:23 +02:00
|
|
|
const int maxDesiredWidth = screen.width() - 10;
|
2011-09-16 13:10:06 +02:00
|
|
|
const QSize &minHint = d->m_popupFrame->minimumSizeHint();
|
2011-04-15 16:19:23 +02:00
|
|
|
if (minHint.width() > maxDesiredWidth) {
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_hintLabel->setWordWrap(true);
|
|
|
|
|
d->m_popupFrame->setFixedWidth(maxDesiredWidth);
|
|
|
|
|
const int extra = d->m_popupFrame->contentsMargins().bottom() +
|
|
|
|
|
d->m_popupFrame->contentsMargins().top();
|
|
|
|
|
d->m_popupFrame->setFixedHeight(
|
|
|
|
|
d->m_hintLabel->heightForWidth(maxDesiredWidth - d->m_pager->width()) + extra);
|
2011-04-15 16:19:23 +02:00
|
|
|
} else {
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_popupFrame->setFixedSize(minHint);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
2011-09-16 13:10:06 +02:00
|
|
|
const QSize &sz = d->m_popupFrame->size();
|
|
|
|
|
QPoint pos = d->m_displayRect.topLeft();
|
2011-04-15 16:19:23 +02:00
|
|
|
pos.setY(pos.y() - sz.height() - 1);
|
|
|
|
|
if (pos.x() + sz.width() > screen.right())
|
|
|
|
|
pos.setX(screen.right() - sz.width());
|
2011-09-16 13:10:06 +02:00
|
|
|
d->m_popupFrame->move(pos);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // TextEditor
|