Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/awt/X11InputMethod.java
32287 views
1
/*
2
* Copyright (c) 1997, 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.awt;
27
28
import java.util.Collections;
29
import java.util.Locale;
30
import java.util.Map;
31
import java.util.HashMap;
32
import java.awt.AWTEvent;
33
import java.awt.AWTException;
34
import java.awt.Component;
35
import java.awt.Container;
36
import java.awt.EventQueue;
37
import java.awt.Window;
38
import java.awt.im.InputContext;
39
import java.awt.im.InputMethodHighlight;
40
import java.awt.im.spi.InputMethodContext;
41
import sun.awt.im.InputMethodAdapter;
42
import java.awt.event.InputEvent;
43
import java.awt.event.KeyEvent;
44
import java.awt.event.MouseEvent;
45
import java.awt.event.FocusEvent;
46
import java.awt.event.ComponentEvent;
47
import java.awt.event.WindowEvent;
48
import java.awt.event.InputMethodEvent;
49
import java.awt.font.TextAttribute;
50
import java.awt.font.TextHitInfo;
51
import java.awt.peer.ComponentPeer;
52
import java.lang.Character.Subset;
53
import java.text.AttributedString;
54
import java.text.AttributedCharacterIterator;
55
56
import java.io.File;
57
import java.io.FileReader;
58
import java.io.BufferedReader;
59
import java.io.IOException;
60
import java.lang.ref.WeakReference;
61
import sun.util.logging.PlatformLogger;
62
import java.util.StringTokenizer;
63
import java.util.regex.Pattern;
64
65
66
/**
67
* Input Method Adapter for XIM
68
*
69
* @author JavaSoft International
70
*/
71
public abstract class X11InputMethod extends InputMethodAdapter {
72
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11InputMethod");
73
/*
74
* The following XIM* values must be the same as those defined in
75
* Xlib.h
76
*/
77
private static final int XIMReverse = (1<<0);
78
private static final int XIMUnderline = (1<<1);
79
private static final int XIMHighlight = (1<<2);
80
private static final int XIMPrimary = (1<<5);
81
private static final int XIMSecondary = (1<<6);
82
private static final int XIMTertiary = (1<<7);
83
84
/*
85
* visible position values
86
*/
87
private static final int XIMVisibleToForward = (1<<8);
88
private static final int XIMVisibleToBackward = (1<<9);
89
private static final int XIMVisibleCenter = (1<<10);
90
private static final int XIMVisibleMask = (XIMVisibleToForward|
91
XIMVisibleToBackward|
92
XIMVisibleCenter);
93
94
private Locale locale;
95
private static boolean isXIMOpened = false;
96
protected Container clientComponentWindow = null;
97
private Component awtFocussedComponent = null;
98
private Component lastXICFocussedComponent = null;
99
private boolean isLastXICActive = false;
100
private boolean isLastTemporary = false;
101
private boolean isActive = false;
102
private boolean isActiveClient = false;
103
private static Map[] highlightStyles;
104
private boolean disposed = false;
105
106
//reset the XIC if necessary
107
private boolean needResetXIC = false;
108
private WeakReference<Component> needResetXICClient = new WeakReference<>(null);
109
110
// The use of compositionEnableSupported is to reduce unnecessary
111
// native calls if set/isCompositionEnabled
112
// throws UnsupportedOperationException.
113
// It is set to false if that exception is thrown first time
114
// either of the two methods are called.
115
private boolean compositionEnableSupported = true;
116
// The savedCompositionState indicates the composition mode when
117
// endComposition or setCompositionEnabled is called. It doesn't always
118
// reflect the actual composition state because it doesn't get updated
119
// when the user changes the composition state through direct interaction
120
// with the input method. It is used to save the composition mode when
121
// focus is traversed across different client components sharing the
122
// same java input context. Also if set/isCompositionEnabled are not
123
// supported, it remains false.
124
private boolean savedCompositionState = false;
125
126
// variables to keep track of preedit context.
127
// these variables need to be accessed within AWT_LOCK/UNLOCK
128
private String committedText = null;
129
private StringBuffer composedText = null;
130
private IntBuffer rawFeedbacks;
131
132
// private data (X11InputMethodData structure defined in
133
// awt_InputMethod.c) for native methods
134
// this structure needs to be accessed within AWT_LOCK/UNLOCK
135
transient private long pData = 0; // accessed by native
136
137
// Initialize highlight mapping table
138
static {
139
Map styles[] = new Map[4];
140
HashMap map;
141
142
// UNSELECTED_RAW_TEXT_HIGHLIGHT
143
map = new HashMap(1);
144
map.put(TextAttribute.WEIGHT,
145
TextAttribute.WEIGHT_BOLD);
146
styles[0] = Collections.unmodifiableMap(map);
147
148
// SELECTED_RAW_TEXT_HIGHLIGHT
149
map = new HashMap(1);
150
map.put(TextAttribute.SWAP_COLORS,
151
TextAttribute.SWAP_COLORS_ON);
152
styles[1] = Collections.unmodifiableMap(map);
153
154
// UNSELECTED_CONVERTED_TEXT_HIGHLIGHT
155
map = new HashMap(1);
156
map.put(TextAttribute.INPUT_METHOD_UNDERLINE,
157
TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
158
styles[2] = Collections.unmodifiableMap(map);
159
160
// SELECTED_CONVERTED_TEXT_HIGHLIGHT
161
map = new HashMap(1);
162
map.put(TextAttribute.SWAP_COLORS,
163
TextAttribute.SWAP_COLORS_ON);
164
styles[3] = Collections.unmodifiableMap(map);
165
166
highlightStyles = styles;
167
}
168
169
static {
170
initIDs();
171
}
172
173
/**
174
* Initialize JNI field and method IDs for fields that may be
175
accessed from C.
176
*/
177
private static native void initIDs();
178
179
/**
180
* Constructs an X11InputMethod instance. It initializes the XIM
181
* environment if it's not done yet.
182
*
183
* @exception AWTException if XOpenIM() failed.
184
*/
185
public X11InputMethod() throws AWTException {
186
// supports only the locale in which the VM is started
187
locale = X11InputMethodDescriptor.getSupportedLocale();
188
if (initXIM() == false) {
189
throw new AWTException("Cannot open X Input Method");
190
}
191
}
192
193
protected void finalize() throws Throwable {
194
dispose();
195
super.finalize();
196
}
197
198
/**
199
* Invokes openIM() that invokes XOpenIM() if it's not opened yet.
200
* @return true if openXIM() is successful or it's already been opened.
201
*/
202
private synchronized boolean initXIM() {
203
if (isXIMOpened == false)
204
isXIMOpened = openXIM();
205
return isXIMOpened;
206
}
207
208
protected abstract boolean openXIM();
209
210
protected boolean isDisposed() {
211
return disposed;
212
}
213
214
protected abstract void setXICFocus(ComponentPeer peer,
215
boolean value, boolean active);
216
217
/**
218
* Does nothing - this adapter doesn't use the input method context.
219
*
220
* @see java.awt.im.spi.InputMethod#setInputMethodContext
221
*/
222
public void setInputMethodContext(InputMethodContext context) {
223
}
224
225
/**
226
* Set locale to input. If input method doesn't support specified locale,
227
* false will be returned and its behavior is not changed.
228
*
229
* @param lang locale to input
230
* @return the true is returned when specified locale is supported.
231
*/
232
public boolean setLocale(Locale lang) {
233
if (lang.equals(locale)) {
234
return true;
235
}
236
// special compatibility rule for Japanese and Korean
237
if (locale.equals(Locale.JAPAN) && lang.equals(Locale.JAPANESE) ||
238
locale.equals(Locale.KOREA) && lang.equals(Locale.KOREAN)) {
239
return true;
240
}
241
return false;
242
}
243
244
/**
245
* Returns current input locale.
246
*/
247
public Locale getLocale() {
248
return locale;
249
}
250
251
/**
252
* Does nothing - XIM doesn't let you specify which characters you expect.
253
*
254
* @see java.awt.im.spi.InputMethod#setCharacterSubsets
255
*/
256
public void setCharacterSubsets(Subset[] subsets) {
257
}
258
259
/**
260
* Dispatch event to input method. InputContext dispatch event with this
261
* method. Input method set consume flag if event is consumed in
262
* input method.
263
*
264
* @param e event
265
*/
266
public void dispatchEvent(AWTEvent e) {
267
}
268
269
270
protected final void resetXICifneeded(){
271
/* needResetXIC is used to indicate whether to call
272
resetXIC on the active client. resetXIC will always be
273
called on the passive client when endComposition is called.
274
*/
275
if (needResetXIC && haveActiveClient() &&
276
getClientComponent() != needResetXICClient.get()){
277
resetXIC();
278
279
// needs to reset the last xic focussed component.
280
lastXICFocussedComponent = null;
281
isLastXICActive = false;
282
283
needResetXICClient.clear();
284
needResetXIC = false;
285
}
286
}
287
288
/**
289
* Reset the composition state to the current composition state.
290
*/
291
private void resetCompositionState() {
292
if (compositionEnableSupported) {
293
try {
294
/* Restore the composition mode to the last saved composition
295
mode. */
296
setCompositionEnabled(savedCompositionState);
297
} catch (UnsupportedOperationException e) {
298
compositionEnableSupported = false;
299
}
300
}
301
}
302
303
/**
304
* Query and then return the current composition state.
305
* @returns the composition state if isCompositionEnabled call
306
* is successful. Otherwise, it returns false.
307
*/
308
private boolean getCompositionState() {
309
boolean compositionState = false;
310
if (compositionEnableSupported) {
311
try {
312
compositionState = isCompositionEnabled();
313
} catch (UnsupportedOperationException e) {
314
compositionEnableSupported = false;
315
}
316
}
317
return compositionState;
318
}
319
320
/**
321
* Activate input method.
322
*/
323
public synchronized void activate() {
324
clientComponentWindow = getClientComponentWindow();
325
if (clientComponentWindow == null)
326
return;
327
328
if (lastXICFocussedComponent != null){
329
if (log.isLoggable(PlatformLogger.Level.FINE)) {
330
log.fine("XICFocused {0}, AWTFocused {1}",
331
lastXICFocussedComponent, awtFocussedComponent);
332
}
333
}
334
335
if (pData == 0) {
336
if (!createXIC()) {
337
return;
338
}
339
disposed = false;
340
}
341
342
/* reset input context if necessary and set the XIC focus
343
*/
344
resetXICifneeded();
345
ComponentPeer lastXICFocussedComponentPeer = null;
346
ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
347
348
if (lastXICFocussedComponent != null) {
349
lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);
350
}
351
352
/* If the last XIC focussed component has a different peer as the
353
current focussed component, change the XIC focus to the newly
354
focussed component.
355
*/
356
if (isLastTemporary || lastXICFocussedComponentPeer != awtFocussedComponentPeer ||
357
isLastXICActive != haveActiveClient()) {
358
if (lastXICFocussedComponentPeer != null) {
359
setXICFocus(lastXICFocussedComponentPeer, false, isLastXICActive);
360
}
361
if (awtFocussedComponentPeer != null) {
362
setXICFocus(awtFocussedComponentPeer, true, haveActiveClient());
363
}
364
lastXICFocussedComponent = awtFocussedComponent;
365
isLastXICActive = haveActiveClient();
366
}
367
resetCompositionState();
368
isActive = true;
369
}
370
371
protected abstract boolean createXIC();
372
373
/**
374
* Deactivate input method.
375
*/
376
public synchronized void deactivate(boolean isTemporary) {
377
boolean isAc = haveActiveClient();
378
/* Usually as the client component, let's call it component A,
379
loses the focus, this method is called. Then when another client
380
component, let's call it component B, gets the focus, activate is first called on
381
the previous focused compoent which is A, then endComposition is called on A,
382
deactivate is called on A again. And finally activate is called on the newly
383
focused component B. Here is the call sequence.
384
385
A loses focus B gains focus
386
-------------> deactivate A -------------> activate A -> endComposition A ->
387
deactivate A -> activate B ----....
388
389
So in order to carry the composition mode across the components sharing the same
390
input context, we save it when deactivate is called so that when activate is
391
called, it can be restored correctly till activate is called on the newly focused
392
component. (See also sun/awt/im/InputContext and bug 6184471).
393
Last note, getCompositionState should be called before setXICFocus since
394
setXICFocus here sets the XIC to 0.
395
*/
396
savedCompositionState = getCompositionState();
397
398
if (isTemporary){
399
//turn the status window off...
400
turnoffStatusWindow();
401
}
402
403
/* Delay resetting the XIC focus until activate is called and the newly
404
focussed component has a different peer as the last focussed component.
405
*/
406
lastXICFocussedComponent = awtFocussedComponent;
407
isLastXICActive = isAc;
408
isLastTemporary = isTemporary;
409
isActive = false;
410
}
411
412
/**
413
* Explicitly disable the native IME. Native IME is not disabled when
414
* deactivate is called.
415
*/
416
public void disableInputMethod() {
417
if (lastXICFocussedComponent != null) {
418
setXICFocus(getPeer(lastXICFocussedComponent), false, isLastXICActive);
419
lastXICFocussedComponent = null;
420
isLastXICActive = false;
421
422
resetXIC();
423
needResetXICClient.clear();
424
needResetXIC = false;
425
}
426
}
427
428
// implements java.awt.im.spi.InputMethod.hideWindows
429
public void hideWindows() {
430
// ??? need real implementation
431
}
432
433
/**
434
* @see java.awt.Toolkit#mapInputMethodHighlight
435
*/
436
public static Map mapInputMethodHighlight(InputMethodHighlight highlight) {
437
int index;
438
int state = highlight.getState();
439
if (state == InputMethodHighlight.RAW_TEXT) {
440
index = 0;
441
} else if (state == InputMethodHighlight.CONVERTED_TEXT) {
442
index = 2;
443
} else {
444
return null;
445
}
446
if (highlight.isSelected()) {
447
index += 1;
448
}
449
return highlightStyles[index];
450
}
451
452
/**
453
* @see sun.awt.im.InputMethodAdapter#setAWTFocussedComponent
454
*/
455
protected void setAWTFocussedComponent(Component component) {
456
if (component == null) {
457
return;
458
}
459
if (isActive) {
460
// deactivate/activate are being suppressed during a focus change -
461
// this may happen when an input method window is made visible
462
boolean ac = haveActiveClient();
463
setXICFocus(getPeer(awtFocussedComponent), false, ac);
464
setXICFocus(getPeer(component), true, ac);
465
}
466
awtFocussedComponent = component;
467
}
468
469
/**
470
* @see sun.awt.im.InputMethodAdapter#stopListening
471
*/
472
protected void stopListening() {
473
// It is desirable to disable XIM by calling XSetICValues with
474
// XNPreeditState == XIMPreeditDisable. But Solaris 2.6 and
475
// Solaris 7 do not implement this correctly without a patch,
476
// so just call resetXIC here. Prior endComposition call commits
477
// the existing composed text.
478
endComposition();
479
// disable the native input method so that the other input
480
// method could get the input focus.
481
disableInputMethod();
482
if (needResetXIC) {
483
resetXIC();
484
needResetXICClient.clear();
485
needResetXIC = false;
486
}
487
}
488
489
/**
490
* Returns the Window instance in which the client component is
491
* contained. If not found, null is returned. (IS THIS POSSIBLE?)
492
*/
493
// NOTE: This method may be called by privileged threads.
494
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
495
private Window getClientComponentWindow() {
496
Component client = getClientComponent();
497
Container container;
498
499
if (client instanceof Container) {
500
container = (Container) client;
501
} else {
502
container = getParent(client);
503
}
504
505
while (container != null && !(container instanceof java.awt.Window)) {
506
container = getParent(container);
507
}
508
return (Window) container;
509
}
510
511
protected abstract Container getParent(Component client);
512
513
/**
514
* Returns peer of the given client component. If the given client component
515
* doesn't have peer, peer of the native container of the client is returned.
516
*/
517
protected abstract ComponentPeer getPeer(Component client);
518
519
/**
520
* Used to protect preedit data
521
*/
522
protected abstract void awtLock();
523
protected abstract void awtUnlock();
524
525
/**
526
* Creates an input method event from the arguments given
527
* and posts it on the AWT event queue. For arguments,
528
* see InputMethodEvent. Called by input method.
529
*
530
* @see java.awt.event.InputMethodEvent#InputMethodEvent
531
*/
532
private void postInputMethodEvent(int id,
533
AttributedCharacterIterator text,
534
int committedCharacterCount,
535
TextHitInfo caret,
536
TextHitInfo visiblePosition,
537
long when) {
538
Component source = getClientComponent();
539
if (source != null) {
540
InputMethodEvent event = new InputMethodEvent(source,
541
id, when, text, committedCharacterCount, caret, visiblePosition);
542
SunToolkit.postEvent(SunToolkit.targetToAppContext(source), (AWTEvent)event);
543
}
544
}
545
546
private void postInputMethodEvent(int id,
547
AttributedCharacterIterator text,
548
int committedCharacterCount,
549
TextHitInfo caret,
550
TextHitInfo visiblePosition) {
551
postInputMethodEvent(id, text, committedCharacterCount,
552
caret, visiblePosition, EventQueue.getMostRecentEventTime());
553
}
554
555
/**
556
* Dispatches committed text from XIM to the awt event queue. This
557
* method is invoked from the event handler in canvas.c in the
558
* AWT Toolkit thread context and thus inside the AWT Lock.
559
* @param str committed text
560
* @param long when
561
*/
562
// NOTE: This method may be called by privileged threads.
563
// This functionality is implemented in a package-private method
564
// to insure that it cannot be overridden by client subclasses.
565
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
566
void dispatchCommittedText(String str, long when) {
567
if (str == null)
568
return;
569
570
if (composedText == null) {
571
AttributedString attrstr = new AttributedString(str);
572
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
573
attrstr.getIterator(),
574
str.length(),
575
null,
576
null,
577
when);
578
} else {
579
// if there is composed text, wait until the preedit
580
// callback is invoked.
581
committedText = str;
582
}
583
}
584
585
private void dispatchCommittedText(String str) {
586
dispatchCommittedText(str, EventQueue.getMostRecentEventTime());
587
}
588
589
/**
590
* Updates composed text with XIM preedit information and
591
* posts composed text to the awt event queue. The args of
592
* this method correspond to the XIM preedit callback
593
* information. The XIM highlight attributes are translated via
594
* fixed mapping (i.e., independent from any underlying input
595
* method engine). This method is invoked in the AWT Toolkit
596
* (X event loop) thread context and thus inside the AWT Lock.
597
*/
598
// NOTE: This method may be called by privileged threads.
599
// This functionality is implemented in a package-private method
600
// to insure that it cannot be overridden by client subclasses.
601
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
602
void dispatchComposedText(String chgText,
603
int chgStyles[],
604
int chgOffset,
605
int chgLength,
606
int caretPosition,
607
long when) {
608
if (disposed) {
609
return;
610
}
611
612
//Workaround for deadlock bug on solaris2.6_zh bug#4170760
613
if (chgText == null
614
&& chgStyles == null
615
&& chgOffset == 0
616
&& chgLength == 0
617
&& caretPosition == 0
618
&& composedText == null
619
&& committedText == null)
620
return;
621
622
if (composedText == null) {
623
// TODO: avoid reallocation of those buffers
624
composedText = new StringBuffer(INITIAL_SIZE);
625
rawFeedbacks = new IntBuffer(INITIAL_SIZE);
626
}
627
if (chgLength > 0) {
628
if (chgText == null && chgStyles != null) {
629
rawFeedbacks.replace(chgOffset, chgStyles);
630
} else {
631
if (chgLength == composedText.length()) {
632
// optimization for the special case to replace the
633
// entire previous text
634
composedText = new StringBuffer(INITIAL_SIZE);
635
rawFeedbacks = new IntBuffer(INITIAL_SIZE);
636
} else {
637
if (composedText.length() > 0) {
638
if (chgOffset+chgLength < composedText.length()) {
639
String text;
640
text = composedText.toString().substring(chgOffset+chgLength,
641
composedText.length());
642
composedText.setLength(chgOffset);
643
composedText.append(text);
644
} else {
645
// in case to remove substring from chgOffset
646
// to the end
647
composedText.setLength(chgOffset);
648
}
649
rawFeedbacks.remove(chgOffset, chgLength);
650
}
651
}
652
}
653
}
654
if (chgText != null) {
655
composedText.insert(chgOffset, chgText);
656
if (chgStyles != null)
657
rawFeedbacks.insert(chgOffset, chgStyles);
658
}
659
660
if (composedText.length() == 0) {
661
composedText = null;
662
rawFeedbacks = null;
663
664
// if there is any outstanding committed text stored by
665
// dispatchCommittedText(), it has to be sent to the
666
// client component.
667
if (committedText != null) {
668
dispatchCommittedText(committedText, when);
669
committedText = null;
670
return;
671
}
672
673
// otherwise, send null text to delete client's composed
674
// text.
675
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
676
null,
677
0,
678
null,
679
null,
680
when);
681
682
return;
683
}
684
685
// Now sending the composed text to the client
686
int composedOffset;
687
AttributedString inputText;
688
689
// if there is any partially committed text, concatenate it to
690
// the composed text.
691
if (committedText != null) {
692
composedOffset = committedText.length();
693
inputText = new AttributedString(committedText + composedText);
694
committedText = null;
695
} else {
696
composedOffset = 0;
697
inputText = new AttributedString(composedText.toString());
698
}
699
700
int currentFeedback;
701
int nextFeedback;
702
int startOffset = 0;
703
int currentOffset;
704
int visiblePosition = 0;
705
TextHitInfo visiblePositionInfo = null;
706
707
rawFeedbacks.rewind();
708
currentFeedback = rawFeedbacks.getNext();
709
rawFeedbacks.unget();
710
while ((nextFeedback = rawFeedbacks.getNext()) != -1) {
711
if (visiblePosition == 0) {
712
visiblePosition = nextFeedback & XIMVisibleMask;
713
if (visiblePosition != 0) {
714
int index = rawFeedbacks.getOffset() - 1;
715
716
if (visiblePosition == XIMVisibleToBackward)
717
visiblePositionInfo = TextHitInfo.leading(index);
718
else
719
visiblePositionInfo = TextHitInfo.trailing(index);
720
}
721
}
722
nextFeedback &= ~XIMVisibleMask;
723
if (currentFeedback != nextFeedback) {
724
rawFeedbacks.unget();
725
currentOffset = rawFeedbacks.getOffset();
726
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
727
convertVisualFeedbackToHighlight(currentFeedback),
728
composedOffset + startOffset,
729
composedOffset + currentOffset);
730
startOffset = currentOffset;
731
currentFeedback = nextFeedback;
732
}
733
}
734
currentOffset = rawFeedbacks.getOffset();
735
if (currentOffset >= 0) {
736
inputText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
737
convertVisualFeedbackToHighlight(currentFeedback),
738
composedOffset + startOffset,
739
composedOffset + currentOffset);
740
}
741
742
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
743
inputText.getIterator(),
744
composedOffset,
745
TextHitInfo.leading(caretPosition),
746
visiblePositionInfo,
747
when);
748
}
749
750
/**
751
* Flushes composed and committed text held in this context.
752
* This method is invoked in the AWT Toolkit (X event loop) thread context
753
* and thus inside the AWT Lock.
754
*/
755
// NOTE: This method may be called by privileged threads.
756
// This functionality is implemented in a package-private method
757
// to insure that it cannot be overridden by client subclasses.
758
// DO NOT INVOKE CLIENT CODE ON THIS THREAD!
759
void flushText() {
760
String flush = (committedText != null ? committedText : "");
761
if (composedText != null) {
762
flush += composedText.toString();
763
}
764
765
if (!flush.equals("")) {
766
AttributedString attrstr = new AttributedString(flush);
767
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
768
attrstr.getIterator(),
769
flush.length(),
770
null,
771
null,
772
EventQueue.getMostRecentEventTime());
773
composedText = null;
774
committedText = null;
775
}
776
}
777
778
/*
779
* Subclasses should override disposeImpl() instead of dispose(). Client
780
* code should always invoke dispose(), never disposeImpl().
781
*/
782
protected synchronized void disposeImpl() {
783
disposeXIC();
784
awtLock();
785
composedText = null;
786
committedText = null;
787
rawFeedbacks = null;
788
awtUnlock();
789
awtFocussedComponent = null;
790
lastXICFocussedComponent = null;
791
}
792
793
/**
794
* Frees all X Window resources associated with this object.
795
*
796
* @see java.awt.im.spi.InputMethod#dispose
797
*/
798
public final void dispose() {
799
boolean call_disposeImpl = false;
800
801
if (!disposed) {
802
synchronized (this) {
803
if (!disposed) {
804
disposed = call_disposeImpl = true;
805
}
806
}
807
}
808
809
if (call_disposeImpl) {
810
disposeImpl();
811
}
812
}
813
814
/**
815
* Returns null.
816
*
817
* @see java.awt.im.spi.InputMethod#getControlObject
818
*/
819
public Object getControlObject() {
820
return null;
821
}
822
823
/**
824
* @see java.awt.im.spi.InputMethod#removeNotify
825
*/
826
public synchronized void removeNotify() {
827
dispose();
828
}
829
830
/**
831
* @see java.awt.im.spi.InputMethod#setCompositionEnabled(boolean)
832
*/
833
public void setCompositionEnabled(boolean enable) {
834
/* If the composition state is successfully changed, set
835
the savedCompositionState to 'enable'. Otherwise, simply
836
return.
837
setCompositionEnabledNative may throw UnsupportedOperationException.
838
Don't try to catch it since the method may be called by clients.
839
Use package private mthod 'resetCompositionState' if you want the
840
exception to be caught.
841
*/
842
if (setCompositionEnabledNative(enable)) {
843
savedCompositionState = enable;
844
}
845
}
846
847
/**
848
* @see java.awt.im.spi.InputMethod#isCompositionEnabled
849
*/
850
public boolean isCompositionEnabled() {
851
/* isCompositionEnabledNative may throw UnsupportedOperationException.
852
Don't try to catch it since this method may be called by clients.
853
Use package private method 'getCompositionState' if you want the
854
exception to be caught.
855
*/
856
return isCompositionEnabledNative();
857
}
858
859
/**
860
* Ends any input composition that may currently be going on in this
861
* context. Depending on the platform and possibly user preferences,
862
* this may commit or delete uncommitted text. Any changes to the text
863
* are communicated to the active component using an input method event.
864
*
865
* <p>
866
* A text editing component may call this in a variety of situations,
867
* for example, when the user moves the insertion point within the text
868
* (but outside the composed text), or when the component's text is
869
* saved to a file or copied to the clipboard.
870
*
871
*/
872
public void endComposition() {
873
if (disposed) {
874
return;
875
}
876
877
/* Before calling resetXIC, record the current composition mode
878
so that it can be restored later. */
879
savedCompositionState = getCompositionState();
880
boolean active = haveActiveClient();
881
if (active && composedText == null && committedText == null){
882
needResetXIC = true;
883
needResetXICClient = new WeakReference<>(getClientComponent());
884
return;
885
}
886
887
String text = resetXIC();
888
/* needResetXIC is only set to true for active client. So passive
889
client should not reset the flag to false. */
890
if (active) {
891
needResetXIC = false;
892
}
893
894
// Remove any existing composed text by posting an InputMethodEvent
895
// with null composed text. It would be desirable to wait for a
896
// dispatchComposedText call from X input method engine, but some
897
// input method does not conform to the XIM specification and does
898
// not call the preedit callback to erase preedit text on calling
899
// XmbResetIC. To work around this problem, do it here by ourselves.
900
awtLock();
901
composedText = null;
902
postInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
903
null,
904
0,
905
null,
906
null);
907
908
if (text != null && text.length() > 0) {
909
dispatchCommittedText(text);
910
}
911
awtUnlock();
912
913
// Restore the preedit state if it was enabled
914
if (savedCompositionState) {
915
resetCompositionState();
916
}
917
}
918
919
/**
920
* Returns a string with information about the current input method server, or null.
921
* On both Linux & SunOS, the value of environment variable XMODIFIERS is
922
* returned if set. Otherwise, on SunOS, $HOME/.dtprofile will be parsed
923
* to find out the language service engine (atok or wnn) since there is
924
* no API in Xlib which returns the information of native
925
* IM server or language service and we want to try our best to return as much
926
* information as possible.
927
*
928
* Note: This method could return null on Linux if XMODIFIERS is not set properly or
929
* if any IOException is thrown.
930
* See man page of XSetLocaleModifiers(3X11) for the usgae of XMODIFIERS,
931
* atok12setup(1) and wnn6setup(1) for the information written to
932
* $HOME/.dtprofile when you run these two commands.
933
*
934
*/
935
public String getNativeInputMethodInfo() {
936
String xmodifiers = System.getenv("XMODIFIERS");
937
String imInfo = null;
938
939
// If XMODIFIERS is set, return the value
940
if (xmodifiers != null) {
941
int imIndex = xmodifiers.indexOf("@im=");
942
if (imIndex != -1) {
943
imInfo = xmodifiers.substring(imIndex + 4);
944
}
945
} else if (System.getProperty("os.name").startsWith("SunOS")) {
946
File dtprofile = new File(System.getProperty("user.home") +
947
"/.dtprofile");
948
String languageEngineInfo = null;
949
try {
950
BufferedReader br = new BufferedReader(new FileReader(dtprofile));
951
String line = null;
952
953
while ( languageEngineInfo == null && (line = br.readLine()) != null) {
954
if (line.contains("atok") || line.contains("wnn")) {
955
StringTokenizer tokens = new StringTokenizer(line);
956
while (tokens.hasMoreTokens()) {
957
String token = tokens.nextToken();
958
if (Pattern.matches("atok.*setup", token) ||
959
Pattern.matches("wnn.*setup", token)){
960
languageEngineInfo = token.substring(0, token.indexOf("setup"));
961
break;
962
}
963
}
964
}
965
}
966
967
br.close();
968
} catch(IOException ioex) {
969
// Since this method is provided for internal testing only,
970
// we dump the stack trace for the ease of debugging.
971
ioex.printStackTrace();
972
}
973
974
imInfo = "htt " + languageEngineInfo;
975
}
976
977
return imInfo;
978
}
979
980
981
/**
982
* Performs mapping from an XIM visible feedback value to Java IM highlight.
983
* @return Java input method highlight
984
*/
985
private InputMethodHighlight convertVisualFeedbackToHighlight(int feedback) {
986
InputMethodHighlight highlight;
987
988
switch (feedback) {
989
case XIMUnderline:
990
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
991
break;
992
case XIMReverse:
993
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
994
break;
995
case XIMHighlight:
996
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
997
break;
998
case XIMPrimary:
999
highlight = InputMethodHighlight.UNSELECTED_CONVERTED_TEXT_HIGHLIGHT;
1000
break;
1001
case XIMSecondary:
1002
highlight = InputMethodHighlight.SELECTED_CONVERTED_TEXT_HIGHLIGHT;
1003
break;
1004
case XIMTertiary:
1005
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
1006
break;
1007
default:
1008
highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
1009
break;
1010
}
1011
return highlight;
1012
}
1013
1014
// initial capacity size for string buffer, etc.
1015
private static final int INITIAL_SIZE = 64;
1016
1017
/**
1018
* IntBuffer is an inner class that manipulates an int array and
1019
* provides UNIX file io stream-like programming interfaces to
1020
* access it. (An alternative would be to use ArrayList which may
1021
* be too expensive for the work.)
1022
*/
1023
private final class IntBuffer {
1024
private int[] intArray;
1025
private int size;
1026
private int index;
1027
1028
IntBuffer(int initialCapacity) {
1029
intArray = new int[initialCapacity];
1030
size = 0;
1031
index = 0;
1032
}
1033
1034
void insert(int offset, int[] values) {
1035
int newSize = size + values.length;
1036
if (intArray.length < newSize) {
1037
int[] newIntArray = new int[newSize * 2];
1038
System.arraycopy(intArray, 0, newIntArray, 0, size);
1039
intArray = newIntArray;
1040
}
1041
System.arraycopy(intArray, offset, intArray, offset+values.length,
1042
size - offset);
1043
System.arraycopy(values, 0, intArray, offset, values.length);
1044
size += values.length;
1045
if (index > offset)
1046
index = offset;
1047
}
1048
1049
void remove(int offset, int length) {
1050
if (offset + length != size)
1051
System.arraycopy(intArray, offset+length, intArray, offset,
1052
size - offset - length);
1053
size -= length;
1054
if (index > offset)
1055
index = offset;
1056
}
1057
1058
void replace(int offset, int[] values) {
1059
System.arraycopy(values, 0, intArray, offset, values.length);
1060
}
1061
1062
void removeAll() {
1063
size = 0;
1064
index = 0;
1065
}
1066
1067
void rewind() {
1068
index = 0;
1069
}
1070
1071
int getNext() {
1072
if (index == size)
1073
return -1;
1074
return intArray[index++];
1075
}
1076
1077
void unget() {
1078
if (index != 0)
1079
index--;
1080
}
1081
1082
int getOffset() {
1083
return index;
1084
}
1085
1086
public String toString() {
1087
StringBuffer s = new StringBuffer();
1088
for (int i = 0; i < size;) {
1089
s.append(intArray[i++]);
1090
if (i < size)
1091
s.append(",");
1092
}
1093
return s.toString();
1094
}
1095
}
1096
1097
/*
1098
* Native methods
1099
*/
1100
protected native String resetXIC();
1101
private native void disposeXIC();
1102
private native boolean setCompositionEnabledNative(boolean enable);
1103
private native boolean isCompositionEnabledNative();
1104
private native void turnoffStatusWindow();
1105
}
1106
1107