aboutsummaryrefslogtreecommitdiffstats
path: root/src/winextras/qwinthumbnailtoolbar.cpp
diff options
context:
space:
mode:
authorIvan Vizir <define-true-false@yandex.com>2013-07-31 14:18:44 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-08-02 10:20:13 +0200
commit42f2241d55b0921a071b6137b26488f139ac23cf (patch)
tree9a9414dfcd91055c5af468192f836e4574186852 /src/winextras/qwinthumbnailtoolbar.cpp
parentb99e9e1b9a34b35cdf8ec4e635f8e3c1420820d6 (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.cpp375
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"