1 /*
2 * Copyright (c) 1996, 2010, 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_TextArea.h"
28 #include "awt_TextComponent.h"
29 #include "awt_Canvas.h"
30 #include "awt_Window.h"
31 #include "awt_Frame.h"
32
33 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
34 */
35
36 /***********************************************************************/
37 // Struct for _ReplaceText() method
38 struct ReplaceTextStruct {
39 jobject textComponent;
40 jstring text;
41 int start, end;
42 };
43
44 /************************************************************************
45 * AwtTextArea fields
46 */
47
48 jfieldID AwtTextArea::scrollbarVisibilityID;
49
50 WNDPROC AwtTextArea::sm_pDefWindowProc = NULL;
51
52 /************************************************************************
53 * AwtTextArea methods
54 */
55
56 AwtTextArea::AwtTextArea() {
57 m_bIgnoreEnChange = FALSE;
58 m_bCanUndo = FALSE;
59 m_hEditCtrl = NULL;
60 m_lHDeltaAccum = 0;
61 m_lVDeltaAccum = 0;
62 }
63
64 AwtTextArea::~AwtTextArea()
65 {
66 }
67
68 void AwtTextArea::Dispose()
69 {
70 if (m_hEditCtrl != NULL) {
71 VERIFY(::DestroyWindow(m_hEditCtrl));
72 m_hEditCtrl = NULL;
73 }
74 AwtTextComponent::Dispose();
75 }
76
77 /* Create a new AwtTextArea object and window. */
78 AwtTextArea* AwtTextArea::Create(jobject peer, jobject parent)
79 {
80 return (AwtTextArea*) AwtTextComponent::Create(peer, parent, true);
81 }
82
83 void AwtTextArea::EditSetSel(CHARRANGE &cr) {
84 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling
85 SendMessage(EM_HIDESELECTION, FALSE, TRUE);
86 SendMessage(EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&cr));
87 SendMessage(EM_HIDESELECTION, TRUE, TRUE);
88 // 6417581: force expected drawing
89 if (IS_WINVISTA && cr.cpMin == cr.cpMax) {
90 ::InvalidateRect(GetHWnd(), NULL, TRUE);
91 }
92 }
93
94 void AwtTextArea::EditGetSel(CHARRANGE &cr) {
95 SendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&cr));
96 }
97
98 /* Count how many '\n's are there in jStr */
99 size_t AwtTextArea::CountNewLines(JNIEnv *env, jstring jStr, size_t maxlen)
100 {
101 size_t nNewlines = 0;
102
103 if (jStr == NULL) {
104 return nNewlines;
105 }
106 /*
107 * Fix for BugTraq Id 4260109.
108 * Don't use TO_WSTRING since it allocates memory on the stack
109 * causing stack overflow when the text is very long.
110 */
111 size_t length = env->GetStringLength(jStr) + 1;
112 WCHAR *string = new WCHAR[length];
113 env->GetStringRegion(jStr, 0, static_cast<jsize>(length - 1), reinterpret_cast<jchar*>(string));
114 string[length-1] = '\0';
115 for (size_t i = 0; i < maxlen && i < length - 1; i++) {
116 if (string[i] == L'\n') {
117 nNewlines++;
118 }
119 }
120 delete[] string;
121 return nNewlines;
122 }
123
124 BOOL AwtTextArea::InheritsNativeMouseWheelBehavior() {return true;}
125
126
127 LRESULT
128 AwtTextArea::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
129
130 LRESULT retValue = 0;
131 MsgRouting mr = mrDoDefault;
132
133 switch (message) {
134 case EM_SETCHARFORMAT:
135 case WM_SETFONT:
136 SetIgnoreEnChange(TRUE);
137 break;
138 }
139
140 retValue = AwtTextComponent::WindowProc(message, wParam, lParam);
141
142 switch (message) {
143 case EM_SETCHARFORMAT:
144 case WM_SETFONT:
145 SetIgnoreEnChange(FALSE);
146 break;
147 }
148
149 return retValue;
150 }
151
152 /*
153 * This routine is a window procedure for the subclass of the standard edit control
154 * used to generate context menu. RichEdit controls don't have built-in context menu.
155 * To implement this functionality we have to create an invisible edit control and
156 * forward WM_CONTEXTMENU messages from a RichEdit control to this helper edit control.
157 * While the edit control context menu is active we intercept the message generated in
158 * response to particular item selection and forward it back to the RichEdit control.
159 * (See AwtTextArea::WmContextMenu for more details).
160 */
161 LRESULT
162 AwtTextArea::EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
163
164 static BOOL bContextMenuActive = FALSE;
165
166 LRESULT retValue = 0;
167 MsgRouting mr = mrDoDefault;
168
169 DASSERT(::IsWindow(::GetParent(hWnd)));
170
171 switch (message) {
172 case WM_UNDO:
173 case WM_CUT:
174 case WM_COPY:
175 case WM_PASTE:
176 case WM_CLEAR:
177 case EM_SETSEL:
178 if (bContextMenuActive) {
179 ::SendMessage(::GetParent(hWnd), message, wParam, lParam);
180 mr = mrConsume;
181 }
182 break;
183 case WM_CONTEXTMENU:
184 bContextMenuActive = TRUE;
185 break;
186 }
187
188 if (mr == mrDoDefault) {
189 DASSERT(sm_pDefWindowProc != NULL);
190 retValue = ::CallWindowProc(sm_pDefWindowProc,
191 hWnd, message, wParam, lParam);
192 }
193
194 if (message == WM_CONTEXTMENU) {
195 bContextMenuActive = FALSE;
196 }
197
198 return retValue;
199 }
200
201 MsgRouting
202 AwtTextArea::WmContextMenu(HWND hCtrl, UINT xPos, UINT yPos) {
203 /* Use the system provided edit control class to generate context menu. */
204 if (m_hEditCtrl == NULL) {
205 DWORD dwStyle = WS_CHILD;
206 DWORD dwExStyle = 0;
207 m_hEditCtrl = ::CreateWindowEx(dwExStyle,
208 L"EDIT",
209 L"TEXT",
210 dwStyle,
211 0, 0, 0, 0,
212 GetHWnd(),
213 reinterpret_cast<HMENU>(
214 static_cast<INT_PTR>(
215 CreateControlID())),
216 AwtToolkit::GetInstance().GetModuleHandle(),
217 NULL);
218 DASSERT(m_hEditCtrl != NULL);
219 if (sm_pDefWindowProc == NULL) {
220 sm_pDefWindowProc = (WNDPROC)::GetWindowLongPtr(m_hEditCtrl,
221 GWLP_WNDPROC);
222 }
223 ::SetLastError(0);
224 INT_PTR ret = ::SetWindowLongPtr(m_hEditCtrl, GWLP_WNDPROC,
225 (INT_PTR)AwtTextArea::EditProc);
226 DASSERT(ret != 0 || ::GetLastError() == 0);
227 }
228
229 /*
230 * Tricks on the edit control to ensure that its context menu has
231 * the correct set of enabled items according to the RichEdit state.
232 */
233 ::SetWindowText(m_hEditCtrl, TEXT("TEXT"));
234
235 if (m_bCanUndo == TRUE && SendMessage(EM_CANUNDO)) {
236 /* Enable 'Undo' item. */
237 ::SendMessage(m_hEditCtrl, WM_CHAR, 'A', 0);
238 }
239
240 {
241 /*
242 * Initial selection for the edit control - (0,1).
243 * This enables 'Cut', 'Copy' and 'Delete' and 'Select All'.
244 */
245 INT nStart = 0;
246 INT nEnd = 1;
247 if (SendMessage(EM_SELECTIONTYPE) == SEL_EMPTY) {
248 /*
249 * RichEdit selection is empty - clear selection of the edit control.
250 * This disables 'Cut', 'Copy' and 'Delete'.
251 */
252 nStart = -1;
253 nEnd = 0;
254 } else {
255
256 CHARRANGE cr;
257 EditGetSel(cr);
258 /* Check if all the text is selected. */
259 if (cr.cpMin == 0) {
260
261 int len = ::GetWindowTextLength(GetHWnd());
262 if (cr.cpMin == 0 && cr.cpMax >= len) {
263 /*
264 * All the text is selected in RichEdit - select all the
265 * text in the edit control. This disables 'Select All'.
266 */
267 nStart = 0;
268 nEnd = -1;
269 }
270 }
271 }
272 ::SendMessage(m_hEditCtrl, EM_SETSEL, (WPARAM)nStart, (LPARAM)nEnd);
273 }
274
275 /* Disable 'Paste' item if the RichEdit control is read-only. */
276 ::SendMessage(m_hEditCtrl, EM_SETREADONLY,
277 GetStyle() & ES_READONLY ? TRUE : FALSE, 0);
278
279 POINT p;
280 p.x = xPos;
281 p.y = yPos;
282
283 /*
284 * If the context menu is requested with SHIFT+F10 or VK_APPS key,
285 * we position its top left corner to the center of the RichEdit
286 * client rect.
287 */
288 if (p.x == -1 && p.y == -1) {
289 RECT r;
290 VERIFY(::GetClientRect(GetHWnd(), &r));
291 p.x = (r.left + r.right) / 2;
292 p.y = (r.top + r.bottom) / 2;
293 VERIFY(::ClientToScreen(GetHWnd(), &p));
294 }
295
296 // The context menu steals focus from the proxy.
297 // So, set the focus-restore flag up.
298 SetRestoreFocus(TRUE);
299 ::SendMessage(m_hEditCtrl, WM_CONTEXTMENU, (WPARAM)m_hEditCtrl, MAKELPARAM(p.x, p.y));
300 SetRestoreFocus(FALSE);
301
302 return mrConsume;
303 }
304
305 MsgRouting
306 AwtTextArea::WmNcHitTest(UINT x, UINT y, LRESULT& retVal)
307 {
308 if (::IsWindow(AwtWindow::GetModalBlocker(AwtComponent::GetTopLevelParentForWindow(GetHWnd())))) {
309 retVal = HTCLIENT;
310 return mrConsume;
311 }
312 return AwtTextComponent::WmNcHitTest(x, y, retVal);
313 }
314
315
316 MsgRouting
317 AwtTextArea::WmNotify(UINT notifyCode)
318 {
319 if (notifyCode == EN_CHANGE) {
320 /*
321 * Ignore notifications if the text hasn't been changed.
322 * EN_CHANGE sent on character formatting changes as well.
323 */
324 if (m_bIgnoreEnChange == FALSE) {
325 m_bCanUndo = TRUE;
326 DoCallback("valueChanged", "()V");
327 } else {
328 m_bCanUndo = FALSE;
329 }
330 }
331 return mrDoDefault;
332 }
333
334 MsgRouting
335 AwtTextArea::HandleEvent(MSG *msg, BOOL synthetic)
336 {
337 MsgRouting returnVal;
338 /*
339 * RichEdit 1.0 control starts internal message loop if the
340 * left mouse button is pressed while the cursor is not over
341 * the current selection or the current selection is empty.
342 * Because of this we don't receive WM_MOUSEMOVE messages
343 * while the left mouse button is pressed. To work around
344 * this behavior we process the relevant mouse messages
345 * by ourselves.
346 * By consuming WM_MOUSEMOVE messages we also don't give
347 * the RichEdit control a chance to recognize a drag gesture
348 * and initiate its own drag-n-drop operation.
349 *
350 * The workaround also allows us to implement synthetic focus mechanism.
351 *
352 */
353 if (IsFocusingMouseMessage(msg)) {
354 CHARRANGE cr;
355
356 LONG lCurPos = EditGetCharFromPos(msg->pt);
357
358 EditGetSel(cr);
359 /*
360 * NOTE: Plain EDIT control always clears selection on mouse
361 * button press. We are clearing the current selection only if
362 * the mouse pointer is not over the selected region.
363 * In this case we sacrifice backward compatibility
364 * to allow dnd of the current selection.
365 */
366 if (lCurPos < cr.cpMin || cr.cpMax <= lCurPos) {
367 if (msg->message == WM_LBUTTONDBLCLK) {
368 SetStartSelectionPos(static_cast<LONG>(SendMessage(
369 EM_FINDWORDBREAK, WB_MOVEWORDLEFT, lCurPos)));
370 SetEndSelectionPos(static_cast<LONG>(SendMessage(
371 EM_FINDWORDBREAK, WB_MOVEWORDRIGHT, lCurPos)));
372 } else {
373 SetStartSelectionPos(lCurPos);
374 SetEndSelectionPos(lCurPos);
375 }
376 cr.cpMin = GetStartSelectionPos();
377 cr.cpMax = GetEndSelectionPos();
378 EditSetSel(cr);
379 }
380
381 delete msg;
382 return mrConsume;
383 } else if (msg->message == WM_LBUTTONUP) {
384
385 /*
386 * If the left mouse button is pressed on the selected region
387 * we don't clear the current selection. We clear it on button
388 * release instead. This is to allow dnd of the current selection.
389 */
390 if (GetStartSelectionPos() == -1 && GetEndSelectionPos() == -1) {
391 CHARRANGE cr;
392
393 LONG lCurPos = EditGetCharFromPos(msg->pt);
394
395 cr.cpMin = lCurPos;
396 cr.cpMax = lCurPos;
397 EditSetSel(cr);
398 }
399
400 /*
401 * Cleanup the state variables when left mouse button is released.
402 * These state variables are designed to reflect the selection state
403 * while the left mouse button is pressed and be set to -1 otherwise.
404 */
405 SetStartSelectionPos(-1);
406 SetEndSelectionPos(-1);
407 SetLastSelectionPos(-1);
408
409 delete msg;
410 return mrConsume;
411 } else if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON)) {
412
413 /*
414 * We consume WM_MOUSEMOVE while the left mouse button is pressed,
415 * so we have to simulate autoscrolling when mouse is moved outside
416 * of the client area.
417 */
418 POINT p;
419 RECT r;
420 BOOL bScrollLeft = FALSE;
421 BOOL bScrollRight = FALSE;
422 BOOL bScrollUp = FALSE;
423 BOOL bScrollDown = FALSE;
424
425 p.x = msg->pt.x;
426 p.y = msg->pt.y;
427 VERIFY(::GetClientRect(GetHWnd(), &r));
428
429 if (p.x < 0) {
430 bScrollLeft = TRUE;
431 p.x = 0;
432 } else if (p.x > r.right) {
433 bScrollRight = TRUE;
434 p.x = r.right - 1;
435 }
436 if (p.y < 0) {
437 bScrollUp = TRUE;
438 p.y = 0;
439 } else if (p.y > r.bottom) {
440 bScrollDown = TRUE;
441 p.y = r.bottom - 1;
442 }
443
444 LONG lCurPos = EditGetCharFromPos(p);
445
446 if (GetStartSelectionPos() != -1 &&
447 GetEndSelectionPos() != -1 &&
448 lCurPos != GetLastSelectionPos()) {
449
450 CHARRANGE cr;
451
452 SetLastSelectionPos(lCurPos);
453
454 cr.cpMin = GetStartSelectionPos();
455 cr.cpMax = GetLastSelectionPos();
456
457 EditSetSel(cr);
458 }
459
460 if (bScrollLeft == TRUE || bScrollRight == TRUE) {
461 SCROLLINFO si;
462 memset(&si, 0, sizeof(si));
463 si.cbSize = sizeof(si);
464 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
465
466 VERIFY(::GetScrollInfo(GetHWnd(), SB_HORZ, &si));
467 if (bScrollLeft == TRUE) {
468 si.nPos = si.nPos - si.nPage / 2;
469 si.nPos = max(si.nMin, si.nPos);
470 } else if (bScrollRight == TRUE) {
471 si.nPos = si.nPos + si.nPage / 2;
472 si.nPos = min(si.nPos, si.nMax);
473 }
474 /*
475 * Okay to use 16-bit position since RichEdit control adjusts
476 * its scrollbars so that their range is always 16-bit.
477 */
478 DASSERT(abs(si.nPos) < 0x8000);
479 SendMessage(WM_HSCROLL,
480 MAKEWPARAM(SB_THUMBPOSITION, LOWORD(si.nPos)));
481 }
482 if (bScrollUp == TRUE) {
483 SendMessage(EM_LINESCROLL, 0, -1);
484 } else if (bScrollDown == TRUE) {
485 SendMessage(EM_LINESCROLL, 0, 1);
486 }
487 delete msg;
488 return mrConsume;
489 } else if (msg->message == WM_RBUTTONUP ||
490 (msg->message == WM_SYSKEYDOWN && msg->wParam == VK_F10 &&
491 HIBYTE(::GetKeyState(VK_SHIFT)))) {
492 POINT p;
493 if (msg->message == WM_RBUTTONUP) {
494 VERIFY(::GetCursorPos(&p));
495 } else {
496 p.x = -1;
497 p.y = -1;
498 }
499
500 if (!::PostMessage(GetHWnd(), WM_CONTEXTMENU, (WPARAM)GetHWnd(),
501 MAKELPARAM(p.x, p.y))) {
502 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
503 JNU_ThrowInternalError(env, "Message not posted, native event queue may be full.");
504 env->ExceptionDescribe();
505 env->ExceptionClear();
506 }
507 delete msg;
508 return mrConsume;
509 } else if (msg->message == WM_MOUSEWHEEL) {
510 // 4417236: If there is an old version of RichEd32.dll which
511 // does not provide the mouse wheel scrolling we have to
512 // interpret WM_MOUSEWHEEL as a sequence of scroll messages.
513 // kdm@sparc.spb.su
514 UINT platfScrollLines = 3;
515 // Retrieve a number of scroll lines.
516 ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
517 &platfScrollLines, 0);
518
519 if (platfScrollLines > 0) {
520 HWND hWnd = GetHWnd();
521 LONG styles = ::GetWindowLong(hWnd, GWL_STYLE);
522
523 RECT rect;
524 // rect.left and rect.top are zero.
525 // rect.right and rect.bottom contain the width and height
526 VERIFY(::GetClientRect(hWnd, &rect));
527
528 // calculate a number of visible lines
529 TEXTMETRIC tm;
530 HDC hDC = ::GetDC(hWnd);
531 DASSERT(hDC != NULL);
532 VERIFY(::GetTextMetrics(hDC, &tm));
533 VERIFY(::ReleaseDC(hWnd, hDC));
534 LONG visibleLines = rect.bottom / tm.tmHeight + 1;
535
536 LONG lineCount = static_cast<LONG>(::SendMessage(hWnd,
537 EM_GETLINECOUNT, 0, 0));
538 BOOL sb_vert_disabled = (styles & WS_VSCROLL) == 0
539 || (lineCount <= visibleLines);
540
541 LONG *delta_accum = &m_lVDeltaAccum;
542 UINT wm_msg = WM_VSCROLL;
543 int sb_type = SB_VERT;
544
545 if (sb_vert_disabled && (styles & WS_HSCROLL)) {
546 delta_accum = &m_lHDeltaAccum;
547 wm_msg = WM_HSCROLL;
548 sb_type = SB_HORZ;
549 }
550
551 int delta = (short) HIWORD(msg->wParam);
552 *delta_accum += delta;
553 if (abs(*delta_accum) >= WHEEL_DELTA) {
554 if (platfScrollLines == WHEEL_PAGESCROLL) {
555 // Synthesize a page down or a page up message.
556 ::SendMessage(hWnd, wm_msg,
557 (delta > 0) ? SB_PAGEUP : SB_PAGEDOWN, 0);
558 *delta_accum = 0;
559 } else {
560 // We provide a friendly behavior of text scrolling.
561 // During of scrolling the text can be out of the client
562 // area's boundary. Here we try to prevent this behavior.
563 SCROLLINFO si;
564 ::ZeroMemory(&si, sizeof(si));
565 si.cbSize = sizeof(SCROLLINFO);
566 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
567 int actualScrollLines =
568 abs((int)(platfScrollLines * (*delta_accum / WHEEL_DELTA)));
569 for (int i = 0; i < actualScrollLines; i++) {
570 if (::GetScrollInfo(hWnd, sb_type, &si)) {
571 if ((wm_msg == WM_VSCROLL)
572 && ((*delta_accum < 0
573 && si.nPos >= (si.nMax - (int) si.nPage))
574 || (*delta_accum > 0
575 && si.nPos <= si.nMin))) {
576 break;
577 }
578 }
579 // Here we don't send EM_LINESCROLL or EM_SCROLL
580 // messages to rich edit because it doesn't
581 // provide horizontal scrolling.
582 // So it's only possible to scroll by 1 line
583 // at a time to prevent scrolling when the
584 // scrollbar thumb reaches its boundary position.
585 ::SendMessage(hWnd, wm_msg,
586 (*delta_accum>0) ? SB_LINEUP : SB_LINEDOWN, 0);
587 }
588 *delta_accum %= WHEEL_DELTA;
589 }
590 } else {
591 *delta_accum = 0;
592 }
593 }
594 delete msg;
595 return mrConsume;
596 // 4417236: end of fix
597 }
598
599 /*
600 * Store the 'synthetic' parameter so that the WM_PASTE security check
601 * happens only for synthetic events.
602 */
603 m_synthetic = synthetic;
604 returnVal = AwtComponent::HandleEvent(msg, synthetic);
605 m_synthetic = FALSE;
606
607 return returnVal;
608 }
609
610
611 /* Fix for 4776535, 4648702
612 * If width is 0 or 1 Windows hides the horizontal scroll bar even
613 * if the WS_HSCROLL style is set. It is a bug in Windows.
614 * As a workaround we should set an initial width to 2.
615 * kdm@sparc.spb.su
616 */
617 void AwtTextArea::Reshape(int x, int y, int w, int h)
618 {
619 if (w < 2) {
620 w = 2;
621 }
622 AwtTextComponent::Reshape(x, y, w, h);
623 }
624
625 LONG AwtTextArea::getJavaSelPos(LONG orgPos)
626 {
627 long wlen;
628 long pos = orgPos;
629 long cur = 0;
630 long retval = 0;
631 LPTSTR wbuf;
632
633 if ((wlen = GetTextLength()) == 0)
634 return 0;
635 wbuf = new TCHAR[wlen + 1];
636 GetText(wbuf, wlen + 1);
637 if (m_isLFonly == TRUE) {
638 wlen = RemoveCR(wbuf);
639 }
640
641 while (cur < pos && cur < wlen) {
642 if (wbuf[cur] == _T('\r') && wbuf[cur + 1] == _T('\n')) {
643 pos++;
644 }
645 cur++;
646 retval++;
647 }
648 delete[] wbuf;
649 return retval;
650 }
651
652 LONG AwtTextArea::getWin32SelPos(LONG orgPos)
653 {
654 if (GetTextLength() == 0)
655 return 0;
656 return orgPos;
657 }
658
659 void AwtTextArea::SetSelRange(LONG start, LONG end)
660 {
661 CHARRANGE cr;
662 cr.cpMin = getWin32SelPos(start);
663 cr.cpMax = getWin32SelPos(end);
664 EditSetSel(cr);
665 }
666
667
668 void AwtTextArea::_ReplaceText(void *param)
669 {
670 JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
671
672 ReplaceTextStruct *rts = (ReplaceTextStruct *)param;
673
674 jobject textComponent = rts->textComponent;
675 jstring text = rts->text;
676 jint start = rts->start;
677 jint end = rts->end;
678
679 AwtTextComponent *c = NULL;
680
681 PDATA pData;
682 JNI_CHECK_PEER_GOTO(textComponent, done);
683 JNI_CHECK_NULL_GOTO(text, "null string", done);
684
685 c = (AwtTextComponent *)pData;
686 if (::IsWindow(c->GetHWnd()))
687 {
688 jsize length = env->GetStringLength(text) + 1;
689 // Bugid 4141477 - Can't use TO_WSTRING here because it uses alloca
690 // WCHAR* buffer = TO_WSTRING(text);
691 TCHAR *buffer = new TCHAR[length];
692 env->GetStringRegion(text, 0, length-1, reinterpret_cast<jchar*>(buffer));
693 buffer[length-1] = '\0';
694
695 c->CheckLineSeparator(buffer);
696 c->RemoveCR(buffer);
697 // Fix for 5003402: added restoring/hiding selection to enable automatic scrolling
698 c->SendMessage(EM_HIDESELECTION, FALSE, TRUE);
699 c->SendMessage(EM_SETSEL, start, end);
700 c->SendMessage(EM_REPLACESEL, FALSE, (LPARAM)buffer);
701 c->SendMessage(EM_HIDESELECTION, TRUE, TRUE);
702
703 delete[] buffer;
704 }
705
706 done:
707 env->DeleteGlobalRef(textComponent);
708 env->DeleteGlobalRef(text);
709
710 delete rts;
711 }
712
713
714 /************************************************************************
715 * TextArea native methods
716 */
717
718 extern "C" {
719
720 /*
721 * Class: java_awt_TextArea
722 * Method: initIDs
723 * Signature: ()V
724 */
725 JNIEXPORT void JNICALL
726 Java_java_awt_TextArea_initIDs(JNIEnv *env, jclass cls)
727 {
728 TRY;
729
730 AwtTextArea::scrollbarVisibilityID =
731 env->GetFieldID(cls, "scrollbarVisibility", "I");
732
733 DASSERT(AwtTextArea::scrollbarVisibilityID != NULL);
734
735 CATCH_BAD_ALLOC;
736 }
737
738 } /* extern "C" */
739
740
741 /************************************************************************
742 * WTextAreaPeer native methods
743 */
744
745 extern "C" {
746
747 /*
748 * Class: sun_awt_windows_WTextAreaPeer
749 * Method: create
750 * Signature: (Lsun/awt/windows/WComponentPeer;)V
751 */
752 JNIEXPORT void JNICALL
753 Java_sun_awt_windows_WTextAreaPeer_create(JNIEnv *env, jobject self,
754 jobject parent)
755 {
756 TRY;
757
758 PDATA pData;
759 JNI_CHECK_PEER_RETURN(parent);
760 AwtToolkit::CreateComponent(self, parent,
761 (AwtToolkit::ComponentFactory)
762 AwtTextArea::Create);
763 JNI_CHECK_PEER_CREATION_RETURN(self);
764
765 CATCH_BAD_ALLOC;
766 }
767
768 /*
769 * Class: sun_awt_windows_WTextAreaPeer
770 * Method: replaceText
771 * Signature: (Ljava/lang/String;II)V
772 */
773 JNIEXPORT void JNICALL
774 Java_sun_awt_windows_WTextAreaPeer_replaceText(JNIEnv *env, jobject self,
775 jstring text,
776 jint start, jint end)
777 {
778 TRY;
779
780 jobject selfGlobalRef = env->NewGlobalRef(self);
781 jstring textGlobalRef = (jstring)env->NewGlobalRef(text);
782
783 ReplaceTextStruct *rts = new ReplaceTextStruct;
784 rts->textComponent = selfGlobalRef;
785 rts->text = textGlobalRef;
786 rts->start = start;
787 rts->end = end;
788
789 AwtToolkit::GetInstance().SyncCall(AwtTextArea::_ReplaceText, rts);
790 // global refs and rts are deleted in _ReplaceText()
791
792 CATCH_BAD_ALLOC;
793 }
794
795 /*
796 * Class: sun_awt_windows_WTextAreaPeer
797 * Method: insertText
798 * Signature: (Ljava/lang/String;I)V
799 */
800 JNIEXPORT void JNICALL
801 Java_sun_awt_windows_WTextAreaPeer_insertText(JNIEnv *env, jobject self,
802 jstring text, jint pos)
803 {
804 Java_sun_awt_windows_WTextAreaPeer_replaceText(env, self, text, pos, pos);
805 }
806
807 } /* extern "C" */
808
809
--- EOF ---