File indexing completed on 2024-05-12 16:36:08

0001 /* This file is part of the KDE project
0002    Copyright 1999-2006 The KSpread Team <calligra-devel@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "FunctionCompletion.h"
0021 
0022 // Sheets
0023 #include "CellEditor.h"
0024 #include "Function.h"
0025 #include "FunctionDescription.h"
0026 #include "FunctionRepository.h"
0027 
0028 // Qt
0029 #include <QApplication>
0030 #include <QDesktopWidget>
0031 #include <QKeyEvent>
0032 #include <QLabel>
0033 #include <QListWidget>
0034 #include <QScrollBar>
0035 #include <QToolTip>
0036 #include <QVBoxLayout>
0037 
0038 using namespace Calligra::Sheets;
0039 
0040 class FunctionCompletion::Private
0041 {
0042 public:
0043     CellEditor* editor;
0044     QFrame *completionPopup;
0045     QListWidget *completionListBox;
0046     QLabel* hintLabel;
0047 };
0048 
0049 FunctionCompletion::FunctionCompletion(CellEditor* editor)
0050         : QObject(editor)
0051         , d(new Private)
0052 {
0053     d->editor = editor;
0054     d->hintLabel = 0;
0055 
0056     d->completionPopup = new QFrame(editor->topLevelWidget(), Qt::Popup);
0057     d->completionPopup->setFrameStyle(QFrame::Box | QFrame::Plain);
0058     d->completionPopup->setLineWidth(1);
0059     d->completionPopup->installEventFilter(this);
0060     d->completionPopup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
0061     QVBoxLayout *layout = new QVBoxLayout(d->completionPopup);
0062     layout->setMargin(0);
0063     layout->setSpacing(0);
0064 
0065     d->completionListBox = new QListWidget(d->completionPopup);
0066     d->completionPopup->setFocusProxy(d->completionListBox);
0067     d->completionListBox->setFrameStyle(QFrame::NoFrame);
0068 //   d->completionListBox->setVariableWidth( true );
0069     d->completionListBox->installEventFilter(this);
0070     connect(d->completionListBox, SIGNAL(currentRowChanged(int)), SLOT(itemSelected()));
0071     // When items are activated on single click, also change the help page on mouse-over, otherwise there is no (easy) way to get
0072     // the help (with the mouse) without inserting the function
0073     if (d->completionListBox->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, d->completionListBox)) {
0074         connect(d->completionListBox, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(itemSelected(QListWidgetItem*)));
0075         d->completionListBox->setMouseTracking(true);
0076     }
0077 
0078     connect(d->completionListBox, SIGNAL(itemActivated(QListWidgetItem*)),
0079             this, SLOT(doneCompletion()));
0080     layout->addWidget(d->completionListBox);
0081 
0082     d->hintLabel = new QLabel(0, Qt::FramelessWindowHint | Qt::Tool |  Qt::X11BypassWindowManagerHint);
0083     d->hintLabel->setFrameStyle(QFrame::Plain | QFrame::Box);
0084     d->hintLabel->setPalette(QToolTip::palette());
0085     d->hintLabel->setWordWrap(true);
0086     d->hintLabel->hide();
0087 }
0088 
0089 FunctionCompletion::~FunctionCompletion()
0090 {
0091     delete d->hintLabel;
0092     delete d;
0093 }
0094 
0095 void FunctionCompletion::itemSelected(QListWidgetItem* listItem)
0096 {
0097     QString item;
0098     if (listItem) {
0099         item = listItem->text();
0100     } else {
0101         listItem = d->completionListBox->currentItem();
0102         if (listItem) {
0103             item = listItem->text();
0104         }
0105     }
0106 
0107     Calligra::Sheets::FunctionDescription* desc;
0108     desc = Calligra::Sheets::FunctionRepository::self()->functionInfo(item);
0109     if (!desc) {
0110         d->hintLabel->hide();
0111         return;
0112     }
0113 
0114     const QStringList helpTexts = desc->helpText();
0115     QString helpText = helpTexts.isEmpty() ? QString() : helpTexts.first();
0116     if (helpText.isEmpty()) {
0117         d->hintLabel->hide();
0118         return;
0119     }
0120 
0121     helpText.append("</qt>").prepend("<qt>");
0122     d->hintLabel->setText(helpText);
0123     d->hintLabel->adjustSize();
0124 
0125     // reposition nicely
0126     QPoint pos = d->editor->mapToGlobal(QPoint(d->editor->width(), 0));
0127     pos.setY(pos.y() - d->hintLabel->height() - 1);
0128     d->hintLabel->move(pos);
0129     d->hintLabel->show();
0130     d->hintLabel->raise();
0131 
0132     // do not show it forever
0133     //QTimer::singleShot( 5000, d->hintLabel, SLOT(hide()) );
0134 }
0135 
0136 bool FunctionCompletion::eventFilter(QObject *obj, QEvent *ev)
0137 {
0138     if (obj != d->completionPopup && obj != d->completionListBox)
0139         return false;
0140 
0141     if (ev->type() == QEvent::KeyPress) {
0142         QKeyEvent *ke = static_cast<QKeyEvent*>(ev);
0143         switch (ke->key()) {
0144         case Qt::Key_Enter:
0145         case Qt::Key_Return:
0146             doneCompletion();
0147             return true;
0148         case Qt::Key_Left:
0149         case Qt::Key_Right:
0150         case Qt::Key_Up:
0151         case Qt::Key_Down:
0152         case Qt::Key_Home:
0153         case Qt::Key_End:
0154         case Qt::Key_PageUp:
0155         case Qt::Key_PageDown:
0156             return false;
0157         default:
0158             d->hintLabel->hide();
0159             d->completionPopup->close();
0160             d->editor->setFocus();
0161             QApplication::sendEvent(d->editor, ev);
0162             return true;
0163         }
0164     }
0165 
0166     if (ev->type() == QEvent::Close) {
0167         d->hintLabel->hide();
0168     }
0169 
0170     if (ev->type() == QEvent::MouseButtonDblClick) {
0171         doneCompletion();
0172         return true;
0173     }
0174     return false;
0175 }
0176 
0177 void FunctionCompletion::doneCompletion()
0178 {
0179     d->hintLabel->hide();
0180     d->completionPopup->close();
0181     d->editor->setFocus();
0182     emit selectedCompletion(d->completionListBox->currentItem()->text());
0183 }
0184 
0185 void FunctionCompletion::showCompletion(const QStringList &choices)
0186 {
0187     if (!choices.count()) return;
0188 
0189     d->completionListBox->clear();
0190     d->completionListBox->addItems(choices);
0191     d->completionListBox->setCurrentItem(0);
0192 
0193     // size of the pop-up
0194     d->completionPopup->setMaximumHeight(100);
0195     d->completionPopup->resize(d->completionListBox->sizeHint() +
0196                                QSize(d->completionListBox->verticalScrollBar()->width() + 4,
0197                                      d->completionListBox->horizontalScrollBar()->height() + 4));
0198     int h = d->completionListBox->height();
0199     int w = d->completionListBox->width();
0200 
0201     QPoint pos = d->editor->globalCursorPosition();
0202 
0203     // if popup is partially invisible, move to other position
0204     // FIXME check it if it works in Xinerama multihead
0205     int screen_num = QApplication::desktop()->screenNumber(d->completionPopup);
0206     QRect screen = QApplication::desktop()->screenGeometry(screen_num);
0207     if (pos.y() + h > screen.y() + screen.height())
0208         pos.setY(pos.y() - h - d->editor->height());
0209     if (pos.x() + w > screen.x() + screen.width())
0210         pos.setX(screen.x() + screen.width() - w);
0211 
0212     d->completionPopup->move(pos);
0213     d->completionListBox->setFocus();
0214     d->completionPopup->show();
0215 }