1/*
2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "awt.h"
27#include <windowsx.h>
28#include <shellapi.h>
29#include <shlwapi.h>
30
31#include "awt_Toolkit.h"
32#include "awt_TrayIcon.h"
33#include "awt_AWTEvent.h"
34
35#include <java_awt_event_InputEvent.h>
36
37/***********************************************************************/
38// Struct for _SetToolTip() method
39struct SetToolTipStruct {
40    jobject trayIcon;
41    jstring tooltip;
42};
43// Struct for _SetIcon() method
44struct SetIconStruct {
45    jobject trayIcon;
46    HICON hIcon;
47};
48// Struct for _UpdateIcon() method
49struct UpdateIconStruct {
50    jobject trayIcon;
51    jboolean update;
52};
53// Struct for _DisplayMessage() method
54struct DisplayMessageStruct {
55    jobject trayIcon;
56    jstring caption;
57    jstring text;
58    jstring msgType;
59};
60
61typedef struct tagBitmapheader  {
62    BITMAPV5HEADER bmiHeader;
63    DWORD            dwMasks[256];
64} Bitmapheader, *LPBITMAPHEADER;
65
66
67/************************************************************************
68 * AwtTrayIcon fields
69 */
70
71jfieldID AwtTrayIcon::idID;
72jfieldID AwtTrayIcon::actionCommandID;
73
74HWND AwtTrayIcon::sm_msgWindow = NULL;
75AwtTrayIcon::TrayIconListItem* AwtTrayIcon::sm_trayIconList = NULL;
76int AwtTrayIcon::sm_instCount = 0;
77
78/************************************************************************
79 * AwtTrayIcon methods
80 */
81
82AwtTrayIcon::AwtTrayIcon() {
83    ::ZeroMemory(&m_nid, sizeof(m_nid));
84
85    if (sm_instCount++ == 0 && AwtTrayIcon::sm_msgWindow == NULL) {
86        sm_msgWindow = AwtTrayIcon::CreateMessageWindow();
87    }
88    m_mouseButtonClickAllowed = 0;
89}
90
91AwtTrayIcon::~AwtTrayIcon() {
92}
93
94void AwtTrayIcon::Dispose() {
95    SendTrayMessage(NIM_DELETE);
96
97    // Destroy the icon to avoid leak of GDI objects
98    if (m_nid.hIcon != NULL) {
99        ::DestroyIcon(m_nid.hIcon);
100    }
101
102    UnlinkObjects();
103
104    if (--sm_instCount == 0) {
105        AwtTrayIcon::DestroyMessageWindow();
106    }
107
108    AwtObject::Dispose();
109}
110
111LPCTSTR AwtTrayIcon::GetClassName() {
112    return TEXT("SunAwtTrayIcon");
113}
114
115void AwtTrayIcon::FillClassInfo(WNDCLASS *lpwc)
116{
117    lpwc->style         = 0L;
118    lpwc->lpfnWndProc   = (WNDPROC)TrayWindowProc;
119    lpwc->cbClsExtra    = 0;
120    lpwc->cbWndExtra    = 0;
121    lpwc->hInstance     = AwtToolkit::GetInstance().GetModuleHandle(),
122    lpwc->hIcon         = AwtToolkit::GetInstance().GetAwtIcon();
123    lpwc->hCursor       = NULL;
124    lpwc->hbrBackground = NULL;
125    lpwc->lpszMenuName  = NULL;
126    lpwc->lpszClassName = AwtTrayIcon::GetClassName();
127}
128
129void AwtTrayIcon::RegisterClass()
130{
131    WNDCLASS  wc;
132
133    ::ZeroMemory(&wc, sizeof(wc));
134
135    if (!::GetClassInfo(AwtToolkit::GetInstance().GetModuleHandle(),
136                        AwtTrayIcon::GetClassName(), &wc))
137    {
138        AwtTrayIcon::FillClassInfo(&wc);
139        ATOM atom = ::RegisterClass(&wc);
140        DASSERT(atom != 0);
141    }
142}
143
144void AwtTrayIcon::UnregisterClass()
145{
146    ::UnregisterClass(AwtTrayIcon::GetClassName(), AwtToolkit::GetInstance().GetModuleHandle());
147}
148
149HWND AwtTrayIcon::CreateMessageWindow()
150{
151    AwtTrayIcon::RegisterClass();
152
153    HWND hWnd = ::CreateWindow(AwtTrayIcon::GetClassName(), TEXT("TrayMessageWindow"),
154                               0, 0, 0, 0, 0, NULL, NULL,
155                               AwtToolkit::GetInstance().GetModuleHandle(), NULL);
156    return hWnd;
157}
158
159void AwtTrayIcon::DestroyMessageWindow()
160{
161    ::DestroyWindow(AwtTrayIcon::sm_msgWindow);
162    AwtTrayIcon::sm_msgWindow = NULL;
163    AwtTrayIcon::UnregisterClass();
164}
165
166AwtTrayIcon* AwtTrayIcon::Create(jobject self, jobject parent)
167{
168    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
169    jobject target = NULL;
170    AwtTrayIcon* awtTrayIcon = NULL;
171
172    target  = env->GetObjectField(self, AwtObject::targetID);
173    DASSERT(target);
174
175    awtTrayIcon = new AwtTrayIcon();
176    awtTrayIcon->LinkObjects(env, self);
177    awtTrayIcon->InitNID(env->GetIntField(target, AwtTrayIcon::idID));
178    awtTrayIcon->AddTrayIconItem(awtTrayIcon->GetID());
179
180    env->DeleteLocalRef(target);
181    return awtTrayIcon;
182}
183
184void AwtTrayIcon::InitNID(UINT uID)
185{
186    // fix for 6271589: we MUST set the size of the structure to match
187    // the shell version, otherwise some errors may occur (like missing
188    // balloon messages on win2k)
189    DLLVERSIONINFO dllVersionInfo;
190    dllVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
191    int shellVersion = 5; // WIN_2000
192    // MSDN: DllGetVersion should not be implicitly called, but rather
193    // loaded using GetProcAddress
194    HMODULE hShell = JDK_LoadSystemLibrary("Shell32.dll");
195    if (hShell != NULL) {
196        DLLGETVERSIONPROC proc = (DLLGETVERSIONPROC)GetProcAddress(hShell, "DllGetVersion");
197        if (proc != NULL) {
198            if (proc(&dllVersionInfo) == NOERROR) {
199                shellVersion = dllVersionInfo.dwMajorVersion;
200            }
201        }
202    }
203    FreeLibrary(hShell);
204    switch (shellVersion) {
205        case 5: // WIN_2000
206            m_nid.cbSize = (BYTE *)(&m_nid.guidItem) - (BYTE *)(&m_nid.cbSize);
207            break;
208        case 6: // WIN_XP
209            m_nid.cbSize = (BYTE *)(&m_nid.hBalloonIcon) - (BYTE *)(&m_nid.cbSize);
210            break;
211        default: // WIN_VISTA
212            m_nid.cbSize = sizeof(m_nid);
213            break;
214    }
215    m_nid.hWnd = AwtTrayIcon::sm_msgWindow;
216    m_nid.uID = uID;
217    m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
218    m_nid.uCallbackMessage = WM_AWT_TRAY_NOTIFY;
219    m_nid.hIcon = AwtToolkit::GetInstance().GetAwtIcon();
220    m_nid.szTip[0] = '\0';
221    m_nid.uVersion = NOTIFYICON_VERSION;
222}
223
224BOOL AwtTrayIcon::SendTrayMessage(DWORD dwMessage)
225{
226    return Shell_NotifyIcon(dwMessage, (PNOTIFYICONDATA)&m_nid);
227}
228
229static UINT lastMessage = WM_NULL;
230
231LRESULT CALLBACK AwtTrayIcon::TrayWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
232{
233    LRESULT retValue = 0;
234    MsgRouting mr = mrDoDefault;
235    static UINT s_msgTaskbarCreated;
236
237    switch(uMsg)
238    {
239        case WM_CREATE:
240            // Fix for CR#6369062
241            s_msgTaskbarCreated = ::RegisterWindowMessage(TEXT("TaskbarCreated"));
242            break;
243        case WM_AWT_TRAY_NOTIFY:
244            if (hwnd == AwtTrayIcon::sm_msgWindow) {
245                AwtTrayIcon* trayIcon = AwtTrayIcon::SearchTrayIconItem((UINT)wParam);
246                if (trayIcon != NULL) {
247                    mr = trayIcon->WmAwtTrayNotify(wParam, lParam);
248                }
249            }
250            break;
251        default:
252            if(uMsg == s_msgTaskbarCreated) {
253                if (hwnd == AwtTrayIcon::sm_msgWindow) {
254                    mr = WmTaskbarCreated();
255                }
256            }
257            break;
258    }
259
260    if (mr != mrConsume) {
261        retValue = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
262    }
263    return retValue;
264}
265
266/*
267 * This function processes callback messages for taskbar icons.
268 */
269MsgRouting AwtTrayIcon::WmAwtTrayNotify(WPARAM wParam, LPARAM lParam)
270{
271    MsgRouting mr = mrDoDefault;
272
273    POINT pos = {0, 0};
274    ::GetCursorPos(&pos);
275
276    lastMessage = (UINT)lParam;
277    UINT flags = AwtToolkit::GetInstance().GetMouseKeyState();
278
279    switch((UINT)lParam)
280    {
281        case WM_MOUSEMOVE:
282            mr = WmMouseMove(flags, pos.x, pos.y);
283            break;
284        case WM_LBUTTONDBLCLK:
285        case WM_LBUTTONDOWN:
286            mr = WmMouseDown(flags, pos.x, pos.y, LEFT_BUTTON);
287            break;
288        case WM_LBUTTONUP:
289            mr = WmMouseUp(flags, pos.x, pos.y, LEFT_BUTTON);
290            break;
291        case WM_RBUTTONDBLCLK:
292        case WM_RBUTTONDOWN:
293            mr = WmMouseDown(flags, pos.x, pos.y, RIGHT_BUTTON);
294            break;
295        case WM_RBUTTONUP:
296            mr = WmMouseUp(flags, pos.x, pos.y, RIGHT_BUTTON);
297            break;
298        case WM_MBUTTONDBLCLK:
299        case WM_MBUTTONDOWN:
300            mr = WmMouseDown(flags, pos.x, pos.y, MIDDLE_BUTTON);
301            break;
302        case WM_MBUTTONUP:
303            mr = WmMouseUp(flags, pos.x, pos.y, MIDDLE_BUTTON);
304            break;
305        case WM_CONTEXTMENU:
306            mr = WmContextMenu(0, pos.x, pos.y);
307            break;
308        case NIN_KEYSELECT:
309            mr = WmKeySelect(0, pos.x, pos.y);
310            break;
311        case NIN_SELECT:
312            mr = WmSelect(0, pos.x, pos.y);
313            break;
314        case NIN_BALLOONUSERCLICK:
315            mr = WmBalloonUserClick(0, pos.x, pos.y);
316            break;
317    }
318    return mr;
319}
320
321/* Double-click variables. */
322static jlong multiClickTime = ::GetDoubleClickTime();
323static int multiClickMaxX = ::GetSystemMetrics(SM_CXDOUBLECLK);
324static int multiClickMaxY = ::GetSystemMetrics(SM_CYDOUBLECLK);
325static AwtTrayIcon* lastClickTrIc = NULL;
326static jlong lastTime = 0;
327static int lastClickX = 0;
328static int lastClickY = 0;
329static int lastButton = 0;
330static int clickCount = 0;
331
332MsgRouting AwtTrayIcon::WmMouseDown(UINT flags, int x, int y, int button)
333{
334    jlong now = ::JVM_CurrentTimeMillis(NULL, 0);
335    jint javaModif = AwtComponent::GetJavaModifiers();
336
337    if (lastClickTrIc == this &&
338        lastButton == button &&
339        (now - lastTime) <= multiClickTime &&
340        abs(x - lastClickX) <= multiClickMaxX &&
341        abs(y - lastClickY) <= multiClickMaxY)
342    {
343        clickCount++;
344    } else {
345        clickCount = 1;
346        lastClickTrIc = this;
347        lastButton = button;
348        lastClickX = x;
349        lastClickY = y;
350    }
351    lastTime = now;
352    // it's needed only if WM_LBUTTONUP doesn't come for some reason
353    m_mouseButtonClickAllowed |= AwtComponent::GetButtonMK(button);
354
355    MSG msg;
356    AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
357
358    SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y,
359                   javaModif, clickCount, JNI_FALSE,
360                   AwtComponent::GetButton(button), &msg);
361
362    return mrConsume;
363}
364
365MsgRouting AwtTrayIcon::WmMouseUp(UINT flags, int x, int y, int button)
366{
367    MSG msg;
368    AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
369
370    SendMouseEvent(java_awt_event_MouseEvent_MOUSE_RELEASED, ::JVM_CurrentTimeMillis(NULL, 0),
371                   x, y, AwtComponent::GetJavaModifiers(), clickCount,
372                   (AwtComponent::GetButton(button) == java_awt_event_MouseEvent_BUTTON3 ?
373                    TRUE : FALSE), AwtComponent::GetButton(button), &msg);
374
375    if ((m_mouseButtonClickAllowed & AwtComponent::GetButtonMK(button)) != 0) { // No up-button in the drag-state
376        SendMouseEvent(java_awt_event_MouseEvent_MOUSE_CLICKED,
377                       ::JVM_CurrentTimeMillis(NULL, 0), x, y, AwtComponent::GetJavaModifiers(),
378                       clickCount, JNI_FALSE, AwtComponent::GetButton(button));
379    }
380    m_mouseButtonClickAllowed &= ~AwtComponent::GetButtonMK(button); // Exclude the up-button from the drag-state
381
382    return mrConsume;
383}
384
385MsgRouting AwtTrayIcon::WmMouseMove(UINT flags, int x, int y)
386{
387    MSG msg;
388    static AwtTrayIcon* lastComp = NULL;
389    static int lastX = 0;
390    static int lastY = 0;
391
392    /*
393     * Workaround for CR#6267980
394     * Windows sends WM_MOUSEMOVE if mouse is motionless
395     */
396    if (lastComp != this || x != lastX || y != lastY) {
397        lastComp = this;
398        lastX = x;
399        lastY = y;
400        AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
401        if ((flags & ALL_MK_BUTTONS) != 0) {
402            m_mouseButtonClickAllowed = 0;
403        } else {
404            SendMouseEvent(java_awt_event_MouseEvent_MOUSE_MOVED, ::JVM_CurrentTimeMillis(NULL, 0), x, y,
405                           AwtComponent::GetJavaModifiers(), 0, JNI_FALSE,
406                           java_awt_event_MouseEvent_NOBUTTON, &msg);
407        }
408    }
409    return mrConsume;
410}
411
412MsgRouting AwtTrayIcon::WmBalloonUserClick(UINT flags, int x, int y)
413{
414    if (AwtComponent::GetJavaModifiers() & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) {
415        MSG msg;
416        AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
417        SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
418                        AwtComponent::GetActionModifiers(), &msg);
419    }
420    return mrConsume;
421}
422
423MsgRouting AwtTrayIcon::WmKeySelect(UINT flags, int x, int y)
424{
425    static jlong lastKeySelectTime = 0;
426    jlong now = ::JVM_CurrentTimeMillis(NULL, 0);
427
428    // If a user selects a notify icon with the ENTER key,
429    // Shell 5.0 sends double NIN_KEYSELECT notification.
430    if (lastKeySelectTime != now) {
431        MSG msg;
432        AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
433        SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
434                        AwtComponent::GetActionModifiers(), &msg);
435    }
436    lastKeySelectTime = now;
437
438    return mrConsume;
439}
440
441MsgRouting AwtTrayIcon::WmSelect(UINT flags, int x, int y)
442{
443
444    // If a user click on a notify icon with the mouse,
445    // Shell 5.0 sends NIN_SELECT notification on every click.
446    // To be compatible with JDK6.0 only second click is important.
447    if (clickCount == 2) {
448        MSG msg;
449        AwtComponent::InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y);
450        SendActionEvent(java_awt_event_ActionEvent_ACTION_PERFORMED, ::JVM_CurrentTimeMillis(NULL, 0),
451                        AwtComponent::GetActionModifiers(), &msg);
452    }
453    return mrConsume;
454}
455
456MsgRouting AwtTrayIcon::WmContextMenu(UINT flags, int x, int y)
457{
458    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
459    jobject peer = GetPeer(env);
460    if (peer != NULL) {
461        JNU_CallMethodByName(env, NULL, peer, "showPopupMenu",
462                             "(II)V", x, y);
463    }
464    return mrConsume;
465}
466
467/*
468 * Adds all icons we already have to taskbar.
469 * We use this method on taskbar recreation (see 6369062).
470 */
471MsgRouting AwtTrayIcon::WmTaskbarCreated() {
472    TrayIconListItem* item;
473    for (item = sm_trayIconList; item != NULL; item = item->m_next) {
474        BOOL result = item->m_trayIcon->SendTrayMessage(NIM_ADD);
475        // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
476        if (result) {
477            item->m_trayIcon->SendTrayMessage(NIM_SETVERSION);
478        }
479    }
480    return mrDoDefault;
481}
482
483void AwtTrayIcon::SendMouseEvent(jint id, jlong when, jint x, jint y,
484                                 jint modifiers, jint clickCount,
485                                 jboolean popupTrigger, jint button,
486                                 MSG *pMsg)
487{
488    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
489    if (GetPeer(env) == NULL) {
490        /* event received during termination. */
491        return;
492    }
493
494    static jclass mouseEventCls;
495    if (mouseEventCls == NULL) {
496        jclass mouseEventClsLocal =
497            env->FindClass("java/awt/event/MouseEvent");
498        if (!mouseEventClsLocal) {
499            /* exception already thrown */
500            return;
501        }
502        mouseEventCls = (jclass)env->NewGlobalRef(mouseEventClsLocal);
503        env->DeleteLocalRef(mouseEventClsLocal);
504    }
505
506    static jmethodID mouseEventConst;
507    if (mouseEventConst == NULL) {
508        mouseEventConst =
509            env->GetMethodID(mouseEventCls, "<init>",
510                             "(Ljava/awt/Component;IJIIIIIIZI)V");
511        DASSERT(mouseEventConst);
512        CHECK_NULL(mouseEventConst);
513    }
514    if (env->EnsureLocalCapacity(2) < 0) {
515        return;
516    }
517    jobject target = GetTarget(env);
518    jobject mouseEvent = env->NewObject(mouseEventCls, mouseEventConst,
519                                        target,
520                                        id, when, modifiers,
521                                        x, y, // no client area coordinates
522                                        x, y,
523                                        clickCount, popupTrigger, button);
524
525    if (safe_ExceptionOccurred(env)) {
526        env->ExceptionDescribe();
527        env->ExceptionClear();
528    }
529
530    DASSERT(mouseEvent != NULL);
531    if (pMsg != 0) {
532        AwtAWTEvent::saveMSG(env, pMsg, mouseEvent);
533    }
534    SendEvent(mouseEvent);
535
536    env->DeleteLocalRef(mouseEvent);
537    env->DeleteLocalRef(target);
538}
539
540void AwtTrayIcon::SendActionEvent(jint id, jlong when, jint modifiers, MSG *pMsg)
541{
542    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
543    if (GetPeer(env) == NULL) {
544        /* event received during termination. */
545        return;
546    }
547
548    static jclass actionEventCls;
549    if (actionEventCls == NULL) {
550        jclass actionEventClsLocal =
551            env->FindClass("java/awt/event/ActionEvent");
552        if (!actionEventClsLocal) {
553            /* exception already thrown */
554            return;
555        }
556        actionEventCls = (jclass)env->NewGlobalRef(actionEventClsLocal);
557        env->DeleteLocalRef(actionEventClsLocal);
558    }
559
560    static jmethodID actionEventConst;
561    if (actionEventConst == NULL) {
562        actionEventConst =
563            env->GetMethodID(actionEventCls, "<init>",
564                             "(Ljava/lang/Object;ILjava/lang/String;JI)V");
565        DASSERT(actionEventConst);
566        CHECK_NULL(actionEventConst);
567    }
568    if (env->EnsureLocalCapacity(2) < 0) {
569        return;
570    }
571    jobject target = GetTarget(env);
572    jstring actionCommand = (jstring)env->GetObjectField(target, AwtTrayIcon::actionCommandID);
573    jobject actionEvent = env->NewObject(actionEventCls, actionEventConst,
574                                         target, id, actionCommand, when, modifiers);
575
576    if (safe_ExceptionOccurred(env)) {
577        env->ExceptionDescribe();
578        env->ExceptionClear();
579    }
580
581    DASSERT(actionEvent != NULL);
582    if (pMsg != 0) {
583        AwtAWTEvent::saveMSG(env, pMsg, actionEvent);
584    }
585    SendEvent(actionEvent);
586
587    env->DeleteLocalRef(actionEvent);
588    env->DeleteLocalRef(target);
589    env->DeleteLocalRef(actionCommand);
590}
591
592AwtTrayIcon* AwtTrayIcon::SearchTrayIconItem(UINT id) {
593    TrayIconListItem* item;
594    for (item = sm_trayIconList; item != NULL; item = item->m_next) {
595        if (item->m_ID == id) {
596            return item->m_trayIcon;
597        }
598    }
599    /*
600     * DASSERT(FALSE);
601     * This should not be happend if all tray icons are recorded
602     */
603    return NULL;
604}
605
606void AwtTrayIcon::RemoveTrayIconItem(UINT id) {
607    TrayIconListItem* item = sm_trayIconList;
608    TrayIconListItem* lastItem = NULL;
609    while (item != NULL) {
610        if (item->m_ID == id) {
611            if (lastItem == NULL) {
612                sm_trayIconList = item->m_next;
613            } else {
614                lastItem->m_next = item->m_next;
615            }
616            item->m_next = NULL;
617            DASSERT(item != NULL);
618            delete item;
619            return;
620        }
621        lastItem = item;
622        item = item->m_next;
623    }
624}
625
626void AwtTrayIcon::LinkObjects(JNIEnv *env, jobject peer)
627{
628    if (m_peerObject == NULL) {
629        m_peerObject = env->NewGlobalRef(peer);
630    }
631
632    /* Bind JavaPeer -> C++*/
633    JNI_SET_PDATA(peer, this);
634}
635
636void AwtTrayIcon::UnlinkObjects()
637{
638    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
639    if (m_peerObject) {
640        JNI_SET_PDATA(m_peerObject, static_cast<PDATA>(NULL));
641        env->DeleteGlobalRef(m_peerObject);
642        m_peerObject = NULL;
643    }
644}
645
646HBITMAP AwtTrayIcon::CreateBMP(HWND hW,int* imageData,int nSS, int nW, int nH)
647{
648    Bitmapheader    bmhHeader = {0};
649    HDC             hDC;
650    char            *ptrImageData;
651    HBITMAP         hbmpBitmap;
652    HBITMAP         hBitmap;
653    int             nNumChannels    = 4;
654
655    if (!hW) {
656        hW = ::GetDesktopWindow();
657    }
658    hDC = ::GetDC(hW);
659    if (!hDC) {
660        return NULL;
661    }
662
663    bmhHeader.bmiHeader.bV5Size              = sizeof(BITMAPV5HEADER);
664    bmhHeader.bmiHeader.bV5Width             = nW;
665    bmhHeader.bmiHeader.bV5Height            = -nH;
666    bmhHeader.bmiHeader.bV5Planes            = 1;
667
668    bmhHeader.bmiHeader.bV5BitCount          = 32;
669    bmhHeader.bmiHeader.bV5Compression       = BI_BITFIELDS;
670
671    // The following mask specification specifies a supported 32 BPP
672    // alpha format for Windows XP.
673    bmhHeader.bmiHeader.bV5RedMask   =  0x00FF0000;
674    bmhHeader.bmiHeader.bV5GreenMask =  0x0000FF00;
675    bmhHeader.bmiHeader.bV5BlueMask  =  0x000000FF;
676    bmhHeader.bmiHeader.bV5AlphaMask =  0xFF000000;
677
678    hbmpBitmap = ::CreateDIBSection(hDC, (BITMAPINFO*)&(bmhHeader),
679                                    DIB_RGB_COLORS,
680                                    (void**)&(ptrImageData),
681                                    NULL, 0);
682    int  *srcPtr = imageData;
683    char *dstPtr = ptrImageData;
684    if (!dstPtr) {
685        ReleaseDC(hW, hDC);
686        return NULL;
687    }
688    for (int nOutern = 0; nOutern < nH; nOutern++) {
689        for (int nInner = 0; nInner < nSS; nInner++) {
690            dstPtr[3] = (*srcPtr >> 0x18) & 0xFF;
691            dstPtr[2] = (*srcPtr >> 0x10) & 0xFF;
692            dstPtr[1] = (*srcPtr >> 0x08) & 0xFF;
693            dstPtr[0] = *srcPtr & 0xFF;
694
695            srcPtr++;
696            dstPtr += nNumChannels;
697        }
698    }
699
700    // convert it into DDB to make CustomCursor work on WIN95
701    hBitmap = CreateDIBitmap(hDC,
702                             (BITMAPINFOHEADER*)&bmhHeader,
703                             CBM_INIT,
704                             (void *)ptrImageData,
705                             (BITMAPINFO*)&bmhHeader,
706                             DIB_RGB_COLORS);
707
708    ::DeleteObject(hbmpBitmap);
709    ::ReleaseDC(hW, hDC);
710//  ::GdiFlush();
711    return hBitmap;
712}
713
714void AwtTrayIcon::SetToolTip(LPCTSTR tooltip)
715{
716    if (tooltip == NULL) {
717        m_nid.szTip[0] = '\0';
718    } else if (lstrlen(tooltip) >= TRAY_ICON_TOOLTIP_MAX_SIZE) {
719        _tcsncpy(m_nid.szTip, tooltip, TRAY_ICON_TOOLTIP_MAX_SIZE);
720        m_nid.szTip[TRAY_ICON_TOOLTIP_MAX_SIZE - 1] = '\0';
721    } else {
722        _tcscpy_s(m_nid.szTip, TRAY_ICON_TOOLTIP_MAX_SIZE, tooltip);
723    }
724
725    SendTrayMessage(NIM_MODIFY);
726}
727
728void AwtTrayIcon::_SetToolTip(void *param)
729{
730    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
731    SetToolTipStruct *sts = (SetToolTipStruct *)param;
732    jobject self = sts->trayIcon;
733    jstring jtooltip = sts->tooltip;
734    AwtTrayIcon *trayIcon = NULL;
735    LPCTSTR tooltipStr = NULL;
736
737    PDATA pData;
738    JNI_CHECK_PEER_GOTO(self, ret);
739    trayIcon = (AwtTrayIcon *)pData;
740
741    if (jtooltip == NULL) {
742        trayIcon->SetToolTip(NULL);
743        goto ret;
744    }
745
746    tooltipStr = JNU_GetStringPlatformChars(env, jtooltip, (jboolean *)NULL);
747    if (env->ExceptionCheck()) goto ret;
748    trayIcon->SetToolTip(tooltipStr);
749    JNU_ReleaseStringPlatformChars(env, jtooltip, tooltipStr);
750ret:
751    env->DeleteGlobalRef(self);
752    env->DeleteGlobalRef(jtooltip);
753    delete sts;
754}
755
756void AwtTrayIcon::SetIcon(HICON hIcon)
757{
758    ::DestroyIcon(m_nid.hIcon);
759    m_nid.hIcon = hIcon;
760}
761
762void AwtTrayIcon::_SetIcon(void *param)
763{
764    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
765    SetIconStruct *sis = (SetIconStruct *)param;
766    jobject self = sis->trayIcon;
767    HICON hIcon = sis->hIcon;
768    AwtTrayIcon *trayIcon = NULL;
769
770    PDATA pData;
771    JNI_CHECK_PEER_GOTO(self, ret);
772    trayIcon = (AwtTrayIcon *)pData;
773
774    trayIcon->SetIcon(hIcon);
775
776ret:
777    env->DeleteGlobalRef(self);
778    delete sis;
779}
780
781void AwtTrayIcon::_UpdateIcon(void *param)
782{
783    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
784    UpdateIconStruct *uis = (UpdateIconStruct *)param;
785    jobject self = uis->trayIcon;
786    jboolean jupdate = uis->update;
787    AwtTrayIcon *trayIcon = NULL;
788
789    PDATA pData;
790    JNI_CHECK_PEER_GOTO(self, ret);
791    trayIcon = (AwtTrayIcon *)pData;
792
793    BOOL result = trayIcon->SendTrayMessage(jupdate == JNI_TRUE ? NIM_MODIFY : NIM_ADD);
794    // 6270114: Instructs the taskbar to behave according to the Shell version 5.0
795    if (result && jupdate == JNI_FALSE) {
796        trayIcon->SendTrayMessage(NIM_SETVERSION);
797    }
798ret:
799    env->DeleteGlobalRef(self);
800    delete uis;
801}
802
803void AwtTrayIcon::DisplayMessage(LPCTSTR caption, LPCTSTR text, LPCTSTR msgType)
804{
805    m_nid.uFlags |= NIF_INFO;
806    m_nid.uTimeout = 10000;
807
808    if (lstrcmp(msgType, TEXT("ERROR")) == 0) {
809        m_nid.dwInfoFlags = NIIF_ERROR;
810    } else if (lstrcmp(msgType, TEXT("WARNING")) == 0) {
811        m_nid.dwInfoFlags = NIIF_WARNING;
812    } else if (lstrcmp(msgType, TEXT("INFO")) == 0) {
813        m_nid.dwInfoFlags = NIIF_INFO;
814    } else if (lstrcmp(msgType, TEXT("NONE")) == 0) {
815        m_nid.dwInfoFlags = NIIF_NONE;
816    } else {
817        m_nid.dwInfoFlags = NIIF_NONE;
818    }
819
820    if (caption[0] == '\0') {
821        m_nid.szInfoTitle[0] = '\0';
822
823    } else if (lstrlen(caption) >= TRAY_ICON_BALLOON_TITLE_MAX_SIZE) {
824
825        _tcsncpy(m_nid.szInfoTitle, caption, TRAY_ICON_BALLOON_TITLE_MAX_SIZE);
826        m_nid.szInfoTitle[TRAY_ICON_BALLOON_TITLE_MAX_SIZE - 1] = '\0';
827
828    } else {
829        _tcscpy_s(m_nid.szInfoTitle, TRAY_ICON_BALLOON_TITLE_MAX_SIZE, caption);
830    }
831
832    if (text[0] == '\0') {
833        m_nid.szInfo[0] = ' ';
834        m_nid.szInfo[1] = '\0';
835
836    } else if (lstrlen(text) >= TRAY_ICON_BALLOON_INFO_MAX_SIZE) {
837
838        _tcsncpy(m_nid.szInfo, text, TRAY_ICON_BALLOON_INFO_MAX_SIZE);
839        m_nid.szInfo[TRAY_ICON_BALLOON_INFO_MAX_SIZE - 1] = '\0';
840
841    } else {
842        _tcscpy_s(m_nid.szInfo, TRAY_ICON_BALLOON_INFO_MAX_SIZE, text);
843    }
844
845    SendTrayMessage(NIM_MODIFY);
846    m_nid.uFlags &= ~NIF_INFO;
847}
848
849void AwtTrayIcon::_DisplayMessage(void *param)
850{
851    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
852    DisplayMessageStruct *dms = (DisplayMessageStruct *)param;
853    jobject self = dms->trayIcon;
854    jstring jcaption = dms->caption;
855    jstring jtext = dms-> text;
856    jstring jmsgType = dms->msgType;
857    AwtTrayIcon *trayIcon = NULL;
858    LPCTSTR captionStr = NULL;
859    LPCTSTR textStr = NULL;
860    LPCTSTR msgTypeStr = NULL;
861
862    PDATA pData;
863    JNI_CHECK_PEER_GOTO(self, ret);
864    trayIcon = (AwtTrayIcon *)pData;
865
866    captionStr = JNU_GetStringPlatformChars(env, jcaption, (jboolean *)NULL);
867    if (env->ExceptionCheck()) goto ret;
868    textStr = JNU_GetStringPlatformChars(env, jtext, (jboolean *)NULL);
869    if (env->ExceptionCheck()) {
870        JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
871        goto ret;
872    }
873    msgTypeStr = JNU_GetStringPlatformChars(env, jmsgType, (jboolean *)NULL);
874    if (env->ExceptionCheck()) {
875        JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
876        JNU_ReleaseStringPlatformChars(env, jtext, textStr);
877        goto ret;
878    }
879    trayIcon->DisplayMessage(captionStr, textStr, msgTypeStr);
880
881    JNU_ReleaseStringPlatformChars(env, jcaption, captionStr);
882    JNU_ReleaseStringPlatformChars(env, jtext, textStr);
883    JNU_ReleaseStringPlatformChars(env, jmsgType, msgTypeStr);
884ret:
885    env->DeleteGlobalRef(self);
886    env->DeleteGlobalRef(jcaption);
887    env->DeleteGlobalRef(jtext);
888    env->DeleteGlobalRef(jmsgType);
889    delete dms;
890}
891
892/************************************************************************
893 * TrayIcon native methods
894 */
895
896extern "C" {
897
898/*
899 * Class:     java_awt_TrayIcon
900 * Method:    initIDs
901 * Signature: ()V
902 */
903JNIEXPORT void JNICALL
904Java_java_awt_TrayIcon_initIDs(JNIEnv *env, jclass cls)
905{
906    TRY;
907
908    /* init field ids */
909    AwtTrayIcon::idID = env->GetFieldID(cls, "id", "I");
910    DASSERT(AwtTrayIcon::idID != NULL);
911    CHECK_NULL(AwtTrayIcon::idID);
912
913    AwtTrayIcon::actionCommandID = env->GetFieldID(cls, "actionCommand", "Ljava/lang/String;");
914    DASSERT(AwtTrayIcon::actionCommandID != NULL);
915    CHECK_NULL( AwtTrayIcon::actionCommandID);
916
917    CATCH_BAD_ALLOC;
918}
919
920/*
921 * Class:     sun_awt_windows_WTrayIconPeer
922 * Method:    create
923 * Signature: ()V
924 */
925JNIEXPORT void JNICALL
926Java_sun_awt_windows_WTrayIconPeer_create(JNIEnv *env, jobject self)
927{
928    TRY;
929
930    AwtToolkit::CreateComponent(self, NULL,
931                                (AwtToolkit::ComponentFactory)
932                                AwtTrayIcon::Create);
933    PDATA pData;
934    JNI_CHECK_PEER_CREATION_RETURN(self);
935
936    CATCH_BAD_ALLOC;
937}
938
939/*
940 * Class:     sun_awt_windows_WTrayIconPeer
941 * Method:    _dispose
942 * Signature: ()V
943 */
944JNIEXPORT void JNICALL
945Java_sun_awt_windows_WTrayIconPeer__1dispose(JNIEnv *env, jobject self)
946{
947    TRY;
948
949    AwtObject::_Dispose(self);
950
951    CATCH_BAD_ALLOC;
952}
953
954/*
955 * Class:     sun_awt_windows_WTrayIconPeer
956 * Method:    _setToolTip
957 * Signature: ()V
958 */
959JNIEXPORT void JNICALL
960Java_sun_awt_windows_WTrayIconPeer_setToolTip(JNIEnv *env, jobject self,
961                                              jstring tooltip)
962{
963    TRY;
964
965    SetToolTipStruct *sts = new SetToolTipStruct;
966    sts->trayIcon = env->NewGlobalRef(self);
967    if (tooltip != NULL) {
968        sts->tooltip = (jstring)env->NewGlobalRef(tooltip);
969    } else {
970        sts->tooltip = NULL;
971    }
972
973    AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetToolTip, sts);
974    // global ref and sts are deleted in _SetToolTip
975
976    CATCH_BAD_ALLOC;
977}
978
979/*
980 * Class:     sun_awt_windows_WTrayIconPeer
981 * Method:    setNativeIcon
982 * Signature: (I[B[IIIII)V
983 */
984JNIEXPORT void JNICALL
985Java_sun_awt_windows_WTrayIconPeer_setNativeIcon(JNIEnv *env, jobject self,
986                                                 jintArray intRasterData, jbyteArray andMask,
987                                                 jint nSS, jint nW, jint nH)
988{
989    TRY;
990
991    int length = env->GetArrayLength(andMask);
992    jbyte *andMaskPtr = new jbyte[length];
993
994    env->GetByteArrayRegion(andMask, 0, length, andMaskPtr);
995
996    HBITMAP hMask = ::CreateBitmap(nW, nH, 1, 1, (BYTE *)andMaskPtr);
997//    ::GdiFlush();
998
999    delete[] andMaskPtr;
1000
1001    jint *intRasterDataPtr = NULL;
1002    HBITMAP hColor = NULL;
1003    try {
1004        intRasterDataPtr = (jint *)env->GetPrimitiveArrayCritical(intRasterData, 0);
1005        if (intRasterDataPtr == NULL) {
1006            ::DeleteObject(hMask);
1007            return;
1008        }
1009        hColor = AwtTrayIcon::CreateBMP(NULL, (int *)intRasterDataPtr, nSS, nW, nH);
1010    } catch (...) {
1011        if (intRasterDataPtr != NULL) {
1012            env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1013        }
1014        ::DeleteObject(hMask);
1015        throw;
1016    }
1017
1018    env->ReleasePrimitiveArrayCritical(intRasterData, intRasterDataPtr, 0);
1019    intRasterDataPtr = NULL;
1020
1021    HICON hIcon = NULL;
1022
1023    if (hMask && hColor) {
1024        ICONINFO icnInfo;
1025        memset(&icnInfo, 0, sizeof(ICONINFO));
1026        icnInfo.hbmMask = hMask;
1027        icnInfo.hbmColor = hColor;
1028        icnInfo.fIcon = TRUE;
1029        icnInfo.xHotspot = TRAY_ICON_X_HOTSPOT;
1030        icnInfo.yHotspot = TRAY_ICON_Y_HOTSPOT;
1031
1032        hIcon = ::CreateIconIndirect(&icnInfo);
1033    }
1034    ::DeleteObject(hColor);
1035    ::DeleteObject(hMask);
1036
1037    //////////////////////////////////////////
1038
1039    SetIconStruct *sis = new SetIconStruct;
1040    sis->trayIcon = env->NewGlobalRef(self);
1041    sis->hIcon = hIcon;
1042
1043    AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_SetIcon, sis);
1044    // global ref is deleted in _SetIcon
1045
1046    CATCH_BAD_ALLOC;
1047}
1048
1049/*
1050 * Class:     sun_awt_windows_WTrayIconPeer
1051 * Method:    updateNativeIcon
1052 * Signature: (Z)V
1053 */
1054JNIEXPORT void JNICALL
1055Java_sun_awt_windows_WTrayIconPeer_updateNativeIcon(JNIEnv *env, jobject self,
1056                                                    jboolean doUpdate)
1057{
1058    TRY;
1059
1060    UpdateIconStruct *uis = new UpdateIconStruct;
1061    uis->trayIcon = env->NewGlobalRef(self);
1062    uis->update = doUpdate;
1063
1064    AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_UpdateIcon, uis);
1065    // global ref is deleted in _UpdateIcon
1066
1067    CATCH_BAD_ALLOC;
1068}
1069
1070/*
1071 * Class:     sun_awt_windows_WTrayIconPeer
1072 * Method:    displayMessage
1073 * Signature: ()V;
1074 */
1075JNIEXPORT void JNICALL
1076Java_sun_awt_windows_WTrayIconPeer__1displayMessage(JNIEnv *env, jobject self,
1077    jstring caption, jstring text, jstring msgType)
1078{
1079    TRY;
1080
1081    DisplayMessageStruct *dms = new DisplayMessageStruct;
1082    dms->trayIcon = env->NewGlobalRef(self);
1083    dms->caption = (jstring)env->NewGlobalRef(caption);
1084    dms->text = (jstring)env->NewGlobalRef(text);
1085    dms->msgType = (jstring)env->NewGlobalRef(msgType);
1086
1087    AwtToolkit::GetInstance().SyncCall(AwtTrayIcon::_DisplayMessage, dms);
1088    // global ref is deleted in _DisplayMessage
1089
1090    CATCH_BAD_ALLOC(NULL);
1091}
1092
1093} /* extern "C" */
1094