1 /*******************************************************************************
2 * Copyright (c) 2010, 2017 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 * Tasktop Technologies - initial API and implementation
14 *******************************************************************************/
15 package org.eclipse.swt.widgets;
20 import org.eclipse.swt.*;
21 import org.eclipse.swt.graphics.*;
22 import org.eclipse.swt.internal.*;
23 import org.eclipse.swt.internal.ole.win32.*;
24 import org.eclipse.swt.internal.win32.*;
27 * Instances of this class represent the system task bar.
30 * <dt><b>Styles:</b></dt>
32 * <dt><b>Events:</b></dt>
36 * @see Display#getSystemTaskBar
37 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
41 * @noextend This class is not intended to be subclassed by clients.
43 public class TaskBar extends Widget {
45 TaskItem [] items = new TaskItem [4];
46 ITaskbarList3 mTaskbarList3;
49 static final char [] EXE_PATH;
50 static final PROPERTYKEY PKEY_Title = new PROPERTYKEY ();
51 static final PROPERTYKEY PKEY_AppUserModel_IsDestListSeparator = new PROPERTYKEY ();
52 static final String EXE_PATH_KEY = "org.eclipse.swt.win32.taskbar.executable"; //$NON-NLS-1$
53 static final String EXE_ARGS_KEY = "org.eclipse.swt.win32.taskbar.arguments"; //$NON-NLS-1$
54 static final String ICON_KEY = "org.eclipse.swt.win32.taskbar.icon"; //$NON-NLS-1$
55 static final String ICON_INDEX_KEY = "org.eclipse.swt.win32.taskbar.icon.index"; //$NON-NLS-1$
57 OS.PSPropertyKeyFromString ("{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 2\0".toCharArray (), PKEY_Title); //$NON-NLS-1$
58 OS.PSPropertyKeyFromString ("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 6\0".toCharArray (), PKEY_AppUserModel_IsDestListSeparator); //$NON-NLS-1$
59 char [] buffer = new char [OS.MAX_PATH];
60 while (OS.GetModuleFileName (0, buffer, buffer.length) == buffer.length) {
61 buffer = new char [buffer.length + OS.MAX_PATH];
66 TaskBar (Display display, int style) {
67 this.display = display;
72 void createHandle () {
73 long[] ppv = new long [1];
74 int hr = COM.CoCreateInstance (COM.CLSID_TaskbarList, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_ITaskbarList3, ppv);
75 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
76 mTaskbarList3 = new ITaskbarList3 (ppv [0]);
79 void createItem (TaskItem item, int index) {
80 if (index == -1) index = itemCount;
81 if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE);
82 if (itemCount == items.length) {
83 TaskItem [] newItems = new TaskItem [items.length + 4];
84 System.arraycopy (items, 0, newItems, 0, items.length);
87 System.arraycopy (items, index, items, index + 1, itemCount++ - index);
92 Shell [] shells = display.getShells ();
93 for (int i = 0; i < shells.length; i++) {
99 IShellLink createShellLink (MenuItem item) {
100 int style = item.getStyle ();
101 if ((style & SWT.CASCADE) != 0) return null;
102 long [] ppv = new long [1];
103 int hr = COM.CoCreateInstance (COM.CLSID_ShellLink, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IShellLinkW, ppv);
104 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
105 IShellLink pLink = new IShellLink (ppv [0]);
107 long hHeap = OS.GetProcessHeap ();
108 long pv = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, OS.PROPVARIANT_sizeof());
111 if ((style & SWT.SEPARATOR) != 0) {
112 OS.MoveMemory (pv, new short [] {OS.VT_BOOL}, 2);
113 OS.MoveMemory (pv + 8, new short [] {OS.VARIANT_TRUE}, 2);
114 key = PKEY_AppUserModel_IsDestListSeparator;
116 String text = item.getText ();
117 int length = text.length ();
118 char [] buffer = new char [length + 1];
119 text.getChars (0, length, buffer, 0);
120 titlePtr = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, buffer.length * 2);
121 OS.MoveMemory (titlePtr, buffer, buffer.length * 2);
122 OS.MoveMemory (pv, new short [] {OS.VT_LPWSTR}, 2);
123 OS.MoveMemory (pv + 8, new long [] {titlePtr}, C.PTR_SIZEOF);
126 String exePath = (String)item.getData (EXE_PATH_KEY);
127 if (exePath != null) {
128 length = exePath.length ();
129 buffer = new char [length + 1];
130 exePath.getChars (0, length, buffer, 0);
134 hr = pLink.SetPath(buffer);
135 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
137 text = (String)item.getData (EXE_ARGS_KEY);
138 if (text == null) text = Display.LAUNCHER_PREFIX + Display.TASKBAR_EVENT + item.id;
139 length = text.length ();
140 buffer = new char [length + 1];
141 text.getChars (0, length, buffer, 0);
142 hr = pLink.SetArguments(buffer);
143 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
145 /* This code is intentionally commented */
146 // String tooltip = item.tooltip;
147 // if (tooltip != null) {
148 // length = tooltip.length ();
149 // buffer = new char [length + 1];
150 // tooltip.getChars (0, length, buffer, 0);
151 // hr = pLink.SetDescription (buffer);
152 // if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
155 String icon = (String)item.getData (ICON_KEY);
158 text = (String)item.getData (ICON_INDEX_KEY);
159 if (text != null) index = Integer.parseInt (text);
161 String directory = null;
162 Image image = item.getImage ();
163 if (image != null) directory = getIconsDir ();
164 if (directory != null) {
165 icon = directory + "\\" + "menu" + item.id + ".ico";
167 if (item.hBitmap != 0) {
168 Image image2 = Image.win32_new (display, SWT.BITMAP, item.hBitmap);
169 data = image2.getImageData (DPIUtil.getDeviceZoom ());
171 data = image.getImageData (DPIUtil.getDeviceZoom ());
173 ImageLoader loader = new ImageLoader ();
174 loader.data = new ImageData [] {data};
175 loader.save (icon, SWT.IMAGE_ICO);
179 length = icon.length ();
180 buffer = new char [length + 1];
181 icon.getChars (0, length, buffer, 0);
182 hr = pLink.SetIconLocation(buffer, index);
183 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
187 hr = pLink.QueryInterface(COM.IID_IPropertyStore, ppv);
188 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
189 IPropertyStore pPropStore = new IPropertyStore (ppv [0]);
190 hr = pPropStore.SetValue(key, pv);
191 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
193 pPropStore.Release();
195 OS.HeapFree (hHeap, 0, pv);
196 if (titlePtr != 0) OS.HeapFree (hHeap, 0, titlePtr);
200 IObjectArray createShellLinkArray (MenuItem [] items) {
201 if (items == null) return null;
202 if (items.length == 0) return null;
203 long [] ppv = new long [1];
204 int hr = COM.CoCreateInstance (COM.CLSID_EnumerableObjectCollection, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IObjectCollection, ppv);
205 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
206 IObjectCollection pObjColl = new IObjectCollection (ppv [0]);
207 for (int i = 0; i < items.length; i++) {
208 IShellLink pLink = createShellLink (items[i]);
210 /*IObjectCollection::AddObject*/
211 pObjColl.AddObject (pLink);
212 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
216 /*IUnknown::QueryInterface*/
217 hr = pObjColl.QueryInterface(COM.IID_IObjectArray, ppv);
218 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
219 IObjectArray poa = new IObjectArray (ppv [0]);
224 void destroyItem (TaskItem item) {
226 while (index < itemCount) {
227 if (items [index] == item) break;
230 if (index == itemCount) return;
231 System.arraycopy (items, index + 1, items, index, --itemCount - index);
232 items [itemCount] = null;
235 String getIconsDir() {
236 if (iconsDir != null) return iconsDir;
237 String appData = System.getenv("LOCALAPPDATA");
238 String appName = Display.APP_NAME;
239 if (appData == null || appName == null) return null;
240 appName = appName.replaceAll("[\\\\/:*?\"<>|]", "_");
241 File dir = new File(appData + "\\" + appName + "\\ico_dir");
244 for (File file : dir.listFiles()) file.delete();
245 } else if (!dir.mkdirs()) {
248 return iconsDir = dir.getPath();
252 * Returns the item at the given, zero-relative index in the
253 * receiver. Throws an exception if the index is out of range.
255 * @param index the index of the item to return
256 * @return the item at the given index
258 * @exception IllegalArgumentException <ul>
259 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
261 * @exception SWTException <ul>
262 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
263 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
266 public TaskItem getItem (int index) {
269 if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE);
270 return items [index];
274 * Returns the <code>TaskItem</code> for the given <code>Shell</code> or the <code>TaskItem</code>
275 * for the application if the <code>Shell</code> parameter is <code>null</code>.
276 * If the requested item is not supported by the platform it returns <code>null</code>.
278 * @param shell the shell for which the task item is requested, or null to request the application item
279 * @return the task item for the given shell or the application
281 * @exception SWTException <ul>
282 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
283 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
286 public TaskItem getItem (Shell shell) {
288 for (int i = 0; i < items.length; i++) {
289 if (items [i] != null && items [i].shell == shell) {
293 TaskItem item = new TaskItem (this, SWT.NONE);
294 if (shell != null) item.setShell (shell);
299 * Returns the number of items contained in the receiver.
301 * @return the number of items
303 * @exception SWTException <ul>
304 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
305 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
308 public int getItemCount () {
315 * Returns an array of <code>TaskItem</code>s which are the items
318 * Note: This is not the actual structure used by the receiver
319 * to maintain its list of items, so modifying the array will
320 * not affect the receiver.
323 * @return the items in the receiver
325 * @exception SWTException <ul>
326 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
327 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
330 public TaskItem [] getItems () {
333 TaskItem [] result = new TaskItem [itemCount];
334 System.arraycopy (items, 0, result, 0, result.length);
339 void releaseChildren (boolean destroy) {
341 for (int i=0; i<items.length; i++) {
342 TaskItem item = items [i];
343 if (item != null && !item.isDisposed ()) {
344 item.release (false);
349 super.releaseChildren (destroy);
353 void releaseParent () {
354 super.releaseParent ();
355 if (display.taskBar == this) display.taskBar = null;
359 void releaseWidget () {
360 super.releaseWidget ();
361 mTaskbarList3.Release();
362 mTaskbarList3 = null;
366 void reskinChildren (int flags) {
368 for (int i=0; i<items.length; i++) {
369 TaskItem item = items [i];
370 if (item != null) item.reskin (flags);
373 super.reskinChildren (flags);
376 void setMenu (Menu menu) {
377 long [] ppv = new long [1];
378 int hr = COM.CoCreateInstance (COM.CLSID_DestinationList, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_ICustomDestinationList, ppv);
379 if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
380 ICustomDestinationList pDestList = new ICustomDestinationList (ppv [0]);
381 String appName = Display.APP_NAME;
382 char [] buffer = {'S', 'W', 'T', '\0'};
383 if (appName != null && appName.length () > 0) {
384 int length = appName.length ();
385 buffer = new char [length + 1];
386 appName.getChars (0, length, buffer, 0);
388 MenuItem [] items = null;
389 if (menu != null && (items = menu.getItems ()).length != 0) {
390 IObjectArray poa = createShellLinkArray (items);
392 hr = pDestList.SetAppID (buffer);
393 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
395 int [] cMaxSlots = new int [1];
396 pDestList.BeginList(cMaxSlots, COM.IID_IObjectArray, ppv);
397 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
398 IObjectArray pRemovedItems = new IObjectArray (ppv [0]);
400 int [] count = new int [1];
401 poa.GetCount (count);
402 if (count [0] != 0) {
403 hr = pDestList.AddUserTasks (poa);
404 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
407 for (int i = 0; i < items.length; i++) {
408 MenuItem item = items [i];
409 if ((item.getStyle () & SWT.CASCADE) != 0) {
410 Menu subMenu = item.getMenu ();
411 if (subMenu != null) {
412 MenuItem [] subItems = subMenu.getItems ();
413 IObjectArray poa2 = createShellLinkArray (subItems);
415 poa2.GetCount (count);
416 if (count [0] != 0) {
417 String text = item.getText ();
418 int length = text.length ();
419 char [] buffer2 = new char [length + 1];
420 text.getChars (0, length, buffer2, 0);
421 hr = pDestList.AppendCategory (buffer2, poa2);
422 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
424 /*IUnknown::Release*/
431 hr = pDestList.CommitList ();
432 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
433 pRemovedItems.Release ();
436 hr = pDestList.DeleteList (buffer);
437 if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
439 pDestList.Release ();