00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "qwhatsthis.h"
00025 #ifndef QT_NO_WHATSTHIS
00026 #include "qpointer.h"
00027 #include "qapplication.h"
00028 #include "qdesktopwidget.h"
00029 #include "qevent.h"
00030 #include "qpixmap.h"
00031 #include "qpainter.h"
00032 #include "qtimer.h"
00033 #include "qhash.h"
00034 #include "qaction.h"
00035 #include "qcursor.h"
00036 #include "qbitmap.h"
00037 #include "qtextdocument.h"
00038 #include "../text/qtextdocumentlayout_p.h"
00039 #include "qtoolbutton.h"
00040 #include "qdebug.h"
00041 #ifndef QT_NO_ACCESSIBILITY
00042 #include "qaccessible.h"
00043 #endif
00044 #if defined(Q_WS_WIN)
00045 #include "qt_windows.h"
00046 #ifndef SPI_GETDROPSHADOW
00047 #define SPI_GETDROPSHADOW 0x1024
00048 #endif
00049 #endif
00050 #if defined(Q_WS_X11)
00051 #include "qx11info_x11.h"
00052 #include <qwidget.h>
00053 #endif
00054
00128 class QWhatsThat : public QWidget
00129 {
00130 Q_OBJECT
00131
00132 public:
00133 QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor);
00134 ~QWhatsThat() ;
00135
00136 static QWhatsThat *instance;
00137
00138 protected:
00139 void showEvent(QShowEvent *e);
00140 void mousePressEvent(QMouseEvent*);
00141 void mouseReleaseEvent(QMouseEvent*);
00142 void mouseMoveEvent(QMouseEvent*);
00143 void keyPressEvent(QKeyEvent*);
00144 void paintEvent(QPaintEvent*);
00145
00146 private:
00147 QPointer<QWidget>widget;
00148 bool pressed;
00149 QString text;
00150 QTextDocument* doc;
00151 QString anchor;
00152 QPixmap background;
00153 };
00154
00155 QWhatsThat *QWhatsThat::instance = 0;
00156
00157
00158 static int shadowWidth = 6;
00159 static const int vMargin = 8;
00160 static const int hMargin = 12;
00161
00162 QWhatsThat::QWhatsThat(const QString& txt, QWidget* parent, QWidget *showTextFor)
00163 : QWidget(parent, Qt::Popup),
00164 widget(showTextFor), pressed(false), text(txt)
00165 {
00166 delete instance;
00167 instance = this;
00168 setAttribute(Qt::WA_DeleteOnClose, true);
00169 setAttribute(Qt::WA_NoSystemBackground, true);
00170 QPalette pal(Qt::black, QColor(255,255,238),
00171 QColor(96,96,96), QColor(192,192,192), Qt::black,
00172 Qt::black, QColor(255,255,238));
00173 setPalette(pal);
00174 setMouseTracking(true);
00175 setFocusPolicy(Qt::StrongFocus);
00176 #ifndef QT_NO_CURSOR
00177 setCursor(Qt::ArrowCursor);
00178 #endif
00179
00180 QRect r;
00181 doc = 0;
00182 if (Qt::mightBeRichText(text)) {
00183 doc = new QTextDocument();
00184 doc->setUndoRedoEnabled(false);
00185 doc->setDefaultFont(QApplication::font(this));
00186 doc->setHtml(text);
00187 doc->setUndoRedoEnabled(false);
00188 doc->adjustSize();
00189 r.setTop(0);
00190 r.setLeft(0);
00191 r.setSize(doc->size().toSize());
00192 }
00193 else
00194 {
00195 int sw = QApplication::desktop()->width() / 3;
00196 if (sw < 200)
00197 sw = 200;
00198 else if (sw > 300)
00199 sw = 300;
00200
00201 r = fontMetrics().boundingRect(0, 0, sw, 1000,
00202 Qt::AlignLeft + Qt::AlignTop
00203 + Qt::TextWordWrap + Qt::TextExpandTabs,
00204 text);
00205 }
00206 #if defined(Q_WS_WIN)
00207 if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
00208 BOOL shadow;
00209 SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
00210 shadowWidth = shadow ? 0 : 6;
00211 }
00212 #endif
00213 resize(r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth);
00214 }
00215
00216 QWhatsThat::~QWhatsThat()
00217 {
00218 instance = 0;
00219 if (doc)
00220 delete doc;
00221 }
00222
00223 void QWhatsThat::showEvent(QShowEvent *)
00224 {
00225 background = QPixmap::grabWindow(QApplication::desktop()->internalWinId(),
00226 x(), y(), width(), height());
00227 }
00228
00229 void QWhatsThat::mousePressEvent(QMouseEvent* e)
00230 {
00231 pressed = true;
00232 if (e->button() == Qt::LeftButton && rect().contains(e->pos())) {
00233 if (doc)
00234 anchor = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
00235 return;
00236 }
00237 close();
00238 }
00239
00240 void QWhatsThat::mouseReleaseEvent(QMouseEvent* e)
00241 {
00242 if (!pressed)
00243 return;
00244 if (widget && e->button() == Qt::LeftButton && doc && rect().contains(e->pos())) {
00245 QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
00246 QString href;
00247 if (anchor == a)
00248 href = a;
00249 anchor.clear();
00250 if (!href.isEmpty()) {
00251 QWhatsThisClickedEvent e(href);
00252 if (QApplication::sendEvent(widget, &e))
00253 return;
00254 }
00255 }
00256 close();
00257 }
00258
00259 void QWhatsThat::mouseMoveEvent(QMouseEvent* e)
00260 {
00261 #ifdef QT_NO_CURSOR
00262 Q_UNUSED(e);
00263 #else
00264 if (!doc)
00265 return;
00266 QString a = doc->documentLayout()->anchorAt(e->pos() - QPoint(hMargin, vMargin));
00267 if (!a.isEmpty())
00268 setCursor(Qt::PointingHandCursor);
00269 else
00270 setCursor(Qt::ArrowCursor);
00271 #endif
00272 }
00273
00274 void QWhatsThat::keyPressEvent(QKeyEvent*)
00275 {
00276 close();
00277 }
00278
00279 void QWhatsThat::paintEvent(QPaintEvent*)
00280 {
00281 bool drawShadow = true;
00282 #if defined(Q_WS_WIN)
00283 if ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_2000) {
00284 BOOL shadow;
00285 SystemParametersInfo(SPI_GETDROPSHADOW, 0, &shadow, 0);
00286 drawShadow = !shadow;
00287 }
00288 #elif defined(Q_WS_MAC) || defined(Q_WS_QWS)
00289 drawShadow = false;
00290 #endif
00291
00292 QRect r = rect();
00293 r.adjust(0, 0, -1, -1);
00294 if (drawShadow)
00295 r.adjust(0, 0, -shadowWidth, -shadowWidth);
00296 QPainter p(this);
00297 p.drawPixmap(0, 0, background);
00298 p.setPen(palette().foreground().color());
00299 p.setBrush(palette().brush(QPalette::Window));
00300 p.drawRect(r);
00301 int w = r.width();
00302 int h = r.height();
00303 p.setPen(palette().brush(QPalette::Dark).color());
00304 p.drawRect(1, 1, w-2, h-2);
00305 if (drawShadow) {
00306 p.setPen(palette().shadow().color());
00307 p.drawPoint(w + 5, 6);
00308 p.drawLine(w + 3, 6, w + 5, 8);
00309 p.drawLine(w + 1, 6, w + 5, 10);
00310 int i;
00311 for(i=7; i < h; i += 2)
00312 p.drawLine(w, i, w + 5, i + 5);
00313 for(i = w - i + h; i > 6; i -= 2)
00314 p.drawLine(i, h, i + 5, h + 5);
00315 for(; i > 0 ; i -= 2)
00316 p.drawLine(6, h + 6 - i, i + 5, h + 5);
00317 }
00318 p.setPen(palette().foreground().color());
00319 r.adjust(hMargin, vMargin, -hMargin, -vMargin);
00320
00321 if (doc) {
00322 p.translate(r.x(), r.y());
00323 QRect rect = r;
00324 rect.translate(-r.x(), -r.y());
00325 p.setClipRect(rect);
00326 QAbstractTextDocumentLayout::PaintContext context;
00327 doc->documentLayout()->draw(&p, context);
00328 }
00329 else
00330 {
00331 p.drawText(r, Qt::AlignLeft + Qt::AlignTop + Qt::TextWordWrap + Qt::TextExpandTabs, text);
00332 }
00333 }
00334
00335 static const char * const button_image[] = {
00336 "16 16 3 1",
00337 " c None",
00338 "o c #000000",
00339 "a c #000080",
00340 "o aaaaa ",
00341 "oo aaa aaa ",
00342 "ooo aaa aaa",
00343 "oooo aa aa",
00344 "ooooo aa aa",
00345 "oooooo a aaa",
00346 "ooooooo aaa ",
00347 "oooooooo aaa ",
00348 "ooooooooo aaa ",
00349 "ooooo aaa ",
00350 "oo ooo ",
00351 "o ooo aaa ",
00352 " ooo aaa ",
00353 " ooo ",
00354 " ooo ",
00355 " ooo "};
00356
00357 class QWhatsThisPrivate : public QObject
00358 {
00359 public:
00360 QWhatsThisPrivate();
00361 ~QWhatsThisPrivate();
00362 static QWhatsThisPrivate *instance;
00363 bool eventFilter(QObject *, QEvent *);
00364 QPointer<QAction> action;
00365 #ifdef QT3_SUPPORT
00366 QPointer<QToolButton> button;
00367 #endif
00368 static void say(QWidget *, const QString &, int x = 0, int y = 0);
00369 static void notifyToplevels(QEvent *e);
00370 };
00371
00372 void QWhatsThisPrivate::notifyToplevels(QEvent *e)
00373 {
00374 QWidgetList toplevels = QApplication::topLevelWidgets();
00375 for (int i = 0; i < toplevels.count(); ++i) {
00376 register QWidget *w = toplevels.at(i);
00377 QApplication::sendEvent(w, e);
00378 }
00379 }
00380
00381 QWhatsThisPrivate *QWhatsThisPrivate::instance = 0;
00382
00383 QWhatsThisPrivate::QWhatsThisPrivate()
00384 {
00385 instance = this;
00386 qApp->installEventFilter(this);
00387
00388 QPoint pos = QCursor::pos();
00389 if (QWidget *w = QApplication::widgetAt(pos)) {
00390 QHelpEvent e(QEvent::QueryWhatsThis, w->mapFromGlobal(pos), pos);
00391 bool sentEvent = QApplication::sendEvent(w, &e);
00392 #ifdef QT_NO_CURSOR
00393 Q_UNUSED(sentEvent);
00394 #else
00395 QApplication::setOverrideCursor((!sentEvent || !e.isAccepted())?
00396 Qt::ForbiddenCursor:Qt::WhatsThisCursor);
00397 } else {
00398 QApplication::setOverrideCursor(Qt::WhatsThisCursor);
00399 #endif
00400 }
00401 #ifndef QT_NO_ACCESSIBILITY
00402 QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpStart);
00403 #endif
00404 }
00405
00406 QWhatsThisPrivate::~QWhatsThisPrivate()
00407 {
00408 if (action)
00409 action->setChecked(false);
00410 #ifdef QT3_SUPPORT
00411 if (button)
00412 button->setChecked(false);
00413 #endif
00414 #ifndef QT_NO_CURSOR
00415 QApplication::restoreOverrideCursor();
00416 #endif
00417 #ifndef QT_NO_ACCESSIBILITY
00418 QAccessible::updateAccessibility(this, 0, QAccessible::ContextHelpEnd);
00419 #endif
00420 instance = 0;
00421 }
00422
00423 bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e)
00424 {
00425 if (!o->isWidgetType())
00426 return false;
00427 QWidget * w = static_cast<QWidget *>(o);
00428 bool customWhatsThis = w->testAttribute(Qt::WA_CustomWhatsThis);
00429 switch (e->type()) {
00430 case QEvent::MouseButtonPress:
00431 {
00432 QMouseEvent *me = static_cast<QMouseEvent*>(e);
00433 if (me->button() == Qt::RightButton || customWhatsThis)
00434 return false;
00435 QHelpEvent e(QEvent::WhatsThis, me->pos(), me->globalPos());
00436 if (!QApplication::sendEvent(w, &e) || !e.isAccepted())
00437 QWhatsThis::leaveWhatsThisMode();
00438
00439 } break;
00440
00441 case QEvent::MouseMove:
00442 {
00443 QMouseEvent *me = static_cast<QMouseEvent*>(e);
00444 QHelpEvent e(QEvent::QueryWhatsThis, me->pos(), me->globalPos());
00445 bool sentEvent = QApplication::sendEvent(w, &e);
00446 #ifdef QT_NO_CURSOR
00447 Q_UNUSED(sentEvent);
00448 #else
00449 QApplication::changeOverrideCursor((!sentEvent || !e.isAccepted())?
00450 Qt::ForbiddenCursor:Qt::WhatsThisCursor);
00451 #endif
00452 }
00453
00454 case QEvent::MouseButtonRelease:
00455 case QEvent::MouseButtonDblClick:
00456 if (static_cast<QMouseEvent*>(e)->button() == Qt::RightButton || customWhatsThis)
00457 return false;
00458 break;
00459 case QEvent::KeyPress:
00460 {
00461 QKeyEvent* kev = (QKeyEvent*)e;
00462
00463 if (kev->key() == Qt::Key_Escape) {
00464 QWhatsThis::leaveWhatsThisMode();
00465 return true;
00466 } else if (customWhatsThis) {
00467 return false;
00468 } else if (kev->key() == Qt::Key_Menu ||
00469 (kev->key() == Qt::Key_F10 &&
00470 kev->modifiers() == Qt::ShiftModifier)) {
00471
00472 return false;
00473 } else if (kev->key() != Qt::Key_Shift && kev->key() != Qt::Key_Alt
00474 && kev->key() != Qt::Key_Control && kev->key() != Qt::Key_Meta) {
00475 QWhatsThis::leaveWhatsThisMode();
00476 }
00477 } break;
00478 default:
00479 return false;
00480 }
00481 return true;
00482 }
00483
00484 class QWhatsThisAction: public QAction
00485 {
00486 Q_OBJECT
00487
00488 public:
00489 explicit QWhatsThisAction(QObject* parent = 0);
00490
00491 private slots:
00492 void actionTriggered();
00493 };
00494
00495 QWhatsThisAction::QWhatsThisAction(QObject *parent) : QAction(tr("What's This?"), parent)
00496 {
00497 #ifndef QT_NO_IMAGEFORMAT_XPM
00498 QPixmap p((const char**)button_image);
00499 setIcon(p);
00500 #endif
00501 setCheckable(true);
00502 connect(this, SIGNAL(triggered()), this, SLOT(actionTriggered()));
00503 #ifndef QT_NO_SHORTCUT
00504 setShortcut(Qt::ShiftModifier + Qt::Key_F1);
00505 #endif
00506 }
00507
00508 void QWhatsThisAction::actionTriggered()
00509 {
00510 if (isChecked()) {
00511 QWhatsThis::enterWhatsThisMode();
00512 QWhatsThisPrivate::instance->action = this;
00513 }
00514 }
00515
00516 QWhatsThis::QWhatsThis()
00517 {
00518 }
00519
00520 #ifdef QT3_SUPPORT
00521
00528 void QWhatsThis::add(QWidget *w, const QString &s)
00529 {
00530 w->setWhatsThis(s);
00531 }
00532
00540 void QWhatsThis::remove(QWidget *w)
00541 {
00542 w->setWhatsThis(QString());
00543 }
00544
00545 class QWhatsThisButton : public QToolButton
00546 {
00547 Q_OBJECT
00548 public:
00549 QWhatsThisButton(QWidget *p) : QToolButton(p) {
00550 setCheckable(true);
00551 QPixmap pix( const_cast<const char**>(button_image) );
00552 setIcon( pix );
00553 QObject::connect(this, SIGNAL(toggled(bool)), this, SLOT(whatToggled(bool)));
00554 setAutoRaise(true);
00555 setFocusPolicy(Qt::NoFocus);
00556 }
00557
00558 public slots:
00559 void whatToggled(bool b) {
00560 if (b) {
00561 QWhatsThis::enterWhatsThisMode();
00562 QWhatsThisPrivate::instance->button = this;
00563 }
00564 }
00565 };
00566
00575 QToolButton * QWhatsThis::whatsThisButton(QWidget * parent)
00576 {
00577 return new QWhatsThisButton(parent);
00578 }
00579 #endif
00580
00592 void QWhatsThis::enterWhatsThisMode()
00593 {
00594 if (QWhatsThisPrivate::instance)
00595 return;
00596 (void) new QWhatsThisPrivate;
00597 QEvent e(QEvent::EnterWhatsThisMode);
00598 QWhatsThisPrivate::notifyToplevels(&e);
00599 }
00600
00607 bool QWhatsThis::inWhatsThisMode()
00608 {
00609 return (QWhatsThisPrivate::instance != 0);
00610 }
00611
00621 void QWhatsThis::leaveWhatsThisMode()
00622 {
00623 delete QWhatsThisPrivate::instance;
00624 QEvent e(QEvent::LeaveWhatsThisMode);
00625 QWhatsThisPrivate::notifyToplevels(&e);
00626 }
00627
00628 void QWhatsThisPrivate::say(QWidget * widget, const QString &text, int x, int y)
00629 {
00630 if (text.size() == 0)
00631 return;
00632
00633 QWhatsThat *whatsThat = new QWhatsThat(
00634 text,
00635 #if defined(Q_WS_X11)
00636 QApplication::desktop()->screen(widget ? widget->x11Info().screen() : QCursor::x11Screen()),
00637 #else
00638 0,
00639 #endif
00640 widget
00641 );
00642
00643
00644
00645
00646 int scr = (widget ?
00647 QApplication::desktop()->screenNumber(widget) :
00648 #if defined(Q_WS_X11)
00649 QCursor::x11Screen()
00650 #else
00651 QApplication::desktop()->screenNumber(QPoint(x,y))
00652 #endif // Q_WS_X11
00653 );
00654 QRect screen = QApplication::desktop()->screenGeometry(scr);
00655
00656 int w = whatsThat->width();
00657 int h = whatsThat->height();
00658 int sx = screen.x();
00659 int sy = screen.y();
00660
00661
00662
00663 QPoint pos;
00664 if (widget)
00665 pos = widget->mapToGlobal(QPoint(0,0));
00666
00667 if (widget && w > widget->width() + 16)
00668 x = pos.x() + widget->width()/2 - w/2;
00669 else
00670 x = x - w/2;
00671
00672
00673
00674 if (x + w + shadowWidth > sx+screen.width())
00675 x = (widget? (qMin(screen.width(),
00676 pos.x() + widget->width())
00677 ) : screen.width())
00678 - w;
00679
00680 if (x < sx)
00681 x = sx;
00682
00683 if (widget && h > widget->height() + 16) {
00684 y = pos.y() + widget->height() + 2;
00685
00686 if (y + h + 10 > sy+screen.height())
00687 y = pos.y() + 2 - shadowWidth - h;
00688 }
00689 y = y + 2;
00690
00691
00692
00693 if (y + h + shadowWidth > sy+screen.height())
00694 y = (widget ? (qMin(screen.height(),
00695 pos.y() + widget->height())
00696 ) : screen.height())
00697 - h;
00698 if (y < sy)
00699 y = sy;
00700
00701 whatsThat->move(x, y);
00702 whatsThat->show();
00703 whatsThat->grabKeyboard();
00704 }
00705
00713 void QWhatsThis::showText(const QPoint &pos, const QString &text, QWidget *w)
00714 {
00715 leaveWhatsThisMode();
00716 QWhatsThisPrivate::say(w, text, pos.x(), pos.y());
00717 }
00718
00724 void QWhatsThis::hideText()
00725 {
00726 delete QWhatsThat::instance;
00727 }
00728
00736 QAction *QWhatsThis::createAction(QObject *parent)
00737 {
00738 return new QWhatsThisAction(parent);
00739 }
00740
00741 #include "qwhatsthis.moc"
00742 #endif