~ubuntu-branches/ubuntu/wily/psi/wily-proposed

« back to all changes in this revision

Viewing changes to src/widgets/psitooltip.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2008-08-28 18:46:52 UTC
  • mfrom: (1.2.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20080828184652-iiik12dl91nq7cdi
Tags: 0.12-2
Uploading to unstable (Closes: Bug#494352)

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
#include "psitooltip.h"
22
22
 
23
 
#include <QFrame>
24
23
#include <QApplication>
25
24
#include <QDesktopWidget>
26
 
#include <QEvent>
27
25
#include <QKeyEvent>
 
26
#include <QPointer>
28
27
#include <QHash>
29
 
#include <QPointer>
30
 
#include <QStyle>
31
 
#include <QStyleOption>
32
 
#include <QStylePainter>
33
 
#include <QTimer>
34
28
#include <QToolTip>
35
 
#include <QAbstractTextDocumentLayout>
36
 
#include <QTextFrame>
37
 
#include <QTextEdit>
38
29
#include "private/qeffects_p.h"
39
30
 
40
 
#include "psirichtext.h"
41
 
 
42
 
//----------------------------------------------------------------------------
43
 
// PsiTipLabel
44
 
//----------------------------------------------------------------------------
45
 
 
46
 
class PsiTipLabel : public QFrame
47
 
{
48
 
        Q_OBJECT
49
 
public:
50
 
        PsiTipLabel(const QString& text, QWidget* parent);
51
 
        ~PsiTipLabel();
52
 
        static PsiTipLabel *instance;
53
 
 
54
 
        // int heightForWidth(int w) const;
55
 
        QSize sizeHint() const;
56
 
        QSize minimumSizeHint() const;
57
 
        bool eventFilter(QObject *, QEvent *);
58
 
 
59
 
        QString theText() const;
60
 
 
61
 
        QBasicTimer hideTimer, deleteTimer;
62
 
 
63
 
        void hideTip();
64
 
protected:
65
 
        void enterEvent(QEvent*){ hideTip(); }
66
 
        void timerEvent(QTimerEvent *e);
67
 
        void paintEvent(QPaintEvent *e);
68
 
        // QSize sizeForWidth(int w) const;
69
 
 
70
 
private:
71
 
        QTextDocument *doc;
72
 
        QString theText_;
73
 
        bool isRichText;
74
 
        int margin;
75
 
        // int indent;
76
 
};
77
 
 
78
 
PsiTipLabel *PsiTipLabel::instance = 0;
79
 
 
80
 
PsiTipLabel::PsiTipLabel(const QString& text, QWidget* parent)
81
 
        : QFrame(parent, Qt::ToolTip)
82
 
{
83
 
        delete instance;
84
 
        instance = this;
85
 
 
86
 
        margin = 1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this);
87
 
        setFrameStyle(QFrame::NoFrame);
88
 
 
89
 
        // doc = new QTextDocument(this);
90
 
        // QTextDocumentLayout is private in Qt4
91
 
        // and it's impossible to set wrapping mode directly.
92
 
        // So we create this QTextEdit instance and use its QTextDocument,
93
 
        // just because QTextEdit can set the wrapping mode.
94
 
        // Yes, this is crazy...
95
 
        QTextEdit *edit = new QTextEdit(this);
96
 
        edit->hide();
97
 
        edit->setWordWrapMode(QTextOption::WordWrap);
98
 
        doc = edit->document();
99
 
        doc->setUndoRedoEnabled(false);
100
 
        doc->setDefaultFont(font());
101
 
 
102
 
        ensurePolished();
103
 
        
104
 
        theText_ = text;
105
 
        isRichText = false;
106
 
        if (Qt::mightBeRichText(theText_)) {
107
 
                isRichText = true;
108
 
                PsiRichText::install(doc);
109
 
                PsiRichText::setText(doc, theText_);
110
 
        }
111
 
        else {
112
 
                doc->setPlainText(theText_);
113
 
        }
114
 
 
115
 
        resize(sizeHint());
116
 
        qApp->installEventFilter(this);
117
 
        hideTimer.start(10000, this);
118
 
        setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0);
119
 
        // No resources for this yet (unlike on Windows).
120
 
        //QPalette pal(Qt::black, QColor(255,255,220),
121
 
        //             QColor(96,96,96), Qt::black, Qt::black,
122
 
        //             Qt::black, QColor(255,255,220));
123
 
        setPalette(QToolTip::palette());
124
 
}
125
 
 
126
 
QString PsiTipLabel::theText() const
127
 
{
128
 
        return theText_;
129
 
}
130
 
 
131
 
/*
132
 
QSize PsiTipLabel::sizeForWidth(int w) const
133
 
{
134
 
        QRect br;
135
 
        
136
 
        int hextra = 2 * margin;
137
 
        int vextra = hextra;
138
 
        
139
 
        if (isRichText) {
140
 
                hextra = 1;
141
 
                vextra = 1;
142
 
        }
143
 
        
144
 
        PsiRichText::ensureTextLayouted(doc, w);
145
 
        const qreal oldTextWidth = doc->textWidth();
146
 
        
147
 
        doc->adjustSize();
148
 
        br = QRect(QPoint(0, 0), doc->size().toSize());
149
 
        doc->setTextWidth(oldTextWidth);
150
 
        
151
 
        QFontMetrics fm(font());
152
 
        QSize extra(hextra + 1, vextra);
153
 
 
154
 
        // Make it look good with the default ToolTip font on Mac, which has a small descent.
155
 
        if (fm.descent() == 2 && fm.ascent() >= 11)
156
 
                vextra++;
157
 
        
158
 
        const QSize contentsSize(br.width() + hextra, br.height() + vextra);
159
 
        return contentsSize;
160
 
}
161
 
*/
162
 
QSize PsiTipLabel::sizeHint() const
163
 
{
164
 
        QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
165
 
        fmt.setMargin(0);
166
 
        doc->rootFrame()->setFrameFormat(fmt);
167
 
        // PsiRichText::ensureTextLayouted(doc, -1);
168
 
 
169
 
        doc->adjustSize();
170
 
        // br = QRect(QPoint(0, 0), doc->size().toSize());
171
 
        // this way helps to fight empty space on the right:
172
 
        QSize docSize = QSize(doc->idealWidth(), doc->size().toSize().height());
173
 
 
174
 
        QFontMetrics fm(font());
175
 
        QSize extra(2*margin + 2, 2*margin + 1);        // "+" for tip's frame
176
 
        // Make it look good with the default ToolTip font on Mac, which has a small descent.
177
 
        if (fm.descent() == 2 && fm.ascent() >= 11)
178
 
                ++extra.rheight();
179
 
 
180
 
        return docSize + extra;
181
 
}
182
 
 
183
 
QSize PsiTipLabel::minimumSizeHint() const
184
 
{
185
 
        return sizeHint();
186
 
        // qWarning("PsiTipLabel::minimumSizeHint");
187
 
        // ensurePolished();
188
 
        // QSize sh = sizeForWidth(-1);
189
 
        // QSize msh(-1, -1);
190
 
        // 
191
 
        // msh.rheight() = sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
192
 
        // msh.rwidth()  = sizeForWidth(0).width(); // wrap ? size of biggest word : min doc size
193
 
        // if (sh.height() < msh.height())
194
 
        //      msh.rheight() = sh.height();
195
 
        // 
196
 
        // return msh;
197
 
}
198
 
 
199
 
void PsiTipLabel::paintEvent(QPaintEvent *)
200
 
{
201
 
        QStylePainter p(this);
202
 
        QStyleOptionFrame opt;
203
 
        opt.init(this);
204
 
        p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
205
 
        p.end();
206
 
 
207
 
        // stolen from QLabel::paintEvent
208
 
        QPainter painter(this);
209
 
        drawFrame(&painter);
210
 
        QRect cr = contentsRect();
211
 
        cr.adjust(margin, margin, -margin, -margin);
212
 
 
213
 
        PsiRichText::ensureTextLayouted(doc, width() - 2*margin);
214
 
        QAbstractTextDocumentLayout *layout = doc->documentLayout();
215
 
        // QRect lr = rect();
216
 
        QRect lr = cr;
217
 
 
218
 
        QAbstractTextDocumentLayout::PaintContext context;
219
 
 
220
 
        // Adjust the palette
221
 
        context.palette = palette();
222
 
        if (foregroundRole() != QPalette::Text && isEnabled())
223
 
                context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
224
 
 
225
 
        painter.save();
226
 
        painter.translate(lr.x() + 1, lr.y() + 1);
227
 
        painter.setClipRect(lr.translated(-lr.x() - 1, -lr.y() - 1));
228
 
        layout->draw(&painter, context);
229
 
        painter.restore();
230
 
}
231
 
 
232
 
PsiTipLabel::~PsiTipLabel()
233
 
{
234
 
        instance = 0;
235
 
}
236
 
 
237
 
void PsiTipLabel::hideTip()
238
 
{
239
 
        hide();
240
 
        // timer based deletion to prevent animation
241
 
        deleteTimer.start(250, this);
242
 
}
243
 
 
244
 
void PsiTipLabel::timerEvent(QTimerEvent *e)
245
 
{
246
 
        if (e->timerId() == hideTimer.timerId())
247
 
                hideTip();
248
 
        else if (e->timerId() == deleteTimer.timerId())
249
 
                delete this;
250
 
}
251
 
 
252
 
bool PsiTipLabel::eventFilter(QObject *, QEvent *e)
253
 
{
254
 
        switch (e->type()) {
255
 
                case QEvent::KeyPress:
256
 
                case QEvent::KeyRelease: {
257
 
                        int key = static_cast<QKeyEvent *>(e)->key();
258
 
                        Qt::KeyboardModifiers mody = static_cast<QKeyEvent *>(e)->modifiers();
259
 
 
260
 
                        if ((mody & Qt::KeyboardModifierMask)
261
 
                            || (key == Qt::Key_Shift || key == Qt::Key_Control
262
 
                            || key == Qt::Key_Alt || key == Qt::Key_Meta))
263
 
                                break;
264
 
                }
265
 
                case QEvent::Leave:
266
 
                case QEvent::WindowActivate:
267
 
                case QEvent::WindowDeactivate:
268
 
                case QEvent::MouseButtonPress:
269
 
                case QEvent::MouseButtonRelease:
270
 
                case QEvent::MouseButtonDblClick:
271
 
                case QEvent::FocusIn:
272
 
                case QEvent::FocusOut:
273
 
                case QEvent::Wheel:
274
 
                        hideTip();
275
 
                default:
276
 
                        ;
277
 
        }
278
 
        return false;
279
 
}
 
31
#include "psitiplabel.h"
280
32
 
281
33
//----------------------------------------------------------------------------
282
34
// PsiToolTipHandler
297
49
 
298
50
        void install(QWidget *widget)
299
51
        {
300
 
                widget->installEventFilter(this);
301
 
        }
302
 
        
 
52
                watchedWidgets_[widget] = true;
 
53
                connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
 
54
        }
 
55
 
 
56
private slots:
 
57
        void widgetDestroyed(QObject* obj)
 
58
        {
 
59
                QWidget* widget = static_cast<QWidget*>(obj);
 
60
                watchedWidgets_.remove(widget);
 
61
        }
 
62
 
303
63
private:
 
64
        QHash<QWidget*, bool> watchedWidgets_;
 
65
 
304
66
        PsiToolTipHandler()
305
67
                : QObject(qApp)
306
 
        { }
 
68
        {
 
69
                qApp->installEventFilter(this);
 
70
        }
307
71
 
308
72
        bool eventFilter(QObject *obj, QEvent *event)
309
73
        {
310
74
                if (event->type() == QEvent::ToolTip) {
311
 
                        QWidget *widget = dynamic_cast<QWidget *>(obj);
312
 
                        if (widget->isActiveWindow()) {
 
75
                        QWidget *widget = static_cast<QWidget *>(obj);
 
76
                        if (watchedWidgets_.contains(widget) &&
 
77
                            (widget->isActiveWindow() ||
 
78
                             widget->window()->testAttribute(Qt::WA_AlwaysShowToolTips))) {
313
79
                                QPoint pos = dynamic_cast<QHelpEvent *>(event)->globalPos();
314
80
                                PsiToolTip::showText(pos, widget->toolTip(), widget);
315
81
                                event->setAccepted(true);
317
83
                        }
318
84
                }
319
85
 
320
 
                return QObject::eventFilter(obj, event);
 
86
                return false;
321
87
        }
322
88
};
323
89
 
325
91
// ToolTipPosition
326
92
//----------------------------------------------------------------------------
327
93
 
328
 
class ToolTipPosition
329
 
{
330
 
private:
331
 
        QPoint pos;
332
 
        QWidget *w;
333
 
        
334
 
public:
335
 
        ToolTipPosition(const QPoint &_pos, QWidget *_w)
336
 
        {
337
 
                pos = _pos;
338
 
                w   = _w;
339
 
        }
340
 
 
341
 
        int getScreenNumber()
342
 
        {
343
 
                if (QApplication::desktop()->isVirtualDesktop())
344
 
                        return QApplication::desktop()->screenNumber(pos);
345
 
                
346
 
                return QApplication::desktop()->screenNumber(w);
347
 
        }
348
 
 
349
 
        QPoint calculateTipPosition(QWidget *label)
350
 
        {
 
94
ToolTipPosition::ToolTipPosition(const QPoint& _pos, const QWidget* _w)
 
95
        : pos(_pos)
 
96
        , w(_w)
 
97
{
 
98
}
 
99
 
 
100
int ToolTipPosition::getScreenNumber() const
 
101
{
 
102
        if (QApplication::desktop()->isVirtualDesktop())
 
103
                return QApplication::desktop()->screenNumber(pos);
 
104
 
 
105
        return QApplication::desktop()->screenNumber(w);
 
106
}
 
107
 
 
108
QRect ToolTipPosition::screenRect() const
 
109
{
351
110
#ifdef Q_WS_MAC
352
 
                QRect screen = QApplication::desktop()->availableGeometry(getScreenNumber());
 
111
        return QApplication::desktop()->availableGeometry(getScreenNumber());
353
112
#else
354
 
                QRect screen = QApplication::desktop()->screenGeometry(getScreenNumber());
 
113
        return QApplication::desktop()->screenGeometry(getScreenNumber());
355
114
#endif
356
 
 
357
 
                QPoint p = pos;
358
 
                p += QPoint(2,
 
115
}
 
116
 
 
117
QPoint ToolTipPosition::calculateTipPosition(const QWidget* label) const
 
118
{
 
119
        QRect screen = screenRect();
 
120
 
 
121
        QPoint p = pos;
 
122
        p += QPoint(2,
359
123
#ifdef Q_WS_WIN
360
 
                              24
 
124
                      24
361
125
#else
362
 
                              16
 
126
                      16
363
127
#endif
364
 
                           );
365
 
 
366
 
                if (p.x() + label->width() > screen.x() + screen.width())
367
 
                        p.rx() -= 4 + label->width();
368
 
                if (p.y() + label->height() > screen.y() + screen.height())
369
 
                        p.ry() -= 24 + label->height();
370
 
                if (p.y() < screen.y())
371
 
                        p.setY(screen.y());
372
 
                if (p.x() + label->width() > screen.x() + screen.width())
373
 
                        p.setX(screen.x() + screen.width() - label->width());
374
 
                if (p.x() < screen.x())
375
 
                        p.setX(screen.x());
376
 
                if (p.y() + label->height() > screen.y() + screen.height())
377
 
                        p.setY(screen.y() + screen.height() - label->height());
378
 
        
379
 
                return p;
380
 
        }
381
 
};
 
128
                   );
 
129
 
 
130
        if (p.x() + label->width() > screen.x() + screen.width())
 
131
                p.rx() -= 4 + label->width();
 
132
        if (p.y() + label->height() > screen.y() + screen.height())
 
133
                p.ry() -= 24 + label->height();
 
134
        if (p.y() < screen.y())
 
135
                p.setY(screen.y());
 
136
        if (p.x() + label->width() > screen.x() + screen.width())
 
137
                p.setX(screen.x() + screen.width() - label->width());
 
138
        if (p.x() < screen.x())
 
139
                p.setX(screen.x());
 
140
        if (p.y() + label->height() > screen.y() + screen.height())
 
141
                p.setY(screen.y() + screen.height() - label->height());
 
142
 
 
143
        return p;
 
144
}
382
145
 
383
146
//----------------------------------------------------------------------------
384
147
// PsiToolTip
385
148
//----------------------------------------------------------------------------
386
149
 
 
150
PsiToolTip::PsiToolTip()
 
151
        : QObject(QCoreApplication::instance())
 
152
{}
 
153
 
387
154
/**
388
155
 * QTipLabel's font is being determined at run-time. However QTipLabel's and
389
156
 * QToolTip's font is the same, so we install our PsiTextLabel's font to be
405
172
 * optional widget argument, \a w, is used to determine the
406
173
 * appropriate screen on multi-head systems.
407
174
 */
408
 
void PsiToolTip::showText(const QPoint &pos, const QString &text, QWidget *w)
 
175
void PsiToolTip::doShowText(const QPoint &pos, const QString &text, const QWidget *w)
409
176
{
410
 
        if (text.isEmpty()) {
411
 
                if (PsiTipLabel::instance)
412
 
                        PsiTipLabel::instance->hideTip();
 
177
        if (text.isEmpty() || (w && !w->underMouse())) {
 
178
                if (PsiTipLabel::instance())
 
179
                        PsiTipLabel::instance()->hideTip();
413
180
                return;
414
181
        }
415
182
 
416
 
        if (!w->underMouse())
417
 
                return;
 
183
        QPointer<ToolTipPosition> calc(createTipPosition(pos, w));
 
184
        calc->deleteLater();
 
185
        if (PsiTipLabel::instance() && moveAndUpdateTipLabel(PsiTipLabel::instance(), text)) {
 
186
                updateTipLabel(PsiTipLabel::instance(), text);
418
187
 
419
 
        ToolTipPosition calc(pos, w);
420
 
        if (PsiTipLabel::instance && PsiTipLabel::instance->theText() == text) {
421
188
                // fancy moving tooltip effect
422
 
                PsiTipLabel::instance->move(calc.calculateTipPosition(PsiTipLabel::instance));
 
189
                PsiTipLabel::instance()->move(calc->calculateTipPosition(PsiTipLabel::instance()));
423
190
                return;
424
191
        }
425
192
 
426
 
        bool preventAnimation = (PsiTipLabel::instance != 0);
 
193
        bool preventAnimation = (PsiTipLabel::instance() != 0);
427
194
 
428
195
        installPsiToolTipFont();
429
 
        QFrame *label = new PsiTipLabel(text, QApplication::desktop()->screen(calc.getScreenNumber()));
430
 
        label->move(calc.calculateTipPosition(label));
 
196
        QFrame *label = createTipLabel(text, QApplication::desktop()->screen(calc->getScreenNumber()));
 
197
        label->move(calc->calculateTipPosition(label));
431
198
 
432
199
        if ( QApplication::isEffectEnabled(Qt::UI_AnimateTooltip) == false || preventAnimation)
433
200
                label->show();
437
204
                qScrollEffect(label);
438
205
}
439
206
 
 
207
bool PsiToolTip::moveAndUpdateTipLabel(PsiTipLabel* label, const QString& text)
 
208
{
 
209
        return label->theText() == text;
 
210
}
 
211
 
 
212
void PsiToolTip::updateTipLabel(PsiTipLabel* label, const QString& text)
 
213
{
 
214
        Q_UNUSED(label);
 
215
        Q_UNUSED(text);
 
216
}
 
217
 
 
218
ToolTipPosition* PsiToolTip::createTipPosition(const QPoint& cursorPos, const QWidget* parentWidget)
 
219
{
 
220
        return new ToolTipPosition(cursorPos, parentWidget);
 
221
}
 
222
 
 
223
PsiTipLabel* PsiToolTip::createTipLabel(const QString& text, QWidget* parent)
 
224
{
 
225
        PsiTipLabel* label = new PsiTipLabel(parent);
 
226
        label->init(text);
 
227
        return label;
 
228
}
 
229
 
440
230
/**
441
231
 * After installation, all tool tips in specified widget will be processed
442
232
 * through PsiToolTip and thus <icon> tags would be correctly handled.
443
233
 */
444
 
void PsiToolTip::install(QWidget *w)
 
234
void PsiToolTip::doInstall(QWidget *w)
445
235
{
446
236
        PsiToolTipHandler::getInstance()->install(w);
447
237
}
448
238
 
 
239
PsiToolTip *PsiToolTip::instance_ = 0;
 
240
 
 
241
PsiToolTip* PsiToolTip::instance()
 
242
{
 
243
        if (!instance_)
 
244
                instance_ = new PsiToolTip();
 
245
        return instance_;
 
246
}
 
247
 
449
248
#include "psitooltip.moc"