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