blob: c1fc17826b0ccd34630948e46623f6e9486416da [file] [log] [blame]
/*
* Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "awt_Toolkit.h"
#include "awt_TextComponent.h"
#include "awt_Canvas.h"
#include "jni.h"
#include "awt_Font.h"
/***********************************************************************/
// struct for _SetText() method
struct SetTextStruct {
jobject textcomponent;
jstring text;
};
// struct for _Select() method
struct SelectStruct {
jobject textcomponent;
jint start, end;
};
// struct for _EnableEditing() method
struct EnableEditingStruct {
jobject textcomponent;
jboolean on;
};
/************************************************************************
* AwtTextComponent fields
*/
/* java.awt.TextComponent fields */
jfieldID AwtTextComponent::canAccessClipboardID;
/************************************************************************
* AwtTextComponent methods
*/
AwtTextComponent::AwtTextComponent() {
m_synthetic = FALSE;
m_lStartPos = -1;
m_lEndPos = -1;
m_lLastPos = -1;
m_isLFonly = FALSE;
m_EOLchecked = FALSE;
// javaEventsMask = 0; // accessibility support
}
LPCTSTR AwtTextComponent::GetClassName() {
return TEXT("EDIT"); /* System provided edit control class */
}
/* Set a suitable font to IME against the component font. */
void AwtTextComponent::SetFont(AwtFont* font)
{
DASSERT(font != NULL);
if (font->GetAscent() < 0) {
AwtFont::SetupAscent(font);
}
int index = font->GetInputHFontIndex();
if (index < 0)
/* In this case, user cannot get any suitable font for input. */
index = 0;
//im --- changed for over the spot composing
m_hFont = font->GetHFont(index);
SendMessage(WM_SETFONT, (WPARAM)m_hFont, MAKELPARAM(FALSE, 0));
SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,
MAKELPARAM(1, 1));
/*
* WM_SETFONT reverts foreground color to the default for
* rich edit controls. So we have to restore it manually.
*/
SetColor(GetColor());
VERIFY(::InvalidateRect(GetHWnd(), NULL, TRUE));
//im --- end
}
int AwtTextComponent::RemoveCR(WCHAR *pStr)
{
int i, nLen = 0;
if (pStr) {
/* check to see if there are any CR's */
if (wcschr(pStr, L'\r') == NULL) {
return static_cast<int>(wcslen(pStr));
}
for (i=0; pStr[i] != 0; i++) {
if (m_isLFonly == TRUE) {
if (pStr[i] == L'\r') {
continue;
}
} else {
if (pStr[i] == L'\r' && pStr[i + 1] != L'\n') {
continue;
}
}
pStr[nLen++] = pStr[i];
}
pStr[nLen] = 0;
}
return nLen;
}
MsgRouting
AwtTextComponent::WmNotify(UINT notifyCode)
{
if (notifyCode == EN_CHANGE) {
DoCallback("valueChanged", "()V");
}
return mrDoDefault;
}
BOOL AwtTextComponent::IsFocusingMouseMessage(MSG *pMsg)
{
return pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONDBLCLK;
}
MsgRouting
AwtTextComponent::HandleEvent(MSG *msg, BOOL synthetic)
{
MsgRouting returnVal;
/*
* Store the 'synthetic' parameter so that the WM_PASTE security check
* happens only for synthetic events.
*/
m_synthetic = synthetic;
returnVal = AwtComponent::HandleEvent(msg, synthetic);
m_synthetic = FALSE;
return returnVal;
}
/*
* If this Paste is occuring because of a synthetic Java event (e.g.,
* a synthesized <CTRL>-V KeyEvent), then verify that the TextComponent
* has permission to access the Clipboard before pasting. If permission
* is denied, we should throw a SecurityException, but currently do not
* because when we detect the security violation, we are in the Toolkit
* thread, not the thread which dispatched the illegal event.
*/
MsgRouting
AwtTextComponent::WmPaste()
{
if (m_synthetic) {
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env->EnsureLocalCapacity(1) < 0) {
return mrConsume;
}
jobject target = GetTarget(env);
jboolean canAccessClipboard =
env->GetBooleanField(target,
AwtTextComponent::canAccessClipboardID);
env->DeleteLocalRef(target);
return (canAccessClipboard) ? mrDoDefault : mrConsume;
}
else {
return mrDoDefault;
}
}
//im --- override to over the spot composition
void AwtTextComponent::SetCompositionWindow(RECT& rc)
{
HIMC hIMC = ImmGetContext();
// rc is not used for text component.
COMPOSITIONFORM cf = { CFS_POINT, {0,0}, {0,0,0,0} };
GetCaretPos(&(cf.ptCurrentPos));
ImmSetCompositionWindow(hIMC, &cf);
LOGFONT lf;
GetObject(m_hFont, sizeof(LOGFONT), &lf);
ImmSetCompositionFont(hIMC, &lf);
}
//im --- end
LONG AwtTextComponent::getJavaSelPos(LONG orgPos)
{
long wlen;
long pos = 0;
long cur = 0;
LPTSTR wbuf;
if ((wlen = GetTextLength()) == 0)
return 0;
wbuf = new TCHAR[wlen + 1];
GetText(wbuf, wlen + 1);
if (m_isLFonly == TRUE) {
wlen = RemoveCR(wbuf);
}
while (cur < orgPos && pos++ < wlen) {
if (wbuf[cur] == _T('\r') && wbuf[cur + 1] == _T('\n')) {
cur++;
}
cur++;
}
delete[] wbuf;
return pos;
}
LONG AwtTextComponent::getWin32SelPos(LONG orgPos)
{
long wlen;
long pos = 0;
long cur = 0;
LPTSTR wbuf;
if ((wlen = GetTextLength()) == 0)
return 0;
wbuf = new TCHAR[wlen + 1];
GetText(wbuf, wlen + 1);
if (m_isLFonly == TRUE) {
RemoveCR(wbuf);
}
while (cur < orgPos && pos < wlen) {
if (wbuf[pos] == _T('\r') && wbuf[pos + 1] == _T('\n')) {
pos++;
}
pos++;
cur++;
}
delete[] wbuf;
return pos;
}
void AwtTextComponent::CheckLineSeparator(WCHAR *pStr)
{
if (pStr == NULL) {
return;
}
if (GetTextLength() == 0) {
m_EOLchecked = FALSE;
}
// check to see if there are any LF's
if (m_EOLchecked == TRUE || wcschr(pStr, L'\n') == NULL) {
return;
}
for (int i=0; pStr[i] != 0; i++) {
if (pStr[i] == L'\n') {
if (i > 0 && pStr[i-1] == L'\r') {
m_isLFonly = FALSE;
} else {
m_isLFonly = TRUE;
}
m_EOLchecked = TRUE;
return;
}
}
}
void AwtTextComponent::SetSelRange(LONG start, LONG end)
{
SendMessage(EM_SETSEL,
getWin32SelPos(start),
getWin32SelPos(end));
// it isn't necessary to wrap this in EM_HIDESELECTION or setting/clearing
// ES_NOHIDESEL, as regular edit control honors EM_SCROLLCARET even when not in focus
}
jstring AwtTextComponent::_GetText(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
AwtTextComponent *c = NULL;
jstring result = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
int len = ::GetWindowTextLength(c->GetHWnd());
if (len == 0) {
/* Make java null string */
jchar *jc = new jchar[0];
result = env->NewString(jc, 0);
delete [] jc;
} else {
WCHAR* buf = new WCHAR[len + 1];
c->GetText(buf, len + 1);
c->RemoveCR(buf);
result = JNU_NewStringPlatform(env, buf);
delete [] buf;
}
}
ret:
env->DeleteGlobalRef(self);
if (result != NULL)
{
jstring globalRef = (jstring)env->NewGlobalRef(result);
env->DeleteLocalRef(result);
return globalRef;
}
else
{
return NULL;
}
}
void AwtTextComponent::_SetText(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
SetTextStruct *sts = (SetTextStruct *)param;
jobject self = sts->textcomponent;
jstring text = sts->text;
AwtTextComponent *c = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
int length = env->GetStringLength(text);
WCHAR* buffer = new WCHAR[length + 1];
env->GetStringRegion(text, 0, length, reinterpret_cast<jchar*>(buffer));
buffer[length] = 0;
c->CheckLineSeparator(buffer);
c->RemoveCR(buffer);
c->SetText(buffer);
delete[] buffer;
}
ret:
env->DeleteGlobalRef(self);
env->DeleteGlobalRef(text);
delete sts;
}
jint AwtTextComponent::_GetSelectionStart(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
jint result = 0;
AwtTextComponent *c = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
long start;
c->SendMessage(EM_GETSEL, (WPARAM)&start);
result = c->getJavaSelPos(start);
}
ret:
env->DeleteGlobalRef(self);
return result;
}
jint AwtTextComponent::_GetSelectionEnd(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject self = (jobject)param;
jint result = 0;
AwtTextComponent *c = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
long end;
c->SendMessage(EM_GETSEL, 0, (LPARAM)&end);
result = c->getJavaSelPos(end);
}
ret:
env->DeleteGlobalRef(self);
return result;
}
void AwtTextComponent::_Select(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
SelectStruct *ss = (SelectStruct *)param;
jobject self = ss->textcomponent;
jint start = ss->start;
jint end = ss->end;
AwtTextComponent *c = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
c->SetSelRange(start, end);
c->SendMessage(EM_SCROLLCARET);
}
ret:
env->DeleteGlobalRef(self);
delete ss;
}
void AwtTextComponent::_EnableEditing(void *param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
EnableEditingStruct *ees = (EnableEditingStruct *)param;
jobject self = ees->textcomponent;
jboolean on = ees->on;
AwtTextComponent *c = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
c = (AwtTextComponent *)pData;
if (::IsWindow(c->GetHWnd()))
{
c->SendMessage(EM_SETREADONLY, !on);
}
ret:
env->DeleteGlobalRef(self);
delete ees;
}
/************************************************************************
* WTextComponentPeer native methods
*/
extern "C" {
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getText
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_sun_awt_windows_WTextComponentPeer_getText(JNIEnv *env, jobject self)
{
TRY;
jobject selfGlobalRef = env->NewGlobalRef(self);
jstring globalRef = (jstring)AwtToolkit::GetInstance().SyncCall(
(void*(*)(void*))AwtTextComponent::_GetText,
(void *)selfGlobalRef);
// selfGlobalRef is deleted in _GetText
if (globalRef != NULL)
{
jstring localRef = (jstring)env->NewLocalRef(globalRef);
env->DeleteGlobalRef(globalRef);
return localRef;
}
else
{
return NULL;
}
CATCH_BAD_ALLOC_RET(NULL);
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: setText
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextComponentPeer_setText(JNIEnv *env, jobject self,
jstring text)
{
TRY;
SetTextStruct *sts = new SetTextStruct;
sts->textcomponent = env->NewGlobalRef(self);
sts->text = (jstring)env->NewGlobalRef(text);
AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_SetText, sts);
// global refs and sts are deleted in _SetText
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getSelectionStart
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WTextComponentPeer_getSelectionStart(JNIEnv *env,
jobject self)
{
TRY;
return static_cast<jint>(reinterpret_cast<INT_PTR>(AwtToolkit::GetInstance().SyncCall(
(void *(*)(void *))AwtTextComponent::_GetSelectionStart,
env->NewGlobalRef(self))));
// global ref is deleted in _GetSelectionStart()
CATCH_BAD_ALLOC_RET(0);
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getSelectionEnd
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WTextComponentPeer_getSelectionEnd(JNIEnv *env,
jobject self)
{
TRY;
return static_cast<jint>(reinterpret_cast<INT_PTR>(AwtToolkit::GetInstance().SyncCall(
(void *(*)(void *))AwtTextComponent::_GetSelectionEnd,
env->NewGlobalRef(self))));
// global ref is deleted in _GetSelectionEnd()
CATCH_BAD_ALLOC_RET(0);
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: select
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextComponentPeer_select(JNIEnv *env, jobject self,
jint start, jint end)
{
TRY;
SelectStruct *ss = new SelectStruct;
ss->textcomponent = env->NewGlobalRef(self);
ss->start = start;
ss->end = end;
AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_Select, ss);
// global ref and ss are deleted in _Select
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: enableEditing
* Signature: (Z)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextComponentPeer_enableEditing(JNIEnv *env,
jobject self,
jboolean on)
{
TRY;
EnableEditingStruct *ees = new EnableEditingStruct;
ees->textcomponent = env->NewGlobalRef(self);
ees->on = on;
AwtToolkit::GetInstance().SyncCall(AwtTextComponent::_EnableEditing, ees);
// global ref and ees are deleted in _EnableEditing()
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WTextComponentPeer
* Method: initIDs
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WTextComponentPeer_initIDs(JNIEnv *env, jclass cls)
{
TRY;
cls = env->FindClass("java/awt/TextComponent");
if (cls != NULL) {
AwtTextComponent::canAccessClipboardID =
env->GetFieldID(cls, "canAccessClipboard", "Z");
DASSERT(AwtTextComponent::canAccessClipboardID != NULL);
}
CATCH_BAD_ALLOC;
}
//
// Accessibility support
//
/* To be fully implemented in a future release
*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getIndexAtPoint
* Signature: (II)I
*
JNIEXPORT jlong JNICALL
Java_sun_awt_windows_WTextComponentPeer_filterEvents(JNIEnv *env, jobject self, jlong mask)
{
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN_NULL(self);
AwtTextComponent* c = (AwtTextComponent*)pData;
jlong oldMask = c->javaEventsMask;
c->javaEventsMask = mask;
return oldMask;
CATCH_BAD_ALLOC_RET(0);
}
*/
// [[[FIXME]]] need to switch to rich edit field; look for EN_SELCHANGE event instead
/*
* Handle WmKeyDown to catch keystrokes which may move the caret,
* and fire events as appropriate when that happens, if they are wanted
*
* Note: mouse clicks come through WmKeyDown as well (do they??!?!)
*
MsgRouting AwtTextComponent::WmKeyDown(UINT wkey, UINT repCnt,
UINT flags, BOOL system) {
printf("AwtTextComponent::WmKeyDown called\r\n");
// NOTE: WmKeyDown won't be processed 'till well after we return
// so we need to modify the values based on the keystroke
//
static long oldStart = -1;
static long oldEnd = -1;
// most keystrokes can move the caret
// so we'll simply check to see if the caret has moved!
if (javaEventsMask & (jlong) java_awt_TextComponent_textSelectionMask) {
long start;
long end;
SendMessage(EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
if (start != oldStart || end != oldEnd) {
printf(" -> calling TextComponent.selectionValuesChanged()\r\n");
printf(" -> old = (%d, %d); new = (%d, %d)\r\n",
oldStart, oldEnd, start, end);
DoCallback("selectionValuesChanged", "(II)V", start, end); // let Java-side track details...
oldStart = start;
oldEnd = end;
}
}
return AwtComponent::WmKeyDown(wkey, repCnt, flags, system);
}
*/
/* To be fully implemented in a future release
*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getIndexAtPoint
* Signature: (II)I
*
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WTextComponentPeer_getIndexAtPoint(JNIEnv *env, jobject self, jint x, jint y)
{
TRY;
PDATA pData;
// JNI_CHECK_PEER_RETURN_VAL(self, -1); [[[FIXME]]] Peter Korn -> should return -1 here
JNI_CHECK_PEER_RETURN_NULL(self);
AwtTextComponent* c = (AwtTextComponent*)pData;
int indicies = c->SendMessage(EM_CHARFROMPOS, (WPARAM) 0, (LPARAM) MAKELPARAM(x, y));
int index = LOWORD(indicies); // index into the line the (x,y) coord is on
int lineIndex = c->SendMessage(EM_LINEINDEX, HIWORD(indicies)); // index of start of line
return (index + lineIndex);
CATCH_BAD_ALLOC_RET(-1);
}
*/
/* To be fully implemented in a future release
*
* Class: sun_awt_windows_WTextComponentPeer
* Method: getCharacterBounds
* Signature: (I)Ljava/awt/Rectangle;
*
JNIEXPORT jobject JNICALL
Java_sun_awt_windows_WTextComponentPeer_getCharacterBounds(JNIEnv *env, jobject self, jint i)
{
// loop through lines with EM_LINELENGTH? e.g.:
// line = 0; ttl = 0; // index is passed in as 'i' above
// while (ttl < index) {
// ttl += SendMessage(EM_LINELENGTH, line++);
// }
// line-- (decrement back again)
// alternately, we could use EM_LINEINDEX to the same effect; perhaps slightly cleaner:
// computedIndex = 0; line = 0;
// while (computedIndex < index) {
// computedIndex = SendMessage(EM_LINEINDEX, 1 + line++);
// }
// line--;
// EM_POSFROMCHAR - convert char index into a Point
// wParam = (LPPOINT) lpPoint; // address of structure
// receiving character position
// lParam = (LPARAM) wCharIndex; // zero-based index of character
//
// still need to turn the above into a Rect somehow...
// (use font metrics on font info for letter to get height? use
// getLineHeight type of message?).
// WM_GETFONT - get the font struct for the window control
// wParam = lParam = 0
// returns an HFONT
// -or-
// GetTextMetrics(hDC) to get the text info for the font selected
// into the hDC of the control (tmHeight is what we want in the
// TEXTMETRIC struct).
// also GetCharWidth32() with the char at the index in question to get
// the width of that char
// *** Can't use GetTextMetrics/GetCharWidth32, as we don't have an hDC!! ***
TRY;
PDATA pData;
JNI_CHECK_PEER_RETURN_NULL(self);
AwtComponent* c = (AwtComponent*)pData;
/*
int line = 0;
int lineIndex = 0;
while (lineIndex < i) {
lineIndex = c->SendMessage(EM_LINEINDEX, 1 + line++);
}
line--; // line is now the line which contains our character at position 'i'
int offsetIndex = i - lineIndex; // offsetIndex is now distance in on the line
* /
POINT p;
c->SendMessage(EM_POSFROMCHAR, (WPARAM) &p, (LPARAM) i); // x coord is meaningful; y may not be
// need to calculate charWidth, charHeight, and set p.y to something meangful
jint charWidth;
jint charHeight;
/*
HFONT font = c->SendMessage(WM_GETFONT);
if (GetCharWidth32(c->hdc, i, i, &charWidth) != 0) { // [[[FIXME]]] need to get hDC!
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject rect = JNU_NewObjectByName(env, "java/awt/Rectangle", "(IIII)V",
(jint) p.x, (jint) p.y, charWidth, charHeight);
return rect;
}
* /
return (jobject) 0;
CATCH_BAD_ALLOC_RET(0);
}
*/
} /* extern "C" */