1 /* This file is part of Clementine.
2 Copyright 2010, David Sansome <me@davidsansome.com>
3
4 Clementine is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 Clementine is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Clementine. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "windows7thumbbar.h"
19 #include "core/logging.h"
20
21 #include <QAction>
22 #include <QtDebug>
23
24 #ifdef Q_OS_WIN32
25 # ifndef _WIN32_WINNT
26 # define _WIN32_WINNT 0x0600
27 # endif
28 # include <windows.h>
29 # include <commctrl.h>
30 # include <shobjidl.h>
31 #endif // Q_OS_WIN32
32
33 const int Windows7ThumbBar::kIconSize = 16;
34 const int Windows7ThumbBar::kMaxButtonCount = 7;
35
Windows7ThumbBar(QWidget * widget)36 Windows7ThumbBar::Windows7ThumbBar(QWidget* widget)
37 : QObject(widget),
38 widget_(widget),
39 button_created_message_id_(0),
40 taskbar_list_(nullptr) {}
41
SetActions(const QList<QAction * > & actions)42 void Windows7ThumbBar::SetActions(const QList<QAction*>& actions) {
43 #ifdef Q_OS_WIN32
44 qLog(Debug) << "Setting actions";
45 Q_ASSERT(actions.count() <= kMaxButtonCount);
46
47 actions_ = actions;
48 for (QAction* action : actions) {
49 if (action) {
50 connect(action, SIGNAL(changed()), SLOT(ActionChanged()));
51 }
52 }
53 qLog(Debug) << "Done";
54 #endif // Q_OS_WIN32
55 }
56
57 #ifdef Q_OS_WIN32
58
59 extern HICON qt_pixmapToWinHICON(const QPixmap &p);
60
SetupButton(const QAction * action,THUMBBUTTON * button)61 static void SetupButton(const QAction* action, THUMBBUTTON* button) {
62 if (action) {
63 button->hIcon =
64 qt_pixmapToWinHICON(action->icon().pixmap(Windows7ThumbBar::kIconSize));
65 button->dwFlags = action->isEnabled() ? THBF_ENABLED : THBF_DISABLED;
66 // This is unsafe - doesn't obey 260-char restriction
67 action->text().toWCharArray(button->szTip);
68 button->szTip[action->text().count()] = L'\0';
69
70 if (!action->isVisible()) {
71 button->dwFlags = THUMBBUTTONFLAGS(button->dwFlags | THBF_HIDDEN);
72 }
73 button->dwMask = THUMBBUTTONMASK(THB_ICON | THB_TOOLTIP | THB_FLAGS);
74 } else {
75 button->hIcon = 0;
76 button->szTip[0] = L'\0';
77 button->dwFlags = THBF_NOBACKGROUND;
78 button->dwMask = THUMBBUTTONMASK(THB_FLAGS);
79 }
80 }
81 #endif // Q_OS_WIN32
82
HandleWinEvent(MSG * msg)83 void Windows7ThumbBar::HandleWinEvent(MSG* msg) {
84 #ifdef Q_OS_WIN32
85 if (button_created_message_id_ == 0) {
86 // Compute the value for the TaskbarButtonCreated message
87 button_created_message_id_ = RegisterWindowMessage("TaskbarButtonCreated");
88 qLog(Debug) << "TaskbarButtonCreated message ID registered"
89 << button_created_message_id_;
90 }
91
92 if (msg->message == button_created_message_id_) {
93 HRESULT hr;
94 qLog(Debug) << "Button created";
95 // Unref the old taskbar list if we had one
96 if (taskbar_list_) {
97 qLog(Debug) << "Releasing old taskbar list";
98 reinterpret_cast<ITaskbarList3*>(taskbar_list_)->Release();
99 taskbar_list_ = nullptr;
100 }
101
102 // Copied from win7 SDK shobjidl.h
103 static const GUID CLSID_ITaskbarList = {
104 0x56FDF344,
105 0xFD6D,
106 0x11d0,
107 {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}};
108 // Create the taskbar list
109 hr = CoCreateInstance(CLSID_ITaskbarList, nullptr, CLSCTX_ALL,
110 IID_ITaskbarList3, (void**)&taskbar_list_);
111 if (hr != S_OK) {
112 qLog(Warning) << "Error creating the ITaskbarList3 interface" << hex
113 << DWORD(hr);
114 return;
115 }
116
117 ITaskbarList3* taskbar_list =
118 reinterpret_cast<ITaskbarList3*>(taskbar_list_);
119 hr = taskbar_list->HrInit();
120 if (hr != S_OK) {
121 qLog(Warning) << "Error initialising taskbar list" << hex << DWORD(hr);
122 taskbar_list->Release();
123 taskbar_list_ = nullptr;
124 return;
125 }
126
127 // Add the buttons
128 qLog(Debug) << "Initialising" << actions_.count() << "buttons";
129 THUMBBUTTON buttons[kMaxButtonCount];
130 for (int i = 0; i < actions_.count(); ++i) {
131 const QAction* action = actions_[i];
132 THUMBBUTTON* button = &buttons[i];
133 button->iId = i;
134 SetupButton(action, button);
135 }
136
137 qLog(Debug) << "Adding buttons";
138 hr = taskbar_list->ThumbBarAddButtons((HWND)widget_->winId(), actions_.count(),
139 buttons);
140 if (hr != S_OK) qLog(Debug) << "Failed to add buttons" << hex << DWORD(hr);
141 for (int i = 0; i < actions_.count(); i++) {
142 if (buttons[i].hIcon > 0) DestroyIcon(buttons[i].hIcon);
143 }
144 } else if (msg->message == WM_COMMAND) {
145 const int button_id = LOWORD(msg->wParam);
146
147 if (button_id >= 0 && button_id < actions_.count()) {
148 if (actions_[button_id]) {
149 qLog(Debug) << "Button activated";
150 actions_[button_id]->activate(QAction::Trigger);
151 }
152 }
153 }
154 #endif // Q_OS_WIN32
155 }
156
ActionChanged()157 void Windows7ThumbBar::ActionChanged() {
158 #ifdef Q_OS_WIN32
159 if (!taskbar_list_) return;
160 ITaskbarList3* taskbar_list = reinterpret_cast<ITaskbarList3*>(taskbar_list_);
161
162 THUMBBUTTON buttons[kMaxButtonCount];
163 for (int i = 0; i < actions_.count(); ++i) {
164 const QAction* action = actions_[i];
165 THUMBBUTTON* button = &buttons[i];
166
167 button->iId = i;
168 SetupButton(action, button);
169 if (buttons->hIcon > 0) DestroyIcon(buttons->hIcon);
170 }
171
172 taskbar_list->ThumbBarUpdateButtons((HWND)widget_->winId(), actions_.count(),
173 buttons);
174 #endif // Q_OS_WIN32
175 }
176