blob: 0ce7708c8745def3cbf1e23ac0d803c3944de583 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import java.io.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.ole.win32.*;
import org.eclipse.swt.internal.win32.*;
/**
* Instances of this class represent the system task bar.
*
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>(none)</dd>
* </dl>
*
* @see Display#getSystemTaskBar
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
*
* @since 3.6
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class TaskBar extends Widget {
int itemCount;
TaskItem [] items = new TaskItem [4];
ITaskbarList3 mTaskbarList3;
String iconsDir;
static final char [] EXE_PATH;
static final PROPERTYKEY PKEY_Title = new PROPERTYKEY ();
static final PROPERTYKEY PKEY_AppUserModel_IsDestListSeparator = new PROPERTYKEY ();
static final String EXE_PATH_KEY = "org.eclipse.swt.win32.taskbar.executable"; //$NON-NLS-1$
static final String EXE_ARGS_KEY = "org.eclipse.swt.win32.taskbar.arguments"; //$NON-NLS-1$
static final String ICON_KEY = "org.eclipse.swt.win32.taskbar.icon"; //$NON-NLS-1$
static final String ICON_INDEX_KEY = "org.eclipse.swt.win32.taskbar.icon.index"; //$NON-NLS-1$
static {
OS.PSPropertyKeyFromString ("{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 2\0".toCharArray (), PKEY_Title); //$NON-NLS-1$
OS.PSPropertyKeyFromString ("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 6\0".toCharArray (), PKEY_AppUserModel_IsDestListSeparator); //$NON-NLS-1$
char [] buffer = new char [OS.MAX_PATH];
while (OS.GetModuleFileName (0, buffer, buffer.length) == buffer.length) {
buffer = new char [buffer.length + OS.MAX_PATH];
}
EXE_PATH = buffer;
}
TaskBar (Display display, int style) {
this.display = display;
createHandle ();
reskinWidget ();
}
void createHandle () {
long[] ppv = new long [1];
int hr = COM.CoCreateInstance (COM.CLSID_TaskbarList, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_ITaskbarList3, ppv);
if (hr == COM.REGDB_E_CLASSNOTREG) error (SWT.ERROR_NOT_IMPLEMENTED);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
mTaskbarList3 = new ITaskbarList3 (ppv [0]);
}
void createItem (TaskItem item, int index) {
if (index == -1) index = itemCount;
if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE);
if (itemCount == items.length) {
TaskItem [] newItems = new TaskItem [items.length + 4];
System.arraycopy (items, 0, newItems, 0, items.length);
items = newItems;
}
System.arraycopy (items, index, items, index + 1, itemCount++ - index);
items [index] = item;
}
void createItems () {
for (Shell shell : display.getShells ()) {
getItem (shell);
}
getItem (null);
}
IShellLink createShellLink (MenuItem item) {
int style = item.getStyle ();
if ((style & SWT.CASCADE) != 0) return null;
long [] ppv = new long [1];
int hr = COM.CoCreateInstance (COM.CLSID_ShellLink, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IShellLinkW, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
IShellLink pLink = new IShellLink (ppv [0]);
long hHeap = OS.GetProcessHeap ();
long pv = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, OS.PROPVARIANT_sizeof());
long titlePtr = 0;
PROPERTYKEY key;
if ((style & SWT.SEPARATOR) != 0) {
OS.MoveMemory (pv, new short [] {OS.VT_BOOL}, 2);
OS.MoveMemory (pv + 8, new short [] {OS.VARIANT_TRUE}, 2);
key = PKEY_AppUserModel_IsDestListSeparator;
} else {
String text = item.getText ();
int length = text.length ();
char [] buffer = new char [length + 1];
text.getChars (0, length, buffer, 0);
titlePtr = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, buffer.length * 2);
OS.MoveMemory (titlePtr, buffer, buffer.length * 2);
OS.MoveMemory (pv, new short [] {OS.VT_LPWSTR}, 2);
OS.MoveMemory (pv + 8, new long [] {titlePtr}, C.PTR_SIZEOF);
key = PKEY_Title;
String exePath = (String)item.getData (EXE_PATH_KEY);
if (exePath != null) {
length = exePath.length ();
buffer = new char [length + 1];
exePath.getChars (0, length, buffer, 0);
} else {
buffer = EXE_PATH;
}
hr = pLink.SetPath(buffer);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
text = (String)item.getData (EXE_ARGS_KEY);
if (text == null) text = Display.LAUNCHER_PREFIX + Display.TASKBAR_EVENT + item.id;
length = text.length ();
buffer = new char [length + 1];
text.getChars (0, length, buffer, 0);
hr = pLink.SetArguments(buffer);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
/* This code is intentionally commented */
// String tooltip = item.tooltip;
// if (tooltip != null) {
// length = tooltip.length ();
// buffer = new char [length + 1];
// tooltip.getChars (0, length, buffer, 0);
// hr = pLink.SetDescription (buffer);
// if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
// }
String icon = (String)item.getData (ICON_KEY);
int index = 0;
if (icon != null) {
text = (String)item.getData (ICON_INDEX_KEY);
if (text != null) index = Integer.parseInt (text);
} else {
String directory = null;
Image image = item.getImage ();
if (image != null) directory = getIconsDir ();
if (directory != null) {
icon = directory + "\\" + "menu" + item.id + ".ico";
ImageData data;
if (item.hBitmap != 0) {
Image image2 = Image.win32_new (display, SWT.BITMAP, item.hBitmap);
data = image2.getImageData (DPIUtil.getDeviceZoom ());
} else {
data = image.getImageData (DPIUtil.getDeviceZoom ());
}
ImageLoader loader = new ImageLoader ();
loader.data = new ImageData [] {data};
loader.save (icon, SWT.IMAGE_ICO);
}
}
if (icon != null) {
length = icon.length ();
buffer = new char [length + 1];
icon.getChars (0, length, buffer, 0);
hr = pLink.SetIconLocation(buffer, index);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
}
}
hr = pLink.QueryInterface(COM.IID_IPropertyStore, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
IPropertyStore pPropStore = new IPropertyStore (ppv [0]);
hr = pPropStore.SetValue(key, pv);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
pPropStore.Commit();
pPropStore.Release();
OS.HeapFree (hHeap, 0, pv);
if (titlePtr != 0) OS.HeapFree (hHeap, 0, titlePtr);
return pLink;
}
IObjectArray createShellLinkArray (MenuItem [] items) {
if (items == null) return null;
if (items.length == 0) return null;
long [] ppv = new long [1];
int hr = COM.CoCreateInstance (COM.CLSID_EnumerableObjectCollection, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IObjectCollection, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
IObjectCollection pObjColl = new IObjectCollection (ppv [0]);
for (MenuItem item : items) {
IShellLink pLink = createShellLink (item);
if (pLink != null) {
pObjColl.AddObject (pLink);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
pLink.Release ();
}
}
hr = pObjColl.QueryInterface(COM.IID_IObjectArray, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
IObjectArray poa = new IObjectArray (ppv [0]);
pObjColl.Release ();
return poa;
}
void destroyItem (TaskItem item) {
int index = 0;
while (index < itemCount) {
if (items [index] == item) break;
index++;
}
if (index == itemCount) return;
System.arraycopy (items, index + 1, items, index, --itemCount - index);
items [itemCount] = null;
}
String getIconsDir() {
if (iconsDir != null) return iconsDir;
File dir = new File(display.appLocalDir + "\\ico_dir");
if (dir.exists()) {
// remove old icons
for (File file : dir.listFiles()) file.delete();
} else if (!dir.mkdirs()) {
return null;
}
return iconsDir = dir.getPath();
}
/**
* Returns the item at the given, zero-relative index in the
* receiver. Throws an exception if the index is out of range.
*
* @param index the index of the item to return
* @return the item at the given index
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TaskItem getItem (int index) {
checkWidget ();
createItems ();
if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE);
return items [index];
}
/**
* Returns the <code>TaskItem</code> for the given <code>Shell</code> or the <code>TaskItem</code>
* for the application if the <code>Shell</code> parameter is <code>null</code>.
* If the requested item is not supported by the platform it returns <code>null</code>.
*
* @param shell the shell for which the task item is requested, or null to request the application item
* @return the task item for the given shell or the application
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TaskItem getItem (Shell shell) {
checkWidget ();
for (TaskItem item : items) {
if (item != null && item.shell == shell) {
return item;
}
}
TaskItem item = new TaskItem (this, SWT.NONE);
if (shell != null) item.setShell (shell);
return item;
}
/**
* Returns the number of items contained in the receiver.
*
* @return the number of items
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getItemCount () {
checkWidget ();
createItems ();
return itemCount;
}
/**
* Returns an array of <code>TaskItem</code>s which are the items
* in the receiver.
* <p>
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
* </p>
*
* @return the items in the receiver
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TaskItem [] getItems () {
checkWidget ();
createItems ();
TaskItem [] result = new TaskItem [itemCount];
System.arraycopy (items, 0, result, 0, result.length);
return result;
}
@Override
void releaseChildren (boolean destroy) {
if (items != null) {
for (TaskItem item : items) {
if (item != null && !item.isDisposed ()) {
item.release (false);
}
}
items = null;
}
super.releaseChildren (destroy);
}
@Override
void releaseParent () {
super.releaseParent ();
if (display.taskBar == this) display.taskBar = null;
}
@Override
void releaseWidget () {
super.releaseWidget ();
mTaskbarList3.Release();
mTaskbarList3 = null;
}
@Override
void reskinChildren (int flags) {
if (items != null) {
for (TaskItem item : items) {
if (item != null) item.reskin (flags);
}
}
super.reskinChildren (flags);
}
void setMenu (Menu menu) {
long [] ppv = new long [1];
int hr = COM.CoCreateInstance (COM.CLSID_DestinationList, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_ICustomDestinationList, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_NO_HANDLES);
ICustomDestinationList pDestList = new ICustomDestinationList (ppv [0]);
String appName = Display.APP_NAME;
char [] buffer = {'S', 'W', 'T', '\0'};
if (appName != null && appName.length () > 0) {
int length = appName.length ();
buffer = new char [length + 1];
appName.getChars (0, length, buffer, 0);
}
MenuItem [] items = null;
if (menu != null && (items = menu.getItems ()).length != 0) {
IObjectArray poa = createShellLinkArray (items);
if (poa != null) {
hr = pDestList.SetAppID (buffer);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
int [] cMaxSlots = new int [1];
pDestList.BeginList(cMaxSlots, COM.IID_IObjectArray, ppv);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
IObjectArray pRemovedItems = new IObjectArray (ppv [0]);
int [] count = new int [1];
poa.GetCount (count);
if (count [0] != 0) {
hr = pDestList.AddUserTasks (poa);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
}
for (MenuItem item : items) {
if ((item.getStyle () & SWT.CASCADE) != 0) {
Menu subMenu = item.getMenu ();
if (subMenu != null) {
MenuItem [] subItems = subMenu.getItems ();
IObjectArray poa2 = createShellLinkArray (subItems);
if (poa2 != null) {
poa2.GetCount (count);
if (count [0] != 0) {
String text = item.getText ();
int length = text.length ();
char [] buffer2 = new char [length + 1];
text.getChars (0, length, buffer2, 0);
hr = pDestList.AppendCategory (buffer2, poa2);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
}
poa2.Release ();
}
}
}
}
poa.Release();
hr = pDestList.CommitList ();
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
pRemovedItems.Release ();
}
} else {
hr = pDestList.DeleteList (buffer);
if (hr != OS.S_OK) error (SWT.ERROR_INVALID_ARGUMENT);
}
pDestList.Release ();
}
}