1 /* 2 * Copyright (c) 2010, 2015, 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 package com.sun.glass.ui.gtk; 26 27 import com.sun.glass.ui.Pixels; 28 import com.sun.glass.ui.View; 29 import java.nio.Buffer; 30 import java.nio.ByteBuffer; 31 import java.nio.IntBuffer; 32 import java.util.ArrayList; 33 import java.util.Map; 34 35 final class GtkView extends View { 36 37 private boolean imEnabled = false; 38 private boolean isInPreeditMode = false; 39 private final StringBuilder preedit = new StringBuilder(); 40 private ByteBuffer attributes; 41 private int lastCaret; 42 43 private native void enableInputMethodEventsImpl(long ptr, boolean enable); 44 45 @Override 46 protected void _enableInputMethodEvents(long ptr, boolean enable) { 47 enableInputMethodEventsImpl(ptr, enable); 48 if (imEnabled) { 49 preedit.setLength(0); 50 } 51 imEnabled = enable; 52 } 53 54 @Override 55 protected int _getNativeFrameBuffer(long ptr) { 56 return 0; 57 } 58 59 @Override 60 protected native long _create(Map caps); 61 62 @Override 63 protected native long _getNativeView(long ptr); 64 65 @Override 66 protected native int _getX(long ptr); 67 68 @Override 69 protected native int _getY(long ptr); 70 71 @Override 72 protected native void _setParent(long ptr, long parentPtr); 73 74 @Override 75 protected native boolean _close(long ptr); 76 77 @Override 78 protected native void _scheduleRepaint(long ptr); 79 80 @Override 81 protected void _begin(long ptr) {} 82 83 @Override 84 protected void _end(long ptr) {} 85 86 @Override 87 protected void _uploadPixels(long ptr, Pixels pixels) { 88 Buffer data = pixels.getPixels(); 89 if (data.isDirect() == true) { 90 _uploadPixelsDirect(ptr, data, pixels.getWidth(), pixels.getHeight()); 91 } else if (data.hasArray() == true) { 92 if (pixels.getBytesPerComponent() == 1) { 93 ByteBuffer bytes = (ByteBuffer)data; 94 _uploadPixelsByteArray(ptr, bytes.array(), bytes.arrayOffset(), pixels.getWidth(), pixels.getHeight()); 95 } else { 96 IntBuffer ints = (IntBuffer)data; 97 _uploadPixelsIntArray(ptr, ints.array(), ints.arrayOffset(), pixels.getWidth(), pixels.getHeight()); 98 } 99 } else { 100 // gznote: what are the circumstances under which this can happen? 101 _uploadPixelsDirect(ptr, pixels.asByteBuffer(), pixels.getWidth(), pixels.getHeight()); 102 } 103 } 104 private native void _uploadPixelsDirect(long viewPtr, Buffer pixels, int width, int height); 105 private native void _uploadPixelsByteArray(long viewPtr, byte[] pixels, int offset, int width, int height); 106 private native void _uploadPixelsIntArray(long viewPtr, int[] pixels, int offset, int width, int height); 107 108 @Override 109 protected native boolean _enterFullscreen(long ptr, boolean animate, boolean keepRatio, boolean hideCursor); 110 111 @Override 112 protected native void _exitFullscreen(long ptr, boolean animate); 113 114 @Override 115 protected void _finishInputMethodComposition(long ptr) { 116 if (imEnabled && isInPreeditMode) { 117 // Discard any pre-edited text 118 preedit.setLength(0); 119 notifyInputMethod(preedit.toString(), null, null, null, 0, 0, 0); 120 } 121 } 122 123 private void notifyPreeditMode(boolean enabled){ 124 isInPreeditMode = enabled; 125 } 126 127 128 protected void notifyInputMethodDraw(String text, int first, int length, int caret, byte[] attr) { 129 int[] boundary = null; 130 byte[] values = null; 131 132 if (attributes == null ) { 133 attributes = ByteBuffer.allocate(32); 134 } 135 136 if (length > 0) { 137 preedit.replace(first, first + length, ""); 138 } 139 140 if (text != null) { 141 preedit.insert(first, text); 142 } else { 143 if (attr == null) { 144 preedit.setLength(0); 145 } 146 } 147 148 if (attributes.capacity() < preedit.length()) { 149 ByteBuffer tmp = ByteBuffer.allocate((int) (preedit.length() * 1.5)); 150 tmp.put(attributes); 151 attributes = tmp; 152 } 153 154 attributes.limit(preedit.length()); 155 156 if (attr != null && attributes.limit() >= (first + attr.length)) { 157 attributes.position(first); 158 attributes.put(attr); 159 } 160 161 if (attributes.limit() > 0) { 162 ArrayList<Integer> boundaryList = new ArrayList<>(); 163 ArrayList<Byte> valuesList = new ArrayList<>(); 164 attributes.rewind(); 165 byte lastAttribute = attributes.get(); 166 167 boundaryList.add(0); 168 valuesList.add(lastAttribute); 169 170 int i = 1; 171 while (attributes.hasRemaining()) { 172 byte a = attributes.get(); 173 if (lastAttribute != a) { 174 boundaryList.add(i); 175 valuesList.add(a); 176 } 177 lastAttribute = a; 178 i++; 179 } 180 181 boundaryList.add(attributes.limit()); 182 183 boundary = new int[boundaryList.size()]; 184 i = 0; 185 for (Integer e : boundaryList) { 186 boundary[i++] = e; 187 } 188 189 values = new byte[valuesList.size()]; 190 i = 0; 191 for (Byte e: valuesList) { 192 values[i++] = e; 193 } 194 } 195 196 notifyInputMethod(preedit.toString(), boundary, boundary, values, 0, caret, 0); 197 lastCaret = caret; 198 } 199 200 protected void notifyInputMethodCaret(int pos, int direction, int style) { 201 switch (direction) { 202 case 0: //XIMForwardChar 203 lastCaret += pos; 204 break; 205 case 1: //XIMBackwardChar 206 lastCaret -= pos; 207 break; 208 case 10: //XIMAbsolute 209 lastCaret = pos; 210 break; 211 default: 212 //TODO: as we don't know the text structure, we cannot compute the position 213 // for other directions (like forward words, lines, etc...). 214 // Luckily, vast majority of IM uses XIMAbsolute (10) 215 } 216 notifyInputMethod(preedit.toString(), null, null, null, 0, lastCaret, 0); 217 } 218 }