Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-aarch32-jdk8u
Path: blob/jdk8u272-b10-aarch32-20201026/jdk/src/macosx/classes/sun/lwawt/macosx/CInputMethod.java
48795 views
1
/*
2
* Copyright (c) 2011, 2013, 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
package sun.lwawt.macosx;
27
28
import java.awt.im.spi.*;
29
import java.util.*;
30
import java.awt.*;
31
import java.awt.peer.*;
32
import java.awt.event.*;
33
import java.awt.im.*;
34
import java.awt.font.*;
35
import java.lang.Character.Subset;
36
import java.lang.reflect.InvocationTargetException;
37
import java.text.AttributedCharacterIterator.Attribute;
38
import java.text.*;
39
import javax.swing.text.JTextComponent;
40
41
import sun.awt.im.InputMethodAdapter;
42
import sun.lwawt.*;
43
44
public class CInputMethod extends InputMethodAdapter {
45
private InputMethodContext fIMContext;
46
private Component fAwtFocussedComponent;
47
private LWComponentPeer fAwtFocussedComponentPeer;
48
private boolean isActive;
49
50
private static Map<TextAttribute, Integer>[] sHighlightStyles;
51
52
// Intitalize highlight mapping table and its mapper.
53
static {
54
Map<TextAttribute, Integer> styles[] = new Map[4];
55
HashMap<TextAttribute, Integer> map;
56
57
// UNSELECTED_RAW_TEXT_HIGHLIGHT
58
map = new HashMap<TextAttribute, Integer>(1);
59
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
60
TextAttribute.UNDERLINE_LOW_GRAY);
61
styles[0] = Collections.unmodifiableMap(map);
62
63
// SELECTED_RAW_TEXT_HIGHLIGHT
64
map = new HashMap<TextAttribute, Integer>(1);
65
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
66
TextAttribute.UNDERLINE_LOW_GRAY);
67
styles[1] = Collections.unmodifiableMap(map);
68
69
// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
70
map = new HashMap<TextAttribute, Integer>(1);
71
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
72
TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
73
styles[2] = Collections.unmodifiableMap(map);
74
75
// SELECTED_CONVERTED_TEXT_HIGHLIGHT
76
map = new HashMap<TextAttribute, Integer>(1);
77
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
78
TextAttribute.UNDERLINE_LOW_TWO_PIXEL);
79
styles[3] = Collections.unmodifiableMap(map);
80
81
sHighlightStyles = styles;
82
83
nativeInit();
84
85
}
86
87
public CInputMethod() {
88
}
89
90
91
/**
92
* Sets the input method context, which is used to dispatch input method
93
* events to the client component and to request information from
94
* the client component.
95
* <p>
96
* This method is called once immediately after instantiating this input
97
* method.
98
*
99
* @param context the input method context for this input method
100
* @exception NullPointerException if <code>context</code> is null
101
*/
102
public void setInputMethodContext(InputMethodContext context) {
103
fIMContext = context;
104
}
105
106
/**
107
* Attempts to set the input locale. If the input method supports the
108
* desired locale, it changes its behavior to support input for the locale
109
* and returns true.
110
* Otherwise, it returns false and does not change its behavior.
111
* <p>
112
* This method is called
113
* <ul>
114
* <li>by {@link java.awt.im.InputContext#selectInputMethod InputContext.selectInputMethod},
115
* <li>when switching to this input method through the user interface if the user
116
* specified a locale or if the previously selected input method's
117
* {@link java.awt.im.spi.InputMethod#getLocale getLocale} method
118
* returns a non-null value.
119
* </ul>
120
*
121
* @param lang locale to input
122
* @return whether the specified locale is supported
123
* @exception NullPointerException if <code>locale</code> is null
124
*/
125
public boolean setLocale(Locale lang) {
126
return setLocale(lang, false);
127
}
128
129
private boolean setLocale(Locale lang, boolean onActivate) {
130
Object[] available = CInputMethodDescriptor.getAvailableLocalesInternal();
131
for (int i = 0; i < available.length; i++) {
132
Locale locale = (Locale)available[i];
133
if (lang.equals(locale) ||
134
// special compatibility rule for Japanese and Korean
135
locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
136
locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
137
if (isActive) {
138
setNativeLocale(locale.toString(), onActivate);
139
}
140
return true;
141
}
142
}
143
return false;
144
}
145
146
/**
147
* Returns the current input locale. Might return null in exceptional cases.
148
* <p>
149
* This method is called
150
* <ul>
151
* <li>by {@link java.awt.im.InputContext#getLocale InputContext.getLocale} and
152
* <li>when switching from this input method to a different one through the
153
* user interface.
154
* </ul>
155
*
156
* @return the current input locale, or null
157
*/
158
public Locale getLocale() {
159
// On Mac OS X we'll ask the currently active input method what its locale is.
160
Locale returnValue = getNativeLocale();
161
if (returnValue == null) {
162
returnValue = Locale.getDefault();
163
}
164
165
return returnValue;
166
}
167
168
/**
169
* Sets the subsets of the Unicode character set that this input method
170
* is allowed to input. Null may be passed in to indicate that all
171
* characters are allowed.
172
* <p>
173
* This method is called
174
* <ul>
175
* <li>immediately after instantiating this input method,
176
* <li>when switching to this input method from a different one, and
177
* <li>by {@link java.awt.im.InputContext#setCharacterSubsets InputContext.setCharacterSubsets}.
178
* </ul>
179
*
180
* @param subsets the subsets of the Unicode character set from which
181
* characters may be input
182
*/
183
public void setCharacterSubsets(Subset[] subsets) {
184
// -- SAK: Does mac OS X support this?
185
}
186
187
/**
188
* Composition cannot be set on Mac OS X -- the input method remembers this
189
*/
190
public void setCompositionEnabled(boolean enable) {
191
throw new UnsupportedOperationException("Can't adjust composition mode on Mac OS X.");
192
}
193
194
public boolean isCompositionEnabled() {
195
throw new UnsupportedOperationException("Can't adjust composition mode on Mac OS X.");
196
}
197
198
/**
199
* Dispatches the event to the input method. If input method support is
200
* enabled for the focussed component, incoming events of certain types
201
* are dispatched to the current input method for this component before
202
* they are dispatched to the component's methods or event listeners.
203
* The input method decides whether it needs to handle the event. If it
204
* does, it also calls the event's <code>consume</code> method; this
205
* causes the event to not get dispatched to the component's event
206
* processing methods or event listeners.
207
* <p>
208
* Events are dispatched if they are instances of InputEvent or its
209
* subclasses.
210
* This includes instances of the AWT classes KeyEvent and MouseEvent.
211
* <p>
212
* This method is called by {@link java.awt.im.InputContext#dispatchEvent InputContext.dispatchEvent}.
213
*
214
* @param event the event being dispatched to the input method
215
* @exception NullPointerException if <code>event</code> is null
216
*/
217
public void dispatchEvent(final AWTEvent event) {
218
// No-op for Mac OS X.
219
}
220
221
222
/**
223
* Activate and deactivate are no-ops on Mac OS X.
224
* A non-US keyboard layout is an 'input method' in that it generates events the same way as
225
* a CJK input method. A component that doesn't want input method events still wants the dead-key
226
* events.
227
*
228
*
229
*/
230
public void activate() {
231
isActive = true;
232
}
233
234
public void deactivate(boolean isTemporary) {
235
isActive = false;
236
}
237
238
/**
239
* Closes or hides all windows opened by this input method instance or
240
* its class. Deactivate hides windows for us on Mac OS X.
241
*/
242
public void hideWindows() {
243
}
244
245
long getNativeViewPtr(LWComponentPeer peer) {
246
if (peer.getPlatformWindow() instanceof CPlatformWindow) {
247
CPlatformWindow platformWindow = (CPlatformWindow) peer.getPlatformWindow();
248
CPlatformView platformView = platformWindow.getContentView();
249
return platformView.getAWTView();
250
} else {
251
return 0;
252
}
253
}
254
255
/**
256
* Notifies the input method that a client component has been
257
* removed from its containment hierarchy, or that input method
258
* support has been disabled for the component.
259
*/
260
public void removeNotify() {
261
if (fAwtFocussedComponentPeer != null) {
262
nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
263
}
264
265
fAwtFocussedComponentPeer = null;
266
}
267
268
/**
269
* Informs the input method adapter about the component that has the AWT
270
* focus if it's using the input context owning this adapter instance.
271
* We also take the opportunity to tell the native side that we are the input method
272
* to talk to when responding to key events.
273
*/
274
protected void setAWTFocussedComponent(Component component) {
275
LWComponentPeer peer = null;
276
long modelPtr = 0;
277
CInputMethod imInstance = this;
278
279
// component will be null when we are told there's no focused component.
280
// When that happens we need to notify the native architecture to stop generating IMEs
281
if (component == null) {
282
peer = fAwtFocussedComponentPeer;
283
imInstance = null;
284
} else {
285
peer = getNearestNativePeer(component);
286
287
// If we have a passive client, don't pass input method events to it.
288
if (component.getInputMethodRequests() == null) {
289
imInstance = null;
290
}
291
}
292
293
if (peer != null) {
294
modelPtr = getNativeViewPtr(peer);
295
296
// modelPtr refers to the ControlModel that either got or lost focus.
297
nativeNotifyPeer(modelPtr, imInstance);
298
}
299
300
// Track the focused component and its nearest peer.
301
fAwtFocussedComponent = component;
302
fAwtFocussedComponentPeer = getNearestNativePeer(component);
303
}
304
305
/**
306
* @see java.awt.Toolkit#mapInputMethodHighlight
307
*/
308
public static Map mapInputMethodHighlight(InputMethodHighlight highlight) {
309
int index;
310
int state = highlight.getState();
311
if (state == InputMethodHighlight.RAW_TEXT) {
312
index = 0;
313
} else if (state == InputMethodHighlight.CONVERTED_TEXT) {
314
index = 2;
315
} else {
316
return null;
317
}
318
if (highlight.isSelected()) {
319
index += 1;
320
}
321
return sHighlightStyles[index];
322
}
323
324
/**
325
* Ends any input composition that may currently be going on in this
326
* context. Depending on the platform and possibly user preferences,
327
* this may commit or delete uncommitted text. Any changes to the text
328
* are communicated to the active component using an input method event.
329
*
330
* <p>
331
* A text editing component may call this in a variety of situations,
332
* for example, when the user moves the insertion point within the text
333
* (but outside the composed text), or when the component's text is
334
* saved to a file or copied to the clipboard.
335
* <p>
336
* This method is called
337
* <ul>
338
* <li>by {@link java.awt.im.InputContext#endComposition InputContext.endComposition},
339
* <li>by {@link java.awt.im.InputContext#dispatchEvent InputContext.dispatchEvent}
340
* when switching to a different client component
341
* <li>when switching from this input method to a different one using the
342
* user interface or
343
* {@link java.awt.im.InputContext#selectInputMethod InputContext.selectInputMethod}.
344
* </ul>
345
*/
346
public void endComposition() {
347
if (fAwtFocussedComponentPeer != null)
348
nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
349
}
350
351
/**
352
* Disposes of the input method and releases the resources used by it.
353
* In particular, the input method should dispose windows and close files that are no
354
* longer needed.
355
* <p>
356
* This method is called by {@link java.awt.im.InputContext#dispose InputContext.dispose}.
357
* <p>
358
* The method is only called when the input method is inactive.
359
* No method of this interface is called on this instance after dispose.
360
*/
361
public void dispose() {
362
fIMContext = null;
363
fAwtFocussedComponent = null;
364
fAwtFocussedComponentPeer = null;
365
}
366
367
/**
368
* Returns a control object from this input method, or null. A
369
* control object provides methods that control the behavior of the
370
* input method or obtain information from the input method. The type
371
* of the object is an input method specific class. Clients have to
372
* compare the result against known input method control object
373
* classes and cast to the appropriate class to invoke the methods
374
* provided.
375
* <p>
376
* This method is called by
377
* {@link java.awt.im.InputContext#getInputMethodControlObject InputContext.getInputMethodControlObject}.
378
*
379
* @return a control object from this input method, or null
380
*/
381
public Object getControlObject() {
382
return null;
383
}
384
385
// java.awt.Toolkit#getNativeContainer() is not available
386
// from this package
387
private LWComponentPeer getNearestNativePeer(Component comp) {
388
if (comp==null)
389
return null;
390
391
ComponentPeer peer = comp.getPeer();
392
if (peer==null)
393
return null;
394
395
while (peer instanceof java.awt.peer.LightweightPeer) {
396
comp = comp.getParent();
397
if (comp==null)
398
return null;
399
peer = comp.getPeer();
400
if (peer==null)
401
return null;
402
}
403
404
if (peer instanceof LWComponentPeer)
405
return (LWComponentPeer)peer;
406
407
return null;
408
}
409
410
// =========================== NSTextInput callbacks ===========================
411
// The 'marked text' that we get from Cocoa. We need to track this separately, since
412
// Java doesn't let us ask the IM context for it.
413
private AttributedString fCurrentText = null;
414
private String fCurrentTextAsString = null;
415
private int fCurrentTextLength = 0;
416
417
/**
418
* Tell the component to commit all of the characters in the string to the current
419
* text view. This effectively wipes out any text in progress.
420
*/
421
synchronized private void insertText(String aString) {
422
AttributedString attribString = new AttributedString(aString);
423
424
// Set locale information on the new string.
425
attribString.addAttribute(Attribute.LANGUAGE, getLocale(), 0, aString.length());
426
427
TextHitInfo theCaret = TextHitInfo.afterOffset(aString.length() - 1);
428
InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
429
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
430
attribString.getIterator(),
431
aString.length(),
432
theCaret,
433
theCaret);
434
LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
435
fCurrentText = null;
436
fCurrentTextAsString = null;
437
fCurrentTextLength = 0;
438
}
439
440
private void startIMUpdate (String rawText) {
441
fCurrentTextAsString = new String(rawText);
442
fCurrentText = new AttributedString(fCurrentTextAsString);
443
fCurrentTextLength = rawText.length();
444
}
445
446
static private final int kCaretPosition = 0;
447
static private final int kRawText = 1;
448
static private final int kSelectedRawText = 2;
449
static private final int kConvertedText = 3;
450
static private final int kSelectedConvertedText = 4;
451
452
/**
453
* Convert Cocoa text highlight attributes into Java input method highlighting.
454
*/
455
private void addAttribute (boolean isThickUnderline, boolean isGray, int start, int length) {
456
int begin = start;
457
int end = start + length;
458
int markupType = kRawText;
459
460
if (isThickUnderline && isGray) {
461
markupType = kRawText;
462
} else if (!isThickUnderline && isGray) {
463
markupType = kRawText;
464
} else if (isThickUnderline && !isGray) {
465
markupType = kSelectedConvertedText;
466
} else if (!isThickUnderline && !isGray) {
467
markupType = kConvertedText;
468
}
469
470
InputMethodHighlight theHighlight;
471
472
switch (markupType) {
473
case kSelectedRawText:
474
theHighlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
475
break;
476
case kConvertedText:
477
theHighlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
478
break;
479
case kSelectedConvertedText:
480
theHighlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
481
break;
482
case kRawText:
483
default:
484
theHighlight = InputMethodHighlight.UNSELECTED_RAW_TEXT_HIGHLIGHT;
485
break;
486
}
487
488
fCurrentText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, theHighlight, begin, end);
489
}
490
491
/* Called from JNI to select the previously typed glyph during press and hold */
492
private void selectPreviousGlyph() {
493
if (fIMContext == null) return; // ???
494
try {
495
LWCToolkit.invokeLater(new Runnable() {
496
public void run() {
497
final int offset = fIMContext.getInsertPositionOffset();
498
if (offset < 1) return; // ???
499
500
if (fAwtFocussedComponent instanceof JTextComponent) {
501
((JTextComponent) fAwtFocussedComponent).select(offset - 1, offset);
502
return;
503
}
504
505
if (fAwtFocussedComponent instanceof TextComponent) {
506
((TextComponent) fAwtFocussedComponent).select(offset - 1, offset);
507
return;
508
}
509
// TODO: Ideally we want to disable press-and-hold in this case
510
}
511
}, fAwtFocussedComponent);
512
} catch (Exception e) {
513
e.printStackTrace();
514
}
515
}
516
517
private void selectNextGlyph() {
518
if (fIMContext == null || !(fAwtFocussedComponent instanceof JTextComponent)) return;
519
try {
520
LWCToolkit.invokeLater(new Runnable() {
521
public void run() {
522
final int offset = fIMContext.getInsertPositionOffset();
523
if (offset < 0) return;
524
((JTextComponent) fAwtFocussedComponent).select(offset, offset + 1);
525
return;
526
}
527
}, fAwtFocussedComponent);
528
} catch (Exception e) {
529
e.printStackTrace();
530
}
531
}
532
533
private void dispatchText(int selectStart, int selectLength, boolean pressAndHold) {
534
// Nothing to do if we have no text.
535
if (fCurrentText == null)
536
return;
537
538
TextHitInfo theCaret = (selectLength == 0 ? TextHitInfo.beforeOffset(selectStart) : null);
539
TextHitInfo visiblePosition = TextHitInfo.beforeOffset(0);
540
541
InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
542
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
543
fCurrentText.getIterator(),
544
0,
545
theCaret,
546
visiblePosition);
547
LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
548
549
if (pressAndHold) selectNextGlyph();
550
}
551
552
/**
553
* Frequent callbacks from NSTextInput. I think we're supposed to commit it here?
554
*/
555
synchronized private void unmarkText() {
556
if (fCurrentText == null)
557
return;
558
559
TextHitInfo theCaret = TextHitInfo.afterOffset(fCurrentTextLength);
560
TextHitInfo visiblePosition = theCaret;
561
InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
562
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
563
fCurrentText.getIterator(),
564
fCurrentTextLength,
565
theCaret,
566
visiblePosition);
567
LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
568
fCurrentText = null;
569
fCurrentTextAsString = null;
570
fCurrentTextLength = 0;
571
}
572
573
synchronized private boolean hasMarkedText() {
574
return fCurrentText != null;
575
}
576
577
/**
578
* Cocoa assumes the marked text and committed text is all stored in the same storage, but
579
* Java does not. So, we have to see where the request is and based on that return the right
580
* substring.
581
*/
582
synchronized private String attributedSubstringFromRange(final int locationIn, final int lengthIn) {
583
final String[] retString = new String[1];
584
585
try {
586
LWCToolkit.invokeAndWait(new Runnable() {
587
public void run() { synchronized(retString) {
588
int location = locationIn;
589
int length = lengthIn;
590
591
if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {
592
length = fIMContext.getCommittedTextLength() - location;
593
}
594
595
AttributedCharacterIterator theIterator = null;
596
597
if (fCurrentText == null) {
598
theIterator = fIMContext.getCommittedText(location, location + length, null);
599
} else {
600
int insertSpot = fIMContext.getInsertPositionOffset();
601
602
if (location < insertSpot) {
603
theIterator = fIMContext.getCommittedText(location, location + length, null);
604
} else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {
605
theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot +length);
606
} else {
607
theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);
608
}
609
}
610
611
// Get the characters from the iterator
612
char selectedText[] = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];
613
char current = theIterator.first();
614
int index = 0;
615
while (current != CharacterIterator.DONE) {
616
selectedText[index++] = current;
617
current = theIterator.next();
618
}
619
620
retString[0] = new String(selectedText);
621
}}
622
}, fAwtFocussedComponent);
623
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
624
625
synchronized(retString) { return retString[0]; }
626
}
627
628
/**
629
* Cocoa wants the range of characters that are currently selected. We have to synthesize this
630
* by getting the insert location and the length of the selected text. NB: This does NOT allow
631
* for the fact that the insert point in Swing can come AFTER the selected text, making this
632
* potentially incorrect.
633
*/
634
synchronized private int[] selectedRange() {
635
final int[] returnValue = new int[2];
636
637
try {
638
LWCToolkit.invokeAndWait(new Runnable() {
639
public void run() { synchronized(returnValue) {
640
AttributedCharacterIterator theIterator = fIMContext.getSelectedText(null);
641
if (theIterator == null) {
642
returnValue[0] = fIMContext.getInsertPositionOffset();
643
returnValue[1] = 0;
644
return;
645
}
646
647
int startLocation;
648
649
if (fAwtFocussedComponent instanceof JTextComponent) {
650
JTextComponent theComponent = (JTextComponent)fAwtFocussedComponent;
651
startLocation = theComponent.getSelectionStart();
652
} else if (fAwtFocussedComponent instanceof TextComponent) {
653
TextComponent theComponent = (TextComponent)fAwtFocussedComponent;
654
startLocation = theComponent.getSelectionStart();
655
} else {
656
// If we don't have a Swing or AWT component, we have to guess whether the selection is before or after the input spot.
657
startLocation = fIMContext.getInsertPositionOffset() - (theIterator.getEndIndex() - theIterator.getBeginIndex());
658
659
// If the calculated spot is negative the insert spot must be at the beginning of
660
// the selection.
661
if (startLocation < 0) {
662
startLocation = fIMContext.getInsertPositionOffset() + (theIterator.getEndIndex() - theIterator.getBeginIndex());
663
}
664
}
665
666
returnValue[0] = startLocation;
667
returnValue[1] = theIterator.getEndIndex() - theIterator.getBeginIndex();
668
669
}}
670
}, fAwtFocussedComponent);
671
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
672
673
synchronized(returnValue) { return returnValue; }
674
}
675
676
/**
677
* Cocoa wants the range of characters that are currently marked. Since Java doesn't store committed and
678
* text in progress (composed text) together, we have to synthesize it. We know where the text will be
679
* inserted, so we can return that position, and the length of the text in progress. If there is no marked text
680
* return null.
681
*/
682
synchronized private int[] markedRange() {
683
if (fCurrentText == null)
684
return null;
685
686
final int[] returnValue = new int[2];
687
688
try {
689
LWCToolkit.invokeAndWait(new Runnable() {
690
public void run() { synchronized(returnValue) {
691
// The insert position is always after the composed text, so the range start is the
692
// insert spot less the length of the composed text.
693
returnValue[0] = fIMContext.getInsertPositionOffset();
694
}}
695
}, fAwtFocussedComponent);
696
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
697
698
returnValue[1] = fCurrentTextLength;
699
synchronized(returnValue) { return returnValue; }
700
}
701
702
/**
703
* Cocoa wants a rectangle that describes where a particular range is on screen, but only cares about the
704
* location of that rectangle. We are given the index of the character for which we want the location on
705
* screen, which will be a character in the in-progress text. By subtracting the current insert position,
706
* which is always in front of the in-progress text, we get the offset into the composed text, and we get
707
* that location from the input method context.
708
*/
709
synchronized private int[] firstRectForCharacterRange(final int absoluteTextOffset) {
710
final int[] rect = new int[4];
711
712
try {
713
LWCToolkit.invokeAndWait(new Runnable() {
714
public void run() { synchronized(rect) {
715
int insertOffset = fIMContext.getInsertPositionOffset();
716
int composedTextOffset = absoluteTextOffset - insertOffset;
717
if (composedTextOffset < 0) composedTextOffset = 0;
718
Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));
719
rect[0] = r.x;
720
rect[1] = r.y;
721
rect[2] = r.width;
722
rect[3] = r.height;
723
724
// This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores
725
// the TextHitInfo passed to it and always returns the location of the insertion point, which is
726
// at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri
727
// follows the requested offset into the composed text.
728
if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {
729
Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));
730
731
if (r.equals(r2)) {
732
// FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate
733
// window off the right edge of the component.
734
String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);
735
Graphics g = fAwtFocussedComponent.getGraphics();
736
int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);
737
rect[0] += xOffset;
738
g.dispose();
739
}
740
}
741
}}
742
}, fAwtFocussedComponent);
743
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
744
745
synchronized(rect) { return rect; }
746
}
747
748
/* This method returns the index for the character that is nearest to the point described by screenX and screenY.
749
* The coordinates are in Java screen coordinates. If no character in the composed text was hit, we return -1, indicating
750
* not found.
751
*/
752
synchronized private int characterIndexForPoint(final int screenX, final int screenY) {
753
final TextHitInfo[] offsetInfo = new TextHitInfo[1];
754
final int[] insertPositionOffset = new int[1];
755
756
try {
757
LWCToolkit.invokeAndWait(new Runnable() {
758
public void run() { synchronized(offsetInfo) {
759
offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
760
insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
761
}}
762
}, fAwtFocussedComponent);
763
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
764
765
// This bit of gymnastics ensures that the returned location is within the composed text.
766
// If it falls outside that region, the input method will commit the text, which is inconsistent with native
767
// Cocoa apps (see TextEdit, for example.) Clicking to the left of or above the selected text moves the
768
// cursor to the start of the composed text, and to the right or below moves it to one character before the end.
769
if (offsetInfo[0] == null) {
770
return insertPositionOffset[0];
771
}
772
773
int returnValue = offsetInfo[0].getCharIndex() + insertPositionOffset[0];
774
775
if (offsetInfo[0].getCharIndex() == fCurrentTextLength)
776
returnValue --;
777
778
return returnValue;
779
}
780
781
// On Mac OS X we effectively disabled the input method when focus was lost, so
782
// this call can be ignored.
783
public void disableInputMethod()
784
{
785
// Deliberately ignored. See setAWTFocussedComponent above.
786
}
787
788
public String getNativeInputMethodInfo()
789
{
790
return nativeGetCurrentInputMethodInfo();
791
}
792
793
794
// =========================== Native methods ===========================
795
// Note that if nativePeer isn't something that normally accepts keystrokes (i.e., a CPanel)
796
// these calls will be ignored.
797
private native void nativeNotifyPeer(long nativePeer, CInputMethod imInstance);
798
private native void nativeEndComposition(long nativePeer);
799
private native void nativeHandleEvent(LWComponentPeer peer, AWTEvent event);
800
801
// Returns the locale of the active input method.
802
static native Locale getNativeLocale();
803
804
// Switches to the input method with language indicated in localeName
805
static native boolean setNativeLocale(String localeName, boolean onActivate);
806
807
// Returns information about the currently selected input method.
808
static native String nativeGetCurrentInputMethodInfo();
809
810
// Initialize toolbox routines
811
static native void nativeInit();
812
}
813
814