1/*
2 * Copyright (c) 1996, 2016, 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_Toolkit.h"
27#include "awt_TextComponent.h"
28#include "awt_TextArea.h"
29#include "awt_TextField.h"
30#include "awt_Canvas.h"
31
32#include "jni.h"
33#include "awt_Font.h"
34
35
36/***********************************************************************/
37// struct for _SetText() method
38struct SetTextStruct {
39    jobject textcomponent;
40    jstring text;
41};
42// struct for _Select() method
43struct SelectStruct {
44    jobject textcomponent;
45    jint start, end;
46};
47// struct for _EnableEditing() method
48struct EnableEditingStruct {
49    jobject textcomponent;
50    jboolean on;
51};
52/************************************************************************
53 * AwtTextComponent fields
54 */
55
56/************************************************************************
57 * AwtTextComponent methods
58 */
59
60jmethodID AwtTextComponent::canAccessClipboardMID;
61
62AwtTextComponent::AwtTextComponent() {
63    m_synthetic = FALSE;
64    m_lStartPos = -1;
65    m_lEndPos   = -1;
66    m_lLastPos  = -1;
67    m_isLFonly        = FALSE;
68    m_EOLchecked      = FALSE;
69    m_hEditCtrl       = NULL;
70    m_bIgnoreEnChange = FALSE;
71//    javaEventsMask = 0;    // accessibility support
72}
73
74LPCTSTR AwtTextComponent::GetClassName() {
75    static BOOL richedLibraryLoaded = FALSE;
76    if (!richedLibraryLoaded) {
77        JDK_LoadSystemLibrary("RICHED20.DLL");
78        richedLibraryLoaded = TRUE;
79    }
80    return RICHEDIT_CLASS;
81}
82
83/* Create a new AwtTextArea or AwtTextField object and window.   */
84AwtTextComponent* AwtTextComponent::Create(jobject peer, jobject parent, BOOL isMultiline)
85{
86    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
87
88    jobject target = NULL;
89    AwtTextComponent* c = NULL;
90
91    try {
92        if (env->EnsureLocalCapacity(1) < 0) {
93            return NULL;
94        }
95
96        PDATA pData;
97        AwtCanvas* awtParent;
98        JNI_CHECK_PEER_GOTO(parent, done);
99
100        awtParent = (AwtCanvas*)pData;
101        JNI_CHECK_NULL_GOTO(awtParent, "null awtParent", done);
102
103        target = env->GetObjectField(peer, AwtObject::targetID);
104        JNI_CHECK_NULL_GOTO(target, "null target", done);
105
106        if(isMultiline){
107            c = new AwtTextArea();
108        }else{
109            c = new AwtTextField();
110        }
111
112        {
113            /* Adjust style for scrollbar visibility and word wrap  */
114            DWORD scroll_style;
115
116            if(isMultiline){
117
118                 jint scrollbarVisibility =
119                     env->GetIntField(target, AwtTextArea::scrollbarVisibilityID);
120
121                 switch (scrollbarVisibility) {
122                     case java_awt_TextArea_SCROLLBARS_NONE:
123                         scroll_style = ES_AUTOVSCROLL;
124                         break;
125                     case java_awt_TextArea_SCROLLBARS_VERTICAL_ONLY:
126                         scroll_style = WS_VSCROLL | ES_AUTOVSCROLL;
127                         break;
128                     case java_awt_TextArea_SCROLLBARS_HORIZONTAL_ONLY:
129                         scroll_style = WS_HSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL;
130                         break;
131                     case java_awt_TextArea_SCROLLBARS_BOTH:
132                     default:
133                         scroll_style = WS_VSCROLL | WS_HSCROLL |
134                             ES_AUTOVSCROLL | ES_AUTOHSCROLL;
135                         break;
136                }
137            }
138
139          DWORD style = WS_CHILD | WS_CLIPSIBLINGS | ES_LEFT;
140
141          /*
142           * Specify ES_DISABLENOSCROLL - RichEdit control style to disable
143           * scrollbars instead of hiding them when not needed.
144           */
145          style |= isMultiline ?  ES_MULTILINE | ES_WANTRETURN | scroll_style
146              | ES_DISABLENOSCROLL : ES_AUTOHSCROLL;
147
148
149          DWORD exStyle = WS_EX_CLIENTEDGE;
150          if (GetRTL()) {
151              exStyle |= WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR;
152              if (GetRTLReadingOrder())
153                  exStyle |= WS_EX_RTLREADING;
154          }
155
156
157          jint x = env->GetIntField(target, AwtComponent::xID);
158          jint y = env->GetIntField(target, AwtComponent::yID);
159          jint width = env->GetIntField(target, AwtComponent::widthID);
160          jint height = env->GetIntField(target, AwtComponent::heightID);
161
162          c->CreateHWnd(env, L"", style, exStyle,
163                        x, y, width, height,
164                        awtParent->GetHWnd(),
165                        reinterpret_cast<HMENU>(static_cast<INT_PTR>(
166                awtParent->CreateControlID())),
167                        ::GetSysColor(COLOR_WINDOWTEXT),
168                        ::GetSysColor(COLOR_WINDOW),
169                        peer);
170
171          // Fix for 4753116.
172          // If it is not win95 (we are using Richedit 2.0)
173          // we set plain text mode, in which the control is
174          // similar to a standard edit control:
175          //  - The text in a plain text control can have only
176          //    one format.
177          //  - The user cannot paste rich text formats, such as RTF
178          //    or embedded objects into a plain text control.
179          //  - Rich text mode controls always have a default
180          //    end-of-document marker or carriage return,
181          //    to format paragraphs.
182          // kdm@sparc.spb.su
183          c->SendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0);
184
185          c->m_backgroundColorSet = TRUE;
186          /* suppress inheriting parent's color. */
187          c->UpdateBackground(env, target);
188          c->SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,
189                         MAKELPARAM(1, 1));
190          /*
191           * Fix for BugTraq Id 4260109.
192           * Set the text limit to the maximum.
193           * Use EM_EXLIMITTEXT for RichEdit controls.
194           * For some reason RichEdit 1.0 becomes read-only if the
195           * specified limit is greater than 0x7FFFFFFD.
196           */
197          c->SendMessage(EM_EXLIMITTEXT, 0, 0x7FFFFFFD);
198
199          /* Unregister RichEdit built-in drop target. */
200          VERIFY(::RevokeDragDrop(c->GetHWnd()) != DRAGDROP_E_INVALIDHWND);
201
202          /* To enforce CF_TEXT format for paste operations. */
203          VERIFY(c->SendMessage(EM_SETOLECALLBACK, 0,
204                                (LPARAM)&GetOleCallback()));
205
206          c->SendMessage(EM_SETEVENTMASK, 0, ENM_CHANGE);
207        }
208    } catch (...) {
209        env->DeleteLocalRef(target);
210        throw;
211    }
212
213done:
214    env->DeleteLocalRef(target);
215
216    return c;
217}
218
219void AwtTextComponent::Dispose()
220{
221    if (m_hEditCtrl != NULL) {
222        VERIFY(::DestroyWindow(m_hEditCtrl));
223        m_hEditCtrl = NULL;
224    }
225    AwtComponent::Dispose();
226}
227
228
229LRESULT
230AwtTextComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
231
232    switch (message) {
233        case WM_PRINTCLIENT:
234          {
235            FORMATRANGE fr;
236            HDC hPrinterDC = (HDC)wParam;
237            int nHorizRes = ::GetDeviceCaps(hPrinterDC, HORZRES);
238            int nVertRes = ::GetDeviceCaps(hPrinterDC, VERTRES);
239            int nLogPixelsX = ::GetDeviceCaps(hPrinterDC, LOGPIXELSX);
240            int nLogPixelsY = ::GetDeviceCaps(hPrinterDC, LOGPIXELSY);
241
242            // Ensure the printer DC is in MM_TEXT mode.
243            ::SetMapMode ( hPrinterDC, MM_TEXT );
244
245            // Rendering to the same DC we are measuring.
246            ::ZeroMemory(&fr, sizeof(fr));
247            fr.hdc = fr.hdcTarget = hPrinterDC;
248            // Set up the page.
249            fr.rcPage.left     = fr.rcPage.top = 0;
250            fr.rcPage.right    = (nHorizRes/nLogPixelsX) * 1440; // in twips
251            fr.rcPage.bottom   = (nVertRes/nLogPixelsY) * 1440;
252            fr.rc.left   = fr.rcPage.left;
253            fr.rc.top    = fr.rcPage.top;
254            fr.rc.right  = fr.rcPage.right;
255            fr.rc.bottom = fr.rcPage.bottom;
256
257            // start printing from the first visible line
258            LRESULT nLine = SendMessage(EM_GETFIRSTVISIBLELINE, 0, 0);
259            LONG startCh = static_cast<LONG>(SendMessage(EM_LINEINDEX,
260                                                         (WPARAM)nLine, 0));
261            fr.chrg.cpMin = startCh;
262            fr.chrg.cpMax = -1;
263
264            SendMessage(EM_FORMATRANGE, TRUE, (LPARAM)&fr);
265          }
266
267        break;
268    }
269
270    return AwtComponent::WindowProc(message, wParam, lParam);
271}
272
273LONG AwtTextComponent::EditGetCharFromPos(POINT& pt) {
274    return static_cast<LONG>(SendMessage(EM_CHARFROMPOS, 0,
275            reinterpret_cast<LPARAM>(&pt)));
276}
277
278/* Set a suitable font to IME against the component font. */
279void AwtTextComponent::SetFont(AwtFont* font)
280{
281    DASSERT(font != NULL);
282    if (font->GetAscent() < 0) {
283        AwtFont::SetupAscent(font);
284    }
285
286    int index = font->GetInputHFontIndex();
287    if (index < 0)
288        /* In this case, user cannot get any suitable font for input. */
289        index = 0;
290
291    //im --- changed for over the spot composing
292    m_hFont = font->GetHFont(index);
293    SendMessage(WM_SETFONT, (WPARAM)m_hFont, MAKELPARAM(FALSE, 0));
294    SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,
295                MAKELPARAM(1, 1));
296
297    /*
298     * WM_SETFONT reverts foreground color to the default for
299     * rich edit controls. So we have to restore it manually.
300     */
301    SetColor(GetColor());
302    VERIFY(::InvalidateRect(GetHWnd(), NULL, TRUE));
303    //im --- end
304
305}
306
307int AwtTextComponent::RemoveCR(WCHAR *pStr)
308{
309    int i, nLen = 0;
310
311    if (pStr) {
312        /* check to see if there are any CR's */
313        if (wcschr(pStr, L'\r') == NULL) {
314            return static_cast<int>(wcslen(pStr));
315        }
316
317        for (i=0; pStr[i] != 0; i++) {
318            if (m_isLFonly == TRUE) {
319                if (pStr[i] == L'\r') {
320                    continue;
321                }
322            } else {
323                if (pStr[i] == L'\r' && pStr[i + 1] != L'\n') {
324                    continue;
325                }
326            }
327            pStr[nLen++] = pStr[i];
328        }
329        pStr[nLen] = 0;
330    }
331    return nLen;
332}
333
334MsgRouting
335AwtTextComponent::WmNotify(UINT notifyCode)
336{
337    if (notifyCode == EN_CHANGE) {
338        /*
339         * Ignore notifications if the text hasn't been changed.
340         * EN_CHANGE sent on character formatting changes as well.
341         */
342        if (m_bIgnoreEnChange == FALSE) {
343            m_bCanUndo = TRUE;
344            DoCallback("valueChanged", "()V");
345        } else {
346            m_bCanUndo = FALSE;
347        }
348    }
349    return mrDoDefault;
350}
351
352BOOL AwtTextComponent::IsFocusingMouseMessage(MSG *pMsg)
353{
354    return pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONDBLCLK;
355}
356
357MsgRouting
358AwtTextComponent::HandleEvent(MSG *msg, BOOL synthetic)
359{
360    MsgRouting returnVal;
361
362    if (msg->message == WM_RBUTTONUP ||
363               (msg->message == WM_SYSKEYDOWN && msg->wParam == VK_F10 &&
364                HIBYTE(::GetKeyState(VK_SHIFT)))) {
365        POINT p;
366        if (msg->message == WM_RBUTTONUP) {
367            VERIFY(::GetCursorPos(&p));
368        } else {
369            p.x = -1;
370            p.y = -1;
371        }
372
373        if (!::PostMessage(GetHWnd(), WM_CONTEXTMENU, (WPARAM)GetHWnd(),
374                           MAKELPARAM(p.x, p.y))) {
375            JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
376            JNU_ThrowInternalError(env, "Message not posted, native event queue may be full.");
377            env->ExceptionDescribe();
378            env->ExceptionClear();
379        }
380        delete msg;
381        return mrConsume;
382    }
383
384    /*
385     * Store the 'synthetic' parameter so that the WM_PASTE security check
386     * happens only for synthetic events.
387     */
388    m_synthetic = synthetic;
389    returnVal = AwtComponent::HandleEvent(msg, synthetic);
390    m_synthetic = FALSE;
391    return returnVal;
392}
393
394/*
395 * If this Paste is occurring because of a synthetic Java event (e.g.,
396 * a synthesized <CTRL>-V KeyEvent), then verify that the TextComponent
397 * has permission to access the Clipboard before pasting. If permission
398 * is denied, we should throw a SecurityException, but currently do not
399 * because when we detect the security violation, we are in the Toolkit
400 * thread, not the thread which dispatched the illegal event.
401 */
402MsgRouting
403AwtTextComponent::WmPaste()
404{
405    if (m_synthetic) {
406        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
407        if (env->EnsureLocalCapacity(1) < 0) {
408            return mrConsume;
409        }
410        jobject target = GetTarget(env);
411        jboolean canAccessClipboard =
412            env->CallBooleanMethod (target, AwtTextComponent::canAccessClipboardMID);
413        env->DeleteLocalRef(target);
414        return (canAccessClipboard) ? mrDoDefault : mrConsume;
415    }
416    else {
417        return mrDoDefault;
418    }
419}
420
421//im --- override to over the spot composition
422void AwtTextComponent::SetCompositionWindow(RECT& rc)
423{
424    HWND hwnd = ImmGetHWnd();
425    HIMC hIMC = ImmGetContext(hwnd);
426    // rc is not used for text component.
427    COMPOSITIONFORM cf = { CFS_FORCE_POSITION, {0,0}, {0,0,0,0} };
428    GetCaretPos(&(cf.ptCurrentPos));
429    // the proxy is the native focus owner and it contains the composition window
430    // let's convert the position to a coordinate space relative to proxy
431    ::MapWindowPoints(GetHWnd(), GetProxyFocusOwner(), (LPPOINT)&cf.ptCurrentPos, 1);
432    ImmSetCompositionWindow(hIMC, &cf);
433
434    LOGFONT lf;
435    GetObject(m_hFont, sizeof(LOGFONT), &lf);
436    ImmSetCompositionFont(hIMC, &lf);
437    ImmReleaseContext(hwnd, hIMC);
438}
439//im --- end
440
441LONG AwtTextComponent::getJavaSelPos(LONG orgPos)
442{
443    long wlen;
444    long pos = 0;
445    long cur = 0;
446    LPTSTR wbuf;
447
448    if ((wlen = GetTextLength()) == 0)
449        return 0;
450    wbuf = new TCHAR[wlen + 1];
451    GetText(wbuf, wlen + 1);
452    if (m_isLFonly == TRUE) {
453        wlen = RemoveCR(wbuf);
454    }
455
456    while (cur < orgPos && pos++ < wlen) {
457        if (wbuf[cur] == _T('\r') && wbuf[cur + 1] == _T('\n')) {
458            cur++;
459        }
460        cur++;
461    }
462    delete[] wbuf;
463    return pos;
464}
465
466LONG AwtTextComponent::getWin32SelPos(LONG orgPos)
467{
468    long wlen;
469    long pos = 0;
470    long cur = 0;
471    LPTSTR wbuf;
472
473    if ((wlen = GetTextLength()) == 0)
474       return 0;
475    wbuf = new TCHAR[wlen + 1];
476    GetText(wbuf, wlen + 1);
477    if (m_isLFonly == TRUE) {
478        RemoveCR(wbuf);
479    }
480
481    while (cur < orgPos && pos < wlen) {
482        if (wbuf[pos] == _T('\r') && wbuf[pos + 1] == _T('\n')) {
483            pos++;
484        }
485        pos++;
486        cur++;
487    }
488    delete[] wbuf;
489    return pos;
490}
491
492void AwtTextComponent::CheckLineSeparator(WCHAR *pStr)
493{
494    if (pStr == NULL) {
495        return;
496    }
497
498    if (GetTextLength() == 0) {
499        m_EOLchecked = FALSE;
500    }
501
502    // check to see if there are any LF's
503    if (m_EOLchecked == TRUE || wcschr(pStr, L'\n') == NULL) {
504        return;
505    }
506
507    for (int i=0; pStr[i] != 0; i++) {
508        if (pStr[i] == L'\n') {
509            if (i > 0 && pStr[i-1] == L'\r') {
510                m_isLFonly = FALSE;
511            } else {
512                m_isLFonly = TRUE;
513            }
514            m_EOLchecked = TRUE;
515            return;
516        }
517    }
518}
519
520void AwtTextComponent::SetSelRange(LONG start, LONG end)
521{
522    SendMessage(EM_SETSEL,
523                getWin32SelPos(start),
524                getWin32SelPos(end));
525    // it isn't necessary to wrap this in EM_HIDESELECTION or setting/clearing
526    // ES_NOHIDESEL, as regular edit control honors EM_SCROLLCARET even when not in focus
527}
528
529jstring AwtTextComponent::_GetText(void *param)
530{
531    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
532
533    jobject self = (jobject)param;
534
535    AwtTextComponent *c = NULL;
536    jstring result = NULL;
537
538    PDATA pData;
539    JNI_CHECK_PEER_GOTO(self, ret);
540
541    c = (AwtTextComponent *)pData;
542    if (::IsWindow(c->GetHWnd()))
543    {
544        int len = ::GetWindowTextLength(c->GetHWnd());
545        if (len == 0) {
546            /* Make java null string */
547            jchar *jc = new jchar[0];
548            result = env->NewString(jc, 0);
549            delete [] jc;
550        } else {
551            WCHAR* buf = new WCHAR[len + 1];
552            c->GetText(buf, len + 1);
553            c->RemoveCR(buf);
554            result = JNU_NewStringPlatform(env, buf);
555            delete [] buf;
556        }
557    }
558ret:
559    env->DeleteGlobalRef(self);
560
561    if (result != NULL)
562    {
563        jstring globalRef = (jstring)env->NewGlobalRef(result);
564        env->DeleteLocalRef(result);
565        return globalRef;
566    }
567    else
568    {
569        return NULL;
570    }
571}
572
573void AwtTextComponent::_SetText(void *param)
574{
575    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
576
577    SetTextStruct *sts = (SetTextStruct *)param;
578    jobject self = sts->textcomponent;
579    jstring text = sts->text;
580
581    AwtTextComponent *c = NULL;
582
583    PDATA pData;
584    JNI_CHECK_PEER_GOTO(self, ret);
585    c = (AwtTextComponent *)pData;
586    if (::IsWindow(c->GetHWnd()))
587    {
588        int length = env->GetStringLength(text);
589        WCHAR* buffer = new WCHAR[length + 1];
590        env->GetStringRegion(text, 0, length, reinterpret_cast<jchar*>(buffer));
591        buffer[length] = 0;
592        c->CheckLineSeparator(buffer);
593        c->RemoveCR(buffer);
594        c->SetText(buffer);
595        delete[] buffer;
596    }
597ret:
598    env->DeleteGlobalRef(self);
599    env->DeleteGlobalRef(text);
600
601    delete sts;
602}
603
604jint AwtTextComponent::_GetSelectionStart(void *param)
605{
606    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
607
608    jobject self = (jobject)param;
609
610    jint result = 0;
611    AwtTextComponent *c = NULL;
612
613    PDATA pData;
614    JNI_CHECK_PEER_GOTO(self, ret);
615    c = (AwtTextComponent *)pData;
616    if (::IsWindow(c->GetHWnd()))
617    {
618        long start;
619        c->SendMessage(EM_GETSEL, (WPARAM)&start);
620        result = c->getJavaSelPos(start);
621    }
622ret:
623    env->DeleteGlobalRef(self);
624
625    return result;
626}
627
628jint AwtTextComponent::_GetSelectionEnd(void *param)
629{
630    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
631
632    jobject self = (jobject)param;
633
634    jint result = 0;
635    AwtTextComponent *c = NULL;
636
637    PDATA pData;
638    JNI_CHECK_PEER_GOTO(self, ret);
639    c = (AwtTextComponent *)pData;
640    if (::IsWindow(c->GetHWnd()))
641    {
642        long end;
643        c->SendMessage(EM_GETSEL, 0, (LPARAM)&end);
644        result = c->getJavaSelPos(end);
645    }
646ret:
647    env->DeleteGlobalRef(self);
648
649    return result;
650}
651
652void AwtTextComponent::_Select(void *param)
653{
654    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
655
656    SelectStruct *ss = (SelectStruct *)param;
657    jobject self = ss->textcomponent;
658    jint start = ss->start;
659    jint end = ss->end;
660
661    AwtTextComponent *c = NULL;
662
663    PDATA pData;
664    JNI_CHECK_PEER_GOTO(self, ret);
665    c = (AwtTextComponent *)pData;
666    if (::IsWindow(c->GetHWnd()))
667    {
668        c->SetSelRange(start, end);
669        c->SendMessage(EM_SCROLLCARET);
670    }
671ret:
672    env->DeleteGlobalRef(self);
673
674    delete ss;
675}
676
677void AwtTextComponent::_EnableEditing(void *param)
678{
679    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
680
681    EnableEditingStruct *ees = (EnableEditingStruct *)param;
682    jobject self = ees->textcomponent;
683    jboolean on = ees->on;
684
685    AwtTextComponent *c = NULL;
686
687    PDATA pData;
688    JNI_CHECK_PEER_GOTO(self, ret);
689    c = (AwtTextComponent *)pData;
690    if (::IsWindow(c->GetHWnd()))
691    {
692        c->SendMessage(EM_SETREADONLY, !on);
693    }
694ret:
695    env->DeleteGlobalRef(self);
696
697    delete ees;
698}
699
700/*
701 * Disabled edit control has grayed foreground.
702 * Disabled RichEdit 1.0 control has original foreground.
703 * Thus we have to set grayed foreground manually.
704 */
705void AwtTextComponent::Enable(BOOL bEnable)
706{
707    AwtComponent::Enable(bEnable);
708    SetColor(GetColor());
709}
710
711
712/*
713 * WM_CTLCOLOR is not sent by rich edit controls.
714 * Use EM_SETCHARFORMAT and EM_SETBKGNDCOLOR to set
715 * respectively foreground and background color.
716 */
717void AwtTextComponent::SetColor(COLORREF c) {
718    AwtComponent::SetColor(c);
719
720    CHARFORMAT cf;
721    memset(&cf, 0, sizeof(cf));
722    cf.cbSize = sizeof(cf);
723    cf.dwMask = CFM_COLOR;
724
725    cf.crTextColor = ::IsWindowEnabled(GetHWnd()) ? GetColor() : ::GetSysColor(COLOR_3DSHADOW);
726
727    /*
728     * The documentation for EM_GETCHARFORMAT is not exactly
729     * correct. It appears that wParam has the same meaning
730     * as for EM_SETCHARFORMAT. Our task is to secure that
731     * all the characters in the control have the required
732     * formatting. That's why we use SCF_ALL.
733     */
734    VERIFY(SendMessage(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf));
735    VERIFY(SendMessage(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf));
736}
737
738/*
739 * In responce to EM_SETBKGNDCOLOR rich edit changes
740 * its bg color and repaints itself so we don't need
741 * to force repaint.
742 */
743void AwtTextComponent::SetBackgroundColor(COLORREF c) {
744    AwtComponent::SetBackgroundColor(c);
745    SendMessage(EM_SETBKGNDCOLOR, (WPARAM)FALSE, (LPARAM)GetBackgroundColor());
746}
747
748void AwtTextComponent::EditGetSel(CHARRANGE &cr) {
749    SendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&cr));
750}
751
752
753/************************************************************************
754 * WTextComponentPeer native methods
755 */
756
757extern "C" {
758
759/*
760 * Class:     sun_awt_windows_WTextComponentPeer
761 * Method:    getText
762 * Signature: ()Ljava/lang/String;
763 */
764JNIEXPORT jstring JNICALL
765Java_sun_awt_windows_WTextComponentPeer_getText(JNIEnv *env, jobject self)
766{
767    TRY;
768
769    jobject selfGlobalRef = env->NewGlobalRef(self);
770
771    jstring globalRef = (jstring)AwtToolkit::GetInstance().SyncCall(
772        (void*(*)(void*))AwtTextComponent::_GetText,
773        (void *)selfGlobalRef);
774    // selfGlobalRef is deleted in _GetText
775    if (globalRef != NULL)
776    {
777        jstring localRef = (jstring)env->NewLocalRef(globalRef);
778        env->DeleteGlobalRef(globalRef);
779        return localRef;
780    }
781    else
782    {
783        return NULL;
784    }
785
786    CATCH_BAD_ALLOC_RET(NULL);
787}
788
789/*
790 * Class:     sun_awt_windows_WTextComponentPeer
791 * Method:    setText
792 * Signature: (Ljava/lang/String;)V
793 */
794JNIEXPORT void JNICALL
795Java_sun_awt_windows_WTextComponentPeer_setText(JNIEnv *env, jobject self,
796                                                jstring text)
797{
798    TRY;
799
800    SetTextStruct *sts = new SetTextStruct;
801    sts->textcomponent = env->NewGlobalRef(self);
802    sts->text = (jstring)env->NewGlobalRef(text);
803
804    AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_SetText, sts);
805    // global refs and sts are deleted in _SetText
806
807    CATCH_BAD_ALLOC;
808}
809
810/*
811 * Class:     sun_awt_windows_WTextComponentPeer
812 * Method:    getSelectionStart
813 * Signature: ()I
814 */
815JNIEXPORT jint JNICALL
816Java_sun_awt_windows_WTextComponentPeer_getSelectionStart(JNIEnv *env,
817                                                          jobject self)
818{
819    TRY;
820
821    return static_cast<jint>(reinterpret_cast<INT_PTR>(AwtToolkit::GetInstance().SyncCall(
822        (void *(*)(void *))AwtTextComponent::_GetSelectionStart,
823        env->NewGlobalRef(self))));
824    // global ref is deleted in _GetSelectionStart()
825
826    CATCH_BAD_ALLOC_RET(0);
827}
828
829/*
830 * Class:     sun_awt_windows_WTextComponentPeer
831 * Method:    getSelectionEnd
832 * Signature: ()I
833 */
834JNIEXPORT jint JNICALL
835Java_sun_awt_windows_WTextComponentPeer_getSelectionEnd(JNIEnv *env,
836                                                        jobject self)
837{
838    TRY;
839
840    return static_cast<jint>(reinterpret_cast<INT_PTR>(AwtToolkit::GetInstance().SyncCall(
841        (void *(*)(void *))AwtTextComponent::_GetSelectionEnd,
842        env->NewGlobalRef(self))));
843    // global ref is deleted in _GetSelectionEnd()
844
845    CATCH_BAD_ALLOC_RET(0);
846}
847
848/*
849 * Class:     sun_awt_windows_WTextComponentPeer
850 * Method:    select
851 * Signature: (II)V
852 */
853JNIEXPORT void JNICALL
854Java_sun_awt_windows_WTextComponentPeer_select(JNIEnv *env, jobject self,
855                                               jint start, jint end)
856{
857    TRY;
858
859    SelectStruct *ss = new SelectStruct;
860    ss->textcomponent = env->NewGlobalRef(self);
861    ss->start = start;
862    ss->end = end;
863
864    AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_Select, ss);
865    // global ref and ss are deleted in _Select
866
867    CATCH_BAD_ALLOC;
868}
869
870/*
871 * Class:     sun_awt_windows_WTextComponentPeer
872 * Method:    enableEditing
873 * Signature: (Z)V
874 */
875JNIEXPORT void JNICALL
876Java_sun_awt_windows_WTextComponentPeer_enableEditing(JNIEnv *env,
877                                                      jobject self,
878                                                      jboolean on)
879{
880    TRY;
881
882    EnableEditingStruct *ees = new EnableEditingStruct;
883    ees->textcomponent = env->NewGlobalRef(self);
884    ees->on = on;
885
886    AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_EnableEditing, ees);
887    // global ref and ees are deleted in _EnableEditing()
888
889    CATCH_BAD_ALLOC;
890}
891
892/*
893 * Class:     sun_awt_windows_WTextComponentPeer
894 * Method:    initIDs
895 * Signature: ()V
896 */
897JNIEXPORT void JNICALL
898Java_sun_awt_windows_WTextComponentPeer_initIDs(JNIEnv *env, jclass cls)
899{
900    TRY;
901
902    jclass textComponentClassID = env->FindClass("java/awt/TextComponent");
903    CHECK_NULL(textComponentClassID);
904
905    AwtTextComponent::canAccessClipboardMID =
906        env->GetMethodID(textComponentClassID, "canAccessClipboard", "()Z");
907    env->DeleteLocalRef(textComponentClassID);
908
909    DASSERT(AwtTextComponent::canAccessClipboardMID != NULL);
910
911    CATCH_BAD_ALLOC;
912}
913
914
915AwtTextComponent::OleCallback AwtTextComponent::sm_oleCallback;
916
917/************************************************************************
918 * Inner class OleCallback definition.
919 */
920
921AwtTextComponent::OleCallback::OleCallback() {
922    m_refs = 0;
923    AddRef();
924}
925
926STDMETHODIMP
927AwtTextComponent::OleCallback::QueryInterface(REFIID riid, LPVOID * ppvObj) {
928     if (::IsEqualIID(riid, IID_IUnknown) ||::IsEqualIID(riid, IID_IRichEditOleCallback)  ) {
929         *ppvObj = static_cast<IRichEditOleCallback*>(this);
930         AddRef();
931         return S_OK;
932     }
933     *ppvObj = NULL;
934     return E_NOINTERFACE;
935}
936
937
938STDMETHODIMP_(ULONG)
939AwtTextComponent::OleCallback::AddRef() {
940    return ++m_refs;
941}
942
943STDMETHODIMP_(ULONG)
944AwtTextComponent::OleCallback::Release() {
945    return (ULONG)--m_refs;
946}
947
948STDMETHODIMP
949AwtTextComponent::OleCallback::GetNewStorage(LPSTORAGE FAR * ppstg) {
950    return E_NOTIMPL;
951}
952
953STDMETHODIMP
954AwtTextComponent::OleCallback::GetInPlaceContext(LPOLEINPLACEFRAME FAR * ppipframe,
955                                                 LPOLEINPLACEUIWINDOW FAR* ppipuiDoc,
956                                                 LPOLEINPLACEFRAMEINFO pipfinfo)
957{
958    return E_NOTIMPL;
959}
960
961STDMETHODIMP
962AwtTextComponent::OleCallback::ShowContainerUI(BOOL fShow) {
963    return E_NOTIMPL;
964}
965
966STDMETHODIMP
967AwtTextComponent::OleCallback::QueryInsertObject(LPCLSID pclsid,
968                                                 LPSTORAGE pstg,
969                                                 LONG cp) {
970    return S_OK;
971}
972
973STDMETHODIMP
974AwtTextComponent::OleCallback::DeleteObject(LPOLEOBJECT poleobj) {
975    return S_OK;
976}
977
978STDMETHODIMP
979AwtTextComponent::OleCallback::QueryAcceptData(LPDATAOBJECT pdataobj,
980                                               CLIPFORMAT *pcfFormat,
981                                               DWORD reco,
982                                               BOOL fReally,
983                                               HGLOBAL hMetaPict) {
984    if (reco == RECO_PASTE) {
985        // If CF_TEXT format is available edit controls will select it,
986        // otherwise if it is CF_UNICODETEXT is available it will be
987        // selected, otherwise if CF_OEMTEXT is available it will be selected.
988        if (::IsClipboardFormatAvailable(CF_TEXT)) {
989            *pcfFormat = CF_TEXT;
990        } else if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
991            *pcfFormat = CF_UNICODETEXT;
992        } else if (::IsClipboardFormatAvailable(CF_OEMTEXT)) {
993            *pcfFormat = CF_OEMTEXT;
994        } else {
995            // Don't allow rich edit to paste clipboard data
996            // in other formats.
997            *pcfFormat = CF_TEXT;
998        }
999    }
1000
1001    return S_OK;
1002}
1003
1004STDMETHODIMP
1005AwtTextComponent::OleCallback::ContextSensitiveHelp(BOOL fEnterMode) {
1006    return S_OK;
1007}
1008
1009STDMETHODIMP
1010AwtTextComponent::OleCallback::GetClipboardData(CHARRANGE *pchrg,
1011                                                DWORD reco,
1012                                                LPDATAOBJECT *ppdataobj) {
1013    return E_NOTIMPL;
1014}
1015
1016STDMETHODIMP
1017AwtTextComponent::OleCallback::GetDragDropEffect(BOOL fDrag,
1018                                                 DWORD grfKeyState,
1019                                                 LPDWORD pdwEffect) {
1020
1021    return E_NOTIMPL;
1022}
1023
1024
1025STDMETHODIMP
1026AwtTextComponent::OleCallback::GetContextMenu(WORD seltype,
1027                                              LPOLEOBJECT lpoleobj,
1028                                              CHARRANGE FAR * lpchrg,
1029                                              HMENU FAR * lphmenu) {
1030    return E_NOTIMPL;
1031}
1032
1033
1034/*
1035 * This routine is a window procedure for the subclass of the standard edit control
1036 * used to generate context menu. RichEdit controls don't have built-in context menu.
1037 * To implement this functionality we have to create an invisible edit control and
1038 * forward WM_CONTEXTMENU messages from a RichEdit control to this helper edit control.
1039 * While the edit control context menu is active we intercept the message generated in
1040 * response to particular item selection and forward it back to the RichEdit control.
1041 * (See AwtTextArea::WmContextMenu for more details).
1042 */
1043
1044WNDPROC AwtTextComponent::sm_pDefWindowProc = NULL;
1045
1046LRESULT
1047AwtTextComponent::EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
1048
1049    static BOOL bContextMenuActive = FALSE;
1050
1051    LRESULT retValue = 0;
1052    MsgRouting mr = mrDoDefault;
1053
1054    DASSERT(::IsWindow(::GetParent(hWnd)));
1055
1056    switch (message) {
1057    case WM_UNDO:
1058    case WM_CUT:
1059    case WM_COPY:
1060    case WM_PASTE:
1061    case WM_CLEAR:
1062    case EM_SETSEL:
1063        if (bContextMenuActive) {
1064            ::SendMessage(::GetParent(hWnd), message, wParam, lParam);
1065            mr = mrConsume;
1066        }
1067        break;
1068    case WM_CONTEXTMENU:
1069        bContextMenuActive = TRUE;
1070        break;
1071    }
1072
1073    if (mr == mrDoDefault) {
1074        DASSERT(sm_pDefWindowProc != NULL);
1075        retValue = ::CallWindowProc(sm_pDefWindowProc,
1076                                    hWnd, message, wParam, lParam);
1077    }
1078
1079    if (message == WM_CONTEXTMENU) {
1080        bContextMenuActive = FALSE;
1081    }
1082
1083    return retValue;
1084}
1085
1086MsgRouting
1087AwtTextComponent::WmContextMenu(HWND hCtrl, UINT xPos, UINT yPos) {
1088    /* Use the system provided edit control class to generate context menu. */
1089    if (m_hEditCtrl == NULL) {
1090        DWORD dwStyle = WS_CHILD;
1091        DWORD dwExStyle = 0;
1092        m_hEditCtrl = ::CreateWindowEx(dwExStyle,
1093                                        L"EDIT",
1094                                        L"TEXT",
1095                                        dwStyle,
1096                                        0, 0, 0, 0,
1097                                        GetHWnd(),
1098                                        reinterpret_cast<HMENU>(
1099                                         static_cast<INT_PTR>(
1100                                             CreateControlID())),
1101                                        AwtToolkit::GetInstance().GetModuleHandle(),
1102                                        NULL);
1103        DASSERT(m_hEditCtrl != NULL);
1104        if (sm_pDefWindowProc == NULL) {
1105            sm_pDefWindowProc = (WNDPROC)::GetWindowLongPtr(m_hEditCtrl,
1106                                                         GWLP_WNDPROC);
1107        }
1108        ::SetLastError(0);
1109        INT_PTR ret = ::SetWindowLongPtr(m_hEditCtrl, GWLP_WNDPROC,
1110                                   (INT_PTR)AwtTextArea::EditProc);
1111        DASSERT(ret != 0 || ::GetLastError() == 0);
1112    }
1113
1114    /*
1115     * Tricks on the edit control to ensure that its context menu has
1116     * the correct set of enabled items according to the RichEdit state.
1117     */
1118    ::SetWindowText(m_hEditCtrl, TEXT("TEXT"));
1119
1120    if (m_bCanUndo == TRUE && SendMessage(EM_CANUNDO)) {
1121        /* Enable 'Undo' item. */
1122        ::SendMessage(m_hEditCtrl, WM_CHAR, 'A', 0);
1123    }
1124
1125    {
1126        /*
1127         * Initial selection for the edit control - (0,1).
1128         * This enables 'Cut', 'Copy' and 'Delete' and 'Select All'.
1129         */
1130        INT nStart = 0;
1131        INT nEnd = 1;
1132        if (SendMessage(EM_SELECTIONTYPE) == SEL_EMPTY) {
1133            /*
1134             * RichEdit selection is empty - clear selection of the edit control.
1135             * This disables 'Cut', 'Copy' and 'Delete'.
1136             */
1137            nStart = -1;
1138            nEnd = 0;
1139        } else {
1140
1141            CHARRANGE cr;
1142            EditGetSel(cr);
1143            /* Check if all the text is selected. */
1144            if (cr.cpMin == 0) {
1145
1146                int len = ::GetWindowTextLength(GetHWnd());
1147                if (cr.cpMin == 0 && cr.cpMax >= len) {
1148                    /*
1149                     * All the text is selected in RichEdit - select all the
1150                     * text in the edit control. This disables 'Select All'.
1151                     */
1152                    nStart = 0;
1153                    nEnd = -1;
1154                }
1155            }
1156        }
1157        ::SendMessage(m_hEditCtrl, EM_SETSEL, (WPARAM)nStart, (LPARAM)nEnd);
1158    }
1159
1160    /* Disable 'Paste' item if the RichEdit control is read-only. */
1161    ::SendMessage(m_hEditCtrl, EM_SETREADONLY,
1162                  GetStyle() & ES_READONLY ? TRUE : FALSE, 0);
1163
1164    POINT p;
1165    p.x = xPos;
1166    p.y = yPos;
1167
1168    /*
1169     * If the context menu is requested with SHIFT+F10 or VK_APPS key,
1170     * we position its top left corner to the center of the RichEdit
1171     * client rect.
1172     */
1173    if (p.x == -1 && p.y == -1) {
1174        RECT r;
1175        VERIFY(::GetClientRect(GetHWnd(), &r));
1176        p.x = (r.left + r.right) / 2;
1177        p.y = (r.top + r.bottom) / 2;
1178        VERIFY(::ClientToScreen(GetHWnd(), &p));
1179    }
1180
1181    // The context menu steals focus from the proxy.
1182    // So, set the focus-restore flag up.
1183    SetRestoreFocus(TRUE);
1184    ::SendMessage(m_hEditCtrl, WM_CONTEXTMENU, (WPARAM)m_hEditCtrl, MAKELPARAM(p.x, p.y));
1185    SetRestoreFocus(FALSE);
1186
1187    return mrConsume;
1188}
1189
1190//
1191// Accessibility support
1192//
1193
1194// [[[FIXME]]] need to switch to rich edit field; look for EN_SELCHANGE event instead
1195/*
1196 * Handle WmKeyDown to catch keystrokes which may move the caret,
1197 * and fire events as appropriate when that happens, if they are wanted
1198 *
1199 * Note: mouse clicks come through WmKeyDown as well (do they??!?!)
1200 *
1201MsgRouting AwtTextComponent::WmKeyDown(UINT wkey, UINT repCnt,
1202                                   UINT flags, BOOL system) {
1203
1204    printf("AwtTextComponent::WmKeyDown called\r\n");
1205
1206
1207    // NOTE: WmKeyDown won't be processed 'till well after we return
1208    //       so we need to modify the values based on the keystroke
1209    //
1210    static long oldStart = -1;
1211    static long oldEnd = -1;
1212
1213    // most keystrokes can move the caret
1214    // so we'll simply check to see if the caret has moved!
1215    if (javaEventsMask & (jlong) java_awt_TextComponent_textSelectionMask) {
1216        long start;
1217        long end;
1218        SendMessage(EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
1219        if (start != oldStart || end != oldEnd) {
1220
1221            printf("  -> calling TextComponent.selectionValuesChanged()\r\n");
1222            printf("  -> old = (%d, %d); new = (%d, %d)\r\n",
1223                    oldStart, oldEnd, start, end);
1224
1225            DoCallback("selectionValuesChanged", "(II)V", start, end); // let Java-side track details...
1226            oldStart = start;
1227            oldEnd = end;
1228        }
1229    }
1230
1231    return AwtComponent::WmKeyDown(wkey, repCnt, flags, system);
1232}
1233*/
1234} /* extern "C" */
1235