diff options
author | Ivan Vizir <define-true-false@yandex.com> | 2013-07-31 14:18:44 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-08-02 10:20:13 +0200 |
commit | 42f2241d55b0921a071b6137b26488f139ac23cf (patch) | |
tree | 9a9414dfcd91055c5af468192f836e4574186852 /src/winextras/qwinthumbnailtoolbar.cpp | |
parent | b99e9e1b9a34b35cdf8ec4e635f8e3c1420820d6 (diff) |
Introduce QWinThumbnailToolBar and QWinThumbnailToolButton
Change-Id: I55d8f2e01c3647b49d117b2e7e8f7757fa843cba
Reviewed-by: Jake Petroules <jake.petroules@petroules.com>
Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
Diffstat (limited to 'src/winextras/qwinthumbnailtoolbar.cpp')
-rw-r--r-- | src/winextras/qwinthumbnailtoolbar.cpp | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/winextras/qwinthumbnailtoolbar.cpp b/src/winextras/qwinthumbnailtoolbar.cpp new file mode 100644 index 0000000..6021241 --- /dev/null +++ b/src/winextras/qwinthumbnailtoolbar.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** + ** + ** Copyright (C) 2013 Ivan Vizir <define-true-false@yandex.com> + ** Contact: http://www.qt-project.org/legal + ** + ** This file is part of QtWinExtras in the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and Digia. For licensing terms and + ** conditions see http://qt.digia.com/licensing. For further information + ** use the contact form at http://qt.digia.com/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, 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, Digia gives you certain additional + ** rights. These rights are described in the Digia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 3.0 as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 3.0 requirements will be + ** met: http://www.gnu.org/copyleft/gpl.html. + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qwinthumbnailtoolbar.h" +#include "qwinthumbnailtoolbar_p.h" +#include "qwinthumbnailtoolbutton.h" +#include "qwinthumbnailtoolbutton_p.h" + +#include <QWindow> +#include <QCoreApplication> +#include <QTimer> +#include <QDebug> + +#include "qwintaskbarbuttoncreatedevent.h" +#include "qwinfunctions.h" +#include "qwineventfilter_p.h" + +QT_BEGIN_NAMESPACE + +static const int windowsLimitedThumbbarSize = 7; + +/*! + \class QWinThumbnailToolBar + \inmodule QtWinExtras + \since 5.2 + \brief The QWinThumbnailToolBar class allows manipulating the window's thumbnail toolbar. + + This class allows an application to embed a toolbar in the thumbnail of a window, + which is shown when hovering over its taskbar icon. It provides quick access to + the window's commands without requiring the user to restore or activate the window. + + \image thumbbar.png Media player thumbnail toolbar + + \section3 Example + \snippet code/thumbbar.cpp thumbbar_cpp + */ + +/*! + Constructs a QWinThumbnailToolBar with the parent object \a parent. + */ +QWinThumbnailToolBar::QWinThumbnailToolBar(QObject *parent) : + QObject(parent), d_ptr(new QWinThumbnailToolBarPrivate) +{ + Q_D(QWinThumbnailToolBar); + d->q_ptr = this; + QWinEventFilter::setup(); + setWindow(qobject_cast<QWindow *>(parent)); +} + +/*! + Destroys the QWinThumbnailToolBar and clears the toolbar. + */ +QWinThumbnailToolBar::~QWinThumbnailToolBar() +{ +} + +/*! + \property QWinThumbnailToolBar::window + \brief the window whose thumbnail toolbar is manipulated + */ +void QWinThumbnailToolBar::setWindow(QWindow *window) +{ + Q_D(QWinThumbnailToolBar); + if (d->window != window) { + if (d->window) { + d->window->removeEventFilter(d); + d->clearToolbar(); + } + d->window = window; + if (d->window) { + d->window->installEventFilter(d); + if (d->window->isVisible()) { + d->initToolbar(); + d->_q_scheduleUpdate(); + } + } + } +} + +QWindow *QWinThumbnailToolBar::window() const +{ + Q_D(const QWinThumbnailToolBar); + return d->window; +} + +/*! + Appends a \a button to the right of the thumbnail toolbar. + + \note The number of buttons is limited to 7. + */ +void QWinThumbnailToolBar::addButton(QWinThumbnailToolButton *button) +{ + Q_D(QWinThumbnailToolBar); + if (d->buttonList.size() >= windowsLimitedThumbbarSize) { + qWarning() << "Cannot add " << button << " maximum number of buttons (" + << windowsLimitedThumbbarSize << ") reached."; + return; + } + if (button && button->d_func()->toolbar != this) { + if (button->d_func()->toolbar) { + button->d_func()->toolbar->removeButton(button); + } + button->d_func()->toolbar = this; + connect(button, SIGNAL(changed()), this, SLOT(_q_scheduleUpdate())); + d->buttonList.append(button); + d->_q_scheduleUpdate(); + } +} + +/*! + Removes the \a button from this toolbar. + */ +void QWinThumbnailToolBar::removeButton(QWinThumbnailToolButton *button) +{ + Q_D(QWinThumbnailToolBar); + if (button && d->buttonList.contains(button)) { + button->d_func()->toolbar = 0; + disconnect(button, SIGNAL(changed()), this, SLOT(_q_scheduleUpdate())); + d->buttonList.removeAll(button); + d->_q_scheduleUpdate(); + } +} + +/*! + Replaces the currently set toolbar with \a buttons. + */ +void QWinThumbnailToolBar::setButtons(const QList<QWinThumbnailToolButton *> &buttons) +{ + Q_D(QWinThumbnailToolBar); + d->buttonList = buttons; + Q_FOREACH (QWinThumbnailToolButton *button, d->buttonList) + addButton(button); + d->_q_updateToolbar(); +} + +/*! + Returns the list of buttons which this toolbar consists of. + */ +QList<QWinThumbnailToolButton *> QWinThumbnailToolBar::buttons() const +{ + Q_D(const QWinThumbnailToolBar); + return d->buttonList; +} + +/*! + \property QWinThumbnailToolBar::count + \brief the current button count + */ +int QWinThumbnailToolBar::count() const +{ + Q_D(const QWinThumbnailToolBar); + return d->buttonList.size(); +} + +/*! + Removes all buttons from the toolbar. + */ +void QWinThumbnailToolBar::clear() +{ + setButtons(QList<QWinThumbnailToolButton *>()); +} + +static inline ITaskbarList4 *createTaskbarList() +{ + ITaskbarList4 *result = 0; + HRESULT hresult = CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER, IID_ITaskbarList4, reinterpret_cast<void **>(&result)); + if (FAILED(hresult)) { + const QString err = QWinExtras::errorStringFromHresult(hresult); + qWarning("QWinThumbnailToolBar: IID_ITaskbarList4 was not created: %#010x, %s.", + (unsigned)hresult, qPrintable(err)); + return 0; + } + hresult = result->HrInit(); + if (FAILED(hresult)) { + result->Release(); + const QString err = QWinExtras::errorStringFromHresult(hresult); + qWarning("QWinThumbnailToolBar: IID_ITaskbarList4 was not initialized: %#010x, %s.", + (unsigned)hresult, qPrintable(err)); + return 0; + } + return result; +} + +QWinThumbnailToolBarPrivate::QWinThumbnailToolBarPrivate() : + QObject(0), updateScheduled(false), window(0), pTbList(createTaskbarList()), q_ptr(0) +{ + buttonList.reserve(windowsLimitedThumbbarSize); + QCoreApplication::instance()->installNativeEventFilter(this); +} + +QWinThumbnailToolBarPrivate::~QWinThumbnailToolBarPrivate() +{ + if (pTbList) + pTbList->Release(); + QCoreApplication::instance()->removeNativeEventFilter(this); +} + +void QWinThumbnailToolBarPrivate::initToolbar() +{ + if (!pTbList || !window) + return; + THUMBBUTTON buttons[windowsLimitedThumbbarSize]; + initButtons(buttons); + HRESULT hresult = pTbList->ThumbBarAddButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons); + if (FAILED(hresult)) + qWarning() << msgComFailed("ThumbBarAddButtons", hresult); +} + +void QWinThumbnailToolBarPrivate::clearToolbar() +{ + if (!pTbList || !window) + return; + THUMBBUTTON buttons[windowsLimitedThumbbarSize]; + initButtons(buttons); + HRESULT hresult = pTbList->ThumbBarUpdateButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons); + if (FAILED(hresult)) + qWarning() << msgComFailed("ThumbBarUpdateButtons", hresult); +} + +void QWinThumbnailToolBarPrivate::_q_updateToolbar() +{ + updateScheduled = false; + if (!pTbList || !window) + return; + THUMBBUTTON buttons[windowsLimitedThumbbarSize]; + initButtons(buttons); + const int thumbbarSize = qMin(buttonList.size(), windowsLimitedThumbbarSize); + // filling from the right fixes some strange bug which makes last button bg look like first btn bg + for (int i = (windowsLimitedThumbbarSize - thumbbarSize); i < windowsLimitedThumbbarSize; i++) { + QWinThumbnailToolButton *button = buttonList.at(i - (windowsLimitedThumbbarSize - thumbbarSize)); + buttons[i].dwFlags = makeNativeButtonFlags(button); + buttons[i].dwMask = makeButtonMask(button); + if (!button->icon().isNull()) {; + buttons[i].hIcon = QWinExtras::toHICON(button->icon().pixmap(GetSystemMetrics(SM_CXSMICON))); + if (!buttons[i].hIcon) + buttons[i].hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, SM_CXSMICON, SM_CYSMICON, LR_SHARED); + } + if (!button->toolTip().isEmpty()) { + buttons[i].szTip[button->toolTip().left(sizeof(buttons[i].szTip)/sizeof(buttons[i].szTip[0]) - 1).toWCharArray(buttons[i].szTip)] = 0; + } + } + HRESULT hresult = pTbList->ThumbBarUpdateButtons(reinterpret_cast<HWND>(window->winId()), windowsLimitedThumbbarSize, buttons); + if (FAILED(hresult)) + qWarning() << msgComFailed("ThumbBarUpdateButtons", hresult); + freeButtonResources(buttons); +} + +void QWinThumbnailToolBarPrivate::_q_scheduleUpdate() +{ + if (updateScheduled) + return; + Q_Q(QWinThumbnailToolBar); + updateScheduled = true; + QTimer::singleShot(0, q, SLOT(_q_updateToolbar())); +} + +bool QWinThumbnailToolBarPrivate::eventFilter(QObject *object, QEvent *event) +{ + if (object == window && event->type() == QWinTaskbarButtonCreatedEvent::eventType()) { + initToolbar(); + _q_scheduleUpdate(); + } + return QObject::eventFilter(object, event); +} + +bool QWinThumbnailToolBarPrivate::nativeEventFilter(const QByteArray &, void *message, long *result) +{ + MSG *msg = static_cast<MSG *>(message); + if (window && msg->message == WM_COMMAND && HIWORD(msg->wParam) == THBN_CLICKED && msg->hwnd == reinterpret_cast<HWND>(window->winId())) { + int buttonId = LOWORD(msg->wParam); + buttonId = buttonId - (windowsLimitedThumbbarSize - qMin(windowsLimitedThumbbarSize, buttonList.size())); + buttonList.at(buttonId)->click(); + if (result) + *result = 0; + return true; + } + return false; +} + +void QWinThumbnailToolBarPrivate::initButtons(THUMBBUTTON *buttons) +{ + for (int i = 0; i < windowsLimitedThumbbarSize; i++) { + memset(&buttons[i], 0, sizeof buttons[i]); + buttons[i].iId = i; + buttons[i].dwFlags = THBF_HIDDEN; + buttons[i].dwMask = THB_FLAGS; + } +} + +THUMBBUTTONFLAGS QWinThumbnailToolBarPrivate::makeNativeButtonFlags(const QWinThumbnailToolButton *button) +{ + THUMBBUTTONFLAGS nativeFlags = (THUMBBUTTONFLAGS)0; + if (button->isEnabled()) + nativeFlags |= THBF_ENABLED; + else + nativeFlags |= THBF_DISABLED; + if (button->dismissOnClick()) + nativeFlags |= THBF_DISMISSONCLICK; + if (button->isFlat()) + nativeFlags |= THBF_NOBACKGROUND; + if (!button->isVisible()) + nativeFlags |= THBF_HIDDEN; + if (!button->isInteractive()) + nativeFlags |= THBF_NONINTERACTIVE; + return nativeFlags; +} + +THUMBBUTTONMASK QWinThumbnailToolBarPrivate::makeButtonMask(const QWinThumbnailToolButton *button) +{ + THUMBBUTTONMASK mask = (THUMBBUTTONMASK)0; + mask |= THB_FLAGS; + if (!button->icon().isNull()) + mask |= THB_ICON; + if (!button->toolTip().isEmpty()) + mask |= THB_TOOLTIP; + return mask; +} + +void QWinThumbnailToolBarPrivate::freeButtonResources(THUMBBUTTON *buttons) +{ + for (int i = 0; i < windowsLimitedThumbbarSize; i++) { + if (buttons[i].hIcon) + DeleteObject(buttons[i].hIcon); + } +} + +QString QWinThumbnailToolBarPrivate::msgComFailed(const char *function, HRESULT hresult) +{ + return QString::fromLatin1("QWinThumbnailToolBar: %1() failed: #%2: %3") + .arg(QLatin1String(function)) + .arg((unsigned)hresult, 10, 16, QLatin1Char('0')) + .arg(QWinExtras::errorStringFromHresult(hresult)); +} + +QT_END_NAMESPACE + +#include "moc_qwinthumbnailtoolbar.cpp" |