blob: a29c37e6bc6a06121f1767cf841b9ed2c191ceaf [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 <QtCore/qglobal.h>
#ifdef Q_CC_MINGW // MinGW: Include the correct definition of SHARDAPPIDINFOLINK
# if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000
# undef NTDDI_VERSION
# endif
# ifndef NTDDI_VERSION
# define NTDDI_VERSION 0x06010000
# endif
#endif // Q_CC_MINGW
#include "qwinjumplistcategory.h"
#include "qwinjumplistcategory_p.h"
#include "qwinjumplistitem_p.h"
#include "qwinfunctions_p.h"
#include "qwinjumplist_p.h"
#include "winshobjidl_p.h"
#include "windowsguidsdefs_p.h"
#include <QtCore/qdebug.h>
#include <shlobj.h>
QT_BEGIN_NAMESPACE
/*!
\class QWinJumpListCategory
\inmodule QtWinExtras
\since 5.2
\brief The QWinJumpListCategory class represents a jump list category.
*/
/*!
\enum QWinJumpListCategory::Type
This enum describes the available QWinJumpListCategory types.
\value Custom
A custom jump list category.
\value Recent
A jump list category of "recent" items.
\value Frequent
A jump list category of "frequent" items.
\value Tasks
A jump list category of tasks.
*/
QWinJumpListCategory *QWinJumpListCategoryPrivate::create(QWinJumpListCategory::Type type, QWinJumpList *jumpList)
{
QWinJumpListCategory *category = new QWinJumpListCategory;
category->d_func()->type = type;
category->d_func()->jumpList = jumpList;
if (type == QWinJumpListCategory::Recent || type == QWinJumpListCategory::Frequent)
category->d_func()->loadRecents();
return category;
}
void QWinJumpListCategoryPrivate::invalidate()
{
if (jumpList)
QWinJumpListPrivate::get(jumpList)->invalidate();
}
void QWinJumpListCategoryPrivate::loadRecents()
{
Q_ASSERT(jumpList);
IApplicationDocumentLists *pDocList = nullptr;
HRESULT hresult = CoCreateInstance(qCLSID_ApplicationDocumentLists, nullptr, CLSCTX_INPROC_SERVER, qIID_IApplicationDocumentLists, reinterpret_cast<void **>(&pDocList));
if (SUCCEEDED(hresult)) {
if (!jumpList->identifier().isEmpty()) {
wchar_t *id = qt_qstringToNullTerminated(jumpList->identifier());
hresult = pDocList->SetAppID(id);
delete[] id;
}
if (SUCCEEDED(hresult)) {
IObjectArray *array = nullptr;
hresult = pDocList->GetList(type == QWinJumpListCategory::Recent ? ADLT_RECENT : ADLT_FREQUENT,
0, qIID_IObjectArray, reinterpret_cast<void **>(&array));
if (SUCCEEDED(hresult)) {
items = QWinJumpListPrivate::fromComCollection(array);
array->Release();
}
}
pDocList->Release();
}
if (FAILED(hresult))
QWinJumpListPrivate::warning("loadRecents", hresult);
}
void QWinJumpListCategoryPrivate::addRecent(QWinJumpListItem *item)
{
Q_ASSERT(item->type() == QWinJumpListItem::Link);
wchar_t *id = nullptr;
if (jumpList && !jumpList->identifier().isEmpty())
id = qt_qstringToNullTerminated(jumpList->identifier());
SHARDAPPIDINFOLINK info;
info.pszAppID = id;
info.psl = QWinJumpListPrivate::toIShellLink(item);
if (info.psl) {
SHAddToRecentDocs(SHARD_APPIDINFOLINK, &info);
info.psl->Release();
}
delete[] id;
}
void QWinJumpListCategoryPrivate::clearRecents()
{
IApplicationDestinations *pDest = nullptr;
HRESULT hresult = CoCreateInstance(qCLSID_ApplicationDestinations, nullptr, CLSCTX_INPROC_SERVER, qIID_IApplicationDestinations, reinterpret_cast<void **>(&pDest));
if (SUCCEEDED(hresult)) {
const QString identifier = jumpList ? jumpList->identifier() : QString();
if (!identifier.isEmpty()) {
wchar_t *id = qt_qstringToNullTerminated(identifier);
hresult = pDest->SetAppID(id);
delete[] id;
}
hresult = pDest->RemoveAllDestinations();
pDest->Release();
}
if (FAILED(hresult))
QWinJumpListPrivate::warning("clearRecents", hresult);
}
/*!
Constructs a custom QWinJumpListCategory with the specified \a title.
*/
QWinJumpListCategory::QWinJumpListCategory(const QString &title) :
d_ptr(new QWinJumpListCategoryPrivate)
{
d_ptr->title = title;
}
/*!
Destroys the QWinJumpListCategory.
*/
QWinJumpListCategory::~QWinJumpListCategory()
{
Q_D(QWinJumpListCategory);
qDeleteAll(d->items);
d->items.clear();
}
/*!
Returns the category type.
*/
QWinJumpListCategory::Type QWinJumpListCategory::type() const
{
Q_D(const QWinJumpListCategory);
return d->type;
}
/*!
Returns whether the category is visible.
*/
bool QWinJumpListCategory::isVisible() const
{
Q_D(const QWinJumpListCategory);
return d->visible;
}
/*!
Sets the category \a visible.
*/
void QWinJumpListCategory::setVisible(bool visible)
{
Q_D(QWinJumpListCategory);
if (d->visible != visible) {
d->visible = visible;
d->invalidate();
}
}
/*!
Returns the category title.
*/
QString QWinJumpListCategory::title() const
{
Q_D(const QWinJumpListCategory);
return d->title;
}
/*!
Sets the category \a title.
*/
void QWinJumpListCategory::setTitle(const QString &title)
{
Q_D(QWinJumpListCategory);
if (d->title != title) {
d->title = title;
d->invalidate();
}
}
/*!
Returns the amount of items in the category.
*/
int QWinJumpListCategory::count() const
{
Q_D(const QWinJumpListCategory);
return d->items.count();
}
/*!
Returns whether the category is empty.
*/
bool QWinJumpListCategory::isEmpty() const
{
Q_D(const QWinJumpListCategory);
return d->items.isEmpty();
}
/*!
Returns the list of items in the category.
*/
QList<QWinJumpListItem *> QWinJumpListCategory::items() const
{
Q_D(const QWinJumpListCategory);
return d->items;
}
/*!
Adds an \a item to the category.
*/
void QWinJumpListCategory::addItem(QWinJumpListItem *item)
{
Q_D(QWinJumpListCategory);
if (!item)
return;
if (d->type == Recent || d->type == Frequent) {
if (item->type() == QWinJumpListItem::Separator) {
qWarning("QWinJumpListCategory::addItem(): only tasks/custom categories support separators.");
return;
}
if (item->type() == QWinJumpListItem::Destination) {
qWarning("QWinJumpListCategory::addItem(): only tasks/custom categories support destinations.");
return;
}
}
QWinJumpListItemPrivate *p = QWinJumpListItemPrivate::get(item);
if (p->category != this) {
p->category = this;
d->items.append(item);
if (d->type == QWinJumpListCategory::Recent || d->type == QWinJumpListCategory::Frequent)
d->addRecent(item);
d->invalidate();
}
}
/*!
Adds a destination to the category pointing to \a filePath.
*/
QWinJumpListItem *QWinJumpListCategory::addDestination(const QString &filePath)
{
auto *item = new QWinJumpListItem(QWinJumpListItem::Destination);
item->setFilePath(filePath);
addItem(item);
return item;
}
/*!
Adds a link to the category using \a title, \a executablePath, and
optionally \a arguments.
*/
QWinJumpListItem *QWinJumpListCategory::addLink(const QString &title, const QString &executablePath, const QStringList &arguments)
{
return addLink(QIcon(), title, executablePath, arguments);
}
/*!
\overload addLink()
Adds a link to the category using \a icon, \a title, \a executablePath,
and optionally \a arguments.
*/
QWinJumpListItem *QWinJumpListCategory::addLink(const QIcon &icon, const QString &title, const QString &executablePath, const QStringList &arguments)
{
auto *item = new QWinJumpListItem(QWinJumpListItem::Link);
item->setFilePath(executablePath);
item->setTitle(title);
item->setArguments(arguments);
item->setIcon(icon);
addItem(item);
return item;
}
/*!
Adds a separator to the category.
\note Only tasks category supports separators.
*/
QWinJumpListItem *QWinJumpListCategory::addSeparator()
{
auto *item = new QWinJumpListItem(QWinJumpListItem::Separator);
addItem(item);
return item;
}
/*!
Clears the category.
*/
void QWinJumpListCategory::clear()
{
Q_D(QWinJumpListCategory);
if (!d->items.isEmpty()) {
qDeleteAll(d->items);
d->items.clear();
if (d->type == QWinJumpListCategory::Recent || d->type == QWinJumpListCategory::Frequent)
d->clearRecents();
d->invalidate();
}
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QWinJumpListCategory *category)
{
QDebugStateSaver saver(debug);
debug.nospace();
debug.noquote();
debug << "QWinJumpListCategory(";
if (category) {
debug << "type=" << category->type() << ", isVisible="
<< category->isVisible() << ", title=\"" << category->title()
<< "\", items=" << category->items();
} else {
debug << '0';
}
debug << ')';
return debug;
return debug;
}
#endif // !QT_NO_DEBUG_STREAM
QT_END_NAMESPACE