/******************************************************************************* * Copyright (c) 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package org.eclipse.swt.custom; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.LineAttributes; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.BidiUtil; import org.eclipse.swt.internal.win32.OS; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Caret; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; public class Field3270 extends Canvas { public static final int LANG_ARABIC = 0x01; public static final int LANG_HEBREW = 0x0d; public static final int LANG_ENGLISH = 0x09; public static final char LRO = '\u202d'; private static final char RLO = '\u202e'; private static final char PDF = '\u202c'; private static final String eolStr = "\r\n"; Listener listener; StyledText styledText; int bidiLangCode = 0; int nonBidiLangCode = 0; boolean isPushMode; boolean isWidgetReversed; boolean isAutoPush; int indxPushSegmentStart = -1; int lengthPushSegment = 0; Caret defaultCaret = null; MenuItem rtlMenuItem; MenuItem autopushMenuItem; char[] arrOfIgnoredChars = null; private int prevLength = 0; public Field3270(Composite parent, int style){ super(parent, 0); this.setLayout(new FillLayout()); if ((parent.getLayout() instanceof FillLayout) || (parent.getLayout() instanceof GridLayout)) styledText = new StyledText(this, style); else styledText = new StyledText(parent, style); addBidiSegmentListener(); styledText.removeListener(SWT.KeyDown, styledText.getListeners(SWT.KeyDown)[0]); addListeners(); styledText.setMenu(createContextMenu()); } public void setBidiLang (int lang) { bidiLangCode = lang; } public void setNonBidiLang (int lang) { nonBidiLangCode = lang; } public void setArrOfIgnoredChars(char[] arr){ arrOfIgnoredChars = (char[])arr.clone(); } private void addListeners() { listener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.MouseDown: handleMouseDown(event); break; case SWT.KeyDown: handleKeyDown(event); break; } } }; styledText.addListener(SWT.KeyDown, listener); styledText.addListener(SWT.MouseDown, listener); styledText.addListener(SWT.Show, listener); styledText.addKeyListener(new KeyListener() { public void keyReleased(KeyEvent keyEvent) { if ((keyEvent.keyCode == 'u') && ((keyEvent.stateMask & SWT.CTRL) != 0)){ setPush(true); keyEvent.doit = false; }else if ((keyEvent.keyCode == 'o') && ((keyEvent.stateMask & SWT.CTRL) != 0)) { setPush(false); keyEvent.doit = false; } else if ((keyEvent.keyCode == 't') && ((keyEvent.stateMask & SWT.CTRL) != 0)){ switchAutoPush(); keyEvent.doit = false; } else if (isPushMode && (keyEvent.keyCode == SWT.HOME || keyEvent.keyCode==SWT.END)){ int carPos = getCaretOffset(); setPush(false); setCaretOffset(carPos); } } public void keyPressed(KeyEvent e) { } }); } private Menu createContextMenu(){ Menu menu; if (styledText.getMenu() == null) menu = new Menu(styledText); else menu = styledText.getMenu(); MenuItem copy = new MenuItem(menu, SWT.CASCADE); copy.setText("&Copy"); //$NON-NLS-1$ copy.setAccelerator(SWT.CTRL+'c'); //$NON-NLS-1$ copy.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { copy(); e.doit = false; } }); MenuItem cut = new MenuItem(menu, SWT.CASCADE); cut.setText("Cu&t"); //$NON-NLS-1$ cut.setAccelerator(SWT.CTRL+'x'); //$NON-NLS-1$ cut.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { cut(); e.doit = false; } }); MenuItem paste = new MenuItem(menu, SWT.CASCADE); paste.setText("&Paste"); //$NON-NLS-1$ paste.setAccelerator(SWT.CTRL+'v'); //$NON-NLS-1$ paste.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { paste(); e.doit = false; } }); rtlMenuItem = new MenuItem(menu, SWT.CHECK); rtlMenuItem.setText("Right to Left Reading Order"); //$NON-NLS-1$ rtlMenuItem.setAccelerator(SWT.CTRL | SWT.SHIFT); //$NON-NLS-1$ rtlMenuItem.setData("#PopupMenu"/*menu.getItem(0).getData()*/); rtlMenuItem.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { e = createEventForSwithchDir(e); handleKeyDown(e); styledText.handleKeyUp(e); rtlMenuItem.setSelection(isWidgetReversed); } private Event createEventForSwithchDir(Event e) { e.widget = styledText; e.keyCode = SWT.SHIFT; if (isWidgetReversed) e.keyLocation = SWT.LEFT; else e.keyLocation = SWT.RIGHT; e.stateMask = SWT.CTRL; return e; } }); new MenuItem(menu, SWT.SEPARATOR); autopushMenuItem = new MenuItem(menu, SWT.CHECK); autopushMenuItem.setText("AutoPush [Ctrl+T]"); autopushMenuItem.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { switchAutoPush(); } }); final MenuItem pushon = new MenuItem(menu, SWT.CASCADE/*SWT.CHECK*/); pushon.setText("Push On [Ctrl+U]"); pushon.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { setPush(true); } }); final MenuItem pushoff = new MenuItem(menu, SWT.CASCADE/*SWT.CHECK*/); pushoff.setText("Push Off [Ctrl+O]"); pushoff.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { setPush(false); } }); return menu; } public void copy() { styledText.copy(); } public void cut() { styledText.cut(); } public void paste() { styledText.paste(); } protected void handleMouseDown (Event event){ styledText.handleMouseDown(event); if (isPushMode && !isCaretInsidePushSegment()){ int caretPos = styledText.getCaretOffset(); setPush(false); styledText.setCaretOffset(getUpdatedCaret(caretPos)); } } protected void handleKeyDown(Event event) { if (event.keyCode == SWT.ARROW_RIGHT && isPushMode){ if (!isWidgetReversed && isCursorAtStartPushSegemet()) setPush(false); else if (isWidgetReversed && isCursorAtEndPushSegemet()){ int newCaretPos = indxPushSegmentStart; setPush(false); styledText.setCaretOffset (newCaretPos); } else if (!isWidgetReversed) styledText.invokeAction(ST.COLUMN_PREVIOUS); else styledText.invokeAction(ST.COLUMN_NEXT); } else if (event.keyCode == SWT.ARROW_LEFT && isPushMode){ if (!isWidgetReversed && isCursorAtEndPushSegemet()){ int newCaretPos = indxPushSegmentStart; setPush(false); styledText.setCaretOffset (newCaretPos); } else if (isWidgetReversed && isCursorAtStartPushSegemet()){ setPush(false); } else if (!isWidgetReversed) styledText.invokeAction(ST.COLUMN_NEXT); else styledText.invokeAction(ST.COLUMN_PREVIOUS); }else if (isPushMode && (event.keyCode == SWT.HOME || event.keyCode==SWT.END)){ setPush(false); styledText.handleKeyDown(event); } else if (event.keyCode == SWT.DEL && isPushMode){ if (!isCursorAtEndPushSegemet()){ styledText.handleKeyDown(event); lengthPushSegment--; } }else if (event.keyCode == SWT.BS && isPushMode){ if (!isCursorAtStartPushSegemet()){ styledText.handleKeyDown(event); lengthPushSegment --; }else { setPush(false); styledText.handleKeyDown(event); } }else if ((event.stateMask & SWT.MODIFIER_MASK) == SWT.CTRL && event.keyCode == SWT.SHIFT && BidiUtil.isBidiPlatform()){ if ((!isWidgetReversed && event.keyLocation == SWT.RIGHT) || (isWidgetReversed && event.keyLocation == SWT.LEFT)){ switchWidgetDir(); styledText.handleKeyDown(event); } } else if (isPushMode && (event.character == SWT.CR) ) { setPush(false); styledText.handleKeyDown(event); } else { if (isAutoPush && event.character >= ' ' && event.character != SWT.DEL){ handleAutoPush(); } if ((arrOfIgnoredChars != null) && ((new String(arrOfIgnoredChars)).indexOf(event.character) != -1)) return; styledText.handleKeyDown(event); } } private boolean isCursorAtStartPushSegemet() { int caretOffset = styledText.getCaretOffset(); if (isCaretAtTheLAstLine()) //it is the last line of string caretOffset --; //pushSegmentStart was stored when string had similar to widget orientation, therefore it needs to be 'mirrored' for comparison int mirroredBoundPosition = calculateMirroredPushSegmentStart(); if (mirroredBoundPosition == (caretOffset-1)) return true; return false; } private boolean isCursorAtEndPushSegemet() { int caretOffset = styledText.getCaretOffset(); if (isCaretAtTheLAstLine()) caretOffset --; //pushSegmentStart was stored when string had similar to widget orientation, therefore it needs to be 'mirrored' for comparison int mirroredBoundPosition = calculateMirroredPushSegmentEnd(); if (mirroredBoundPosition == caretOffset) return true; return false; } private int calculateMirroredCaretPosition(int caretPos) { LineIndx lineIndx = new LineIndx(styledText.getText(),styledText.getCaretOffset()); return lineIndx.getEndIndx() - (caretPos - lineIndx.getStartIndx()); } private int calculateMirroredPushSegmentEnd() { return calculateMirroredCaretPosition(indxPushSegmentStart); } private int calculateMirroredPushSegmentStart() { return calculateMirroredPushSegmentEnd() - lengthPushSegment; } private boolean isCaretAtTheLAstLine(){ LineIndx lineIndx = new LineIndx(styledText.getText(),styledText.getCaretOffset()); if (lineIndx.getEndIndx() == styledText.getText().length()-1) return true; return false; } private boolean isCaretInsidePushSegment(){ int mirroredPushSegmentEnd = calculateMirroredPushSegmentEnd(); int mirroredPushSegmentStart = calculateMirroredPushSegmentStart(); int caretPos = styledText.getCaretOffset(); if ((caretPos >= mirroredPushSegmentStart) && (caretPos <= mirroredPushSegmentEnd)) return true; return false; } protected void switchAutoPush() { isAutoPush = !isAutoPush; autopushMenuItem.setSelection(isAutoPush); } public boolean isPushMode(){ return isPushMode; } private void switchWidgetDir() { isWidgetReversed = !isWidgetReversed; int carPos = styledText.getCaretOffset(); styledText.setText(reverseStr(styledText.getText())); styledText.setCaretOffset (carPos); rtlMenuItem.setSelection(isWidgetReversed); if (isPushMode) setPush(false); if (isWidgetReversed) setBidiKeyboardLanguage(); else setNonBidiKeyboardLanguage(); } public void setPush(boolean pushOn) { if (isPushMode == pushOn) return; isPushMode = pushOn; if (pushOn) { startPushMode(); } else { endPushMode(); indxPushSegmentStart = -1; lengthPushSegment = 0; } } private void endPushMode() { styledText.setCaret(defaultCaret); styledText.defaultCaret = defaultCaret; styledText.caretDirection = SWT.NULL; styledText.setText(reverseStr(styledText.getText())); styledText.setCaretOffset (indxPushSegmentStart + lengthPushSegment); if (!isWidgetReversed) setNonBidiKeyboardLanguage(); else setBidiKeyboardLanguage(); } public static String reverseStr (String str) { String resultStr = ""; String orgStr = new String(str); int i=-1; while ((i = orgStr.indexOf(eolStr)) != -1){ StringBuffer sb = new StringBuffer(orgStr.substring(0,i)); resultStr += sb.reverse() + eolStr; orgStr = orgStr.substring(i + eolStr.length()); } if (orgStr.length()>0){ StringBuffer sb = new StringBuffer(orgStr); resultStr += sb.reverse(); } return resultStr; } public void addBidiSegmentListener() { styledText.addBidiSegmentListener(new BidiSegmentListener() { public void lineGetSegments(BidiSegmentEvent event) { int length = event.lineText.length(); if ((isPushMode && !isWidgetReversed) || (!isPushMode && isWidgetReversed)){ event.segments = new int[] { 0}; event.segmentsChars = new char[] { RLO}; } else { event.segments = new int[] { 0, length }; event.segmentsChars = new char[] { LRO, PDF }; } if (isPushMode && (indxPushSegmentStart != -1) && (prevLength< length)) lengthPushSegment++; prevLength = length; } }); } protected void startPushMode() { final Image image = new Image (styledText.getDisplay(), 20, 20); GC gc = new GC (image); gc.setBackground (styledText.getDisplay().getSystemColor(SWT.COLOR_BLACK)); gc.fillRectangle (0, 0, 20, 20); gc.setForeground (styledText.getDisplay().getSystemColor(SWT.COLOR_WHITE)); gc.setLineAttributes(new LineAttributes(2)); gc.drawLine (0, 13, gc.getFontMetrics().getAverageCharWidth(), 13); gc.dispose (); defaultCaret = styledText.defaultCaret; styledText.getCaret().setImage (image); styledText.defaultCaret = null; int carOffset = styledText.getCaretOffset(); String str = reverseStr(styledText.getText()); styledText.setText(str); indxPushSegmentStart = carOffset; styledText.setCaretOffset(getUpdatedCaret(carOffset)); if (!isWidgetReversed) setBidiKeyboardLanguage(); else setNonBidiKeyboardLanguage(); } private int getUpdatedCaret(int carOffset) { String str = styledText.getText(); LineIndx lineIndx = new LineIndx(str, carOffset); int starIndx = lineIndx.getStartIndx(); int endIndx = lineIndx.getEndIndx(); if (endIndx == str.length()-1) //it is the last line of string carOffset --; return starIndx + (endIndx - carOffset); } private void handleAutoPush(){ if ((isPushMode && !isWidgetReversed && (BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_NON_BIDI))|| (isPushMode && isWidgetReversed && (BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI))) setPush(false); else if ((!isPushMode && !isWidgetReversed && (BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_BIDI)) || (!isPushMode && isWidgetReversed && (BidiUtil.getKeyboardLanguage() == BidiUtil.KEYBOARD_NON_BIDI))) setPush(true); } public boolean isWidgetReversed() { return isWidgetReversed; } private void setBidiKeyboardLanguage(){ if (bidiLangCode == 0) BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_BIDI); else setSpecificKeyboardLanguage(bidiLangCode); } private void setNonBidiKeyboardLanguage(){ if (nonBidiLangCode == 0) BidiUtil.setKeyboardLanguage(BidiUtil.KEYBOARD_NON_BIDI); else setSpecificKeyboardLanguage(nonBidiLangCode); } public static boolean setSpecificKeyboardLanguage(int langCode){ int currentLang = OS.PRIMARYLANGID(OS.LOWORD(OS.GetKeyboardLayout(0))); if (currentLang == langCode) return true; int [] list = getKeyboardLanguageList(); for (int i=0; i