blob: 9df4c3072626f5175f2ead10de264854e633ff59 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 Ivan Vizir <define-true-false@yandex.com>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWinExtras module of 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwintaskbarbutton.h"
#include "qwintaskbarbutton_p.h"
#include "qwintaskbarprogress.h"
#include "qwinfunctions.h"
#include "qwinfunctions_p.h"
#include "qwineventfilter_p.h"
#include "qwinevent.h"
#include "winshobjidl_p.h"
#include "windowsguidsdefs_p.h"
#include <QtGui/qwindow.h>
#include <QtGui/qicon.h>
#include <QtCore/qpair.h>
#include <dwmapi.h>
#include <shobjidl.h>
QT_BEGIN_NAMESPACE
/*!
\class QWinTaskbarButton
\inmodule QtWinExtras
\brief The QWinTaskbarButton class represents the Windows taskbar button for
a top-level window (Windows 7 and newer).
\since 5.2
The QWinTaskbarButton class enables you to set overlay icons on a taskbar
button, and provides access to its progress indicator.
An overlay icon indicates change in the state of an application, whereas
a progress indicator shows how time-consuming tasks are progressing.
\image taskbar-button.png Taskbar Button
The following example code illustrates how to use the QWinTaskbarButton
and QWinTaskbarProgress classes to adjust the look of the taskbar button:
\snippet code/taskbar.cpp taskbar_cpp
\note QWidget::windowHandle() returns a valid instance of a QWindow only
after the widget has been shown. It is therefore recommended to delay the
initialization of the QWinTaskbarButton instances until QWidget::showEvent().
\note The class wraps API only available since Windows 7. Instantiating it
on Windows XP or Windows Vista causes a runtime warning.
\sa QWinTaskbarProgress
*/
static TBPFLAG nativeProgressState(QWinTaskbarProgress *progress)
{
if (!progress || !progress->isVisible())
return TBPF_NOPROGRESS;
if (progress->isStopped())
return TBPF_ERROR;
if (progress->isPaused())
return TBPF_PAUSED;
if (progress->minimum() == 0 && progress->maximum() == 0)
return TBPF_INDETERMINATE;
return TBPF_NORMAL;
}
QWinTaskbarButtonPrivate::QWinTaskbarButtonPrivate()
{
HRESULT hresult = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, qIID_ITaskbarList4, reinterpret_cast<void **>(&pTbList));
if (FAILED(hresult)) {
pTbList = nullptr;
const QString err = QtWin::errorStringFromHresult(hresult);
qWarning("QWinTaskbarButton: qIID_ITaskbarList4 was not created: %#010x, %s.", unsigned(hresult), qPrintable(err));
} else if (FAILED(pTbList->HrInit())) {
pTbList->Release();
pTbList = nullptr;
const QString err = QtWin::errorStringFromHresult(hresult);
qWarning("QWinTaskbarButton: qIID_ITaskbarList4 was not initialized: %#010x, %s.", unsigned(hresult), qPrintable(err));
}
}
QWinTaskbarButtonPrivate::~QWinTaskbarButtonPrivate()
{
if (pTbList)
pTbList->Release();
}
HWND QWinTaskbarButtonPrivate::handle()
{
return reinterpret_cast<HWND>(window->winId());
}
int QWinTaskbarButtonPrivate::iconSize() const
{
return GetSystemMetrics(SM_CXSMICON);
}
void QWinTaskbarButtonPrivate::updateOverlayIcon()
{
if (!pTbList || !window)
return;
wchar_t *descrPtr = nullptr;
HICON hicon = nullptr;
if (!overlayAccessibleDescription.isEmpty())
descrPtr = qt_qstringToNullTerminated(overlayAccessibleDescription);
if (!overlayIcon.isNull())
hicon = QtWin::toHICON(overlayIcon.pixmap(iconSize()));
if (hicon)
pTbList->SetOverlayIcon(handle(), hicon, descrPtr);
else if (!hicon && !overlayIcon.isNull())
pTbList->SetOverlayIcon(handle(), static_cast<HICON>(LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, SM_CXSMICON, SM_CYSMICON, LR_SHARED)), descrPtr);
else
pTbList->SetOverlayIcon(handle(), nullptr, descrPtr);
if (hicon)
DestroyIcon(hicon);
delete[] descrPtr;
}
void QWinTaskbarButtonPrivate::_q_updateProgress()
{
if (!pTbList || !window)
return;
if (progressBar) {
const int min = progressBar->minimum();
const int max = progressBar->maximum();
const int range = max - min;
if (range > 0) {
const int value = qRound(double(100) * (double(progressBar->value() - min)) / double(range));
pTbList->SetProgressValue(handle(), ULONGLONG(value), 100);
}
}
pTbList->SetProgressState(handle(), nativeProgressState(progressBar));
}
/*!
Constructs a QWinTaskbarButton with the specified \a parent.
If \a parent is an instance of QWindow, it is automatically
assigned as the taskbar button's \l window.
*/
QWinTaskbarButton::QWinTaskbarButton(QObject *parent) :
QObject(parent), d_ptr(new QWinTaskbarButtonPrivate)
{
QWinEventFilter::setup();
setWindow(qobject_cast<QWindow *>(parent));
}
/*!
Destroys the QWinTaskbarButton.
*/
QWinTaskbarButton::~QWinTaskbarButton() = default;
/*!
\property QWinTaskbarButton::window
\brief the window whose taskbar button is manipulated
*/
void QWinTaskbarButton::setWindow(QWindow *window)
{
Q_D(QWinTaskbarButton);
if (d->window)
d->window->removeEventFilter(this);
d->window = window;
if (d->window) {
d->window->installEventFilter(this);
if (d->window->isVisible()) {
d->_q_updateProgress();
d->updateOverlayIcon();
}
}
}
QWindow *QWinTaskbarButton::window() const
{
Q_D(const QWinTaskbarButton);
return d->window;
}
/*!
\property QWinTaskbarButton::overlayIcon
\brief the overlay icon of the taskbar button
*/
QIcon QWinTaskbarButton::overlayIcon() const
{
Q_D(const QWinTaskbarButton);
return d->overlayIcon;
}
void QWinTaskbarButton::setOverlayIcon(const QIcon &icon)
{
Q_D(QWinTaskbarButton);
d->overlayIcon = icon;
d->updateOverlayIcon();
}
void QWinTaskbarButton::clearOverlayIcon()
{
setOverlayAccessibleDescription(QString());
setOverlayIcon(QIcon());
}
/*!
\property QWinTaskbarButton::overlayAccessibleDescription
\brief the description of the overlay for accessibility purposes
\sa overlayIcon
*/
QString QWinTaskbarButton::overlayAccessibleDescription() const
{
Q_D(const QWinTaskbarButton);
return d->overlayAccessibleDescription;
}
void QWinTaskbarButton::setOverlayAccessibleDescription(const QString &description)
{
Q_D(QWinTaskbarButton);
d->overlayAccessibleDescription = description;
d->updateOverlayIcon();
}
/*!
\property QWinTaskbarButton::progress
\brief the progress indicator of the taskbar button
\note The progress indicator is not \l{QWinTaskbarProgress::visible}{visible} by default.
*/
QWinTaskbarProgress *QWinTaskbarButton::progress() const
{
Q_D(const QWinTaskbarButton);
if (!d->progressBar) {
auto *that = const_cast<QWinTaskbarButton *>(this);
auto *pbar = new QWinTaskbarProgress(that);
connect(pbar, SIGNAL(destroyed()), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(valueChanged(int)), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(minimumChanged(int)), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(maximumChanged(int)), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(visibilityChanged(bool)), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(pausedChanged(bool)), this, SLOT(_q_updateProgress()));
connect(pbar, SIGNAL(stoppedChanged(bool)), this, SLOT(_q_updateProgress()));
that->d_func()->progressBar = pbar;
that->d_func()->_q_updateProgress();
}
return d->progressBar;
}
/*!
\internal
Intercepts TaskbarButtonCreated messages.
*/
bool QWinTaskbarButton::eventFilter(QObject *object, QEvent *event)
{
Q_D(QWinTaskbarButton);
if (object == d->window && event->type() == QWinEvent::TaskbarButtonCreated) {
d->_q_updateProgress();
d->updateOverlayIcon();
}
return false;
}
QT_END_NAMESPACE
#include "moc_qwintaskbarbutton.cpp"